diff --git a/scapy/layers/inet.py b/scapy/layers/inet.py index 2486ba0515b..02f474426b1 100644 --- a/scapy/layers/inet.py +++ b/scapy/layers/inet.py @@ -843,14 +843,22 @@ def post_build(self, p, pay): tmp_len = len(p) p = p[:4] + struct.pack("!H", tmp_len) + p[6:] if self.chksum is None: + # A Padding layer that belongs to an encapsulated protocol (for + # instance Ethernet over VXLAN) is data as far as UDP is + # concerned. The len field tells how many of the following + # padding bytes are inside the datagram, the remaining ones + # being padding of an under layer. + ck_p = p + if tmp_len > len(p): + ck_p += self.payload.build_padding()[:tmp_len - len(p)] if isinstance(self.underlayer, IP): - ck = in4_chksum(socket.IPPROTO_UDP, self.underlayer, p) + ck = in4_chksum(socket.IPPROTO_UDP, self.underlayer, ck_p) # According to RFC768 if the result checksum is 0, it should be set to 0xFFFF # noqa: E501 if ck == 0: ck = 0xFFFF p = p[:6] + struct.pack("!H", ck) + p[8:] elif isinstance(self.underlayer, scapy.layers.inet6.IPv6) or isinstance(self.underlayer, scapy.layers.inet6._IPv6ExtHdr): # noqa: E501 - ck = scapy.layers.inet6.in6_chksum(socket.IPPROTO_UDP, self.underlayer, p) # noqa: E501 + ck = scapy.layers.inet6.in6_chksum(socket.IPPROTO_UDP, self.underlayer, ck_p) # noqa: E501 # According to RFC2460 if the result checksum is 0, it should be set to 0xFFFF # noqa: E501 if ck == 0: ck = 0xFFFF diff --git a/test/scapy/layers/inet.uts b/test/scapy/layers/inet.uts index 06d49630721..80a11c4a013 100644 --- a/test/scapy/layers/inet.uts +++ b/test/scapy/layers/inet.uts @@ -543,6 +543,216 @@ assert bpkt.chksum == 0x70bd and bpkt.payload.chksum == 0xbb17 conf.debug_dissector = True += UDP checksum covers the padding of an encapsulated layer +~ udp +# The Padding of an inner Ethernet frame is data for the outer UDP layer. +# GH#4906: Ether / Dot1Q / IPv6 / UDP / VXLAN / Dot3 / LLC / data / Padding +pkt_data = bytes.fromhex("ffffffffffffffffffffffff8100000086dd60000000010e1100000000000000000000000000000000000000000000000000000000000000000018bd2118010e01a8738a32362d5473c879f89f16a8f9cd673fec8d6b000d9a2976054be71268024a7817f64d63fad1d4eb2db918f004c198dc03e7ec4e836ca554e1b4f0c157c61728ae722bd55eac05d698f04a0df32b8d21f031ae70a7ad530d027915763af0e736fa6d61a334289b3aef64855b44ed09f02de48485a3337575d409561fe192bca02214a8a5b10cc66c756992eadaeaf56f8eeeb58c18cce9f36fd8983ff7890dedc28ed254080c180231440188d5afc32bff2193a8f94a1b683b1e850a613eaf42ccd37e5af706d93b11e31649a1b9f39c613ce5a4263fcfe7873da6fdd9d09c90f15b687efc3342189cbac70ab686d1dc5f000015128c4d3400080000034584f14d8e217811d6cee444") +pkt = Ether(pkt_data) +assert pkt[UDP].chksum == 0x1a8 +del pkt[UDP].chksum +assert Ether(raw(pkt))[UDP].chksum == 0x1a8 + += UDP checksum covers the padding of an encapsulated layer - IPv4 +~ udp +inner = raw(Dot3(dst="11:22:33:44:55:66", src="aa:bb:cc:dd:ee:ff", len=7)/LLC(dsap=0x06, ssap=0x06, ctrl=3)/Raw(b"abcd")) + b"PPPPPPPP" +pkt = IP(raw(IP(src="1.2.3.4", dst="5.6.7.8")/UDP(sport=1111, dport=4789)/VXLAN(flags=0x08, vni=42)/Raw(inner))) +assert isinstance(pkt[LLC].payload.payload, Padding) +assert pkt[UDP].chksum == 0x94fb +del pkt[UDP].chksum +assert IP(raw(pkt))[UDP].chksum == 0x94fb + += UDP checksum covers the padding of an encapsulated layer - IPv6 +~ udp +inner = raw(Dot3(dst="11:22:33:44:55:66", src="aa:bb:cc:dd:ee:ff", len=7)/LLC(dsap=0x06, ssap=0x06, ctrl=3)/Raw(b"abcd")) + b"PPPPPPPP" +pkt = IPv6(raw(IPv6(src="2001:db8::1", dst="2001:db8::2")/UDP(sport=1111, dport=4789)/VXLAN(flags=0x08, vni=42)/Raw(inner))) +assert pkt[UDP].chksum == 0x499a +del pkt[UDP].chksum +assert IPv6(raw(pkt))[UDP].chksum == 0x499a + += UDP checksum ignores the padding of an under layer (control) +~ udp +# The trailer of the outer Ethernet frame is not part of the datagram +pkt = Ether(raw(Ether(dst="00:11:22:33:44:55", src="66:77:88:99:aa:bb")/IP(src="1.2.3.4", dst="5.6.7.8")/UDP(sport=1, dport=2)/Raw(b"ab")) + b"\x00" * 16) +assert pkt[UDP].len == 10 and isinstance(pkt[Raw].payload, Padding) +del pkt[UDP].chksum +assert Ether(raw(pkt))[UDP].chksum == 0x8e61 + += UDP checksum with a len field larger than the available data (control) +~ udp +with no_debug_dissector(): + pkt = IP(raw(IP()/UDP(len=100)/Raw(b"AAAA"))) + +assert pkt[UDP].chksum == 0x7e8f + += UDP checksum with a len field smaller than the payload (control) +~ udp +with no_debug_dissector(): + pkt = IP(raw(IP()/UDP(len=10)/Raw(b"AAAAAAAAAAAA"))) + +assert pkt[UDP].chksum == 0x79dc + += UDP checksum covers the inner padding but not the trailer of the outer frame +~ udp +# Both kinds of padding at once: the inner Ethernet frame is padded inside the +# datagram, the outer Ethernet frame has a trailer outside of it. +inner = raw(Dot3(dst="11:22:33:44:55:66", src="aa:bb:cc:dd:ee:ff", len=7)/LLC(dsap=0x06, ssap=0x06, ctrl=3)/Raw(b"abcd")) + b"PPPPPPPP" +pkt = Ether(raw(Ether(dst="00:11:22:33:44:55", src="66:77:88:99:aa:bb")/IP(src="1.2.3.4", dst="5.6.7.8")/UDP(sport=1111, dport=4789)/VXLAN(flags=0x08, vni=42)/Raw(inner)) + b"TTTTTTTTTTTTTTTT") +assert pkt[UDP].len == 45 and len([x for x in pkt.iterpayloads() if isinstance(x, Padding)]) == 2 +del pkt[UDP].chksum +assert Ether(raw(pkt))[UDP].chksum == 0x94fb + += UDP checksum covers the padding of an encapsulated layer - IPv6 extension header +~ udp +inner = raw(Dot3(dst="11:22:33:44:55:66", src="aa:bb:cc:dd:ee:ff", len=7)/LLC(dsap=0x06, ssap=0x06, ctrl=3)/Raw(b"abcd")) + b"PPPPPPPP" +pkt = IPv6(raw(IPv6(src="2001:db8::1", dst="2001:db8::2")/IPv6ExtHdrDestOpt()/UDP(sport=1111, dport=4789)/VXLAN(flags=0x08, vni=42)/Raw(inner))) +assert isinstance(pkt[UDP].underlayer, IPv6ExtHdrDestOpt) +assert pkt[UDP].chksum == 0x499a +del pkt[UDP].chksum +assert IPv6(raw(pkt))[UDP].chksum == 0x499a + += UDP checksum with a len field covering only a part of the padding +~ udp +inner = raw(Dot3(dst="11:22:33:44:55:66", src="aa:bb:cc:dd:ee:ff", len=7)/LLC(dsap=0x06, ssap=0x06, ctrl=3)/Raw(b"abcd")) + b"PPPPPPPP" +pkt = IP(raw(IP(src="1.2.3.4", dst="5.6.7.8")/UDP(sport=1111, dport=4789)/VXLAN(flags=0x08, vni=42)/Raw(inner))) +# One single byte of the padding is inside the datagram +pkt[UDP].len = 38 +del pkt[UDP].chksum +with no_debug_dissector(): + assert IP(raw(pkt))[UDP].chksum == 0xd5f3 + += UDP checksum with a len field covering only a part of the padding - IPv6 +~ udp +# in6_chksum takes the length of the pseudo header from the buffer it is +# given, where in4_chksum uses the one of the IP layer: both arms of the +# dispatch need the padding bytes that the len field puts in the datagram. +inner = raw(Dot3(dst="11:22:33:44:55:66", src="aa:bb:cc:dd:ee:ff", len=7)/LLC(dsap=0x06, ssap=0x06, ctrl=3)/Raw(b"abcd")) + b"PPPPPPPP" +pkt = IPv6(raw(IPv6(src="2001:db8::1", dst="2001:db8::2")/UDP(sport=1111, dport=4789)/VXLAN(flags=0x08, vni=42)/Raw(inner))) +pkt[UDP].len = 38 +del pkt[UDP].chksum +with no_debug_dissector(): + assert IPv6(raw(pkt))[UDP].chksum == 0x8a99 + += UDP checksum with a len field claiming more padding than the capture holds +~ udp +inner = raw(Dot3(dst="11:22:33:44:55:66", src="aa:bb:cc:dd:ee:ff", len=7)/LLC(dsap=0x06, ssap=0x06, ctrl=3)/Raw(b"abcd")) + b"PPPPPPPP" +pkt = IP(raw(IP(src="1.2.3.4", dst="5.6.7.8")/UDP(sport=1111, dport=4789)/VXLAN(flags=0x08, vni=42)/Raw(inner))) +pkt[UDP].len = 49 +del pkt[UDP].chksum +with no_debug_dissector(): + assert IP(raw(pkt))[UDP].chksum == 0x94f7 + += UDP checksum with a len field claiming more padding than the capture holds - IPv6 +~ udp +inner = raw(Dot3(dst="11:22:33:44:55:66", src="aa:bb:cc:dd:ee:ff", len=7)/LLC(dsap=0x06, ssap=0x06, ctrl=3)/Raw(b"abcd")) + b"PPPPPPPP" +pkt = IPv6(raw(IPv6(src="2001:db8::1", dst="2001:db8::2")/UDP(sport=1111, dport=4789)/VXLAN(flags=0x08, vni=42)/Raw(inner))) +pkt[UDP].len = 49 +del pkt[UDP].chksum +with no_debug_dissector(): + assert IPv6(raw(pkt))[UDP].chksum == 0x4996 + += UDP checksum with a len field below the payload while padding follows (control) +~ udp +# The len field is not an authority to remove data, only to add padding: the +# checksum must be the one of the payload that is built. +inner = raw(Dot3(dst="11:22:33:44:55:66", src="aa:bb:cc:dd:ee:ff", len=7)/LLC(dsap=0x06, ssap=0x06, ctrl=3)/Raw(b"abcd")) + b"PPPPPPPP" +pkt = IP(raw(IP(src="1.2.3.4", dst="5.6.7.8")/UDP(sport=1111, dport=4789)/VXLAN(flags=0x08, vni=42)/Raw(inner))) +pkt[UDP].len = 36 +del pkt[UDP].chksum +with no_debug_dissector(): + assert IP(raw(pkt))[UDP].chksum == 0xd645 + += UDP checksum with a len field one short of the whole padding +~ udp +# One byte of the inner padding is left out of the datagram by the len field. +inner = raw(Dot3(dst="11:22:33:44:55:66", src="aa:bb:cc:dd:ee:ff", len=7)/LLC(dsap=0x06, ssap=0x06, ctrl=3)/Raw(b"abcd")) + b"PPPPPPPP" +pkt = IP(raw(IP(src="1.2.3.4", dst="5.6.7.8")/UDP(sport=1111, dport=4789)/VXLAN(flags=0x08, vni=42)/Raw(inner))) +pkt[UDP].len = 44 +del pkt[UDP].chksum +with no_debug_dissector(): + assert IP(raw(pkt))[UDP].chksum == 0xe4fc + += UDP checksum with a len field one short of the whole padding - IPv6 +~ udp +inner = raw(Dot3(dst="11:22:33:44:55:66", src="aa:bb:cc:dd:ee:ff", len=7)/LLC(dsap=0x06, ssap=0x06, ctrl=3)/Raw(b"abcd")) + b"PPPPPPPP" +pkt = IPv6(raw(IPv6(src="2001:db8::1", dst="2001:db8::2")/UDP(sport=1111, dport=4789)/VXLAN(flags=0x08, vni=42)/Raw(inner))) +pkt[UDP].len = 44 +del pkt[UDP].chksum +with no_debug_dissector(): + assert IPv6(raw(pkt))[UDP].chksum == 0x999c + += UDP checksum with the maximum len field +~ udp +# 0xffff is the largest value the field holds: the slice bound is far past the +# padding, which build_padding() then returns whole. +inner = raw(Dot3(dst="11:22:33:44:55:66", src="aa:bb:cc:dd:ee:ff", len=7)/LLC(dsap=0x06, ssap=0x06, ctrl=3)/Raw(b"abcd")) + b"PPPPPPPP" +pkt = IP(raw(IP(src="1.2.3.4", dst="5.6.7.8")/UDP(sport=1111, dport=4789)/VXLAN(flags=0x08, vni=42)/Raw(inner))) +pkt[UDP].len = 65535 +del pkt[UDP].chksum +with no_debug_dissector(): + assert IP(raw(pkt))[UDP].chksum == 0x9528 + += UDP checksum with the maximum len field - IPv6 +~ udp +inner = raw(Dot3(dst="11:22:33:44:55:66", src="aa:bb:cc:dd:ee:ff", len=7)/LLC(dsap=0x06, ssap=0x06, ctrl=3)/Raw(b"abcd")) + b"PPPPPPPP" +pkt = IPv6(raw(IPv6(src="2001:db8::1", dst="2001:db8::2")/UDP(sport=1111, dport=4789)/VXLAN(flags=0x08, vni=42)/Raw(inner))) +pkt[UDP].len = 65535 +del pkt[UDP].chksum +with no_debug_dissector(): + assert IPv6(raw(pkt))[UDP].chksum == 0x49c7 + += UDP checksum with a len field covering only a part of the padding - IPv6 extension header +~ udp +# The boundary lengths on the second operand of the under layer dispatch: an +# extension header derives the pseudo header the same way IPv6 does. +inner = raw(Dot3(dst="11:22:33:44:55:66", src="aa:bb:cc:dd:ee:ff", len=7)/LLC(dsap=0x06, ssap=0x06, ctrl=3)/Raw(b"abcd")) + b"PPPPPPPP" +pkt = IPv6(raw(IPv6(src="2001:db8::1", dst="2001:db8::2")/IPv6ExtHdrDestOpt()/UDP(sport=1111, dport=4789)/VXLAN(flags=0x08, vni=42)/Raw(inner))) +pkt[UDP].len = 38 +del pkt[UDP].chksum +with no_debug_dissector(): + assert IPv6(raw(pkt))[UDP].chksum == 0x8a99 + += UDP checksum with a len field reaching into the trailer of the outer frame +~ udp +# build_padding() walks every following Padding layer, so a len field larger +# than the inner padding keeps taking bytes from the outer trailer. +inner = raw(Dot3(dst="11:22:33:44:55:66", src="aa:bb:cc:dd:ee:ff", len=7)/LLC(dsap=0x06, ssap=0x06, ctrl=3)/Raw(b"abcd")) + b"PPPPPPPP" +pkt = Ether(raw(Ether(dst="00:11:22:33:44:55", src="66:77:88:99:aa:bb")/IP(src="1.2.3.4", dst="5.6.7.8")/UDP(sport=1111, dport=4789)/VXLAN(flags=0x08, vni=42)/Raw(inner)) + b"TTTTTTTTTTTTTTTT") +assert pkt[UDP].payload.build_padding() == b"PPPPPPPP" + b"T" * 16 +pkt[UDP].len = 53 +del pkt[UDP].chksum +with no_debug_dissector(): + assert Ether(raw(pkt))[UDP].chksum == 0x43a2 + += UDP checksum with a len field past every padding layer available +~ udp +inner = raw(Dot3(dst="11:22:33:44:55:66", src="aa:bb:cc:dd:ee:ff", len=7)/LLC(dsap=0x06, ssap=0x06, ctrl=3)/Raw(b"abcd")) + b"PPPPPPPP" +pkt = Ether(raw(Ether(dst="00:11:22:33:44:55", src="66:77:88:99:aa:bb")/IP(src="1.2.3.4", dst="5.6.7.8")/UDP(sport=1111, dport=4789)/VXLAN(flags=0x08, vni=42)/Raw(inner)) + b"TTTTTTTTTTTTTTTT") +pkt[UDP].len = 70 +del pkt[UDP].chksum +with no_debug_dissector(): + assert Ether(raw(pkt))[UDP].chksum == 0xf23f + += UDP checksum with a len field equal to the payload while padding follows (control) +~ udp +# tmp_len == len(p) leaves the guard false: the padding stays out of the +# checksum, as it does for a packet built from scratch. +inner = raw(Dot3(dst="11:22:33:44:55:66", src="aa:bb:cc:dd:ee:ff", len=7)/LLC(dsap=0x06, ssap=0x06, ctrl=3)/Raw(b"abcd")) + b"PPPPPPPP" +pkt = IP(raw(IP(src="1.2.3.4", dst="5.6.7.8")/UDP(sport=1111, dport=4789)/VXLAN(flags=0x08, vni=42)/Raw(inner))) +pkt[UDP].len = 37 +del pkt[UDP].chksum +with no_debug_dissector(): + assert IP(raw(pkt))[UDP].chksum == 0xd644 + += UDP checksum of a packet built from scratch with an explicit Padding (control) +~ udp +# Nothing changes when post_build() computes the len field itself: tmp_len is +# then len(p) and the padding is outside of the datagram. +pkt = IP(src="1.2.3.4", dst="5.6.7.8")/UDP(sport=1111, dport=4789)/VXLAN(flags=0x08, vni=42)/Dot3(dst="11:22:33:44:55:66", src="aa:bb:cc:dd:ee:ff", len=7)/LLC(dsap=0x06, ssap=0x06, ctrl=3)/Raw(b"abcd")/Padding(b"PPPPPPPP") +bpkt = IP(raw(pkt)) +assert bpkt[UDP].len == 37 +assert bpkt[UDP].chksum == 0xd64c + = IP with forced-length 0 p = IP()/TCP() p[IP].len = 0