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