# # SPDX-License-Identifier: BSD-2-Clause # # Copyright (c) 2018 Orange Business Services # # 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 "basic" "cleanup" basic_head() { atf_set descr 'Basic pfsync test' atf_set require.user root } basic_body() { common_body } common_body() { defer=$1 pfsynct_init epair_sync=$(vnet_mkepair) epair_one=$(vnet_mkepair) epair_two=$(vnet_mkepair) vnet_mkjail one ${epair_one}a ${epair_sync}a vnet_mkjail two ${epair_two}a ${epair_sync}b # pfsync interface jexec one ifconfig ${epair_sync}a 192.0.2.1/24 up jexec one ifconfig ${epair_one}a 198.51.100.1/24 up jexec one ifconfig pfsync0 \ syncdev ${epair_sync}a \ maxupd 1 \ $defer \ up jexec two ifconfig ${epair_two}a 198.51.100.2/24 up jexec two ifconfig ${epair_sync}b 192.0.2.2/24 up jexec two ifconfig pfsync0 \ syncdev ${epair_sync}b \ maxupd 1 \ $defer \ up # Enable pf! jexec one pfctl -e pft_set_rules one \ "set skip on ${epair_sync}a" \ "pass out keep state" jexec two pfctl -e pft_set_rules two \ "set skip on ${epair_sync}b" \ "pass out keep state" hostid_one=$(jexec one pfctl -si -v | awk '/Hostid:/ { gsub(/0x/, "", $2); printf($2); }') ifconfig ${epair_one}b 198.51.100.254/24 up ping -c 1 -S 198.51.100.254 198.51.100.1 # Give pfsync time to do its thing sleep 2 if ! jexec two pfctl -s states | grep icmp | grep 198.51.100.1 | \ grep 198.51.100.254 ; then atf_fail "state not found on synced host" fi if ! jexec two pfctl -sc | grep ""${hostid_one}""; then jexec two pfctl -sc atf_fail "HostID for host one not found on two" fi } basic_cleanup() { pfsynct_cleanup } atf_test_case "basic_defer" "cleanup" basic_defer_head() { atf_set descr 'Basic defer mode pfsync test' atf_set require.user root } basic_defer_body() { common_body defer } basic_defer_cleanup() { pfsynct_cleanup } atf_test_case "defer" "cleanup" defer_head() { atf_set descr 'Defer mode pfsync test' atf_set require.user root atf_set require.progs python3 scapy } defer_body() { pfsynct_init epair_sync=$(vnet_mkepair) epair_in=$(vnet_mkepair) epair_out=$(vnet_mkepair) vnet_mkjail alcatraz ${epair_sync}a ${epair_in}a ${epair_out}a jexec alcatraz ifconfig ${epair_sync}a 192.0.2.1/24 up jexec alcatraz ifconfig ${epair_out}a 198.51.100.1/24 up jexec alcatraz ifconfig ${epair_in}a 203.0.113.1/24 up jexec alcatraz arp -s 203.0.113.2 00:01:02:03:04:05 jexec alcatraz sysctl net.inet.ip.forwarding=1 # Set a long defer delay jexec alcatraz sysctl net.pfsync.defer_delay=2500 jexec alcatraz ifconfig pfsync0 \ syncdev ${epair_sync}a \ maxupd 1 \ defer \ up ifconfig ${epair_sync}b 192.0.2.2/24 up ifconfig ${epair_out}b 198.51.100.2/24 up ifconfig ${epair_in}b up route add -net 203.0.113.0/24 198.51.100.1 # Enable pf jexec alcatraz sysctl net.pf.filter_local=0 jexec alcatraz pfctl -e pft_set_rules alcatraz \ "set skip on ${epair_sync}a" \ "pass keep state" atf_check -s exit:0 env PYTHONPATH=${common_dir} \ $(atf_get_srcdir)/pfsync_defer.py \ --syncdev ${epair_sync}b \ --indev ${epair_in}b \ --outdev ${epair_out}b # Now disable defer mode and expect failure. jexec alcatraz ifconfig pfsync0 -defer # Flush state pft_set_rules alcatraz \ "set skip on ${epair_sync}a" \ "pass keep state" atf_check -s exit:3 env PYTHONPATH=${common_dir} \ $(atf_get_srcdir)/pfsync_defer.py \ --syncdev ${epair_sync}b \ --indev ${epair_in}b \ --outdev ${epair_out}b } defer_cleanup() { pfsynct_cleanup } atf_test_case "bulk" "cleanup" bulk_head() { atf_set descr 'Test bulk updates' atf_set require.user root } bulk_body() { pfsynct_init epair_sync=$(vnet_mkepair) epair_one=$(vnet_mkepair) epair_two=$(vnet_mkepair) vnet_mkjail one ${epair_one}a ${epair_sync}a vnet_mkjail two ${epair_two}a ${epair_sync}b # pfsync interface jexec one ifconfig ${epair_sync}a 192.0.2.1/24 up jexec one ifconfig ${epair_one}a 198.51.100.1/24 up jexec one ifconfig pfsync0 \ syncdev ${epair_sync}a \ maxupd 1\ up jexec two ifconfig ${epair_two}a 198.51.100.2/24 up jexec two ifconfig ${epair_sync}b 192.0.2.2/24 up # Enable pf jexec one pfctl -e pft_set_rules one \ "set skip on ${epair_sync}a" \ "pass keep state" jexec two pfctl -e pft_set_rules two \ "set skip on ${epair_sync}b" \ "pass keep state" ifconfig ${epair_one}b 198.51.100.254/24 up # Create state prior to setting up pfsync ping -c 1 -S 198.51.100.254 198.51.100.1 # Wait before setting up pfsync on two, so we don't accidentally catch # the update anyway. sleep 1 # Now set up pfsync in jail two jexec two ifconfig pfsync0 \ syncdev ${epair_sync}b \ up # Give pfsync time to do its thing sleep 2 jexec two pfctl -s states if ! jexec two pfctl -s states | grep icmp | grep 198.51.100.1 | \ grep 198.51.100.2 ; then atf_fail "state not found on synced host" fi } bulk_cleanup() { pfsynct_cleanup } atf_test_case "pbr" "cleanup" pbr_head() { atf_set descr 'route_to and reply_to directives test' atf_set require.user root atf_set timeout '600' } pbr_body() { pbr_common_body } pbr_cleanup() { pbr_common_cleanup } atf_test_case "pfsync_pbr" "cleanup" pfsync_pbr_head() { atf_set descr 'route_to and reply_to directives pfsync test' atf_set require.user root atf_set timeout '600' } pfsync_pbr_body() { pbr_common_body backup_promotion } pfsync_pbr_cleanup() { pbr_common_cleanup } pbr_common_body() { # + builds bellow topology and initiate a single ping session # from client to server. # + gw* forward traffic through pbr not fib lookups. # + if backup_promotion arg is given, a carp failover event occurs # during the ping session on both gateways. # ┌──────┐ # │client│ # └───┬──┘ # │ # ┌───┴───┐ # │bridge0│ # └┬─────┬┘ # │ │ # ┌────────────────┴─┐ ┌─┴────────────────┐ # │gw_route_to_master├─┤gw_route_to_backup│ # └────────────────┬─┘ └─┬────────────────┘ # │ │ # ┌┴─────┴┐ # │bridge1│ # └┬─────┬┘ # │ │ # ┌────────────────┴─┐ ┌─┴────────────────┐ # │gw_reply_to_master├─┤gw_reply_to_backup│ # └────────────────┬─┘ └─┬────────────────┘ # │ │ # ┌┴─────┴┐ # │bridge2│ # └───┬───┘ # │ # ┌───┴──┐ # │server│ # └──────┘ if ! kldstat -q -m carp then atf_skip "This test requires carp" fi pfsynct_init vnet_init_bridge bridge0=$(vnet_mkbridge) bridge1=$(vnet_mkbridge) bridge2=$(vnet_mkbridge) epair_sync_gw_route_to=$(vnet_mkepair) epair_sync_gw_reply_to=$(vnet_mkepair) epair_client_bridge0=$(vnet_mkepair) epair_gw_route_to_master_bridge0=$(vnet_mkepair) epair_gw_route_to_backup_bridge0=$(vnet_mkepair) epair_gw_route_to_master_bridge1=$(vnet_mkepair) epair_gw_route_to_backup_bridge1=$(vnet_mkepair) epair_gw_reply_to_master_bridge1=$(vnet_mkepair) epair_gw_reply_to_backup_bridge1=$(vnet_mkepair) epair_gw_reply_to_master_bridge2=$(vnet_mkepair) epair_gw_reply_to_backup_bridge2=$(vnet_mkepair) epair_server_bridge2=$(vnet_mkepair) ifconfig ${bridge0} up ifconfig ${epair_client_bridge0}b up ifconfig ${epair_gw_route_to_master_bridge0}b up ifconfig ${epair_gw_route_to_backup_bridge0}b up ifconfig ${bridge0} \ addm ${epair_client_bridge0}b \ addm ${epair_gw_route_to_master_bridge0}b \ addm ${epair_gw_route_to_backup_bridge0}b ifconfig ${bridge1} up ifconfig ${epair_gw_route_to_master_bridge1}b up ifconfig ${epair_gw_route_to_backup_bridge1}b up ifconfig ${epair_gw_reply_to_master_bridge1}b up ifconfig ${epair_gw_reply_to_backup_bridge1}b up ifconfig ${bridge1} \ addm ${epair_gw_route_to_master_bridge1}b \ addm ${epair_gw_route_to_backup_bridge1}b \ addm ${epair_gw_reply_to_master_bridge1}b \ addm ${epair_gw_reply_to_backup_bridge1}b ifconfig ${bridge2} up ifconfig ${epair_gw_reply_to_master_bridge2}b up ifconfig ${epair_gw_reply_to_backup_bridge2}b up ifconfig ${epair_server_bridge2}b up ifconfig ${bridge2} \ addm ${epair_gw_reply_to_master_bridge2}b \ addm ${epair_gw_reply_to_backup_bridge2}b \ addm ${epair_server_bridge2}b vnet_mkjail client ${epair_client_bridge0}a jexec client hostname client vnet_mkjail gw_route_to_master \ ${epair_gw_route_to_master_bridge0}a \ ${epair_gw_route_to_master_bridge1}a \ ${epair_sync_gw_route_to}a jexec gw_route_to_master hostname gw_route_to_master vnet_mkjail gw_route_to_backup \ ${epair_gw_route_to_backup_bridge0}a \ ${epair_gw_route_to_backup_bridge1}a \ ${epair_sync_gw_route_to}b jexec gw_route_to_backup hostname gw_route_to_backup vnet_mkjail gw_reply_to_master \ ${epair_gw_reply_to_master_bridge1}a \ ${epair_gw_reply_to_master_bridge2}a \ ${epair_sync_gw_reply_to}a jexec gw_reply_to_master hostname gw_reply_to_master vnet_mkjail gw_reply_to_backup \ ${epair_gw_reply_to_backup_bridge1}a \ ${epair_gw_reply_to_backup_bridge2}a \ ${epair_sync_gw_reply_to}b jexec gw_reply_to_backup hostname gw_reply_to_backup vnet_mkjail server ${epair_server_bridge2}a jexec server hostname server jexec client ifconfig ${epair_client_bridge0}a inet 198.18.0.1/24 up jexec client route add 198.18.2.0/24 198.18.0.10 jexec gw_route_to_master ifconfig ${epair_sync_gw_route_to}a \ inet 198.19.10.1/24 up jexec gw_route_to_master ifconfig ${epair_gw_route_to_master_bridge0}a \ inet 198.18.0.8/24 up jexec gw_route_to_master ifconfig ${epair_gw_route_to_master_bridge0}a \ alias 198.18.0.10/32 vhid 10 pass 3WjvVVw7 advskew 50 jexec gw_route_to_master ifconfig ${epair_gw_route_to_master_bridge1}a \ inet 198.18.1.8/24 up jexec gw_route_to_master ifconfig ${epair_gw_route_to_master_bridge1}a \ alias 198.18.1.10/32 vhid 11 pass 3WjvVVw7 advskew 50 jexec gw_route_to_master sysctl net.inet.ip.forwarding=1 jexec gw_route_to_master sysctl net.inet.carp.preempt=1 vnet_ifrename_jail gw_route_to_master ${epair_sync_gw_route_to}a if_pfsync vnet_ifrename_jail gw_route_to_master ${epair_gw_route_to_master_bridge0}a if_br0 vnet_ifrename_jail gw_route_to_master ${epair_gw_route_to_master_bridge1}a if_br1 jexec gw_route_to_master ifconfig pfsync0 \ syncpeer 198.19.10.2 \ syncdev if_pfsync \ maxupd 1 \ up pft_set_rules gw_route_to_master \ "keep_state = 'tag auth_packet keep state'" \ "set timeout { icmp.first 120, icmp.error 60 }" \ "block log all" \ "pass quick on if_pfsync proto pfsync keep state (no-sync)" \ "pass quick on { if_br0 if_br1 } proto carp keep state (no-sync)" \ "block drop in quick to 224.0.0.18/32" \ "pass out quick tagged auth_packet keep state" \ "pass in quick log on if_br0 route-to (if_br1 198.18.1.20) proto { icmp udp tcp } from 198.18.0.0/24 to 198.18.2.0/24 \$keep_state" jexec gw_route_to_master pfctl -e jexec gw_route_to_backup ifconfig ${epair_sync_gw_route_to}b \ inet 198.19.10.2/24 up jexec gw_route_to_backup ifconfig ${epair_gw_route_to_backup_bridge0}a \ inet 198.18.0.9/24 up jexec gw_route_to_backup ifconfig ${epair_gw_route_to_backup_bridge0}a \ alias 198.18.0.10/32 vhid 10 pass 3WjvVVw7 advskew 100 jexec gw_route_to_backup ifconfig ${epair_gw_route_to_backup_bridge1}a \ inet 198.18.1.9/24 up jexec gw_route_to_backup ifconfig ${epair_gw_route_to_backup_bridge1}a \ alias 198.18.1.10/32 vhid 11 pass 3WjvVVw7 advskew 100 jexec gw_route_to_backup sysctl net.inet.ip.forwarding=1 jexec gw_route_to_backup sysctl net.inet.carp.preempt=1 vnet_ifrename_jail gw_route_to_backup ${epair_sync_gw_route_to}b if_pfsync vnet_ifrename_jail gw_route_to_backup ${epair_gw_route_to_backup_bridge0}a if_br0 vnet_ifrename_jail gw_route_to_backup ${epair_gw_route_to_backup_bridge1}a if_br1 jexec gw_route_to_backup ifconfig pfsync0 \ syncpeer 198.19.10.1 \ syncdev if_pfsync \ up pft_set_rules gw_route_to_backup \ "keep_state = 'tag auth_packet keep state'" \ "set timeout { icmp.first 120, icmp.error 60 }" \ "block log all" \ "pass quick on if_pfsync proto pfsync keep state (no-sync)" \ "pass quick on { if_br0 if_br1 } proto carp keep state (no-sync)" \ "block drop in quick to 224.0.0.18/32" \ "pass out quick tagged auth_packet keep state" \ "pass in quick log on if_br0 route-to (if_br1 198.18.1.20) proto { icmp udp tcp } from 198.18.0.0/24 to 198.18.2.0/24 \$keep_state" jexec gw_route_to_backup pfctl -e jexec gw_reply_to_master ifconfig ${epair_sync_gw_reply_to}a \ inet 198.19.20.1/24 up jexec gw_reply_to_master ifconfig ${epair_gw_reply_to_master_bridge1}a \ inet 198.18.1.18/24 up jexec gw_reply_to_master ifconfig ${epair_gw_reply_to_master_bridge1}a \ alias 198.18.1.20/32 vhid 21 pass 3WjvVVw7 advskew 50 jexec gw_reply_to_master ifconfig ${epair_gw_reply_to_master_bridge2}a \ inet 198.18.2.18/24 up jexec gw_reply_to_master ifconfig ${epair_gw_reply_to_master_bridge2}a \ alias 198.18.2.20/32 vhid 22 pass 3WjvVVw7 advskew 50 jexec gw_reply_to_master sysctl net.inet.ip.forwarding=1 jexec gw_reply_to_master sysctl net.inet.carp.preempt=1 vnet_ifrename_jail gw_reply_to_master ${epair_sync_gw_reply_to}a if_pfsync vnet_ifrename_jail gw_reply_to_master ${epair_gw_reply_to_master_bridge1}a if_br1 vnet_ifrename_jail gw_reply_to_master ${epair_gw_reply_to_master_bridge2}a if_br2 jexec gw_reply_to_master ifconfig pfsync0 \ syncpeer 198.19.20.2 \ syncdev if_pfsync \ maxupd 1 \ up pft_set_rules gw_reply_to_master \ "set timeout { icmp.first 120, icmp.error 60 }" \ "block log all" \ "pass quick on if_pfsync proto pfsync keep state (no-sync)" \ "pass quick on { if_br1 if_br2 } proto carp keep state (no-sync)" \ "block drop in quick to 224.0.0.18/32" \ "pass out quick on if_br2 reply-to (if_br1 198.18.1.10) tagged auth_packet_reply_to keep state" \ "pass in quick log on if_br1 proto { icmp udp tcp } from 198.18.0.0/24 to 198.18.2.0/24 tag auth_packet_reply_to keep state" jexec gw_reply_to_master pfctl -e jexec gw_reply_to_backup ifconfig ${epair_sync_gw_reply_to}b \ inet 198.19.20.2/24 up jexec gw_reply_to_backup ifconfig ${epair_gw_reply_to_backup_bridge1}a \ inet 198.18.1.19/24 up jexec gw_reply_to_backup ifconfig ${epair_gw_reply_to_backup_bridge1}a \ alias 198.18.1.20/32 vhid 21 pass 3WjvVVw7 advskew 100 jexec gw_reply_to_backup ifconfig ${epair_gw_reply_to_backup_bridge2}a \ inet 198.18.2.19/24 up jexec gw_reply_to_backup ifconfig ${epair_gw_reply_to_backup_bridge2}a \ alias 198.18.2.20/32 vhid 22 pass 3WjvVVw7 advskew 100 jexec gw_reply_to_backup sysctl net.inet.ip.forwarding=1 jexec gw_reply_to_backup sysctl net.inet.carp.preempt=1 vnet_ifrename_jail gw_reply_to_backup ${epair_sync_gw_reply_to}b if_pfsync vnet_ifrename_jail gw_reply_to_backup ${epair_gw_reply_to_backup_bridge1}a if_br1 vnet_ifrename_jail gw_reply_to_backup ${epair_gw_reply_to_backup_bridge2}a if_br2 jexec gw_reply_to_backup ifconfig pfsync0 \ syncpeer 198.19.20.1 \ syncdev if_pfsync \ up pft_set_rules gw_reply_to_backup \ "set timeout { icmp.first 120, icmp.error 60 }" \ "block log all" \ "pass quick on if_pfsync proto pfsync keep state (no-sync)" \ "pass quick on { if_br1 if_br2 } proto carp keep state (no-sync)" \ "block drop in quick to 224.0.0.18/32" \ "pass out quick on if_br2 reply-to (if_br1 198.18.1.10) tagged auth_packet_reply_to keep state" \ "pass in quick log on if_br1 proto { icmp udp tcp } from 198.18.0.0/24 to 198.18.2.0/24 tag auth_packet_reply_to keep state" jexec gw_reply_to_backup pfctl -e jexec server ifconfig ${epair_server_bridge2}a inet 198.18.2.1/24 up jexec server route add 198.18.0.0/24 198.18.2.20 # Waiting for platform to settle while ! jexec gw_route_to_backup ifconfig | grep 'carp: BACKUP' do sleep 1 done while ! jexec gw_reply_to_backup ifconfig | grep 'carp: BACKUP' do sleep 1 done while ! jexec client ping -c 10 198.18.2.1 | grep ', 0.0% packet loss' do sleep 1 done # Checking cluster members pf.conf checksums match gw_route_to_master_checksum=$(jexec gw_route_to_master pfctl -si -v | grep 'Checksum:' | cut -d ' ' -f 2) gw_route_to_backup_checksum=$(jexec gw_route_to_backup pfctl -si -v | grep 'Checksum:' | cut -d ' ' -f 2) gw_reply_to_master_checksum=$(jexec gw_reply_to_master pfctl -si -v | grep 'Checksum:' | cut -d ' ' -f 2) gw_reply_to_backup_checksum=$(jexec gw_reply_to_backup pfctl -si -v | grep 'Checksum:' | cut -d ' ' -f 2) if [ "$gw_route_to_master_checksum" != "$gw_route_to_backup_checksum" ] then atf_fail "gw_route_to cluster members pf.conf do not match each others" fi if [ "$gw_reply_to_master_checksum" != "$gw_reply_to_backup_checksum" ] then atf_fail "gw_reply_to cluster members pf.conf do not match each others" fi # Creating state entries (jexec client ping -c 10 198.18.2.1 >ping.stdout) & if [ "$1" = "backup_promotion" ] then sleep 1 jexec gw_route_to_backup ifconfig if_br0 vhid 10 advskew 0 jexec gw_route_to_backup ifconfig if_br1 vhid 11 advskew 0 jexec gw_reply_to_backup ifconfig if_br1 vhid 21 advskew 0 jexec gw_reply_to_backup ifconfig if_br2 vhid 22 advskew 0 fi while ! grep -q -e 'packet loss' ping.stdout do sleep 1 done atf_check -s exit:0 -e ignore -o ignore grep ', 0.0% packet loss' ping.stdout } pbr_common_cleanup() { pft_cleanup } atf_test_case "ipsec" "cleanup" ipsec_head() { atf_set descr 'Transport pfsync over IPSec' atf_set require.user root } ipsec_body() { if ! sysctl -q kern.features.ipsec >/dev/null ; then atf_skip "This test requires ipsec" fi # Run the common test, to set up pfsync common_body # But we want unicast pfsync jexec one ifconfig pfsync0 syncpeer 192.0.2.2 jexec two ifconfig pfsync0 syncpeer 192.0.2.1 # Flush existing states jexec one pfctl -Fs jexec two pfctl -Fs # Now define an ipsec policy to run over the epair_sync interfaces echo "flush; spdflush; spdadd 192.0.2.1/32 192.0.2.2/32 any -P out ipsec esp/transport//require; spdadd 192.0.2.2/32 192.0.2.1/32 any -P in ipsec esp/transport//require; add 192.0.2.1 192.0.2.2 esp 0x1000 -E aes-gcm-16 \"12345678901234567890\"; add 192.0.2.2 192.0.2.1 esp 0x1001 -E aes-gcm-16 \"12345678901234567890\";" \ | jexec one setkey -c echo "flush; spdflush; spdadd 192.0.2.2/32 192.0.2.1/32 any -P out ipsec esp/transport//require; spdadd 192.0.2.1/32 192.0.2.2/32 any -P in ipsec esp/transport//require; add 192.0.2.1 192.0.2.2 esp 0x1000 -E aes-gcm-16 \"12345678901234567891\"; add 192.0.2.2 192.0.2.1 esp 0x1001 -E aes-gcm-16 \"12345678901234567891\";" \ | jexec two setkey -c # We've set incompatible keys, so pfsync will be broken. ping -c 1 -S 198.51.100.254 198.51.100.1 # Give pfsync time to do its thing sleep 2 if jexec two pfctl -s states | grep icmp | grep 198.51.100.1 | \ grep 198.51.100.2 ; then atf_fail "state synced although IPSec should have prevented it" fi # Flush existing states jexec one pfctl -Fs jexec two pfctl -Fs # Fix the IPSec key to match echo "flush; spdflush; spdadd 192.0.2.2/32 192.0.2.1/32 any -P out ipsec esp/transport//require; spdadd 192.0.2.1/32 192.0.2.2/32 any -P in ipsec esp/transport//require; add 192.0.2.1 192.0.2.2 esp 0x1000 -E aes-gcm-16 \"12345678901234567890\"; add 192.0.2.2 192.0.2.1 esp 0x1001 -E aes-gcm-16 \"12345678901234567890\";" \ | jexec two setkey -c ping -c 1 -S 198.51.100.254 198.51.100.1 # Give pfsync time to do its thing sleep 2 if ! jexec two pfctl -s states | grep icmp | grep 198.51.100.1 | \ grep 198.51.100.2 ; then atf_fail "state not found on synced host" fi } ipsec_cleanup() { pft_cleanup } atf_test_case "timeout" "cleanup" timeout_head() { atf_set descr 'Trigger pfsync_timeout()' atf_set require.user root } timeout_body() { pft_init vnet_mkjail one jexec one ifconfig lo0 127.0.0.1/8 up jexec one ifconfig lo0 inet6 ::1/128 up pft_set_rules one \ "pass all" jexec one pfctl -e jexec one ifconfig pfsync0 defer up jexec one ping -c 1 ::1 jexec one ping -c 1 127.0.0.1 # Give pfsync_timeout() time to fire (a callout on a 1 second delay) sleep 2 } timeout_cleanup() { pft_cleanup } atf_test_case "basic_ipv6_unicast" "cleanup" basic_ipv6_unicast_head() { atf_set descr 'Basic pfsync test (IPv6)' atf_set require.user root } basic_ipv6_unicast_body() { pfsynct_init epair_sync=$(vnet_mkepair) epair_one=$(vnet_mkepair) epair_two=$(vnet_mkepair) vnet_mkjail one ${epair_one}a ${epair_sync}a vnet_mkjail two ${epair_two}a ${epair_sync}b # pfsync interface jexec one ifconfig ${epair_sync}a inet6 fd2c::1/64 no_dad up jexec one ifconfig ${epair_one}a inet6 fd2b::1/64 no_dad up jexec one ifconfig pfsync0 \ syncdev ${epair_sync}a \ syncpeer fd2c::2 \ maxupd 1 \ up jexec two ifconfig ${epair_two}a inet6 fd2b::2/64 no_dad up jexec two ifconfig ${epair_sync}b inet6 fd2c::2/64 no_dad up jexec two ifconfig pfsync0 \ syncdev ${epair_sync}b \ syncpeer fd2c::1 \ maxupd 1 \ up # Enable pf! jexec one pfctl -e pft_set_rules one \ "block on ${epair_sync}a inet" \ "pass out keep state" jexec two pfctl -e pft_set_rules two \ "block on ${epair_sync}b inet" \ "pass out keep state" ifconfig ${epair_one}b inet6 fd2b::f0/64 no_dad up ping6 -c 1 -S fd2b::f0 fd2b::1 # Give pfsync time to do its thing sleep 2 if ! jexec two pfctl -s states | grep icmp | grep fd2b::1 | \ grep fd2b::f0 ; then atf_fail "state not found on synced host" fi } basic_ipv6_unicast_cleanup() { pfsynct_cleanup } atf_test_case "basic_ipv6" "cleanup" basic_ipv6_head() { atf_set descr 'Basic pfsync test (IPv6)' atf_set require.user root } basic_ipv6_body() { pfsynct_init epair_sync=$(vnet_mkepair) epair_one=$(vnet_mkepair) epair_two=$(vnet_mkepair) vnet_mkjail one ${epair_one}a ${epair_sync}a vnet_mkjail two ${epair_two}a ${epair_sync}b # pfsync interface jexec one ifconfig ${epair_sync}a inet6 fd2c::1/64 no_dad up jexec one ifconfig ${epair_one}a inet6 fd2b::1/64 no_dad up jexec one ifconfig pfsync0 \ syncdev ${epair_sync}a \ syncpeer ff12::f0 \ maxupd 1 \ up jexec two ifconfig ${epair_two}a inet6 fd2b::2/64 no_dad up jexec two ifconfig ${epair_sync}b inet6 fd2c::2/64 no_dad up jexec two ifconfig pfsync0 \ syncdev ${epair_sync}b \ syncpeer ff12::f0 \ maxupd 1 \ up # Enable pf! jexec one pfctl -e pft_set_rules one \ "block on ${epair_sync}a inet" \ "pass out keep state" jexec two pfctl -e pft_set_rules two \ "block on ${epair_sync}b inet" \ "pass out keep state" ifconfig ${epair_one}b inet6 fd2b::f0/64 no_dad up ping6 -c 1 -S fd2b::f0 fd2b::1 # Give pfsync time to do its thing sleep 2 if ! jexec two pfctl -s states | grep icmp | grep fd2b::1 | \ grep fd2b::f0 ; then atf_fail "state not found on synced host" fi } basic_ipv6_cleanup() { pfsynct_cleanup } atf_test_case "rtable" "cleanup" rtable_head() { atf_set descr 'Test handling of invalid rtableid' atf_set require.user root } rtable_body() { pfsynct_init epair_sync=$(vnet_mkepair) epair_one=$(vnet_mkepair) epair_two=$(vnet_mkepair) vnet_mkjail one ${epair_one}a ${epair_sync}a vnet_mkjail two ${epair_two}a ${epair_sync}b # pfsync interface jexec one ifconfig ${epair_sync}a 192.0.2.1/24 up jexec one ifconfig ${epair_one}a 198.51.100.1/24 up jexec one ifconfig pfsync0 \ syncdev ${epair_sync}a \ maxupd 1 \ up jexec two ifconfig ${epair_two}a 198.51.100.1/24 up jexec two ifconfig ${epair_sync}b 192.0.2.2/24 up jexec two ifconfig pfsync0 \ syncdev ${epair_sync}b \ maxupd 1 \ up # Make life easy, give ${epair_two}a the same mac addrss as ${epair_one}a mac=$(jexec one ifconfig ${epair_one}a | awk '/ether/ { print($2); }') jexec two ifconfig ${epair_two}a ether ${mac} # Enable pf! jexec one /sbin/sysctl net.fibs=8 jexec one pfctl -e pft_set_rules one \ "set skip on ${epair_sync}a" \ "pass rtable 3 keep state" # No extra fibs in two jexec two pfctl -e pft_set_rules two \ "set skip on ${epair_sync}b" \ "pass keep state" ifconfig ${epair_one}b 198.51.100.254/24 up ifconfig ${epair_two}b 198.51.100.253/24 up # Create a new state env PYTHONPATH=${common_dir} \ ${common_dir}/pft_ping.py \ --sendif ${epair_one}b \ --fromaddr 198.51.100.254 \ --to 198.51.100.1 \ --recvif ${epair_one}b # Now jexec one pfctl -ss -vv sleep 2 # Now try to use that state on jail two env PYTHONPATH=${common_dir} \ ${common_dir}/pft_ping.py \ --sendif ${epair_two}b \ --fromaddr 198.51.100.254 \ --to 198.51.100.1 \ --recvif ${epair_two}b echo one jexec one pfctl -ss -vv jexec one pfctl -sr -vv echo two jexec two pfctl -ss -vv jexec two pfctl -sr -vv } rtable_cleanup() { pfsynct_cleanup } route_to_common_head() { # TODO: Extend setup_router_server_nat64 to create a 2nd router pfsync_version=$1 shift pfsynct_init epair_sync=$(vnet_mkepair) epair_one=$(vnet_mkepair) epair_two=$(vnet_mkepair) epair_out_one=$(vnet_mkepair) epair_out_two=$(vnet_mkepair) vnet_mkjail one ${epair_one}a ${epair_sync}a ${epair_out_one}a vnet_mkjail two ${epair_two}a ${epair_sync}b ${epair_out_two}a # pfsync interface jexec one ifconfig ${epair_sync}a 192.0.2.1/24 up jexec one ifconfig ${epair_one}a 198.51.100.1/28 up jexec one ifconfig ${epair_one}a inet6 2001:db8:4211::1/64 no_dad jexec one ifconfig ${epair_one}a name inif jexec one ifconfig ${epair_out_one}a 203.0.113.1/24 up jexec one ifconfig ${epair_out_one}a inet6 2001:db8:4200::1/64 no_dad jexec one ifconfig ${epair_out_one}a name outif jexec one sysctl net.inet.ip.forwarding=1 jexec one sysctl net.inet6.ip6.forwarding=1 jexec one arp -s 203.0.113.254 00:01:02:00:00:04 jexec one ndp -s 2001:db8:4200::fe 00:01:02:00:00:06 jexec one ifconfig pfsync0 \ syncdev ${epair_sync}a \ maxupd 1 \ version $pfsync_version \ up jexec two ifconfig ${epair_sync}b 192.0.2.2/24 up jexec two ifconfig ${epair_two}a 198.51.100.17/28 up jexec two ifconfig ${epair_two}a inet6 2001:db8:4212::1/64 no_dad jexec two ifconfig ${epair_two}a name inif jexec two ifconfig ${epair_out_two}a 203.0.113.1/24 up jexec two ifconfig ${epair_out_two}a inet6 2001:db8:4200::2/64 no_dad jexec two ifconfig ${epair_out_two}a name outif jexec two sysctl net.inet.ip.forwarding=1 jexec two sysctl net.inet6.ip6.forwarding=1 jexec two arp -s 203.0.113.254 00:01:02:00:00:04 jexec two ndp -s 2001:db8:4200::fe 00:01:02:00:00:06 jexec two ifconfig pfsync0 \ syncdev ${epair_sync}b \ maxupd 1 \ version $pfsync_version \ up ifconfig ${epair_one}b 198.51.100.2/28 up ifconfig ${epair_one}b inet6 2001:db8:4211::2/64 no_dad ifconfig ${epair_two}b 198.51.100.18/28 up ifconfig ${epair_two}b inet6 2001:db8:4212::2/64 no_dad # Target is behind router "one" route add -net 203.0.113.0/24 198.51.100.1 route add -inet6 -net 64:ff9b::/96 2001:db8:4211::1 ifconfig ${epair_two}b up ifconfig ${epair_out_one}b up ifconfig ${epair_out_two}b up } route_to_common_tail() { atf_check -s exit:0 env PYTHONPATH=${common_dir} \ ${common_dir}/pft_ping.py \ --sendif ${epair_one}b \ --fromaddr 198.51.100.254 \ --to 203.0.113.254 \ --recvif ${epair_out_one}b # Allow time for sync sleep 2 states_one=$(mktemp) states_two=$(mktemp) jexec one pfctl -qvvss | normalize_pfctl_s > $states_one jexec two pfctl -qvvss | normalize_pfctl_s > $states_two } atf_test_case "route_to_1301_body" "cleanup" route_to_1301_head() { atf_set descr 'Test route-to with pfsync version 13.1' atf_set require.user root atf_set require.progs python3 scapy } route_to_1301_body() { route_to_common_head 1301 jexec one pfctl -e pft_set_rules one \ "set skip on ${epair_sync}a" \ "pass out route-to (outif 203.0.113.254)" jexec two pfctl -e pft_set_rules two \ "set skip on ${epair_sync}b" \ "pass out route-to (outif 203.0.113.254)" route_to_common_tail # Sanity check grep -qE 'all icmp 198.51.100.254 -> 203.0.113.254:8 .*, rule 0 .* route-to: 203.0.113.254@outif origif: outif' $states_one || atf_fail "State missing on router one" # With identical ruleset the routing information is recovered from the matching rule. grep -qE 'all icmp 198.51.100.254 -> 203.0.113.254:8 .*, rule 0 .* route-to: 203.0.113.254@outif' $states_two || atf_fail "State missing on router two" true } route_to_1301_cleanup() { pfsynct_cleanup } atf_test_case "route_to_1301_bad_ruleset" "cleanup" route_to_1301_bad_ruleset_head() { atf_set descr 'Test route-to with pfsync version 13.1 and incompatible ruleset' atf_set require.user root atf_set require.progs python3 scapy } route_to_1301_bad_ruleset_body() { route_to_common_head 1301 jexec one pfctl -e pft_set_rules one \ "set skip on ${epair_sync}a" \ "pass out route-to (outif 203.0.113.254)" jexec two pfctl -e pft_set_rules two \ "set debug loud" \ "set skip on ${epair_sync}b" \ "pass out route-to (outif 203.0.113.254)" \ "pass out proto tcp" atf_check -s exit:0 env PYTHONPATH=${common_dir} \ ${common_dir}/pft_ping.py \ --sendif ${epair_one}b \ --fromaddr 198.51.100.254 \ --to 203.0.113.254 \ --recvif ${epair_out_one}b route_to_common_tail # Sanity check grep -qE 'all icmp 198.51.100.254 -> 203.0.113.254:8 .*, rule 0 .* route-to: 203.0.113.254@outif origif: outif' $states_one || atf_fail "State missing on router one" # Different ruleset on each router means the routing information recovery # from rule is impossible. The state is not synced. grep -qE 'all icmp 198.51.100.254 -> 203.0.113.254:8 .*' $states_two && atf_fail "State present on router two" true } route_to_1301_bad_ruleset_cleanup() { pfsynct_cleanup } atf_test_case "route_to_1301_bad_rpool" "cleanup" route_to_1301_bad_rpool_head() { atf_set descr 'Test route-to with pfsync version 13.1 and different interface' atf_set require.user root atf_set require.progs python3 scapy } route_to_1301_bad_rpool_body() { route_to_common_head 1301 jexec one pfctl -e pft_set_rules one \ "set skip on ${epair_sync}a" \ "pass out route-to { (outif 203.0.113.254) (outif 203.0.113.254) }" jexec two pfctl -e pft_set_rules two \ "set skip on ${epair_sync}b" \ "pass out route-to { (outif 203.0.113.254) (outif 203.0.113.254) }" atf_check -s exit:0 env PYTHONPATH=${common_dir} \ ${common_dir}/pft_ping.py \ --sendif ${epair_one}b \ --fromaddr 198.51.100.254 \ --to 203.0.113.254 \ --recvif ${epair_out_one}b route_to_common_tail # Sanity check grep -qE 'all icmp 198.51.100.254 -> 203.0.113.254:8 .*, rule 0 .* route-to: 203.0.113.254@outif origif: outif' $states_one || atf_fail "State missing on router one" # The ruleset is identical but since the redirection pool contains multiple interfaces # pfsync will not attempt to recover the routing information from the rule. grep -qE 'all icmp 198.51.100.254 -> 203.0.113.254:8 .*' $states_two && atf_fail "State present on router two" true } route_to_1301_bad_rpool_cleanup() { pfsynct_cleanup } atf_test_case "route_to_1400_bad_ruleset" "cleanup" route_to_1400_bad_ruleset_head() { atf_set descr 'Test route-to with pfsync version 14.0' atf_set require.user root atf_set require.progs python3 scapy } route_to_1400_bad_ruleset_body() { route_to_common_head 1400 jexec one pfctl -e pft_set_rules one \ "set skip on ${epair_sync}a" \ "pass out route-to (outif 203.0.113.254)" jexec two pfctl -e pft_set_rules two \ "set skip on ${epair_sync}b" route_to_common_tail # Sanity check grep -qE 'all icmp 198.51.100.254 -> 203.0.113.254:8 .*, rule 0 .* route-to: 203.0.113.254@outif origif: outif' $states_one || atf_fail "State missing on router one" # Even with a different ruleset FreeBSD 14 syncs the state just fine. # There's no recovery involved, the pfsync packet contains the routing information. grep -qE 'all icmp 198.51.100.254 -> 203.0.113.254:8 .* route-to: 203.0.113.254@outif' $states_two || atf_fail "State missing on router two" true } route_to_1400_bad_ruleset_cleanup() { pfsynct_cleanup } atf_test_case "route_to_1400_bad_ifname" "cleanup" route_to_1400_bad_ifname_head() { atf_set descr 'Test route-to with pfsync version 14.0' atf_set require.user root atf_set require.progs python3 scapy } route_to_1400_bad_ifname_body() { route_to_common_head 1400 jexec one pfctl -e pft_set_rules one \ "set skip on ${epair_sync}a" \ "pass out route-to (outif 203.0.113.254)" jexec two pfctl -e jexec two ifconfig outif name outif_new pft_set_rules two \ "set skip on ${epair_sync}b" \ "pass out route-to (outif_new 203.0.113.254)" route_to_common_tail # Sanity check grep -qE 'all icmp 198.51.100.254 -> 203.0.113.254:8 .*, rule 0 .* route-to: 203.0.113.254@outif origif: outif' $states_one || atf_fail "State missing on router one" # Since FreeBSD 14 never attempts recovery of missing routing information # a state synced to a router with a different interface name is dropped. grep -qE 'all icmp 198.51.100.254 -> 203.0.113.254:8 .*' $states_two && atf_fail "State present on router two" true } route_to_1400_bad_ifname_cleanup() { pfsynct_cleanup } atf_test_case "af_to_in_floating" "cleanup" af_to_in_floating_head() { atf_set descr 'Test syncing of states created by inbound af-to rules with floating states' atf_set require.user root atf_set require.progs python3 scapy } af_to_in_floating_body() { route_to_common_head 1500 jexec one pfctl -e pft_set_rules one \ "set state-policy floating" \ "set skip on ${epair_sync}a" \ "block" \ "pass inet6 proto icmp6 icmp6-type { neighbrsol, neighbradv } keep state (no-sync)" \ "pass in on inif to 64:ff9b::/96 af-to inet from (outif) keep state" jexec two pfctl -e pft_set_rules two \ "set skip on ${epair_sync}b" \ "block" \ "pass inet6 proto icmp6 icmp6-type { neighbrsol, neighbradv } keep state (no-sync)" # ptf_ping can't deal with nat64, this test will fail but generate states atf_check -s exit:1 env PYTHONPATH=${common_dir} \ ${common_dir}/pft_ping.py \ --sendif ${epair_one}b \ --fromaddr 2001:db8:4201::fe \ --to 64:ff9b::203.0.113.254 \ --recvif ${epair_out_one}b # Allow time for sync sleep 2 states_one=$(mktemp) states_two=$(mktemp) jexec one pfctl -qvvss | normalize_pfctl_s > $states_one jexec two pfctl -qvvss | normalize_pfctl_s > $states_two # Sanity check grep -qE 'all ipv6-icmp 203.0.113.1 \(2001:db8:4201::fe\) -> 203.0.113.254:8 \(64:ff9b::cb00:71fe) .* rule 3 .* origif: inif' $states_one || atf_fail "State missing on router one" grep -qE 'all ipv6-icmp 203.0.113.1 \(2001:db8:4201::fe\) -> 203.0.113.254:8 \(64:ff9b::cb00:71fe) .* origif: inif' $states_two || atf_fail "State missing on router two" } af_to_in_floating_cleanup() { pfsynct_cleanup } atf_test_case "af_to_in_if_bound" "cleanup" af_to_in_if_bound_head() { atf_set descr 'Test syncing of states created by inbound af-to rules with if-bound states' atf_set require.user root atf_set require.progs python3 scapy } af_to_in_if_bound_body() { route_to_common_head 1500 jexec one pfctl -e pft_set_rules one \ "set state-policy if-bound" \ "set skip on ${epair_sync}a" \ "block" \ "pass inet6 proto icmp6 icmp6-type { neighbrsol, neighbradv } keep state (no-sync)" \ "pass in on inif to 64:ff9b::/96 af-to inet from (outif) keep state" jexec two pfctl -e pft_set_rules two \ "set skip on ${epair_sync}b" \ "block" \ "pass inet6 proto icmp6 icmp6-type { neighbrsol, neighbradv } keep state (no-sync)" # ptf_ping can't deal with nat64, this test will fail but generate states atf_check -s exit:1 env PYTHONPATH=${common_dir} \ ${common_dir}/pft_ping.py \ --sendif ${epair_one}b \ --fromaddr 2001:db8:4201::fe \ --to 64:ff9b::203.0.113.254 \ --recvif ${epair_out_one}b # Allow time for sync sleep 2 states_one=$(mktemp) states_two=$(mktemp) jexec one pfctl -qvvss | normalize_pfctl_s > $states_one jexec two pfctl -qvvss | normalize_pfctl_s > $states_two # Sanity check grep -qE 'outif ipv6-icmp 203.0.113.1 \(2001:db8:4201::fe\) -> 203.0.113.254:8 \(64:ff9b::cb00:71fe) .* rule 3 .* origif: inif' $states_one || atf_fail "State missing on router one" grep -qE 'outif ipv6-icmp 203.0.113.1 \(2001:db8:4201::fe\) -> 203.0.113.254:8 \(64:ff9b::cb00:71fe) .* origif: inif' $states_two || atf_fail "State missing on router two" } af_to_in_if_bound_cleanup() { pfsynct_cleanup } atf_test_case "af_to_out_if_bound" "cleanup" af_to_out_if_bound_head() { atf_set descr 'Test syncing of states created by outbound af-to rules with if-bound states' atf_set require.user root atf_set require.progs python3 scapy } af_to_out_if_bound_body() { route_to_common_head 1500 jexec one route add -inet6 -net 64:ff9b::/96 -iface outif jexec one sysctl net.inet6.ip6.forwarding=1 jexec one pfctl -e pft_set_rules one \ "set state-policy if-bound" \ "set skip on ${epair_sync}a" \ "block" \ "pass inet6 proto icmp6 icmp6-type { neighbrsol, neighbradv } keep state (no-sync)" \ "pass in on inif to 64:ff9b::/96 keep state" \ "pass out on outif to 64:ff9b::/96 af-to inet from (outif) keep state" jexec two pfctl -e pft_set_rules two \ "set skip on ${epair_sync}b" \ "block" \ "pass inet6 proto icmp6 icmp6-type { neighbrsol, neighbradv } keep state (no-sync)" # ptf_ping can't deal with nat64, this test will fail but generate states atf_check -s exit:1 env PYTHONPATH=${common_dir} \ ${common_dir}/pft_ping.py \ --sendif ${epair_one}b \ --fromaddr 2001:db8:4201::fe \ --to 64:ff9b::203.0.113.254 \ --recvif ${epair_out_one}b # Allow time for sync sleep 2 states_one=$(mktemp) states_two=$(mktemp) jexec one pfctl -qvvss | normalize_pfctl_s > $states_one jexec two pfctl -qvvss | normalize_pfctl_s > $states_two # Sanity check # st->orig_kif is the same as st->kif, so st->orig_kif is not printed. for state_regexp in \ "inif ipv6-icmp 64:ff9b::cb00:71fe\[128\] <- 2001:db8:4201::fe .* rule 3 .* creatorid: [0-9a-f]+" \ "outif icmp 203.0.113.1 \(64:ff9b::cb00:71fe\[8\]\) -> 203.0.113.254:8 \(2001:db8:4201::fe\) .* rule 4 .* creatorid: [0-9a-f]+" \ ; do grep -qE "${state_regexp}" $states_one || atf_fail "State not found for '${state_regexp}'" done for state_regexp in \ "inif ipv6-icmp 64:ff9b::cb00:71fe\[128\] <- 2001:db8:4201::fe .* creatorid: [0-9a-f]+" \ "outif icmp 203.0.113.1 \(64:ff9b::cb00:71fe\[8\]\) -> 203.0.113.254:8 \(2001:db8:4201::fe\) .* creatorid: [0-9a-f]+" \ ; do grep -qE "${state_regexp}" $states_two || atf_fail "State not found for '${state_regexp}'" done } af_to_out_if_bound_cleanup() { pfsynct_cleanup } atf_test_case "tag" "cleanup" tag_head() { atf_set descr 'Test if the pf tag is synced' atf_set require.user root atf_set require.progs python3 scapy } tag_body() { route_to_common_head 1500 jexec one pfctl -e pft_set_rules one \ "set skip on ${epair_sync}a" \ "block" \ "pass inet6 proto icmp6 icmp6-type { neighbrsol, neighbradv } keep state (no-sync)" \ "pass in on inif inet proto udp tag sometag keep state" \ "pass out on outif tagged sometag keep state (no-sync)" jexec two pfctl -e pft_set_rules two \ "set debug loud" \ "set skip on ${epair_sync}b" \ "block" \ "pass inet6 proto icmp6 icmp6-type { neighbrsol, neighbradv } keep state (no-sync)" \ "block tagged othertag" \ "pass out on outif tagged sometag keep state (no-sync)" atf_check -s exit:0 env PYTHONPATH=${common_dir} \ ${common_dir}/pft_ping.py \ --ping-type=udp \ --sendif ${epair_one}b \ --fromaddr 198.51.100.254 \ --to 203.0.113.254 \ --recvif ${epair_out_one}b # Allow time for sync sleep 2 # Force the next request to go through the 2nd router route change -net 203.0.113.0/24 198.51.100.17 atf_check -s exit:0 env PYTHONPATH=${common_dir} \ ${common_dir}/pft_ping.py \ --ping-type=udp \ --sendif ${epair_two}b \ --fromaddr 198.51.100.254 \ --to 203.0.113.254 \ --recvif ${epair_out_two}b } tag_cleanup() { pfsynct_cleanup } atf_test_case "altq_queues" "cleanup" altq_queues_head() { atf_set descr 'Test if the altq queues are synced' atf_set require.user root atf_set require.progs python3 scapy } altq_queues_body() { route_to_common_head 1500 altq_init is_altq_supported hfsc jexec one pfctl -e pft_set_rules one \ "set skip on ${epair_sync}a" \ "altq on outif bandwidth 30000b hfsc queue { default other1 other2 }" \ "queue default hfsc(linkshare 10000b default)" \ "queue other1 hfsc(linkshare 10000b)" \ "queue other2 hfsc(linkshare 10000b)" \ "block" \ "pass inet6 proto icmp6 icmp6-type { neighbrsol, neighbradv } keep state (no-sync)" \ "pass in on inif inet proto udp queue other1 keep state" \ "pass out on outif inet proto udp keep state" jexec two pfctl -e pft_set_rules two \ "set debug loud" \ "set skip on ${epair_sync}b" \ "altq on outif bandwidth 30000b hfsc queue { default other2 other1 }" \ "queue default hfsc(linkshare 10000b default)" \ "queue other2 hfsc(linkshare 10000b)" \ "queue other1 hfsc(linkshare 10000b)" \ "block" \ "pass inet6 proto icmp6 icmp6-type { neighbrsol, neighbradv } keep state (no-sync)" \ "pass out on outif inet proto udp keep state" atf_check -s exit:0 env PYTHONPATH=${common_dir} \ ${common_dir}/pft_ping.py \ --ping-type=udp \ --sendif ${epair_one}b \ --fromaddr 198.51.100.254 \ --to 203.0.113.254 \ --recvif ${epair_out_one}b queues_one=$(mktemp) jexec one pfctl -qvsq | normalize_pfctl_s > $queues_one echo " === queues one === " cat $queues_one grep -qE 'queue other1 on outif .* pkts: 1 ' $queues_one || atf_fail 'Packets not sent through queue "other1"' # Allow time for sync sleep 2 # Force the next request to go through the 2nd router route change -net 203.0.113.0/24 198.51.100.17 # Send a packet through router "two". It lacks the inbound rule # but the inbound state should have been pfsynced from router "one" # including altq queuing information. However the queues are created # on router "two" in different order and we only sync queue index, # so the packet ends up in a different queue. One must have identical # queue set on both routers! atf_check -s exit:0 env PYTHONPATH=${common_dir} \ ${common_dir}/pft_ping.py \ --ping-type=udp \ --sendif ${epair_two}b \ --fromaddr 198.51.100.254 \ --to 203.0.113.254 \ --recvif ${epair_out_two}b queues_two=$(mktemp) jexec two pfctl -qvsq | normalize_pfctl_s > $queues_two echo " === queues two === " cat $queues_two grep -qE 'queue other2 on outif .* pkts: 1 ' $queues_two || atf_fail 'Packets not sent through queue "other2"' } altq_queues_cleanup() { # Interface detaching seems badly broken in altq. If interfaces are # destroyed when shutting down the vnet and then pf is unloaded, it will # cause a kernel crash. Work around the issue by first flushing the # pf rulesets jexec one pfctl -F all jexec two pfctl -F all pfsynct_cleanup } atf_test_case "rt_af" "cleanup" rt_af_head() { atf_set descr 'Test if the rt_af is synced' atf_set require.user root atf_set require.progs python3 scapy } rt_af_body() { route_to_common_head 1500 jexec one pfctl -e pft_set_rules one \ "set skip on ${epair_sync}a" \ "block" \ "pass inet6 proto icmp6 icmp6-type { neighbrsol, neighbradv } keep state (no-sync)" \ "pass in on inif \ route-to (outif 203.0.113.254) prefer-ipv6-nexthop \ inet proto udp \ to 203.0.113.241 \ keep state" \ "pass in on inif \ route-to (outif 2001:db8:4200::fe) prefer-ipv6-nexthop \ inet proto udp \ to 203.0.113.242 \ keep state" \ "pass in on inif \ route-to (outif 2001:db8:4200::fe) prefer-ipv6-nexthop \ inet6 proto udp \ to 2001:db8:4200::f3 \ keep state" \ "pass out on outif inet proto udp keep state (no-sync)" \ "pass out on outif inet6 proto udp keep state (no-sync)" jexec two pfctl -e pft_set_rules two \ "set debug loud" \ "set skip on ${epair_sync}b" \ "block" \ "pass inet6 proto icmp6 icmp6-type { neighbrsol, neighbradv } keep state (no-sync)" \ # IPv4 packet over IPv4 gateway atf_check -s exit:0 env PYTHONPATH=${common_dir} \ ${common_dir}/pft_ping.py \ --ping-type=udp \ --sendif ${epair_one}b \ --fromaddr 198.51.100.254 \ --to 203.0.113.241 \ --recvif ${epair_out_one}b # FIXME: Routing IPv4 packets over IPv6 gateways with gateway added # with `ndp -s` causes the static NDP entry to become expired. # Pfsync tests don't use "servers" which can reply to ARP and NDP, # but such static entry for gateway and only check if a stateless # ICMP or UDP packet is forward through. # # IPv4 packert over IPv6 gateway #atf_check -s exit:0 env PYTHONPATH=${common_dir} \ # ${common_dir}/pft_ping.py \ # --ping-type=udp \ # --sendif ${epair_one}b \ # --fromaddr 198.51.100.254 \ # --to 203.0.113.242 \ # --recvif ${epair_out_one}b # IPv6 packet over IPv6 gateway atf_check -s exit:0 env PYTHONPATH=${common_dir} \ ${common_dir}/pft_ping.py \ --ping-type=udp \ --sendif ${epair_one}b \ --fromaddr 2001:db8:4211::fe \ --to 2001:db8:4200::f3 \ --recvif ${epair_out_one}b sleep 5 # Wait for pfsync states_one=$(mktemp) states_two=$(mktemp) jexec one pfctl -qvvss | normalize_pfctl_s > $states_one jexec two pfctl -qvvss | normalize_pfctl_s > $states_two echo " === states one === " cat $states_one echo " === states two === " cat $states_two for state_regexp in \ "all udp 203.0.113.241:9 <- 198.51.100.254 .* route-to: 203.0.113.254@outif origif: inif" \ "all udp 2001:db8:4200::f3\[9\] <- 2001:db8:4211::fe .* route-to: 2001:db8:4200::fe@outif origif: inif" \ ; do grep -qE "${state_regexp}" $states_two || atf_fail "State not found for '${state_regexp}' on router two" done } rt_af_cleanup() { jexec one pfctl -qvvsr jexec one pfctl -qvvss jexec one arp -an jexec one ndp -an pfsynct_cleanup } atf_init_test_cases() { atf_add_test_case "basic" atf_add_test_case "basic_defer" atf_add_test_case "defer" atf_add_test_case "bulk" atf_add_test_case "pbr" atf_add_test_case "pfsync_pbr" atf_add_test_case "ipsec" atf_add_test_case "timeout" atf_add_test_case "basic_ipv6_unicast" atf_add_test_case "basic_ipv6" atf_add_test_case "rtable" atf_add_test_case "route_to_1301" atf_add_test_case "route_to_1301_bad_ruleset" atf_add_test_case "route_to_1301_bad_rpool" atf_add_test_case "route_to_1400_bad_ruleset" atf_add_test_case "route_to_1400_bad_ifname" atf_add_test_case "af_to_in_floating" atf_add_test_case "af_to_in_if_bound" atf_add_test_case "af_to_out_if_bound" atf_add_test_case "tag" atf_add_test_case "altq_queues" atf_add_test_case "rt_af" }