Skip to content

Commit 3e69007

Browse files
PPP: fix default size of protocol field (#4106)
In commit 2f5d9bd ("PPP: protocol field can be limited to one byte"), a new class PPP_ was added to manage parsing and generation a PPP header with a one byte PPP protocol field. This was later reworked by commit 834309f ("Small doc cleanups"), which removed the PPP_ class, and changed the default behavior of the PPP class to generate a one byte protocol field by default, when its value was lower than 0x100. The RFC states that "by default, all implementations MUST transmit packets with two octet PPP Protocol fields". A header with a one byte protocol field is issued by implementations when the compression is negociated. This patch reverts to the original behavior, which is to generate a two bytes protocol field by default, but make it possible to explicitly generate a one byte protocol by passing the value as bytes(). The PPP class is still able to parse either a one or two bytes protocol. Link: https://www.rfc-editor.org/rfc/rfc1661.html#section-6.5 Fixes #3913
1 parent e454990 commit 3e69007

File tree

2 files changed

+33
-7
lines changed

2 files changed

+33
-7
lines changed

scapy/layers/ppp.py

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -292,6 +292,14 @@ class _PPPProtoField(EnumField):
292292
293293
See RFC 1661 section 2
294294
<https://tools.ietf.org/html/rfc1661#section-2>
295+
296+
The generated proto field is two bytes when not specified, or when specified
297+
as an integer or a string:
298+
PPP()
299+
PPP(proto=0x21)
300+
PPP(proto="Internet Protocol version 4")
301+
To explicitly forge a one byte proto field, use the bytes representation:
302+
PPP(proto=b'\x21')
295303
"""
296304
def getfield(self, pkt, s):
297305
if ord(s[:1]) & 0x01:
@@ -304,12 +312,18 @@ def getfield(self, pkt, s):
304312
return super(_PPPProtoField, self).getfield(pkt, s)
305313

306314
def addfield(self, pkt, s, val):
307-
if val < 0x100:
308-
self.fmt = "!B"
309-
self.sz = 1
315+
if isinstance(val, bytes):
316+
if len(val) == 1:
317+
fmt, sz = "!B", 1
318+
elif len(val) == 2:
319+
fmt, sz = "!H", 2
320+
else:
321+
raise TypeError('Invalid length for PPP proto')
322+
val = struct.Struct(fmt).unpack(val)[0]
310323
else:
311-
self.fmt = "!H"
312-
self.sz = 2
324+
fmt, sz = "!H", 2
325+
self.fmt = fmt
326+
self.sz = sz
313327
self.struct = struct.Struct(self.fmt)
314328
return super(_PPPProtoField, self).addfield(pkt, s, val)
315329

test/scapy/layers/ppp.uts

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -112,12 +112,24 @@ assert raw(p) == raw(q)
112112
assert q[PPP_ECP_Option].data == b"ABCDEFG"
113113

114114

115-
= PPP with only one byte for protocol
115+
= PPP IP check that default protocol length is 2 bytes
116+
~ ppp ip
117+
118+
p = PPP()/IP()
119+
p
120+
r = raw(p)
121+
r
122+
assert r.startswith(b'\x00\x21')
123+
assert len(r) == 22
124+
125+
126+
= PPP check parsing with only one byte for protocol
116127
~ ppp
117128

118-
assert len(raw(PPP() / IP())) == 21
129+
assert len(raw(PPP(proto=b'\x21') / IP())) == 21
119130

120131
p = PPP(b'!E\x00\x00<\x00\x00@\x008\x06\xa5\xce\x85wP)\xc0\xa8Va\x01\xbbd\x8a\xe2}r\xb8O\x95\xb5\x84\xa0\x12q \xc8\x08\x00\x00\x02\x04\x02\x18\x04\x02\x08\nQ\xdf\xd6\xb0\x00\x07LH\x01\x03\x03\x07Ao')
121132
assert IP in p
122133
assert TCP in p
123134

135+
assert PPP(b"\x00\x21" + raw(IP())) == PPP(b"\x21" + raw(IP()))

0 commit comments

Comments
 (0)