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