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