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