diff --git a/scapy/layers/inet.py b/scapy/layers/inet.py index da98ab018c7..02e4774e665 100644 --- a/scapy/layers/inet.py +++ b/scapy/layers/inet.py @@ -1018,9 +1018,15 @@ def __init__(self): def getfield(self, pkt, s): # RFC4884 section 5.2 says if the ICMP packet length # is >144 then ICMP extensions start at byte 137. + length = getattr(pkt, "length", 0) or 0 if len(pkt.original) < 144: + if not length: + return s, None + offset = length * 8 + else: + offset = 136 + len(s) - len(pkt.original) + if offset > len(s): return s, None - offset = 136 + len(s) - len(pkt.original) data = s[offset:] # Validate checksum if checksum(data) == data[3:5]: @@ -1061,6 +1067,25 @@ def _ICMP_extpad_post_dissection(self, pkt): pkt.extpad = pad.load +def _icmp4884_prepare_ext_build(pkt, p, pay, length_offset): + # RFC4884: set length and pad original datagram when extensions are used + if pkt.ext is None: + return p, pay + if pkt.extpad in (None, b"", ""): + padding_index = pay.rindex(bytes(pkt.ext)) + payload_len = len(pay[:padding_index]) + padding_len = (8 - payload_len % 8) % 8 + if payload_len + padding_len < 128: + padding_len = 128 - payload_len + padding = b"\x00" * padding_len + pay = pay[:padding_index] + padding + pay[padding_index:] + if pkt.length in (None, 0): + ext_index = pay.rindex(bytes(pkt.ext)) + length = len(pay[:ext_index]) // 8 + p = p[:length_offset] + chb(length) + p[length_offset + 1:] + return p, pay + + icmptypes = {0: "echo-reply", 3: "dest-unreach", 4: "source-quench", diff --git a/scapy/layers/inet6.py b/scapy/layers/inet6.py index 4645a5e1344..a67e95c5141 100644 --- a/scapy/layers/inet6.py +++ b/scapy/layers/inet6.py @@ -62,6 +62,7 @@ XShortField, ) from scapy.layers.inet import ( + _icmp4884_prepare_ext_build, _ICMPExtensionField, _ICMPExtensionPadField, _ICMP_extpad_post_dissection, @@ -1510,6 +1511,10 @@ class ICMPv6TimeExceeded(_ICMPv6Error): _ICMPExtensionField()] post_dissection = _ICMP_extpad_post_dissection + def post_build(self, p, pay): + p, pay = _icmp4884_prepare_ext_build(self, p, pay, length_offset=4) + return _ICMPv6.post_build(self, p, pay) + # The default pointer value is set to the next header field of # the encapsulated IPv6 packet diff --git a/test/scapy/layers/inet6.uts b/test/scapy/layers/inet6.uts index eb0079de8cf..1c27c897178 100644 --- a/test/scapy/layers/inet6.uts +++ b/test/scapy/layers/inet6.uts @@ -365,8 +365,27 @@ a[ICMPv6PacketTooBig][TCPerror].chksum == b.chksum ########### ICMPv6TimeExceeded Class ################################ -# To be done but not critical. Same mechanisms and format as -# previous ones. + += ICMPv6TimeExceeded extension - length field build (GH4353) + +pkt = IPv6() / ICMPv6TimeExceeded(ext=ICMPExtension_Header()) / IPv6() / ICMPv6EchoRequest() +raw = bytes(pkt) +pkt2 = IPv6(raw) +assert pkt2[ICMPv6TimeExceeded].length == 16 + += ICMPv6TimeExceeded extension - Ether round-trip (GH4353) + +pkt = Ether() / IPv6() / ICMPv6TimeExceeded(ext=ICMPExtension_Header()) / IPv6() / ICMPv6EchoRequest() +pkt = Ether(bytes(pkt)) +assert pkt[ICMPv6TimeExceeded].length == 16 + += ICMPv6TimeExceeded extension - rebuild stability (GH4353) + +pkt = IPv6() / ICMPv6TimeExceeded(ext=ICMPExtension_Header()) / IPv6() / ICMPv6EchoRequest() +raw = bytes(pkt) +pkt2 = IPv6(raw) +assert isinstance(pkt2[ICMPv6TimeExceeded].ext, ICMPExtension_Header) +assert bytes(pkt2) == raw ########### ICMPv6ParamProblem Class ################################ # See previous note