1# $FreeBSD$ 2# 3# SPDX-License-Identifier: BSD-2-Clause 4# 5# Copyright (c) 2021 Rubicon Communications, LLC (Netgate) 6# 7# Redistribution and use in source and binary forms, with or without 8# modification, are permitted provided that the following conditions 9# are met: 10# 1. Redistributions of source code must retain the above copyright 11# notice, this list of conditions and the following disclaimer. 12# 2. Redistributions in binary form must reproduce the above copyright 13# notice, this list of conditions and the following disclaimer in the 14# documentation and/or other materials provided with the distribution. 15# 16# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26# SUCH DAMAGE. 27 28. $(atf_get_srcdir)/utils.subr 29 30common_dir=$(atf_get_srcdir)/../common 31 32find_state() 33{ 34 jexec alcatraz pfctl -ss | grep icmp | grep 192.0.2.2 35} 36 37find_state_v6() 38{ 39 jexec alcatraz pfctl -ss | grep icmp | grep 2001:db8::2 40} 41 42 43atf_test_case "v4" "cleanup" 44v4_head() 45{ 46 atf_set descr 'Test killing states by IPv4 address' 47 atf_set require.user root 48 atf_set require.progs scapy 49} 50 51v4_body() 52{ 53 pft_init 54 55 epair=$(vnet_mkepair) 56 ifconfig ${epair}a 192.0.2.1/24 up 57 58 vnet_mkjail alcatraz ${epair}b 59 jexec alcatraz ifconfig ${epair}b 192.0.2.2/24 up 60 jexec alcatraz pfctl -e 61 62 pft_set_rules alcatraz "block all" \ 63 "pass in proto icmp" \ 64 "set skip on lo" 65 66 # Sanity check & establish state 67 atf_check -s exit:0 -o ignore ${common_dir}/pft_ping.py \ 68 --sendif ${epair}a \ 69 --to 192.0.2.2 \ 70 --replyif ${epair}a 71 72 # Change rules to now deny the ICMP traffic 73 pft_set_rules noflush alcatraz "block all" 74 if ! find_state; 75 then 76 atf_fail "Setting new rules removed the state." 77 fi 78 79 # Killing with the wrong IP doesn't affect our state 80 jexec alcatraz pfctl -k 192.0.2.3 81 if ! find_state; 82 then 83 atf_fail "Killing with the wrong IP removed our state." 84 fi 85 86 # Killing with one correct address and one incorrect doesn't kill the state 87 jexec alcatraz pfctl -k 192.0.2.1 -k 192.0.2.3 88 if ! find_state; 89 then 90 atf_fail "Killing with one wrong IP removed our state." 91 fi 92 93 # Killing with correct address does remove the state 94 jexec alcatraz pfctl -k 192.0.2.1 95 if find_state; 96 then 97 atf_fail "Killing with the correct IP did not remove our state." 98 fi 99} 100 101v4_cleanup() 102{ 103 pft_cleanup 104} 105 106atf_test_case "v6" "cleanup" 107v6_head() 108{ 109 atf_set descr 'Test killing states by IPv6 address' 110 atf_set require.user root 111 atf_set require.progs scapy 112} 113 114v6_body() 115{ 116 pft_init 117 118 if [ "$(atf_config_get ci false)" = "true" ]; then 119 atf_skip "https://bugs.freebsd.org/260458" 120 fi 121 122 epair=$(vnet_mkepair) 123 ifconfig ${epair}a inet6 2001:db8::1/64 up no_dad 124 125 vnet_mkjail alcatraz ${epair}b 126 jexec alcatraz ifconfig ${epair}b inet6 2001:db8::2/64 up no_dad 127 jexec alcatraz pfctl -e 128 129 pft_set_rules alcatraz "block all" \ 130 "pass in proto icmp6" \ 131 "set skip on lo" 132 133 # Sanity check & establish state 134 atf_check -s exit:0 -o ignore ${common_dir}/pft_ping.py \ 135 --sendif ${epair}a \ 136 --to 2001:db8::2 \ 137 --replyif ${epair}a 138 139 # Change rules to now deny the ICMP traffic 140 pft_set_rules noflush alcatraz "block all" 141 if ! find_state_v6; 142 then 143 atf_fail "Setting new rules removed the state." 144 fi 145 146 # Killing with the wrong IP doesn't affect our state 147 jexec alcatraz pfctl -k 2001:db8::3 148 if ! find_state_v6; 149 then 150 atf_fail "Killing with the wrong IP removed our state." 151 fi 152 153 # Killing with one correct address and one incorrect doesn't kill the state 154 jexec alcatraz pfctl -k 2001:db8::1 -k 2001:db8::3 155 if ! find_state_v6; 156 then 157 atf_fail "Killing with one wrong IP removed our state." 158 fi 159 160 # Killing with correct address does remove the state 161 jexec alcatraz pfctl -k 2001:db8::1 162 if find_state_v6; 163 then 164 atf_fail "Killing with the correct IP did not remove our state." 165 fi 166} 167 168v6_cleanup() 169{ 170 pft_cleanup 171} 172 173atf_test_case "label" "cleanup" 174label_head() 175{ 176 atf_set descr 'Test killing states by label' 177 atf_set require.user root 178 atf_set require.progs scapy 179} 180 181label_body() 182{ 183 pft_init 184 185 epair=$(vnet_mkepair) 186 ifconfig ${epair}a 192.0.2.1/24 up 187 188 vnet_mkjail alcatraz ${epair}b 189 jexec alcatraz ifconfig ${epair}b 192.0.2.2/24 up 190 jexec alcatraz pfctl -e 191 192 pft_set_rules alcatraz "block all" \ 193 "pass in proto tcp label bar" \ 194 "pass in proto icmp label foo" \ 195 "set skip on lo" 196 197 # Sanity check & establish state 198 atf_check -s exit:0 -o ignore ${common_dir}/pft_ping.py \ 199 --sendif ${epair}a \ 200 --to 192.0.2.2 \ 201 --replyif ${epair}a 202 203 # Change rules to now deny the ICMP traffic 204 pft_set_rules noflush alcatraz "block all" 205 if ! find_state; 206 then 207 atf_fail "Setting new rules removed the state." 208 fi 209 210 # Killing a label on a different rules keeps the state 211 jexec alcatraz pfctl -k label -k bar 212 if ! find_state; 213 then 214 atf_fail "Killing a different label removed the state." 215 fi 216 217 # Killing a non-existing label keeps the state 218 jexec alcatraz pfctl -k label -k baz 219 if ! find_state; 220 then 221 atf_fail "Killing a non-existing label removed the state." 222 fi 223 224 # Killing the correct label kills the state 225 jexec alcatraz pfctl -k label -k foo 226 if find_state; 227 then 228 atf_fail "Killing the state did not remove it." 229 fi 230} 231 232label_cleanup() 233{ 234 pft_cleanup 235} 236 237atf_test_case "multilabel" "cleanup" 238multilabel_head() 239{ 240 atf_set descr 'Test killing states with multiple labels by label' 241 atf_set require.user root 242 atf_set require.progs scapy 243} 244 245multilabel_body() 246{ 247 pft_init 248 249 epair=$(vnet_mkepair) 250 ifconfig ${epair}a 192.0.2.1/24 up 251 252 vnet_mkjail alcatraz ${epair}b 253 jexec alcatraz ifconfig ${epair}b 192.0.2.2/24 up 254 jexec alcatraz pfctl -e 255 256 pft_set_rules alcatraz "block all" \ 257 "pass in proto icmp label foo label bar" \ 258 "set skip on lo" 259 260 # Sanity check & establish state 261 atf_check -s exit:0 -o ignore ${common_dir}/pft_ping.py \ 262 --sendif ${epair}a \ 263 --to 192.0.2.2 \ 264 --replyif ${epair}a 265 266 # Change rules to now deny the ICMP traffic 267 pft_set_rules noflush alcatraz "block all" 268 if ! find_state; 269 then 270 atf_fail "Setting new rules removed the state." 271 fi 272 273 # Killing a label on a different rules keeps the state 274 jexec alcatraz pfctl -k label -k baz 275 if ! find_state; 276 then 277 atf_fail "Killing a different label removed the state." 278 fi 279 280 # Killing the state with the last label works 281 jexec alcatraz pfctl -k label -k bar 282 if find_state; 283 then 284 atf_fail "Killing with the last label did not remove the state." 285 fi 286 287 pft_set_rules alcatraz "block all" \ 288 "pass in proto icmp label foo label bar" \ 289 "set skip on lo" 290 291 # Reestablish state 292 atf_check -s exit:0 -o ignore ${common_dir}/pft_ping.py \ 293 --sendif ${epair}a \ 294 --to 192.0.2.2 \ 295 --replyif ${epair}a 296 297 # Change rules to now deny the ICMP traffic 298 pft_set_rules noflush alcatraz "block all" 299 if ! find_state; 300 then 301 atf_fail "Setting new rules removed the state." 302 fi 303 304 # Killing with the first label works too 305 jexec alcatraz pfctl -k label -k foo 306 if find_state; 307 then 308 atf_fail "Killing with the first label did not remove the state." 309 fi 310} 311 312multilabel_cleanup() 313{ 314 pft_cleanup 315} 316 317atf_test_case "gateway" "cleanup" 318gateway_head() 319{ 320 atf_set descr 'Test killing states by route-to/reply-to address' 321 atf_set require.user root 322 atf_set require.progs scapy 323} 324 325gateway_body() 326{ 327 pft_init 328 329 epair=$(vnet_mkepair) 330 ifconfig ${epair}a 192.0.2.1/24 up 331 332 vnet_mkjail alcatraz ${epair}b 333 jexec alcatraz ifconfig ${epair}b 192.0.2.2/24 up 334 jexec alcatraz pfctl -e 335 336 pft_set_rules alcatraz "block all" \ 337 "pass in reply-to (${epair}b 192.0.2.1) proto icmp" \ 338 "set skip on lo" 339 340 # Sanity check & establish state 341 # Note: use pft_ping so we always use the same ID, so pf considers all 342 # echo requests part of the same flow. 343 atf_check -s exit:0 -o ignore ${common_dir}/pft_ping.py \ 344 --sendif ${epair}a \ 345 --to 192.0.2.2 \ 346 --replyif ${epair}a 347 348 # Change rules to now deny the ICMP traffic 349 pft_set_rules noflush alcatraz "block all" 350 if ! find_state; 351 then 352 atf_fail "Setting new rules removed the state." 353 fi 354 355 # Killing with a different gateway does not affect our state 356 jexec alcatraz pfctl -k gateway -k 192.0.2.2 357 if ! find_state; 358 then 359 atf_fail "Killing with a different gateway removed the state." 360 fi 361 362 # Killing states with the relevant gateway does terminate our state 363 jexec alcatraz pfctl -k gateway -k 192.0.2.1 364 if find_state; 365 then 366 atf_fail "Killing with the gateway did not remove the state." 367 fi 368} 369 370gateway_cleanup() 371{ 372 pft_cleanup 373} 374 375atf_test_case "match" "cleanup" 376match_head() 377{ 378 atf_set descr 'Test killing matching states' 379 atf_set require.user root 380} 381 382wait_for_state() 383{ 384 jail=$1 385 addr=$2 386 387 while ! jexec $jail pfctl -s s | grep $addr >/dev/null; 388 do 389 sleep .1 390 done 391} 392 393match_body() 394{ 395 pft_init 396 397 epair_one=$(vnet_mkepair) 398 ifconfig ${epair_one}a 192.0.2.1/24 up 399 400 epair_two=$(vnet_mkepair) 401 402 vnet_mkjail alcatraz ${epair_one}b ${epair_two}a 403 jexec alcatraz ifconfig ${epair_one}b 192.0.2.2/24 up 404 jexec alcatraz ifconfig ${epair_two}a 198.51.100.1/24 up 405 jexec alcatraz sysctl net.inet.ip.forwarding=1 406 jexec alcatraz pfctl -e 407 408 vnet_mkjail singsing ${epair_two}b 409 jexec singsing ifconfig ${epair_two}b 198.51.100.2/24 up 410 jexec singsing route add default 198.51.100.1 411 jexec singsing /usr/sbin/inetd -p inetd-echo.pid \ 412 $(atf_get_srcdir)/echo_inetd.conf 413 414 route add 198.51.100.0/24 192.0.2.2 415 416 pft_set_rules alcatraz \ 417 "nat on ${epair_two}a from 192.0.2.0/24 -> (${epair_two}a)" \ 418 "pass all" 419 420 nc 198.51.100.2 7 & 421 wait_for_state alcatraz 192.0.2.1 422 423 # Expect two states 424 states=$(jexec alcatraz pfctl -s s | grep 192.0.2.1 | wc -l) 425 if [ $states -ne 2 ] ; 426 then 427 atf_fail "Expected two states, found $states" 428 fi 429 430 # If we don't kill the matching NAT state one should be left 431 jexec alcatraz pfctl -k 192.0.2.1 432 states=$(jexec alcatraz pfctl -s s | grep 192.0.2.1 | wc -l) 433 if [ $states -ne 1 ] ; 434 then 435 atf_fail "Expected one states, found $states" 436 fi 437 438 # Flush 439 jexec alcatraz pfctl -F states 440 441 nc 198.51.100.2 7 & 442 wait_for_state alcatraz 192.0.2.1 443 444 # Kill matching states, expect all of them to be gone 445 jexec alcatraz pfctl -M -k 192.0.2.1 446 states=$(jexec alcatraz pfctl -s s | grep 192.0.2.1 | wc -l) 447 if [ $states -ne 0 ] ; 448 then 449 atf_fail "Expected zero states, found $states" 450 fi 451} 452 453match_cleanup() 454{ 455 pft_cleanup 456} 457 458atf_test_case "interface" "cleanup" 459interface_head() 460{ 461 atf_set descr 'Test killing states based on interface' 462 atf_set require.user root 463 atf_set require.progs scapy 464} 465 466interface_body() 467{ 468 pft_init 469 470 epair=$(vnet_mkepair) 471 ifconfig ${epair}a 192.0.2.1/24 up 472 473 vnet_mkjail alcatraz ${epair}b 474 jexec alcatraz ifconfig ${epair}b 192.0.2.2/24 up 475 jexec alcatraz pfctl -e 476 477 pft_set_rules alcatraz "block all" \ 478 "pass in proto icmp" \ 479 "set skip on lo" 480 481 # Sanity check & establish state 482 atf_check -s exit:0 -o ignore ${common_dir}/pft_ping.py \ 483 --sendif ${epair}a \ 484 --to 192.0.2.2 \ 485 --replyif ${epair}a 486 487 # Change rules to now deny the ICMP traffic 488 pft_set_rules noflush alcatraz "block all" 489 if ! find_state; 490 then 491 atf_fail "Setting new rules removed the state." 492 fi 493 494 # Flushing states on a different interface doesn't affect our state 495 jexec alcatraz pfctl -i ${epair}a -Fs 496 if ! find_state; 497 then 498 atf_fail "Flushing on a different interface removed the state." 499 fi 500 501 # Flushing on the correct interface does (even with floating states) 502 jexec alcatraz pfctl -i ${epair}b -Fs 503 if find_state; 504 then 505 atf_fail "Flushing on a the interface did not remove the state." 506 fi 507} 508 509interface_cleanup() 510{ 511 pft_cleanup 512} 513 514atf_test_case "id" "cleanup" 515id_head() 516{ 517 atf_set descr 'Test killing states by id' 518 atf_set require.user root 519 atf_set require.progs scapy 520} 521 522id_body() 523{ 524 pft_init 525 526 epair=$(vnet_mkepair) 527 ifconfig ${epair}a 192.0.2.1/24 up 528 529 vnet_mkjail alcatraz ${epair}b 530 jexec alcatraz ifconfig ${epair}b 192.0.2.2/24 up 531 jexec alcatraz pfctl -e 532 533 pft_set_rules alcatraz "block all" \ 534 "pass in proto tcp" \ 535 "pass in proto icmp" \ 536 "set skip on lo" 537 538 # Sanity check & establish state 539 atf_check -s exit:0 -o ignore ${common_dir}/pft_ping.py \ 540 --sendif ${epair}a \ 541 --to 192.0.2.2 \ 542 --replyif ${epair}a 543 544 # Change rules to now deny the ICMP traffic 545 pft_set_rules noflush alcatraz "block all" 546 if ! find_state; 547 then 548 atf_fail "Setting new rules removed the state." 549 fi 550 551 # Get the state ID 552 id=$(jexec alcatraz pfctl -ss -vvv | grep -A 3 icmp | 553 grep -A 3 192.0.2.2 | awk '/id:/ { printf("%s/%s", $2, $4); }') 554 555 # Kill the wrong ID 556 jexec alcatraz pfctl -k id -k 1 557 if ! find_state; 558 then 559 atf_fail "Killing a different ID removed the state." 560 fi 561 562 # Kill the correct ID 563 jexec alcatraz pfctl -k id -k ${id} 564 if find_state; 565 then 566 atf_fail "Killing the state did not remove it." 567 fi 568} 569 570id_cleanup() 571{ 572 pft_cleanup 573} 574 575atf_init_test_cases() 576{ 577 atf_add_test_case "v4" 578 atf_add_test_case "v6" 579 atf_add_test_case "label" 580 atf_add_test_case "multilabel" 581 atf_add_test_case "gateway" 582 atf_add_test_case "match" 583 atf_add_test_case "interface" 584 atf_add_test_case "id" 585} 586