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 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 416 # Expect two states 417 states=$(jexec alcatraz pfctl -s s | wc -l) 418 if [ $states -ne 2 ] ; 419 then 420 atf_fail "Expected two states, found $states" 421 fi 422 423 # If we don't kill the matching NAT state one should be left 424 jexec alcatraz pfctl -k 192.0.2.1 425 states=$(jexec alcatraz pfctl -s s | wc -l) 426 if [ $states -ne 1 ] ; 427 then 428 atf_fail "Expected one states, found $states" 429 fi 430 431 # Flush 432 jexec alcatraz pfctl -F states 433 434 nc 198.51.100.2 7 & 435 436 # Kill matching states, expect all of them to be gone 437 jexec alcatraz pfctl -M -k 192.0.2.1 438 states=$(jexec alcatraz pfctl -s s | wc -l) 439 if [ $states -ne 0 ] ; 440 then 441 atf_fail "Expected zero states, found $states" 442 fi 443} 444 445match_cleanup() 446{ 447 pft_cleanup 448} 449 450atf_test_case "interface" "cleanup" 451interface_head() 452{ 453 atf_set descr 'Test killing states based on interface' 454 atf_set require.user root 455 atf_set require.progs scapy 456} 457 458interface_body() 459{ 460 pft_init 461 462 epair=$(vnet_mkepair) 463 ifconfig ${epair}a 192.0.2.1/24 up 464 465 vnet_mkjail alcatraz ${epair}b 466 jexec alcatraz ifconfig ${epair}b 192.0.2.2/24 up 467 jexec alcatraz pfctl -e 468 469 pft_set_rules alcatraz "block all" \ 470 "pass in proto icmp" 471 472 # Sanity check & establish state 473 # Note: use pft_ping so we always use the same ID, so pf considers all 474 # echo requests part of the same flow. 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 483 # Established state means we can still ping alcatraz 484 atf_check -s exit:0 -o ignore ${common_dir}/pft_ping.py \ 485 --sendif ${epair}a \ 486 --to 192.0.2.2 \ 487 --replyif ${epair}a 488 489 # Flushing states on a different interface doesn't affect our state 490 jexec alcatraz pfctl -i ${epair}a -Fs 491 atf_check -s exit:0 -o ignore ${common_dir}/pft_ping.py \ 492 --sendif ${epair}a \ 493 --to 192.0.2.2 \ 494 --replyif ${epair}a 495 496 # Flushing on the correct interface does (even with floating states) 497 jexec alcatraz pfctl -i ${epair}b -Fs 498 atf_check -s exit:1 -o ignore ${common_dir}/pft_ping.py \ 499 --sendif ${epair}a \ 500 --to 192.0.2.2 \ 501 --replyif ${epair}a 502} 503 504interface_cleanup() 505{ 506 pft_cleanup 507} 508 509atf_init_test_cases() 510{ 511 atf_add_test_case "v4" 512 atf_add_test_case "v6" 513 atf_add_test_case "label" 514 atf_add_test_case "multilabel" 515 atf_add_test_case "gateway" 516 atf_add_test_case "match" 517 atf_add_test_case "interface" 518} 519