git: 681a04dbda0e - main - pf tests: verify we accept exactly one hop-by-hop header
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Thu, 08 May 2025 13:10:41 UTC
The branch main has been updated by kp: URL: https://cgit.FreeBSD.org/src/commit/?id=681a04dbda0e06cccf2ecc1ab8cac1e9e2be78d2 commit 681a04dbda0e06cccf2ecc1ab8cac1e9e2be78d2 Author: Kristof Provost <kp@FreeBSD.org> AuthorDate: 2025-05-06 10:07:23 +0000 Commit: Kristof Provost <kp@FreeBSD.org> CommitDate: 2025-05-08 13:10:25 +0000 pf tests: verify we accept exactly one hop-by-hop header Sponsored by: Rubicon Communications, LLC ("Netgate") --- tests/sys/netpfil/pf/frag6.py | 90 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) diff --git a/tests/sys/netpfil/pf/frag6.py b/tests/sys/netpfil/pf/frag6.py index c9a71f73c0cf..de14fec528fd 100644 --- a/tests/sys/netpfil/pf/frag6.py +++ b/tests/sys/netpfil/pf/frag6.py @@ -97,6 +97,96 @@ class TestFrag6(VnetTestTemplate): sp.send(pkts, inter = 0.1) +class TestFrag6HopHyHop(VnetTestTemplate): + REQUIRED_MODULES = ["pf"] + TOPOLOGY = { + "vnet1": {"ifaces": ["if1", "if2"]}, + "vnet2": {"ifaces": ["if1", "if2"]}, + "if1": {"prefixes6": [("2001:db8::1/64", "2001:db8::2/64")]}, + "if2": {"prefixes6": [("2001:db8:666::1/64", "2001:db8:1::2/64")]}, + } + + def vnet2_handler(self, vnet): + ifname = vnet.iface_alias_map["if1"].name + ToolsHelper.print_output("/sbin/sysctl net.inet6.ip6.forwarding=1") + ToolsHelper.print_output("/usr/sbin/ndp -s 2001:db8:1::1 00:01:02:03:04:05") + ToolsHelper.print_output("/sbin/pfctl -e") + ToolsHelper.print_output("/sbin/pfctl -x loud") + ToolsHelper.pf_rules([ + "scrub fragment reassemble min-ttl 10", + "pass", + ]) + + @pytest.mark.require_user("root") + @pytest.mark.require_progs(["scapy"]) + def test_hop_by_hop(self): + "Verify that we reject non-first hop-by-hop headers" + if1 = self.vnet.iface_alias_map["if1"].name + if2 = self.vnet.iface_alias_map["if2"].name + ToolsHelper.print_output("/sbin/route add -6 default 2001:db8::2") + ToolsHelper.print_output("/sbin/ping6 -c 1 2001:db8:1::2") + + # Import in the correct vnet, so at to not confuse Scapy + import scapy.all as sp + + # A hop-by-hop header is accepted if it's the first header + pkt = sp.IPv6(src="2001:db8::1", dst="2001:db8:1::1") \ + / sp.IPv6ExtHdrHopByHop() \ + / sp.ICMPv6EchoRequest(data=sp.raw(bytes.fromhex('f0') * 30)) + pkt.show() + + # Delay the send so the sniffer is running when we transmit. + s = DelayedSend(pkt) + + replies = sp.sniff(iface=if2, timeout=3) + found = False + for p in replies: + p.show() + ip6 = p.getlayer(sp.IPv6) + hbh = p.getlayer(sp.IPv6ExtHdrHopByHop) + icmp6 = p.getlayer(sp.ICMPv6EchoRequest) + + if not ip6 or not icmp6: + continue + assert ip6.src == "2001:db8::1" + assert ip6.dst == "2001:db8:1::1" + assert hbh + assert icmp6 + found = True + assert found + + # A hop-by-hop header causes the packet to be dropped if it's not the + # first extension header + pkt = sp.IPv6(src="2001:db8::1", dst="2001:db8:1::1") \ + / sp.IPv6ExtHdrFragment(offset=0, m=0) \ + / sp.IPv6ExtHdrHopByHop() \ + / sp.ICMPv6EchoRequest(data=sp.raw(bytes.fromhex('f0') * 30)) + pkt2 = sp.IPv6(src="2001:db8::1", dst="2001:db8:1::1") \ + / sp.ICMPv6EchoRequest(data=sp.raw(bytes.fromhex('f0') * 30)) + + # Delay the send so the sniffer is running when we transmit. + ToolsHelper.print_output("/sbin/ping6 -c 1 2001:db8:1::2") + + s = DelayedSend([ pkt2, pkt ]) + replies = sp.sniff(iface=if2, timeout=10) + found = False + for p in replies: + # Expect to find the packet without the hop-by-hop header, not the + # one with + p.show() + ip6 = p.getlayer(sp.IPv6) + hbh = p.getlayer(sp.IPv6ExtHdrHopByHop) + icmp6 = p.getlayer(sp.ICMPv6EchoRequest) + + if not ip6 or not icmp6: + continue + assert ip6.src == "2001:db8::1" + assert ip6.dst == "2001:db8:1::1" + assert not hbh + assert icmp6 + found = True + assert found + class TestFrag6_Overlap(VnetTestTemplate): REQUIRED_MODULES = ["pf"] TOPOLOGY = {