Skip to content

Commit c424205

Browse files
authored
DHCPv4: add the Forcerenew Nonce Protocol Capability Option (#4630)
https://www.rfc-editor.org/rfc/rfc6704.html#section-3.1.1 ``` The FORCERENEW_NONCE_CAPABLE option contains code 145, length n, and a sequence of algorithms the client supports: Code Len Algorithms +-----+-----+----+----+----+ | 145 | n | A1 | A2 | A3 | .... +-----+-----+----+----+----+ ``` The `_DHCPParamReqFieldListField` class was renamed because the format of the Parameter Request List option is the same in the sense that it's just a sequence of bytes so it can be reused to implement the Forcerenew Nonce Protocol Capability option as well to make fuzz() work. The patch was also tested with Wireshark: ``` >>> tdecode(Ether()/IP()/UDP()/BOOTP()/DHCP(options=[('forcerenew_nonce_capable', ['HMAC-MD5', 2, 3])])) ... Option: (145) Forcerenew Nonce Capable Length: 3 Algorithm: HMAC-MD5 (1) Algorithm: Unknown (2) Algorithm: Unknown (3) ```
1 parent a28d74a commit c424205

File tree

2 files changed

+11
-6
lines changed

2 files changed

+11
-6
lines changed

scapy/layers/dhcp.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -115,12 +115,12 @@ def answers(self, other):
115115
return self.xid == other.xid
116116

117117

118-
class _DHCPParamReqFieldListField(FieldListField):
118+
class _DHCPByteFieldListField(FieldListField):
119119
def randval(self):
120-
class _RandReqFieldList(RandField):
120+
class _RandByteFieldList(RandField):
121121
def _fix(self):
122122
return [RandByte()] * int(RandByte())
123-
return _RandReqFieldList()
123+
return _RandByteFieldList()
124124

125125

126126
class RandClasslessStaticRoutesField(RandField):
@@ -277,7 +277,7 @@ def randval(self):
277277
52: ByteField("dhcp-option-overload", 100),
278278
53: ByteEnumField("message-type", 1, DHCPTypes),
279279
54: IPField("server_id", "0.0.0.0"),
280-
55: _DHCPParamReqFieldListField(
280+
55: _DHCPByteFieldListField(
281281
"param_req_list", [],
282282
ByteField("opcode", 0)),
283283
56: "error_message",
@@ -337,6 +337,9 @@ def randval(self):
337337
137: "v4-lost",
338338
138: IPField("capwap-ac-v4", "0.0.0.0"),
339339
141: "sip_ua_service_domains",
340+
145: _DHCPByteFieldListField(
341+
"forcerenew_nonce_capable", [],
342+
ByteEnumField("algorithm", 1, {1: "HMAC-MD5"})),
340343
146: "rdnss-selection",
341344
150: IPField("tftp_server_address", "0.0.0.0"),
342345
159: "v4-portparams",

test/scapy/layers/dhcp.uts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,8 @@ assert s3 == b'E\x00\x01=\x00\x01\x00\x00@\x11{\xad\x7f\x00\x00\x01\x7f\x00\x00\
4747
s4 = raw(IP(src="127.0.0.1")/UDP()/BOOTP(chaddr="00:01:02:03:04:05")/DHCP(options=[("mud-url", "https://example.org"), ("captive-portal", "https://example.com"), ("ipv6-only-preferred", 0xffffffff), "end"]))
4848
assert s4 == b"E\x00\x01=\x00\x01\x00\x00@\x11{\xad\x7f\x00\x00\x01\x7f\x00\x00\x01\x00C\x00D\x01)L\xd7\x01\x01\x06\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0000:01:02:03:04:0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00c\x82Sc\xa1\x13https://example.orgr\x13https://example.com\x6c\x04\xff\xff\xff\xff\xff"
4949

50-
s5 = raw(IP(src="127.0.0.1")/UDP()/BOOTP(chaddr="00:01:02:03:04:05")/DHCP(options=[("classless_static_routes", "192.168.123.4/32:10.0.0.1", "169.254.254.0/24:10.0.1.2"), ("rapid_commit", b""), "end"]))
51-
assert s5 == b'E\x00\x01"\x00\x01\x00\x00@\x11{\xc8\x7f\x00\x00\x01\x7f\x00\x00\x01\x00C\x00D\x01\x0e\xaa\xfd\x01\x01\x06\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0000:01:02:03:04:0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00c\x82Scy\x11 \xc0\xa8{\x04\n\x00\x00\x01\x18\xa9\xfe\xfe\n\x00\x01\x02P\x00\xff'
50+
s5 = raw(IP(src="127.0.0.1")/UDP()/BOOTP(chaddr="00:01:02:03:04:05")/DHCP(options=[("classless_static_routes", "192.168.123.4/32:10.0.0.1", "169.254.254.0/24:10.0.1.2"), ("rapid_commit", b""), ("forcerenew_nonce_capable", [1, "HMAC-MD5"]), "end"]))
51+
assert s5 == b'E\x00\x01&\x00\x01\x00\x00@\x11{\xc4\x7f\x00\x00\x01\x7f\x00\x00\x01\x00C\x00D\x01\x12\xa7c\x01\x01\x06\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0000:01:02:03:04:0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00c\x82Scy\x11 \xc0\xa8{\x04\n\x00\x00\x01\x18\xa9\xfe\xfe\n\x00\x01\x02P\x00\x91\x02\x01\x01\xff'
5252

5353
= DHCP - fuzz
5454

@@ -87,13 +87,15 @@ p5 = IP(s5)
8787
assert DHCP in p5
8888
assert p5[DHCP].options[0] == ("classless_static_routes", ["192.168.123.4/32:10.0.0.1", "169.254.254.0/24:10.0.1.2"])
8989
assert p5[DHCP].options[1] == ("rapid_commit", b"")
90+
assert p5[DHCP].options[2] == ("forcerenew_nonce_capable", [1, 1])
9091

9192
repr(DHCP(b"\x01\x00"))
9293
assert DHCP(b"\x01\x00").options == [b"\x01\x00"]
9394
assert DHCP(b"\x28\x00").options == [("NIS_domain", b"")]
9495
assert DHCP(b"\x37\x00").options == [("param_req_list", [])]
9596
assert DHCP(b"\x50\x00").options == [("rapid_commit", b"")]
9697
assert DHCP(b"\x79\x00").options == [("classless_static_routes", [])]
98+
assert DHCP(b"\x91\x00").options == [("forcerenew_nonce_capable", [])]
9799
assert DHCP(b"\x01\x0C\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b").options == [("subnet_mask", "0.1.2.3", "4.5.6.7", "8.9.10.11")]
98100

99101
b = b"\x79\x01\xff"

0 commit comments

Comments
 (0)