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