diff --git a/tests/sys/netpfil/pf/Makefile b/tests/sys/netpfil/pf/Makefile index 2edd8e7d2026..8a24fae47dcb 100644 --- a/tests/sys/netpfil/pf/Makefile +++ b/tests/sys/netpfil/pf/Makefile @@ -1,90 +1,94 @@ PACKAGE= tests TESTSDIR= ${TESTSBASE}/sys/netpfil/pf TESTS_SUBDIRS+= ioctl ATF_TESTS_SH+= altq \ anchor \ debug \ divert-to \ dup \ ether \ forward \ fragmentation_compat \ fragmentation_pass \ fragmentation_no_reassembly \ get_state \ icmp \ icmp6 \ if_enc \ limits \ loginterface \ killstate \ macro \ map_e \ match \ max_states \ mbuf \ modulate \ names \ nat \ nat64 \ pass_block \ pflog \ pflow \ pfsync \ prio \ proxy \ rdr \ ridentifier \ route_to \ rtable \ rules_counter \ scrub_compat \ scrub_pass \ sctp \ set_skip \ set_tos \ src_track \ status \ syncookie \ synproxy \ table \ tcp \ tos ATF_TESTS_PYTEST+= frag6.py ATF_TESTS_PYTEST+= icmp.py ATF_TESTS_PYTEST+= nat64.py ATF_TESTS_PYTEST+= nat66.py ATF_TESTS_PYTEST+= sctp.py # Allow tests to run in parallel in their own jails TEST_METADATA+= execenv="jail" TEST_METADATA+= execenv_jail_params="vnet allow.raw_sockets" ${PACKAGE}FILES+= CVE-2019-5597.py \ CVE-2019-5598.py \ daytime_inetd.conf \ echo_inetd.conf \ fragcommon.py \ frag-overindex.py \ frag-overlimit.py \ frag-overreplace.py \ + frag-overhole.py \ + frag-adjhole.py \ pfsync_defer.py \ pft_ether.py \ pft_read_ipfix.py \ rdr-srcport.py \ utils.subr ${PACKAGE}FILESMODE_CVE-2019-5597.py= 0555 ${PACKAGE}FILESMODE_CVE-2019-5598.py= 0555 ${PACKAGE}FILESMODE_fragcommon.py= 0555 ${PACKAGE}FILESMODE_frag-overindex.py= 0555 ${PACKAGE}FILESMODE_frag-overlimit.py= 0555 ${PACKAGE}FILESMODE_frag-overreplace.py= 0555 +${PACKAGE}FILESMODE_frag-overhole.py= 0555 +${PACKAGE}FILESMODE_frag-adjhole.py= 0555 ${PACKAGE}FILESMODE_pfsync_defer.py= 0555 ${PACKAGE}FILESMODE_pft_ether.py= 0555 ${PACKAGE}FILESMODE_pft_read_ipfix.py= 0555 .include diff --git a/tests/sys/netpfil/pf/frag-adjhole.py b/tests/sys/netpfil/pf/frag-adjhole.py new file mode 100644 index 000000000000..99caf66617dd --- /dev/null +++ b/tests/sys/netpfil/pf/frag-adjhole.py @@ -0,0 +1,58 @@ +#!/usr/bin/env python3 +# +# Copyright (c) 2025 Alexander Bluhm + +from fragcommon import * + +# |--------| +# |--------| +# |-------| +# |----| + +def send(src, dst, send_if, recv_if): + pid = os.getpid() + eid = pid & 0xffff + payload = b"ABCDEFGHIJKLMNOP" * 2 + packet = sp.IP(src=src, dst=dst)/ \ + sp.ICMP(type='echo-request', id=eid) / payload + frag = [] + fid = pid & 0xffff + frag.append(sp.IP(src=src, dst=dst, proto=1, id=fid, + flags='MF') / bytes(packet)[20:36]) + frag.append(sp.IP(src=src, dst=dst, proto=1, id=fid, + frag=2, flags='MF') / bytes(packet)[36:52]) + frag.append(sp.IP(src=src, dst=dst, proto=1, id=fid, + frag=1, flags='MF') / bytes(packet)[28:44]) + frag.append(sp.IP(src=src, dst=dst, proto=1, id=fid, + frag=4) / bytes(packet)[52:60]) + eth=[] + for f in frag: + eth.append(sp.Ether()/f) + if os.fork() == 0: + time.sleep(1) + sp.sendp(eth, iface=send_if) + os._exit(0) + + ans = sp.sniff(iface=recv_if, timeout=3, filter= + "ip and src " + dst + " and dst " + src + " and icmp") + for a in ans: + if a and a.type == sp.ETH_P_IP and \ + a.payload.proto == 1 and \ + a.payload.frag == 0 and a.payload.flags == 0 and \ + sp.icmptypes[a.payload.payload.type] == 'echo-reply': + id = a.payload.payload.id + print("id=%#x" % (id)) + if id != eid: + print("WRONG ECHO REPLY ID") + exit(2) + data = a.payload.payload.payload.load + print("payload=%s" % (data)) + if data == payload: + exit(0) + print("PAYLOAD!=%s" % (payload)) + exit(1) + print("NO ECHO REPLY") + exit(2) + +if __name__ == '__main__': + main(send) diff --git a/tests/sys/netpfil/pf/frag-overhole.py b/tests/sys/netpfil/pf/frag-overhole.py new file mode 100644 index 000000000000..91697b6b83c6 --- /dev/null +++ b/tests/sys/netpfil/pf/frag-overhole.py @@ -0,0 +1,83 @@ +#!/usr/bin/env python3 +# +# Copyright (c) 2025 Alexander Bluhm + +from fragcommon import * + +# index boundary 4096 | +# |--------------| +# .... +# |--------------| +# |----------| +# |XXXX----------| +# |XXXX----| +# |---| + +# this should trigger "frag tail overlap %d" and "frag head overlap %d" +def send(src, dst, send_if, recv_if): + pid = os.getpid() + eid = pid & 0xffff + payload = b"ABCDEFGHIJKLMNOP" + dummy = b"01234567" + fragsize = 1024 + boundary = 4096 + fragnum = int(boundary / fragsize) + packet = sp.IP(src=src, dst=dst)/ \ + sp.ICMP(type='echo-request', id=eid)/ \ + ((int((boundary + fragsize) / len(payload)) + 1) * payload) + packet_length = len(packet) + frag = [] + fid = pid & 0xffff + for i in range(fragnum-1): + frag.append(sp.IP(src=src, dst=dst, proto=1, id=fid, + frag=(i * fragsize)>>3, flags='MF')/ + bytes(packet)[20 + i * fragsize:20 + (i + 1) * fragsize]) + frag.append(sp.IP(src=src, dst=dst, proto=1, id=fid, + frag=(boundary - fragsize) >> 3, flags='MF')/ + bytes(packet)[20 + boundary - fragsize:20 + boundary - len(dummy)]) + frag.append(sp.IP(src=src, dst=dst, proto=1, id=fid, + frag=(boundary - len(dummy)) >> 3, flags='MF')/ + (dummy+bytes(packet)[20 + boundary:20 + boundary + fragsize])) + frag.append(sp.IP(src=src, dst=dst, proto=1, id=fid, + frag=(boundary - 8 - len(dummy)) >> 3, flags='MF')/ + (dummy+bytes(packet)[20 + boundary - 8:20 + boundary])) + frag.append(sp.IP(src=src, dst=dst, proto=1, id=fid, + frag=(boundary + fragsize) >> 3)/bytes(packet)[20 + boundary + fragsize:]) + eth=[] + for f in frag: + eth.append(sp.Ether() / f) + + if os.fork() == 0: + time.sleep(1) + for e in eth: + sp.sendp(e, iface=send_if) + time.sleep(0.001) + os._exit(0) + + ans = sp.sniff(iface=recv_if, timeout=3, filter= + "ip and src " + dst + " and dst " + src + " and icmp") + for a in ans: + if a and a.type == sp.ETH_P_IP and \ + a.payload.proto == 1 and \ + a.payload.frag == 0 and \ + sp.icmptypes[a.payload.payload.type] == 'echo-reply': + id = a.payload.payload.id + print("id=%#x" % (id)) + if id != eid: + print("WRONG ECHO REPLY ID") + exit(2) + if a and a.type == sp.ETH_P_IP and \ + a.payload.proto == 1 and \ + a.payload.frag > 0 and \ + a.payload.flags == '': + length = (a.payload.frag << 3) + a.payload.len + print("len=%d" % (length)) + if length != packet_length: + print("WRONG ECHO REPLY LENGTH") + exit(1) + exit(0) + print("NO ECHO REPLY") + exit(1) + +if __name__ == '__main__': + main(send) diff --git a/tests/sys/netpfil/pf/fragmentation_pass.sh b/tests/sys/netpfil/pf/fragmentation_pass.sh index 68b078d42ddc..4e0665094ae6 100644 --- a/tests/sys/netpfil/pf/fragmentation_pass.sh +++ b/tests/sys/netpfil/pf/fragmentation_pass.sh @@ -1,627 +1,665 @@ # # SPDX-License-Identifier: BSD-2-Clause # # Copyright (c) 2017 Kristof Provost # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF # SUCH DAMAGE. . $(atf_get_srcdir)/utils.subr common_dir=$(atf_get_srcdir)/../common atf_test_case "too_many_fragments" "cleanup" too_many_fragments_head() { atf_set descr 'IPv4 fragment limitation test' atf_set require.user root } too_many_fragments_body() { pft_init epair=$(vnet_mkepair) vnet_mkjail alcatraz ${epair}a ifconfig ${epair}b inet 192.0.2.1/24 up jexec alcatraz ifconfig ${epair}a 192.0.2.2/24 up ifconfig ${epair}b mtu 200 jexec alcatraz ifconfig ${epair}a mtu 200 jexec alcatraz pfctl -e pft_set_rules alcatraz \ "set reassemble yes" \ "pass keep state" # So we know pf is limiting things jexec alcatraz sysctl net.inet.ip.maxfragsperpacket=1024 # Sanity check atf_check -s exit:0 -o ignore ping -c 1 192.0.2.2 # We can ping with < 64 fragments atf_check -s exit:0 -o ignore ping -c 1 -s 800 192.0.2.2 # Too many fragments should fail atf_check -s exit:2 -o ignore ping -c 1 -s 20000 192.0.2.2 } too_many_fragments_cleanup() { pft_cleanup } atf_test_case "v6" "cleanup" v6_head() { atf_set descr 'IPv6 fragmentation test' atf_set require.user root atf_set require.progs scapy } v6_body() { pft_init epair_send=$(vnet_mkepair) epair_link=$(vnet_mkepair) vnet_mkjail alcatraz ${epair_send}b ${epair_link}a vnet_mkjail singsing ${epair_link}b ifconfig ${epair_send}a inet6 2001:db8:42::1/64 no_dad up jexec alcatraz ifconfig ${epair_send}b inet6 2001:db8:42::2/64 no_dad up jexec alcatraz ifconfig ${epair_link}a inet6 2001:db8:43::2/64 no_dad up jexec alcatraz sysctl net.inet6.ip6.forwarding=1 jexec singsing ifconfig ${epair_link}b inet6 2001:db8:43::3/64 no_dad up jexec singsing route add -6 2001:db8:42::/64 2001:db8:43::2 route add -6 2001:db8:43::/64 2001:db8:42::2 jexec alcatraz ifconfig ${epair_send}b inet6 -ifdisabled jexec alcatraz ifconfig ${epair_link}a inet6 -ifdisabled jexec singsing ifconfig ${epair_link}b inet6 -ifdisabled ifconfig ${epair_send}a inet6 -ifdisabled ifconfig ${epair_send}a jexec alcatraz ifconfig ${epair_send}b lladdr=$(jexec alcatraz ifconfig ${epair_send}b | awk '/ scopeid / { print($2); }' | cut -f 1 -d %) jexec alcatraz pfctl -e pft_set_rules alcatraz \ "set reassemble yes" \ "pass keep state" \ "block in" \ "pass in inet6 proto icmp6 icmp6-type { neighbrsol, neighbradv }" \ "pass in inet6 proto icmp6 icmp6-type { echoreq, echorep }" \ "set skip on lo" # Host test atf_check -s exit:0 -o ignore \ ping -6 -c 1 2001:db8:42::2 atf_check -s exit:0 -o ignore \ ping -6 -c 1 -s 4500 2001:db8:42::2 atf_check -s exit:0 -o ignore\ ping -6 -c 1 -b 70000 -s 65000 2001:db8:42::2 # Force an NDP lookup ping -6 -c 1 ${lladdr}%${epair_send}a atf_check -s exit:0 -o ignore\ ping -6 -c 1 -b 70000 -s 65000 ${lladdr}%${epair_send}a # Forwarding test atf_check -s exit:0 -o ignore \ ping -6 -c 1 2001:db8:43::3 atf_check -s exit:0 -o ignore \ ping -6 -c 1 -s 4500 2001:db8:43::3 atf_check -s exit:0 -o ignore\ ping -6 -c 1 -b 70000 -s 65000 2001:db8:43::3 $(atf_get_srcdir)/CVE-2019-5597.py \ ${epair_send}a \ 2001:db8:42::1 \ 2001:db8:43::3 } v6_cleanup() { pft_cleanup } atf_test_case "v6_route_to" "cleanup" v6_route_to_head() { atf_set descr 'Test IPv6 reassembly combined with route-to' atf_set require.user root } v6_route_to_body() { pft_init } v6_route_to_cleanup() { pft_cleanup epair_send=$(vnet_mkepair) epair_link=$(vnet_mkepair) vnet_mkjail alcatraz ${epair_send}b ${epair_link}a vnet_mkjail singsing ${epair_link}b ifconfig ${epair_send}a inet6 2001:db8:42::1/64 no_dad up jexec alcatraz ifconfig ${epair_send}b inet6 2001:db8:42::2/64 no_dad up jexec alcatraz ifconfig ${epair_link}a inet6 2001:db8:43::2/64 no_dad up jexec alcatraz sysctl net.inet6.ip6.forwarding=1 jexec singsing ifconfig ${epair_link}b inet6 2001:db8:43::3/64 no_dad up jexec singsing route add -6 2001:db8:42::/64 2001:db8:43::2 route add -6 2001:db8:43::/64 2001:db8:42::2 jexec alcatraz ifconfig ${epair_send}b inet6 -ifdisabled jexec alcatraz ifconfig ${epair_link}a inet6 -ifdisabled jexec singsing ifconfig ${epair_link}b inet6 -ifdisabled ifconfig ${epair_send}a inet6 -ifdisabled jexec alcatraz pfctl -e pft_set_rules alcatraz \ "set reassemble yes" \ "pass" \ "pass in route-to (${epair_link}a 2001:db8:43::3) inet6 proto icmp6 from any to 2001:db8:43::3 keep state" # Forwarding test atf_check -s exit:0 -o ignore \ ping -6 -c 1 2001:db8:43::3 atf_check -s exit:0 -o ignore \ ping -6 -c 1 -s 4500 2001:db8:43::3 atf_check -s exit:0 -o ignore\ ping -6 -c 1 -b 70000 -s 65000 2001:db8:43::3 # Now test this without fragmentation pft_set_rules alcatraz \ "set reassemble no" \ "pass" \ "pass in route-to (${epair_link}a 2001:db8:43::3) inet6 proto icmp6 from any to 2001:db8:43::3 keep state" atf_check -s exit:0 -o ignore \ ping -6 -c 1 2001:db8:43::3 atf_check -s exit:0 -o ignore \ ping -6 -c 1 -s 4500 2001:db8:43::3 atf_check -s exit:0 -o ignore\ ping -6 -c 1 -b 70000 -s 65000 2001:db8:43::3 } atf_test_case "mtu_diff" "cleanup" mtu_diff_head() { atf_set descr 'Test reassembly across different MTUs, PR #255432' atf_set require.user root } mtu_diff_body() { pft_init epair_small=$(vnet_mkepair) epair_large=$(vnet_mkepair) vnet_mkjail first ${epair_small}b ${epair_large}a vnet_mkjail second ${epair_large}b ifconfig ${epair_small}a 192.0.2.1/25 up jexec first ifconfig ${epair_small}b 192.0.2.2/25 up jexec first sysctl net.inet.ip.forwarding=1 jexec first ifconfig ${epair_large}a 192.0.2.130/25 up jexec first ifconfig ${epair_large}a mtu 9000 jexec second ifconfig ${epair_large}b 192.0.2.131/25 up jexec second ifconfig ${epair_large}b mtu 9000 jexec second route add default 192.0.2.130 route add 192.0.2.128/25 192.0.2.2 jexec first pfctl -e pft_set_rules first \ "set reassemble yes" \ "pass keep state" # Sanity checks atf_check -s exit:0 -o ignore ping -c 1 192.0.2.2 atf_check -s exit:0 -o ignore ping -c 1 192.0.2.130 atf_check -s exit:0 -o ignore ping -c 1 192.0.2.131 # Large packet that'll get reassembled and sent out in one on the large # epair atf_check -s exit:0 -o ignore ping -c 1 -s 8000 192.0.2.131 } mtu_diff_cleanup() { pft_cleanup } frag_common() { name=$1 pft_init epair=$(vnet_mkepair) vnet_mkjail alcatraz ${epair}a ifconfig ${epair}b inet 192.0.2.1/24 up jexec alcatraz ifconfig ${epair}a 192.0.2.2/24 up jexec alcatraz pfctl -e pft_set_rules alcatraz \ "set reassemble yes" \ "pass keep state" # Sanity check atf_check -s exit:0 -o ignore ping -c 1 192.0.2.2 atf_check -s exit:0 -o ignore $(atf_get_srcdir)/frag-${1}.py \ --to 192.0.2.2 \ --fromaddr 192.0.2.1 \ --sendif ${epair}b \ --recvif ${epair}b } atf_test_case "overreplace" "cleanup" overreplace_head() { atf_set descr 'ping fragment that overlaps fragment at index boundary and replace it' atf_set require.user root atf_set require.progs scapy } overreplace_body() { frag_common overreplace } overreplace_cleanup() { pft_cleanup } atf_test_case "overindex" "cleanup" overindex_head() { atf_set descr 'ping fragment that overlaps the first fragment at index boundary' atf_set require.user root atf_set require.progs scapy } overindex_body() { frag_common overindex } overindex_cleanup() { pft_cleanup } atf_test_case "overlimit" "cleanup" overlimit_head() { atf_set descr 'ping fragment at index boundary that cannot be requeued' atf_set require.user root atf_set require.progs scapy } overlimit_body() { frag_common overlimit } overlimit_cleanup() { pft_cleanup } +atf_test_case "overhole" "cleanup" +overhole_head() +{ + atf_set descr 'ping fragment at index boundary which modifies pf hole counter' + atf_set require.user root + atf_set require.progs scapy +} + +overhole_body() +{ + frag_common overhole +} + +overhole_cleanup() +{ + pft_cleanup +} + +atf_test_case "adjhole" "cleanup" +adjhole_head() +{ + atf_set descr 'overlapping ping fragments which modifies pf hole counter' + atf_set require.user root + atf_set require.progs scapy +} + +adjhole_body() +{ + frag_common adjhole +} + +adjhole_cleanup() +{ + pft_cleanup +} + atf_test_case "reassemble" "cleanup" reassemble_head() { atf_set descr 'Test reassembly' atf_set require.user root } reassemble_body() { pft_init epair=$(vnet_mkepair) vnet_mkjail alcatraz ${epair}a ifconfig ${epair}b inet 192.0.2.1/24 up jexec alcatraz ifconfig ${epair}a 192.0.2.2/24 up # Sanity check atf_check -s exit:0 -o ignore ping -c 1 192.0.2.2 jexec alcatraz pfctl -e pft_set_rules alcatraz \ "pass out" \ "block in" \ "pass in inet proto icmp all icmp-type echoreq" # Single fragment passes atf_check -s exit:0 -o ignore ping -c 1 192.0.2.2 # But a fragmented ping does not atf_check -s exit:2 -o ignore ping -c 1 -s 2000 192.0.2.2 pft_set_rules alcatraz \ "set reassemble yes" \ "pass out" \ "block in" \ "pass in inet proto icmp all icmp-type echoreq" # Both single packet & fragmented pass when we scrub atf_check -s exit:0 -o ignore ping -c 1 192.0.2.2 atf_check -s exit:0 -o ignore ping -c 1 -s 2000 192.0.2.2 } reassemble_cleanup() { pft_cleanup } atf_test_case "no_df" "cleanup" no_df_head() { atf_set descr 'Test removing of DF flag' atf_set require.user root } no_df_body() { setup_router_server_ipv4 # Tester can send long packets which will get fragmented by the router. # Replies from server will come in fragments which might get # reassembled resulting in a long reply packet sent back to tester. ifconfig ${epair_tester}a mtu 9000 jexec router ifconfig ${epair_tester}b mtu 9000 jexec router ifconfig ${epair_server}a mtu 1500 jexec server ifconfig ${epair_server}b mtu 1500 # Sanity check. ping_server_check_reply exit:0 --ping-type=icmp # Enable packet reassembly with clearing of the no-df flag. pft_set_rules router \ "scrub all fragment reassemble no-df" \ "block" \ "pass inet proto icmp all icmp-type echoreq" # Ping with non-fragmentable packets. # pf will strip the DF flag resulting in fragmentation and packets # getting properly forwarded. ping_server_check_reply exit:0 --ping-type=icmp --send-length=2000 --send-flags DF } no_df_cleanup() { pft_cleanup } atf_test_case "reassemble_slowpath" "cleanup" reassemble_slowpath_head() { atf_set descr 'Test reassembly on the slow path' atf_set require.user root } reassemble_slowpath_body() { if ! sysctl -q kern.features.ipsec >/dev/null ; then atf_skip "This test requires ipsec" fi setup_router_server_ipv4 # Now define an ipsec policy so we end up taking the slow path. # We don't actually need the traffic to go through ipsec, we just don't # want to go through ip_tryforward(). echo "flush; spdflush; spdadd 203.0.113.1/32 203.0.113.2/32 any -P out ipsec esp/transport//require; add 203.0.113.1 203.0.113.2 esp 0x1001 -E aes-gcm-16 \"12345678901234567890\";" \ | jexec router setkey -c # Sanity check. ping_server_check_reply exit:0 --ping-type=icmp # Enable packet reassembly with clearing of the no-df flag. pft_set_rules router \ "scrub in on ${epair_tester}b fragment no reassemble" \ "scrub on ${epair_server}a fragment reassemble" \ "pass" # Ensure that the packet makes it through the slow path atf_check -s exit:0 -o ignore \ ping -c 1 -s 2000 198.51.100.2 } reassemble_slowpath_cleanup() { pft_cleanup } atf_test_case "dummynet" "cleanup" dummynet_head() { atf_set descr 'dummynet + reassembly test' atf_set require.user root } dummynet_body() { pft_init dummynet_init epair=$(vnet_mkepair) vnet_mkjail alcatraz ${epair}a ifconfig ${epair}b inet 192.0.2.1/24 up jexec alcatraz ifconfig ${epair}a 192.0.2.2/24 up # Sanity check atf_check -s exit:0 -o ignore ping -c 1 192.0.2.2 jexec alcatraz dnctl pipe 1 config bw 600Byte/s jexec alcatraz dnctl pipe 2 config bw 700Byte/s jexec alcatraz pfctl -e pft_set_rules alcatraz \ "set reassemble yes" \ "block" \ "pass inet proto icmp all icmp-type echoreq dnpipe (1, 2)" atf_check -s exit:0 -o ignore ping -s 2000 -c 1 192.0.2.2 } dummynet_cleanup() { pft_cleanup } atf_test_case "dummynet_nat" "cleanup" dummynet_nat_head() { atf_set descr 'Test dummynet on NATed fragmented traffic' atf_set require.user root } dummynet_nat_body() { pft_init dummynet_init epair_one=$(vnet_mkepair) ifconfig ${epair_one}a 192.0.2.1/24 up epair_two=$(vnet_mkepair) vnet_mkjail alcatraz ${epair_one}b ${epair_two}a jexec alcatraz ifconfig ${epair_one}b 192.0.2.2/24 up jexec alcatraz ifconfig ${epair_two}a 198.51.100.1/24 up jexec alcatraz sysctl net.inet.ip.forwarding=1 vnet_mkjail singsing ${epair_two}b jexec singsing ifconfig ${epair_two}b 198.51.100.2/24 up jexec singsing route add default 198.51.100.1 route add 198.51.100.0/24 192.0.2.2 jexec alcatraz dnctl pipe 1 config bw 1600Byte/s jexec alcatraz dnctl pipe 2 config bw 1700Byte/s jexec alcatraz pfctl -e pft_set_rules alcatraz \ "set reassemble yes" \ "nat on ${epair_two}a from 192.0.2.0/24 -> (${epair_two}a)" \ "block in" \ "pass in inet proto icmp all icmp-type echoreq dnpipe (1, 2)" atf_check -s exit:0 -o ignore ping -c 1 198.51.100.2 atf_check -s exit:0 -o ignore ping -c 1 -s 2000 198.51.100.2 } dummynet_nat_cleanup() { pft_cleanup } atf_test_case "dummynet_fragmented" "cleanup" dummynet_fragmented_head() { atf_set descr 'Test dummynet on NATed fragmented traffic' atf_set require.user root } dummynet_fragmented_body() { pft_init dummynet_init # No test for IPv6. IPv6 fragment reassembly can't be disabled. setup_router_dummy_ipv4 jexec router dnctl pipe 1 config delay 1 pft_set_rules router \ "set reassemble no" \ "block" \ "pass inet6 proto icmp6 icmp6-type { neighbrsol, neighbradv }" \ "pass in on ${epair_tester}b inet proto udp dnpipe (1, 1)" \ "pass out on ${epair_server}a inet proto udp" \ ping_dummy_check_request exit:0 --ping-type=udp --send-length=10000 --send-frag-length=1280 rules=$(mktemp) || exit 1 jexec router pfctl -qvsr | normalize_pfctl_s > $rules # Count that fragmented packets have hit the rule only once and that # they have not created states. There is no stateful firewall support # for fragmented packets. grep -qE 'pass in on epair0b inet proto udp all keep state dnpipe\(1, 1\) .* Packets: 8 Bytes: 10168 States: 0 ' $rules || atf_fail "Fragmented packets not counted correctly" } dummynet_fragmented_cleanup() { pft_cleanup } atf_init_test_cases() { atf_add_test_case "too_many_fragments" atf_add_test_case "v6" atf_add_test_case "v6_route_to" atf_add_test_case "mtu_diff" atf_add_test_case "overreplace" atf_add_test_case "overindex" atf_add_test_case "overlimit" + atf_add_test_case "overhole" + atf_add_test_case "adjhole" atf_add_test_case "reassemble" atf_add_test_case "no_df" atf_add_test_case "reassemble_slowpath" atf_add_test_case "dummynet" atf_add_test_case "dummynet_nat" atf_add_test_case "dummynet_fragmented" }