From bb38bdc1985849a8078011dbf6b834b336f3f605 Mon Sep 17 00:00:00 2001 From: Evgeny Vereshchagin Date: Mon, 28 Aug 2023 08:00:33 +0000 Subject: [PATCH] [INET6] add the PREF64 ND option https://datatracker.ietf.org/doc/html/rfc8781#name-option-format 0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Type | Length | Scaled Lifetime | PLC | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | | + + | Highest 96 bits of the Prefix | + + | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ Figure 1: NAT64 Prefix Option Format Fields: Type: 8-bit identifier of the PREF64 option type (38) Length: 8-bit unsigned integer. The length of the option (including the Type and Length fields) is in units of 8 octets. The sender MUST set the length to 2. The receiver MUST ignore the PREF64 option if the Length field value is not 2. Scaled Lifetime: 13-bit unsigned integer. The maximum time in units of 8 seconds over which this NAT64 prefix MAY be used. PLC (Prefix Length Code): 3-bit unsigned integer. This field encodes the NAT64 Prefix Length defined in [RFC6052]. The PLC field values 0, 1, 2, 3, 4, and 5 indicate the NAT64 prefix length of 96, 64, 56, 48, 40, and 32 bits, respectively. The receiver MUST ignore the PREF64 option if the Prefix Length Code field is not set to one of those values. Highest 96 bits of the Prefix: 96-bit unsigned integer. Contains bits 0 - 95 of the NAT64 prefix. The patch was also cross-checked with Wireshark: ICMPv6 Option (PREF64 Option) Type: PREF64 Option (38) Length: 2 (16 bytes) 0000 0111 0000 1... = Scaled Lifetime: 225 .... .... .... .001 = PLC (Prefix Length Code): 64 bits prefix length (0x1) Prefix: 2003:da8:1:: (It's mostly prompted by https://github.com/systemd/systemd/issues/28985) --- scapy/layers/inet6.py | 23 +++++++++++++++++++++++ test/scapy/layers/inet6.uts | 26 ++++++++++++++++++++++++++ 2 files changed, 49 insertions(+) diff --git a/scapy/layers/inet6.py b/scapy/layers/inet6.py index 2619c828808..0dbb19e0f14 100644 --- a/scapy/layers/inet6.py +++ b/scapy/layers/inet6.py @@ -1720,6 +1720,7 @@ def extract_padding(self, s): 26: "ICMPv6NDOptEFA", 31: "ICMPv6NDOptDNSSL", 37: "ICMPv6NDOptCaptivePortal", + 38: "ICMPv6NDOptPREF64", } icmp6ndraprefs = {0: "Medium (default)", @@ -2087,6 +2088,28 @@ class ICMPv6NDOptCaptivePortal(_ICMPv6NDGuessPayload, Packet): # RFC 8910 def mysummary(self): return self.sprintf("%name% %URI%") + +class _PREF64(IP6Field): + def addfield(self, pkt, s, val): + return s + self.i2m(pkt, val)[:12] + + def getfield(self, pkt, s): + return s[12:], self.m2i(pkt, s[:12] + b"\x00" * 4) + + +class ICMPv6NDOptPREF64(_ICMPv6NDGuessPayload, Packet): # RFC 8781 + name = "ICMPv6 Neighbor Discovery Option - PREF64 Option" + fields_desc = [ByteField("type", 38), + ByteField("len", 2), + BitField("scaledlifetime", 0, 13), + BitEnumField("plc", 0, 3, + ["/96", "/64", "/56", "/48", "/40", "/32"]), + _PREF64("prefix", "::")] + + def mysummary(self): + plc = self.sprintf("%plc%") if self.plc < 6 else f"[invalid PLC({self.plc})]" + return self.sprintf("%name% %prefix%") + plc + # End of ICMPv6 Neighbor Discovery Options. diff --git a/test/scapy/layers/inet6.uts b/test/scapy/layers/inet6.uts index b9f78a0b9ce..3c06be0c117 100644 --- a/test/scapy/layers/inet6.uts +++ b/test/scapy/layers/inet6.uts @@ -1250,6 +1250,32 @@ a=ICMPv6NDOptEFA(b'\x1a\x01\x00\x00\x00\x00\x00\x00') a.type==26 and a.len==1 and a.res == 0 +############ +############ ++ ICMPv6NDOptPREF64 Class Test + += ICMPv6NDOptPREF64 - Basic Instantiation +raw(ICMPv6NDOptPREF64()) == b'\x26\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + += ICMPv6NDOptPREF64 - Basic Dissection +p = ICMPv6NDOptPREF64(b'\x26\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') +assert p.type == 38 and p.len == 2 and p.scaledlifetime == 0 and p.plc == 0 and p.prefix == '::' + += ICMPv6NDOptPREF64 - Instantiation/Dissection with specific values +p = ICMPv6NDOptPREF64(scaledlifetime=225, plc='/64', prefix='2003:da8:1::') +assert raw(p) == b'\x26\x02\x07\x09\x20\x03\x0d\xa8\x00\x01\x00\x00\x00\x00\x00\x00' + +p = ICMPv6NDOptPREF64(raw(p)) +assert p.type == 38 and p.len == 2 and p.scaledlifetime == 225 and p.plc == 1 and p.prefix == '2003:da8:1::' + +p = ICMPv6NDOptPREF64(raw(p) + b'\x00\x00\x00\x00') +assert Raw in p and len(p[Raw]) == 4 + += ICMPv6NDOptPREF64 - Summary Output +ICMPv6NDOptPREF64(prefix='12:34:56::', plc='/32').mysummary() == "ICMPv6 Neighbor Discovery Option - PREF64 Option 12:34:56::/32" +ICMPv6NDOptPREF64(prefix='12:34:56::', plc=6).mysummary() == "ICMPv6 Neighbor Discovery Option - PREF64 Option 12:34:56::[invalid PLC(6)]" + + ############ ############ + Test Node Information Query - ICMPv6NIQueryNOOP