From 65ef3c8d61d6f685463ab9eb874de81aecc74652 Mon Sep 17 00:00:00 2001 From: Guillaume Valadon Date: Mon, 5 Oct 2026 21:00:23 +0200 Subject: [PATCH] Fix ICMPv6 RFC4884 extension length handling. AI-Assisted: yes (GPT-5.5) --- scapy/layers/inet6.py | 28 ++++++++++++++++++++++++++-- test/scapy/layers/inet6.uts | 5 +++++ 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/scapy/layers/inet6.py b/scapy/layers/inet6.py index 36eb3d9cf75..9c861cb4488 100644 --- a/scapy/layers/inet6.py +++ b/scapy/layers/inet6.py @@ -65,6 +65,7 @@ _ICMPExtensionField, _ICMPExtensionPadField, _ICMP_extpad_post_dissection, + ICMPExtension_Header, IP, IPTools, TCP, @@ -1448,6 +1449,14 @@ class _ICMPv6(Packet): def post_build(self, p, pay): p += pay + if (getattr(self, "ext", None) is not None and + getattr(self, "length", 0) == 0): + # RFC 4884: padded original datagram length, in 64-bit words. + datagram_len = len(p) - len(raw(self.ext)) - 8 + if datagram_len > 0: + length = datagram_len // 8 + if length <= 255: + p = p[:4] + chb(length) + p[5:] if self.cksum is None: chksum = in6_chksum(58, self.underlayer, p) p = p[:2] + struct.pack("!H", chksum) + p[4:] @@ -1475,6 +1484,21 @@ def guess_payload_class(self, p): return IPerror6 +class _ICMPv6ExtensionField(_ICMPExtensionField): + + def getfield(self, pkt, s): + if pkt.length: + # RFC 4884: the extension starts after length * 8 bytes. + offset = 8 * pkt.length + if len(s) <= offset: + return s, None + data = s[offset:] + if checksum(data): + return s, None + return s[:offset], ICMPExtension_Header(data) + return super(_ICMPv6ExtensionField, self).getfield(pkt, s) + + class ICMPv6Unknown(_ICMPv6): name = "Scapy6 ICMPv6 fallback class" fields_desc = [ByteEnumField("type", 1, icmp6types), @@ -1497,7 +1521,7 @@ class ICMPv6DestUnreach(_ICMPv6Error): ByteField("length", 0), X3BytesField("unused", 0), _ICMPExtensionPadField(), - _ICMPExtensionField()] + _ICMPv6ExtensionField()] post_dissection = _ICMP_extpad_post_dissection @@ -1518,7 +1542,7 @@ class ICMPv6TimeExceeded(_ICMPv6Error): ByteField("length", 0), X3BytesField("unused", 0), _ICMPExtensionPadField(), - _ICMPExtensionField()] + _ICMPv6ExtensionField()] post_dissection = _ICMP_extpad_post_dissection diff --git a/test/scapy/layers/inet6.uts b/test/scapy/layers/inet6.uts index 8fc0e99ef50..2d093bc002b 100644 --- a/test/scapy/layers/inet6.uts +++ b/test/scapy/layers/inet6.uts @@ -368,6 +368,11 @@ a[ICMPv6PacketTooBig][TCPerror].chksum == b.chksum # To be done but not critical. Same mechanisms and format as # previous ones. += ICMPv6TimeExceeded Class - RFC4884 extension length +pkt = Ether()/IPv6()/ICMPv6TimeExceeded(ext=ICMPExtension_Header())/IPv6()/ICMPv6EchoRequest() +decoded = Ether(raw(pkt)) +decoded[ICMPv6TimeExceeded].length == 16 and isinstance(decoded[ICMPv6TimeExceeded].ext, ICMPExtension_Header) + ########### ICMPv6ParamProblem Class ################################ # See previous note