From aabb0d4db10b08bf6b6243194fcc28e47389b534 Mon Sep 17 00:00:00 2001 From: Tomek Mrugalski Date: Sat, 15 Mar 2025 08:03:52 +0100 Subject: [PATCH] RFC9686 support added This adds 2 new DHCPv6 messages and 1 new DHCPv6 option - ADDR-REG-INFORM message added - ADDR-REG-REPLY message added - ADDR-REG option added - Flake8 appeased - Unit-tests added --- scapy/layers/dhcp6.py | 37 ++++++++++++++++++++-- test/scapy/layers/dhcp6.uts | 61 +++++++++++++++++++++++++++++++++++++ 2 files changed, 96 insertions(+), 2 deletions(-) diff --git a/scapy/layers/dhcp6.py b/scapy/layers/dhcp6.py index f8eef019574..84737991dff 100644 --- a/scapy/layers/dhcp6.py +++ b/scapy/layers/dhcp6.py @@ -57,7 +57,10 @@ def get_cls(name, fallback_cls): 10: "DHCP6_Reconf", 11: "DHCP6_InfoRequest", 12: "DHCP6_RelayForward", - 13: "DHCP6_RelayReply"} + 13: "DHCP6_RelayReply", + 36: "DHCP6_AddrRegInform", + 37: "DHCP6_AddrRegReply", + } def _dhcp6_dispatcher(x, *args, **kargs): @@ -128,6 +131,7 @@ def _dhcp6_dispatcher(x, *args, **kargs): 79: "OPTION_CLIENT_LINKLAYER_ADDR", # RFC6939 103: "OPTION_CAPTIVE_PORTAL", # RFC8910 112: "OPTION_MUD_URL", # RFC8520 + 148: "OPTION_ADDR_REG_ENABLE", # RFC9686 } dhcp6opts_by_code = {1: "DHCP6OptClientId", @@ -187,10 +191,12 @@ def _dhcp6_dispatcher(x, *args, **kargs): 79: "DHCP6OptClientLinkLayerAddr", # RFC6939 103: "DHCP6OptCaptivePortal", # RFC8910 112: "DHCP6OptMudUrl", # RFC8520 + 148: "DHCP6OptAddrRegEnable", # RFC9686 } # sect 7.3 RFC 8415 : DHCP6 Messages types +# also RFC 9686 dhcp6types = {1: "SOLICIT", 2: "ADVERTISE", 3: "REQUEST", @@ -203,7 +209,10 @@ def _dhcp6_dispatcher(x, *args, **kargs): 10: "RECONFIGURE", 11: "INFORMATION-REQUEST", 12: "RELAY-FORW", - 13: "RELAY-REPL"} + 13: "RELAY-REPL", + 36: "ADDR-REG-INFORM", + 37: "ADDR-REG-REPLY", + } ##################################################################### @@ -1103,6 +1112,12 @@ class DHCP6OptMudUrl(_DHCP6OptGuessPayload): # RFC8520 )] +class DHCP6OptAddrRegEnable(_DHCP6OptGuessPayload): # RFC 9686 sect 4.1 + name = "DHCP6 Address Registration Option" + fields_desc = [ShortEnumField("optcode", 148, dhcp6opts), + ShortField("optlen", 0)] + + ##################################################################### # DHCPv6 messages # ##################################################################### @@ -1437,6 +1452,24 @@ def answers(self, other): self.peeraddr == other.peeraddr) +##################################################################### +# Address Registration-Inform Message (RFC 9686) +# - sent by clients who generated their own address and need it registered + +class DHCP6_AddrRegInform(DHCP6): + name = "DHCPv6 Information Request Message" + msgtype = 36 + +##################################################################### +# Address Registration-Reply Message (RFC 9686) +# - sent by servers who respond to the address registration-inform message + + +class DHCP6_AddrRegReply(DHCP6): + name = "DHCPv6 Information Reply Message" + msgtype = 37 + + bind_bottom_up(UDP, _dhcp6_dispatcher, {"dport": 547}) bind_bottom_up(UDP, _dhcp6_dispatcher, {"dport": 546}) diff --git a/test/scapy/layers/dhcp6.uts b/test/scapy/layers/dhcp6.uts index 9aebfe4c94f..cb87561f730 100644 --- a/test/scapy/layers/dhcp6.uts +++ b/test/scapy/layers/dhcp6.uts @@ -1310,6 +1310,19 @@ p = DHCP6OptVSS(s) assert p.type == 255 +############ +############ ++ Test DHCP6 Option - Address Registration Enabled + += DHCP6OptAddrRegEnable - Basic Instantiation +raw(DHCP6OptAddrRegEnable()) == b'\x00\x94\x00\x00' + += DHCP6OptAddrRegEnable - Basic Dissection +a=DHCP6OptAddrRegEnable(b'\x00\x94\x00\x00') +a.optcode == 148 and a.optlen == 0 + + + ############ ############ + Test DHCP6 Messages - DHCP6_Solicit @@ -1564,4 +1577,52 @@ raw(DHCP6_RelayReply()) == b'\r\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ a=DHCP6_RelayReply(b'\r\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') a.msgtype == 13 and a.hopcount == 0 and a.linkaddr == "::" and a.peeraddr == "::" +############ +############ ++ Test DHCP6 Messages - DHCP6_AddrRegInform + += DHCP6_AddrRegInform - Basic Instantiation +raw(DHCP6_AddrRegInform()) == b'\x24\x00\x00\x00' + += DHCP6_AddrRegInform - Basic Dissection +a = DHCP6_AddrRegInform(b'\x24\x00\x00\x00') +a.msgtype == 36 and a.trid == 0 + += DHCP6_AddrRegInform - Basic test of DHCP6_addrreginform.hashret() +DHCP6_AddrRegInform().hashret() == b'\x00\x00\x00' + += DHCP6_AddrRegInform - Test of DHCP6_addrreginform.hashret() with specific values +DHCP6_AddrRegInform(trid=0xbbccdd).hashret() == b'\xbb\xcc\xdd' + += DHCP6_AddrRegInform - UDP ports overload +a=UDP()/DHCP6_AddrRegInform() +a.sport == 546 and a.dport == 547 + += DHCP6_AddrRegInform - Dispatch based on UDP port +a=UDP(raw(UDP()/DHCP6_AddrRegInform())) +isinstance(a.payload, DHCP6_AddrRegInform) + +############ +############ ++ Test DHCP6 Messages - DHCP6_AddrRegReply + += DHCP6_AddrRegReply - Basic Instantiation +raw(DHCP6_AddrRegReply()) == b'\x25\x00\x00\x00' + += DHCP6_AddrRegReply - Basic Dissection +a = DHCP6_AddrRegReply(b'\x25\x00\x00\x00') +a.msgtype == 37 and a.trid == 0 + += DHCP6_AddrRegReply - Basic test of DHCP6_addrregreply.hashret() +DHCP6_AddrRegReply().hashret() == b'\x00\x00\x00' + += DHCP6_AddrRegReply - Test of DHCP6_addrregreply.hashret() with specific values +DHCP6_AddrRegReply(trid=0xbbccdd).hashret() == b'\xbb\xcc\xdd' + += DHCP6_AddrRegReply - UDP ports overload +a=UDP()/DHCP6_AddrRegReply() +a.sport == 546 and a.dport == 547 += DHCP6_AddrRegReply - Dispatch based on UDP port +a=UDP(raw(UDP()/DHCP6_AddrRegReply())) +isinstance(a.payload, DHCP6_AddrRegReply)