Path: blob/main/tests/sys/netpfil/pf/killstate.sh
105322 views
#1# SPDX-License-Identifier: BSD-2-Clause2#3# Copyright (c) 2021 Rubicon Communications, LLC (Netgate)4#5# Redistribution and use in source and binary forms, with or without6# modification, are permitted provided that the following conditions7# are met:8# 1. Redistributions of source code must retain the above copyright9# notice, this list of conditions and the following disclaimer.10# 2. Redistributions in binary form must reproduce the above copyright11# notice, this list of conditions and the following disclaimer in the12# documentation and/or other materials provided with the distribution.13#14# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND15# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE16# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE17# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE18# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL19# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS20# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)21# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT22# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY23# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF24# SUCH DAMAGE.2526. $(atf_get_srcdir)/utils.subr2728common_dir=$(atf_get_srcdir)/../common2930find_state()31{32jail=${1:-alcatraz}33ip=${2:-192.0.2.2}3435jexec ${jail} pfctl -ss | grep icmp | grep ${ip}36}3738find_state_v6()39{40jexec alcatraz pfctl -ss | grep icmp | grep 2001:db8::241}424344atf_test_case "v4" "cleanup"45v4_head()46{47atf_set descr 'Test killing states by IPv4 address'48atf_set require.user root49atf_set require.progs python3 scapy50}5152v4_body()53{54pft_init5556epair=$(vnet_mkepair)57ifconfig ${epair}a 192.0.2.1/24 up5859vnet_mkjail alcatraz ${epair}b60jexec alcatraz ifconfig ${epair}b 192.0.2.2/24 up61jexec alcatraz pfctl -e6263pft_set_rules alcatraz "block all" \64"pass in proto icmp" \65"set skip on lo"6667# Sanity check & establish state68atf_check -s exit:0 -o ignore ${common_dir}/pft_ping.py \69--sendif ${epair}a \70--to 192.0.2.2 \71--replyif ${epair}a7273# Change rules to now deny the ICMP traffic74pft_set_rules noflush alcatraz "block all"75if ! find_state;76then77atf_fail "Setting new rules removed the state."78fi7980# Killing with the wrong IP doesn't affect our state81jexec alcatraz pfctl -k 192.0.2.382if ! find_state;83then84atf_fail "Killing with the wrong IP removed our state."85fi8687# Killing with one correct address and one incorrect doesn't kill the state88jexec alcatraz pfctl -k 192.0.2.1 -k 192.0.2.389if ! find_state;90then91atf_fail "Killing with one wrong IP removed our state."92fi9394# Killing with correct address does remove the state95jexec alcatraz pfctl -k 192.0.2.196if find_state;97then98atf_fail "Killing with the correct IP did not remove our state."99fi100}101102v4_cleanup()103{104pft_cleanup105}106107atf_test_case "src_dst" "cleanup"108src_dst_head()109{110atf_set descr 'Test killing a state with source and destination specified'111atf_set require.user root112atf_set require.progs python3 scapy113}114115src_dst_body()116{117pft_init118119epair=$(vnet_mkepair)120ifconfig ${epair}a 192.0.2.1/24 up121122vnet_mkjail alcatraz ${epair}b123jexec alcatraz ifconfig ${epair}b 192.0.2.2/24 up124jexec alcatraz pfctl -e125126pft_set_rules alcatraz "block all" \127"pass in proto icmp" \128"set skip on lo"129130# Sanity check & establish state131atf_check -s exit:0 -o ignore ${common_dir}/pft_ping.py \132--sendif ${epair}a \133--to 192.0.2.2 \134--replyif ${epair}a135136# Change rules to now deny the ICMP traffic137pft_set_rules noflush alcatraz "block all"138if ! find_state;139then140atf_fail "Setting new rules removed the state."141fi142143# Killing with the wrong source IP doesn't affect our state144jexec alcatraz pfctl -k 192.0.2.3 -k 192.0.2.2145if ! find_state;146then147atf_fail "Killing with the wrong source IP removed our state."148fi149150# Killing with the wrong destination IP doesn't affect our state151jexec alcatraz pfctl -k 192.0.2.1 -k 192.0.2.3152if ! find_state;153then154atf_fail "Killing with the wrong destination IP removed our state."155fi156157# But it does with the correct one158jexec alcatraz pfctl -k 192.0.2.1 -k 192.0.2.2159if find_state;160then161atf_fail "Killing with the correct IPs did not remove our state."162fi163}164165src_dst_cleanup()166{167pft_cleanup168}169170atf_test_case "v6" "cleanup"171v6_head()172{173atf_set descr 'Test killing states by IPv6 address'174atf_set require.user root175atf_set require.progs python3 scapy176}177178v6_body()179{180pft_init181182epair=$(vnet_mkepair)183ifconfig ${epair}a inet6 2001:db8::1/64 up no_dad184185vnet_mkjail alcatraz ${epair}b186jexec alcatraz ifconfig ${epair}b inet6 2001:db8::2/64 up no_dad187jexec alcatraz pfctl -e188189pft_set_rules alcatraz "block all" \190"pass quick inet6 proto ipv6-icmp all icmp6-type { neighbrsol, neighbradv } no state" \191"pass in proto icmp6" \192"set skip on lo"193194# Sanity check & establish state195atf_check -s exit:0 -o ignore ${common_dir}/pft_ping.py \196--sendif ${epair}a \197--fromaddr 2001:db8::1 \198--to 2001:db8::2 \199--replyif ${epair}a200201# Change rules to now deny the ICMP traffic202pft_set_rules noflush alcatraz "block all"203if ! find_state_v6;204then205atf_fail "Setting new rules removed the state."206fi207208# Killing with the wrong IP doesn't affect our state209jexec alcatraz pfctl -k 2001:db8::3210if ! find_state_v6;211then212atf_fail "Killing with the wrong IP removed our state."213fi214215# Killing with one correct address and one incorrect doesn't kill the state216jexec alcatraz pfctl -k 2001:db8::1 -k 2001:db8::3217if ! find_state_v6;218then219atf_fail "Killing with one wrong IP removed our state."220fi221222# Killing with correct address does remove the state223jexec alcatraz pfctl -k 2001:db8::1224if find_state_v6;225then226atf_fail "Killing with the correct IP did not remove our state."227fi228}229230v6_cleanup()231{232pft_cleanup233}234235atf_test_case "label" "cleanup"236label_head()237{238atf_set descr 'Test killing states by label'239atf_set require.user root240atf_set require.progs python3 scapy241}242243label_body()244{245pft_init246247epair=$(vnet_mkepair)248ifconfig ${epair}a 192.0.2.1/24 up249250vnet_mkjail alcatraz ${epair}b251jexec alcatraz ifconfig ${epair}b 192.0.2.2/24 up252jexec alcatraz pfctl -e253254pft_set_rules alcatraz "block all" \255"pass in proto tcp label bar" \256"pass in proto icmp label foo" \257"set skip on lo"258259# Sanity check & establish state260atf_check -s exit:0 -o ignore ${common_dir}/pft_ping.py \261--sendif ${epair}a \262--to 192.0.2.2 \263--replyif ${epair}a264265# Change rules to now deny the ICMP traffic266pft_set_rules noflush alcatraz "block all"267if ! find_state;268then269atf_fail "Setting new rules removed the state."270fi271272# Killing a label on a different rules keeps the state273jexec alcatraz pfctl -k label -k bar274if ! find_state;275then276atf_fail "Killing a different label removed the state."277fi278279# Killing a non-existing label keeps the state280jexec alcatraz pfctl -k label -k baz281if ! find_state;282then283atf_fail "Killing a non-existing label removed the state."284fi285286# Killing the correct label kills the state287jexec alcatraz pfctl -k label -k foo288if find_state;289then290atf_fail "Killing the state did not remove it."291fi292}293294label_cleanup()295{296pft_cleanup297}298299atf_test_case "multilabel" "cleanup"300multilabel_head()301{302atf_set descr 'Test killing states with multiple labels by label'303atf_set require.user root304atf_set require.progs python3 scapy305}306307multilabel_body()308{309pft_init310311epair=$(vnet_mkepair)312ifconfig ${epair}a 192.0.2.1/24 up313314vnet_mkjail alcatraz ${epair}b315jexec alcatraz ifconfig ${epair}b 192.0.2.2/24 up316jexec alcatraz pfctl -e317318pft_set_rules alcatraz "block all" \319"pass in proto icmp label foo label bar" \320"set skip on lo"321322# Sanity check & establish state323atf_check -s exit:0 -o ignore ${common_dir}/pft_ping.py \324--sendif ${epair}a \325--to 192.0.2.2 \326--replyif ${epair}a327328# Change rules to now deny the ICMP traffic329pft_set_rules noflush alcatraz "block all"330if ! find_state;331then332atf_fail "Setting new rules removed the state."333fi334335# Killing a label on a different rules keeps the state336jexec alcatraz pfctl -k label -k baz337if ! find_state;338then339atf_fail "Killing a different label removed the state."340fi341342# Killing the state with the last label works343jexec alcatraz pfctl -k label -k bar344if find_state;345then346atf_fail "Killing with the last label did not remove the state."347fi348349pft_set_rules alcatraz "block all" \350"pass in proto icmp label foo label bar" \351"set skip on lo"352353# Reestablish state354atf_check -s exit:0 -o ignore ${common_dir}/pft_ping.py \355--sendif ${epair}a \356--to 192.0.2.2 \357--replyif ${epair}a358359# Change rules to now deny the ICMP traffic360pft_set_rules noflush alcatraz "block all"361if ! find_state;362then363atf_fail "Setting new rules removed the state."364fi365366# Killing with the first label works too367jexec alcatraz pfctl -k label -k foo368if find_state;369then370atf_fail "Killing with the first label did not remove the state."371fi372}373374multilabel_cleanup()375{376pft_cleanup377}378379atf_test_case "gateway" "cleanup"380gateway_head()381{382atf_set descr 'Test killing states by route-to/reply-to address'383atf_set require.user root384atf_set require.progs python3 scapy385}386387gateway_body()388{389pft_init390391epair=$(vnet_mkepair)392ifconfig ${epair}a 192.0.2.1/24 up393394vnet_mkjail alcatraz ${epair}b395jexec alcatraz ifconfig ${epair}b 192.0.2.2/24 up396jexec alcatraz pfctl -e397398pft_set_rules alcatraz "block all" \399"pass in reply-to (${epair}b 192.0.2.1) proto icmp" \400"set skip on lo"401402# Sanity check & establish state403# Note: use pft_ping so we always use the same ID, so pf considers all404# echo requests part of the same flow.405atf_check -s exit:0 -o ignore ${common_dir}/pft_ping.py \406--sendif ${epair}a \407--to 192.0.2.2 \408--replyif ${epair}a409410# Change rules to now deny the ICMP traffic411pft_set_rules noflush alcatraz "block all"412if ! find_state;413then414atf_fail "Setting new rules removed the state."415fi416417# Killing with a different gateway does not affect our state418jexec alcatraz pfctl -k gateway -k 192.0.2.2419if ! find_state;420then421atf_fail "Killing with a different gateway removed the state."422fi423424# Killing states with the relevant gateway does terminate our state425jexec alcatraz pfctl -k gateway -k 192.0.2.1426if find_state;427then428atf_fail "Killing with the gateway did not remove the state."429fi430}431432gateway_cleanup()433{434pft_cleanup435}436437atf_test_case "match" "cleanup"438match_head()439{440atf_set descr 'Test killing matching states'441atf_set require.user root442}443444wait_for_state()445{446jail=$1447addr=$2448449while ! jexec $jail pfctl -s s | grep $addr >/dev/null;450do451sleep .1452done453}454455match_body()456{457pft_init458459epair_one=$(vnet_mkepair)460ifconfig ${epair_one}a 192.0.2.1/24 up461462epair_two=$(vnet_mkepair)463464vnet_mkjail alcatraz ${epair_one}b ${epair_two}a465jexec alcatraz ifconfig ${epair_one}b 192.0.2.2/24 up466jexec alcatraz ifconfig ${epair_two}a 198.51.100.1/24 up467jexec alcatraz sysctl net.inet.ip.forwarding=1468jexec alcatraz pfctl -e469470vnet_mkjail singsing ${epair_two}b471jexec singsing ifconfig ${epair_two}b 198.51.100.2/24 up472jexec singsing route add default 198.51.100.1473jexec singsing /usr/sbin/inetd -p ${PWD}/inetd-echo.pid \474$(atf_get_srcdir)/echo_inetd.conf475476route add 198.51.100.0/24 192.0.2.2477478pft_set_rules alcatraz \479"nat on ${epair_two}a from 192.0.2.0/24 -> (${epair_two}a)" \480"pass all"481482nc 198.51.100.2 7 &483wait_for_state alcatraz 192.0.2.1484485# Expect two states486states=$(jexec alcatraz pfctl -s s | grep 192.0.2.1 | wc -l)487if [ $states -ne 2 ] ;488then489atf_fail "Expected two states, found $states"490fi491492# If we don't kill the matching NAT state one should be left493jexec alcatraz pfctl -k 192.0.2.1494states=$(jexec alcatraz pfctl -s s | grep 192.0.2.1 | wc -l)495if [ $states -ne 1 ] ;496then497atf_fail "Expected one states, found $states"498fi499500# Flush501jexec alcatraz pfctl -F states502503nc 198.51.100.2 7 &504wait_for_state alcatraz 192.0.2.1505506# Kill matching states, expect all of them to be gone507jexec alcatraz pfctl -M -k 192.0.2.1508states=$(jexec alcatraz pfctl -s s | grep 192.0.2.1 | wc -l)509if [ $states -ne 0 ] ;510then511atf_fail "Expected zero states, found $states"512fi513}514515match_cleanup()516{517pft_cleanup518}519520atf_test_case "interface" "cleanup"521interface_head()522{523atf_set descr 'Test killing states based on interface'524atf_set require.user root525atf_set require.progs python3 scapy526}527528interface_body()529{530pft_init531532epair=$(vnet_mkepair)533ifconfig ${epair}a 192.0.2.1/24 up534535vnet_mkjail alcatraz ${epair}b536jexec alcatraz ifconfig ${epair}b 192.0.2.2/24 up537jexec alcatraz pfctl -e538539pft_set_rules alcatraz "block all" \540"pass in proto icmp" \541"set skip on lo"542543# Sanity check & establish state544atf_check -s exit:0 -o ignore ${common_dir}/pft_ping.py \545--sendif ${epair}a \546--to 192.0.2.2 \547--replyif ${epair}a548549# Change rules to now deny the ICMP traffic550pft_set_rules noflush alcatraz "block all"551if ! find_state;552then553atf_fail "Setting new rules removed the state."554fi555556# Flushing states on a different interface doesn't affect our state557jexec alcatraz pfctl -i ${epair}a -Fs558if ! find_state;559then560atf_fail "Flushing on a different interface removed the state."561fi562563# Flushing on the correct interface does (even with floating states)564jexec alcatraz pfctl -i ${epair}b -Fs565if find_state;566then567atf_fail "Flushing on a the interface did not remove the state."568fi569}570571interface_cleanup()572{573pft_cleanup574}575576atf_test_case "id" "cleanup"577id_head()578{579atf_set descr 'Test killing states by id'580atf_set require.user root581atf_set require.progs python3 scapy582}583584id_body()585{586pft_init587588epair=$(vnet_mkepair)589ifconfig ${epair}a 192.0.2.1/24 up590591vnet_mkjail alcatraz ${epair}b592jexec alcatraz ifconfig ${epair}b 192.0.2.2/24 up593jexec alcatraz pfctl -e594595pft_set_rules alcatraz "block all" \596"pass in proto tcp" \597"pass in proto icmp" \598"set skip on lo"599600# Sanity check & establish state601atf_check -s exit:0 -o ignore ${common_dir}/pft_ping.py \602--sendif ${epair}a \603--to 192.0.2.2 \604--replyif ${epair}a605606# Change rules to now deny the ICMP traffic607pft_set_rules noflush alcatraz "block all"608if ! find_state;609then610atf_fail "Setting new rules removed the state."611fi612613# Get the state ID614id=$(jexec alcatraz pfctl -ss -vvv | grep -A 3 icmp |615grep -A 3 192.0.2.2 | awk '/id:/ { printf("%s/%s", $2, $4); }')616617# Kill the wrong ID618jexec alcatraz pfctl -k id -k 1619if ! find_state;620then621atf_fail "Killing a different ID removed the state."622fi623624# Kill the correct ID625jexec alcatraz pfctl -k id -k ${id}626if find_state;627then628atf_fail "Killing the state did not remove it."629fi630}631632id_cleanup()633{634pft_cleanup635}636637atf_test_case "key" "cleanup"638key_head()639{640atf_set descr 'Test killing states by their key'641atf_set require.user root642atf_set require.progs python3 scapy643}644645key_body()646{647pft_init648649epair=$(vnet_mkepair)650ifconfig ${epair}a 192.0.2.1/24 up651652vnet_mkjail alcatraz ${epair}b653jexec alcatraz ifconfig ${epair}b 192.0.2.2/24 up654jexec alcatraz pfctl -e655656pft_set_rules alcatraz \657"block all" \658"pass in proto tcp" \659"pass in proto icmp"660661# Sanity check & establish state662atf_check -s exit:0 -o ignore ${common_dir}/pft_ping.py \663--sendif ${epair}a \664--to 192.0.2.2 \665--replyif ${epair}a666667# Get the state key668key=$(jexec alcatraz pfctl -ss -vvv | awk '/icmp/ { print($2 " " $3 " " $4 " " $5); }')669bad_key=$(echo ${key} | sed 's/icmp/tcp/')670671# Kill the wrong key672atf_check -s exit:0 -e "match:killed 0 states" \673jexec alcatraz pfctl -k key -k "${bad_key}"674if ! find_state;675then676atf_fail "Killing a different ID removed the state."677fi678679# Kill the correct key680atf_check -s exit:0 -e "match:killed 1 states" \681jexec alcatraz pfctl -k key -k "${key}"682if find_state;683then684atf_fail "Killing the state did not remove it."685fi686}687688key_cleanup()689{690pft_cleanup691}692693atf_test_case "nat" "cleanup"694nat_head()695{696atf_set descr 'Test killing states by their NAT-ed IP address'697atf_set require.user root698atf_set require.progs python3 scapy699}700701nat_body()702{703pft_init704j="killstate:nat"705706epair_c=$(vnet_mkepair)707epair_srv=$(vnet_mkepair)708709vnet_mkjail ${j}c ${epair_c}a710ifconfig -j ${j}c ${epair_c}a inet 192.0.2.2/24 up711jexec ${j}c route add default 192.0.2.1712713vnet_mkjail ${j}srv ${epair_srv}a714ifconfig -j ${j}srv ${epair_srv}a inet 198.51.100.2/24 up715716vnet_mkjail ${j}r ${epair_c}b ${epair_srv}b717ifconfig -j ${j}r ${epair_c}b inet 192.0.2.1/24 up718ifconfig -j ${j}r ${epair_srv}b inet 198.51.100.1/24 up719jexec ${j}r sysctl net.inet.ip.forwarding=1720721jexec ${j}r pfctl -e722pft_set_rules ${j}r \723"nat on ${epair_srv}b inet from 192.0.2.0/24 to any -> (${epair_srv}b)"724725# Sanity check726atf_check -s exit:0 -o ignore \727jexec ${j}c ping -c 1 192.0.2.1728atf_check -s exit:0 -o ignore \729jexec ${j}srv ping -c 1 198.51.100.1730atf_check -s exit:0 -o ignore \731jexec ${j}c ping -c 1 198.51.100.2732733# Establish state734# Note: use pft_ping so we always use the same ID, so pf considers all735# echo requests part of the same flow.736atf_check -s exit:0 -o ignore jexec ${j}c ${common_dir}/pft_ping.py \737--sendif ${epair_c}a \738--to 198.51.100.1 \739--replyif ${epair_c}a740741# There's NAT here, so the source IP will be 198.51.100.1742if ! find_state ${j}r 198.51.100.1;743then744atf_fail "Expected state not found"745fi746747# By NAT-ed address?748jexec ${j}r pfctl -k nat -k 192.0.2.2749750if find_state ${j}r 198.51.100.1;751then752jexec ${j}r pfctl -ss -v753atf_fail "Failed to remove state"754fi755}756757nat_cleanup()758{759pft_cleanup760}761762atf_init_test_cases()763{764atf_add_test_case "v4"765atf_add_test_case "src_dst"766atf_add_test_case "v6"767atf_add_test_case "label"768atf_add_test_case "multilabel"769atf_add_test_case "gateway"770atf_add_test_case "match"771atf_add_test_case "interface"772atf_add_test_case "id"773atf_add_test_case "key"774atf_add_test_case "nat"775}776777778