From a61c2617f1894c1e09297334160a1ddbe9f32652 Mon Sep 17 00:00:00 2001 From: Kristof Provost Date: Wed, 8 Jan 2025 14:34:22 +0100 Subject: pf: drop IPv6 packets built from overlapping fragments in pf reassembly The reassembly state will be dropped after timeout, all related fragments are dropped until that. This is conforming to RFC 5722. - Sort pf_fragment fields while there. - If the fr_queue is empty, we had overlapping fragments, don't add new ones. - If we detect overlapping IPv6 fragments, flush the fr_queue and drop all fragments immediately. - Rearrange debug output, to make clear what happens. - An IPv4 fragment that is totaly overlapped does not inclease the bad fragment counter. - Put an KASSERT into pf_isfull_fragment() to make sure that the fr_queue is never emtpy there. discussed with Fernando Gont; ok henning@ Obtained from: OpenBSD, bluhm , 8b45f36762 Sponsored by: Rubicon Communications, LLC ("Netgate") (cherry picked from commit 6a3266f72e437aecf3edcfb8aa919466b270d548) --- tests/sys/netpfil/pf/frag6.py | 44 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) (limited to 'tests') diff --git a/tests/sys/netpfil/pf/frag6.py b/tests/sys/netpfil/pf/frag6.py index 28b1829d418c..f54381fba8cb 100644 --- a/tests/sys/netpfil/pf/frag6.py +++ b/tests/sys/netpfil/pf/frag6.py @@ -58,3 +58,47 @@ class TestFrag6(VnetTestTemplate): timeout=3) for p in packets: assert not p.getlayer(sp.ICMPv6EchoReply) + +class TestFrag6_Overlap(VnetTestTemplate): + REQUIRED_MODULES = ["pf"] + TOPOLOGY = { + "vnet1": {"ifaces": ["if1"]}, + "vnet2": {"ifaces": ["if1"]}, + "if1": {"prefixes6": [("2001:db8::1/64", "2001:db8::2/64")]}, + } + + def vnet2_handler(self, vnet): + ToolsHelper.print_output("/sbin/pfctl -e") + ToolsHelper.print_output("/sbin/pfctl -x loud") + ToolsHelper.pf_rules([ + "scrub fragment reassemble", + "pass", + ]) + + @pytest.mark.require_user("root") + def test_overlap(self): + "Ensure we discard packets with overlapping fragments" + + # Import in the correct vnet, so at to not confuse Scapy + import scapy.all as sp + + packet = sp.IPv6(src="2001:db8::1", dst="2001:db8::2") \ + / sp.ICMPv6EchoRequest(data=sp.raw(bytes.fromhex('f00f') * 90)) + frags = sp.fragment6(packet, 128) + assert len(frags) == 3 + + f = frags[0].getlayer(sp.IPv6ExtHdrFragment) + # Fragment with overlap + overlap = sp.IPv6(src="2001:db8::1", dst="2001:db8::2") \ + / sp.IPv6ExtHdrFragment(offset = 4, m = 1, id = f.id, nh = f.nh) \ + / sp.raw(bytes.fromhex('f00f') * 4) + frags = [ frags[0], frags[1], overlap, frags[2] ] + + # Delay the send so the sniffer is running when we transmit. + s = DelayedSend(frags) + + packets = sp.sniff(iface=self.vnet.iface_alias_map["if1"].name, + timeout=3) + for p in packets: + p.show() + assert not p.getlayer(sp.ICMPv6EchoReply) -- cgit v1.2.3