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 jail=${1:-alcatraz} 34 ip=${2:-192.0.2.2} 35 36 jexec ${jail} pfctl -ss | grep icmp | grep ${ip} 37} 38 39find_state_v6() 40{ 41 jexec alcatraz pfctl -ss | grep icmp | grep 2001:db8::2 42} 43 44 45atf_test_case "v4" "cleanup" 46v4_head() 47{ 48 atf_set descr 'Test killing states by IPv4 address' 49 atf_set require.user root 50 atf_set require.progs python3 scapy 51} 52 53v4_body() 54{ 55 pft_init 56 57 epair=$(vnet_mkepair) 58 ifconfig ${epair}a 192.0.2.1/24 up 59 60 vnet_mkjail alcatraz ${epair}b 61 jexec alcatraz ifconfig ${epair}b 192.0.2.2/24 up 62 jexec alcatraz pfctl -e 63 64 pft_set_rules alcatraz "block all" \ 65 "pass in proto icmp" \ 66 "set skip on lo" 67 68 # Sanity check & establish state 69 atf_check -s exit:0 -o ignore ${common_dir}/pft_ping.py \ 70 --sendif ${epair}a \ 71 --to 192.0.2.2 \ 72 --replyif ${epair}a 73 74 # Change rules to now deny the ICMP traffic 75 pft_set_rules noflush alcatraz "block all" 76 if ! find_state; 77 then 78 atf_fail "Setting new rules removed the state." 79 fi 80 81 # Killing with the wrong IP doesn't affect our state 82 jexec alcatraz pfctl -k 192.0.2.3 83 if ! find_state; 84 then 85 atf_fail "Killing with the wrong IP removed our state." 86 fi 87 88 # Killing with one correct address and one incorrect doesn't kill the state 89 jexec alcatraz pfctl -k 192.0.2.1 -k 192.0.2.3 90 if ! find_state; 91 then 92 atf_fail "Killing with one wrong IP removed our state." 93 fi 94 95 # Killing with correct address does remove the state 96 jexec alcatraz pfctl -k 192.0.2.1 97 if find_state; 98 then 99 atf_fail "Killing with the correct IP did not remove our state." 100 fi 101} 102 103v4_cleanup() 104{ 105 pft_cleanup 106} 107 108atf_test_case "src_dst" "cleanup" 109src_dst_head() 110{ 111 atf_set descr 'Test killing a state with source and destination specified' 112 atf_set require.user root 113 atf_set require.progs python3 scapy 114} 115 116src_dst_body() 117{ 118 pft_init 119 120 epair=$(vnet_mkepair) 121 ifconfig ${epair}a 192.0.2.1/24 up 122 123 vnet_mkjail alcatraz ${epair}b 124 jexec alcatraz ifconfig ${epair}b 192.0.2.2/24 up 125 jexec alcatraz pfctl -e 126 127 pft_set_rules alcatraz "block all" \ 128 "pass in proto icmp" \ 129 "set skip on lo" 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 192.0.2.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; 140 then 141 atf_fail "Setting new rules removed the state." 142 fi 143 144 # Killing with the wrong source IP doesn't affect our state 145 jexec alcatraz pfctl -k 192.0.2.3 -k 192.0.2.2 146 if ! find_state; 147 then 148 atf_fail "Killing with the wrong source IP removed our state." 149 fi 150 151 # Killing with the wrong destination IP doesn't affect our state 152 jexec alcatraz pfctl -k 192.0.2.1 -k 192.0.2.3 153 if ! find_state; 154 then 155 atf_fail "Killing with the wrong destination IP removed our state." 156 fi 157 158 # But it does with the correct one 159 jexec alcatraz pfctl -k 192.0.2.1 -k 192.0.2.2 160 if find_state; 161 then 162 atf_fail "Killing with the correct IPs did not remove our state." 163 fi 164} 165 166src_dst_cleanup() 167{ 168 pft_cleanup 169} 170 171atf_test_case "v6" "cleanup" 172v6_head() 173{ 174 atf_set descr 'Test killing states by IPv6 address' 175 atf_set require.user root 176 atf_set require.progs python3 scapy 177} 178 179v6_body() 180{ 181 pft_init 182 183 epair=$(vnet_mkepair) 184 ifconfig ${epair}a inet6 2001:db8::1/64 up no_dad 185 186 vnet_mkjail alcatraz ${epair}b 187 jexec alcatraz ifconfig ${epair}b inet6 2001:db8::2/64 up no_dad 188 jexec alcatraz pfctl -e 189 190 pft_set_rules alcatraz "block all" \ 191 "pass quick inet6 proto ipv6-icmp all icmp6-type { neighbrsol, neighbradv } no state" \ 192 "pass in proto icmp6" \ 193 "set skip on lo" 194 195 # Sanity check & establish state 196 atf_check -s exit:0 -o ignore ${common_dir}/pft_ping.py \ 197 --sendif ${epair}a \ 198 --fromaddr 2001:db8::1 \ 199 --to 2001:db8::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_v6; 205 then 206 atf_fail "Setting new rules removed the state." 207 fi 208 209 # Killing with the wrong IP doesn't affect our state 210 jexec alcatraz pfctl -k 2001:db8::3 211 if ! find_state_v6; 212 then 213 atf_fail "Killing with the wrong IP removed our state." 214 fi 215 216 # Killing with one correct address and one incorrect doesn't kill the state 217 jexec alcatraz pfctl -k 2001:db8::1 -k 2001:db8::3 218 if ! find_state_v6; 219 then 220 atf_fail "Killing with one wrong IP removed our state." 221 fi 222 223 # Killing with correct address does remove the state 224 jexec alcatraz pfctl -k 2001:db8::1 225 if find_state_v6; 226 then 227 atf_fail "Killing with the correct IP did not remove our state." 228 fi 229} 230 231v6_cleanup() 232{ 233 pft_cleanup 234} 235 236atf_test_case "label" "cleanup" 237label_head() 238{ 239 atf_set descr 'Test killing states by label' 240 atf_set require.user root 241 atf_set require.progs python3 scapy 242} 243 244label_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 tcp label bar" \ 257 "pass in proto icmp label foo" \ 258 "set skip on lo" 259 260 # Sanity check & establish state 261 atf_check -s exit:0 -o ignore ${common_dir}/pft_ping.py \ 262 --sendif ${epair}a \ 263 --to 192.0.2.2 \ 264 --replyif ${epair}a 265 266 # Change rules to now deny the ICMP traffic 267 pft_set_rules noflush alcatraz "block all" 268 if ! find_state; 269 then 270 atf_fail "Setting new rules removed the state." 271 fi 272 273 # Killing a label on a different rules keeps the state 274 jexec alcatraz pfctl -k label -k bar 275 if ! find_state; 276 then 277 atf_fail "Killing a different label removed the state." 278 fi 279 280 # Killing a non-existing label keeps the state 281 jexec alcatraz pfctl -k label -k baz 282 if ! find_state; 283 then 284 atf_fail "Killing a non-existing label removed the state." 285 fi 286 287 # Killing the correct label kills the state 288 jexec alcatraz pfctl -k label -k foo 289 if find_state; 290 then 291 atf_fail "Killing the state did not remove it." 292 fi 293} 294 295label_cleanup() 296{ 297 pft_cleanup 298} 299 300atf_test_case "multilabel" "cleanup" 301multilabel_head() 302{ 303 atf_set descr 'Test killing states with multiple labels by label' 304 atf_set require.user root 305 atf_set require.progs python3 scapy 306} 307 308multilabel_body() 309{ 310 pft_init 311 312 epair=$(vnet_mkepair) 313 ifconfig ${epair}a 192.0.2.1/24 up 314 315 vnet_mkjail alcatraz ${epair}b 316 jexec alcatraz ifconfig ${epair}b 192.0.2.2/24 up 317 jexec alcatraz pfctl -e 318 319 pft_set_rules alcatraz "block all" \ 320 "pass in proto icmp label foo label bar" \ 321 "set skip on lo" 322 323 # Sanity check & establish state 324 atf_check -s exit:0 -o ignore ${common_dir}/pft_ping.py \ 325 --sendif ${epair}a \ 326 --to 192.0.2.2 \ 327 --replyif ${epair}a 328 329 # Change rules to now deny the ICMP traffic 330 pft_set_rules noflush alcatraz "block all" 331 if ! find_state; 332 then 333 atf_fail "Setting new rules removed the state." 334 fi 335 336 # Killing a label on a different rules keeps the state 337 jexec alcatraz pfctl -k label -k baz 338 if ! find_state; 339 then 340 atf_fail "Killing a different label removed the state." 341 fi 342 343 # Killing the state with the last label works 344 jexec alcatraz pfctl -k label -k bar 345 if find_state; 346 then 347 atf_fail "Killing with the last label did not remove the state." 348 fi 349 350 pft_set_rules alcatraz "block all" \ 351 "pass in proto icmp label foo label bar" \ 352 "set skip on lo" 353 354 # Reestablish state 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 # Change rules to now deny the ICMP traffic 361 pft_set_rules noflush alcatraz "block all" 362 if ! find_state; 363 then 364 atf_fail "Setting new rules removed the state." 365 fi 366 367 # Killing with the first label works too 368 jexec alcatraz pfctl -k label -k foo 369 if find_state; 370 then 371 atf_fail "Killing with the first label did not remove the state." 372 fi 373} 374 375multilabel_cleanup() 376{ 377 pft_cleanup 378} 379 380atf_test_case "gateway" "cleanup" 381gateway_head() 382{ 383 atf_set descr 'Test killing states by route-to/reply-to address' 384 atf_set require.user root 385 atf_set require.progs python3 scapy 386} 387 388gateway_body() 389{ 390 pft_init 391 392 epair=$(vnet_mkepair) 393 ifconfig ${epair}a 192.0.2.1/24 up 394 395 vnet_mkjail alcatraz ${epair}b 396 jexec alcatraz ifconfig ${epair}b 192.0.2.2/24 up 397 jexec alcatraz pfctl -e 398 399 pft_set_rules alcatraz "block all" \ 400 "pass in reply-to (${epair}b 192.0.2.1) proto icmp" \ 401 "set skip on lo" 402 403 # Sanity check & establish state 404 # Note: use pft_ping so we always use the same ID, so pf considers all 405 # echo requests part of the same flow. 406 atf_check -s exit:0 -o ignore ${common_dir}/pft_ping.py \ 407 --sendif ${epair}a \ 408 --to 192.0.2.2 \ 409 --replyif ${epair}a 410 411 # Change rules to now deny the ICMP traffic 412 pft_set_rules noflush alcatraz "block all" 413 if ! find_state; 414 then 415 atf_fail "Setting new rules removed the state." 416 fi 417 418 # Killing with a different gateway does not affect our state 419 jexec alcatraz pfctl -k gateway -k 192.0.2.2 420 if ! find_state; 421 then 422 atf_fail "Killing with a different gateway removed the state." 423 fi 424 425 # Killing states with the relevant gateway does terminate our state 426 jexec alcatraz pfctl -k gateway -k 192.0.2.1 427 if find_state; 428 then 429 atf_fail "Killing with the gateway did not remove the state." 430 fi 431} 432 433gateway_cleanup() 434{ 435 pft_cleanup 436} 437 438atf_test_case "match" "cleanup" 439match_head() 440{ 441 atf_set descr 'Test killing matching states' 442 atf_set require.user root 443} 444 445wait_for_state() 446{ 447 jail=$1 448 addr=$2 449 450 while ! jexec $jail pfctl -s s | grep $addr >/dev/null; 451 do 452 sleep .1 453 done 454} 455 456match_body() 457{ 458 pft_init 459 460 epair_one=$(vnet_mkepair) 461 ifconfig ${epair_one}a 192.0.2.1/24 up 462 463 epair_two=$(vnet_mkepair) 464 465 vnet_mkjail alcatraz ${epair_one}b ${epair_two}a 466 jexec alcatraz ifconfig ${epair_one}b 192.0.2.2/24 up 467 jexec alcatraz ifconfig ${epair_two}a 198.51.100.1/24 up 468 jexec alcatraz sysctl net.inet.ip.forwarding=1 469 jexec alcatraz pfctl -e 470 471 vnet_mkjail singsing ${epair_two}b 472 jexec singsing ifconfig ${epair_two}b 198.51.100.2/24 up 473 jexec singsing route add default 198.51.100.1 474 jexec singsing /usr/sbin/inetd -p ${PWD}/inetd-echo.pid \ 475 $(atf_get_srcdir)/echo_inetd.conf 476 477 route add 198.51.100.0/24 192.0.2.2 478 479 pft_set_rules alcatraz \ 480 "nat on ${epair_two}a from 192.0.2.0/24 -> (${epair_two}a)" \ 481 "pass all" 482 483 nc 198.51.100.2 7 & 484 wait_for_state alcatraz 192.0.2.1 485 486 # Expect two states 487 states=$(jexec alcatraz pfctl -s s | grep 192.0.2.1 | wc -l) 488 if [ $states -ne 2 ] ; 489 then 490 atf_fail "Expected two states, found $states" 491 fi 492 493 # If we don't kill the matching NAT state one should be left 494 jexec alcatraz pfctl -k 192.0.2.1 495 states=$(jexec alcatraz pfctl -s s | grep 192.0.2.1 | wc -l) 496 if [ $states -ne 1 ] ; 497 then 498 atf_fail "Expected one states, found $states" 499 fi 500 501 # Flush 502 jexec alcatraz pfctl -F states 503 504 nc 198.51.100.2 7 & 505 wait_for_state alcatraz 192.0.2.1 506 507 # Kill matching states, expect all of them to be gone 508 jexec alcatraz pfctl -M -k 192.0.2.1 509 states=$(jexec alcatraz pfctl -s s | grep 192.0.2.1 | wc -l) 510 if [ $states -ne 0 ] ; 511 then 512 atf_fail "Expected zero states, found $states" 513 fi 514} 515 516match_cleanup() 517{ 518 pft_cleanup 519} 520 521atf_test_case "interface" "cleanup" 522interface_head() 523{ 524 atf_set descr 'Test killing states based on interface' 525 atf_set require.user root 526 atf_set require.progs python3 scapy 527} 528 529interface_body() 530{ 531 pft_init 532 533 epair=$(vnet_mkepair) 534 ifconfig ${epair}a 192.0.2.1/24 up 535 536 vnet_mkjail alcatraz ${epair}b 537 jexec alcatraz ifconfig ${epair}b 192.0.2.2/24 up 538 jexec alcatraz pfctl -e 539 540 pft_set_rules alcatraz "block all" \ 541 "pass in proto icmp" \ 542 "set skip on lo" 543 544 # Sanity check & establish state 545 atf_check -s exit:0 -o ignore ${common_dir}/pft_ping.py \ 546 --sendif ${epair}a \ 547 --to 192.0.2.2 \ 548 --replyif ${epair}a 549 550 # Change rules to now deny the ICMP traffic 551 pft_set_rules noflush alcatraz "block all" 552 if ! find_state; 553 then 554 atf_fail "Setting new rules removed the state." 555 fi 556 557 # Flushing states on a different interface doesn't affect our state 558 jexec alcatraz pfctl -i ${epair}a -Fs 559 if ! find_state; 560 then 561 atf_fail "Flushing on a different interface removed the state." 562 fi 563 564 # Flushing on the correct interface does (even with floating states) 565 jexec alcatraz pfctl -i ${epair}b -Fs 566 if find_state; 567 then 568 atf_fail "Flushing on a the interface did not remove the state." 569 fi 570} 571 572interface_cleanup() 573{ 574 pft_cleanup 575} 576 577atf_test_case "id" "cleanup" 578id_head() 579{ 580 atf_set descr 'Test killing states by id' 581 atf_set require.user root 582 atf_set require.progs python3 scapy 583} 584 585id_body() 586{ 587 pft_init 588 589 epair=$(vnet_mkepair) 590 ifconfig ${epair}a 192.0.2.1/24 up 591 592 vnet_mkjail alcatraz ${epair}b 593 jexec alcatraz ifconfig ${epair}b 192.0.2.2/24 up 594 jexec alcatraz pfctl -e 595 596 pft_set_rules alcatraz "block all" \ 597 "pass in proto tcp" \ 598 "pass in proto icmp" \ 599 "set skip on lo" 600 601 # Sanity check & establish state 602 atf_check -s exit:0 -o ignore ${common_dir}/pft_ping.py \ 603 --sendif ${epair}a \ 604 --to 192.0.2.2 \ 605 --replyif ${epair}a 606 607 # Change rules to now deny the ICMP traffic 608 pft_set_rules noflush alcatraz "block all" 609 if ! find_state; 610 then 611 atf_fail "Setting new rules removed the state." 612 fi 613 614 # Get the state ID 615 id=$(jexec alcatraz pfctl -ss -vvv | grep -A 3 icmp | 616 grep -A 3 192.0.2.2 | awk '/id:/ { printf("%s/%s", $2, $4); }') 617 618 # Kill the wrong ID 619 jexec alcatraz pfctl -k id -k 1 620 if ! find_state; 621 then 622 atf_fail "Killing a different ID removed the state." 623 fi 624 625 # Kill the correct ID 626 jexec alcatraz pfctl -k id -k ${id} 627 if find_state; 628 then 629 atf_fail "Killing the state did not remove it." 630 fi 631} 632 633id_cleanup() 634{ 635 pft_cleanup 636} 637 638atf_test_case "key" "cleanup" 639key_head() 640{ 641 atf_set descr 'Test killing states by their key' 642 atf_set require.user root 643 atf_set require.progs python3 scapy 644} 645 646key_body() 647{ 648 pft_init 649 650 epair=$(vnet_mkepair) 651 ifconfig ${epair}a 192.0.2.1/24 up 652 653 vnet_mkjail alcatraz ${epair}b 654 jexec alcatraz ifconfig ${epair}b 192.0.2.2/24 up 655 jexec alcatraz pfctl -e 656 657 pft_set_rules alcatraz \ 658 "block all" \ 659 "pass in proto tcp" \ 660 "pass in proto icmp" 661 662 # Sanity check & establish state 663 atf_check -s exit:0 -o ignore ${common_dir}/pft_ping.py \ 664 --sendif ${epair}a \ 665 --to 192.0.2.2 \ 666 --replyif ${epair}a 667 668 # Get the state key 669 key=$(jexec alcatraz pfctl -ss -vvv | awk '/icmp/ { print($2 " " $3 " " $4 " " $5); }') 670 bad_key=$(echo ${key} | sed 's/icmp/tcp/') 671 672 # Kill the wrong key 673 atf_check -s exit:0 -e "match:killed 0 states" \ 674 jexec alcatraz pfctl -k key -k "${bad_key}" 675 if ! find_state; 676 then 677 atf_fail "Killing a different ID removed the state." 678 fi 679 680 # Kill the correct key 681 atf_check -s exit:0 -e "match:killed 1 states" \ 682 jexec alcatraz pfctl -k key -k "${key}" 683 if find_state; 684 then 685 atf_fail "Killing the state did not remove it." 686 fi 687} 688 689key_cleanup() 690{ 691 pft_cleanup 692} 693 694atf_test_case "nat" "cleanup" 695nat_head() 696{ 697 atf_set descr 'Test killing states by their NAT-ed IP address' 698 atf_set require.user root 699 atf_set require.progs python3 scapy 700} 701 702nat_body() 703{ 704 pft_init 705 j="killstate:nat" 706 707 epair_c=$(vnet_mkepair) 708 epair_srv=$(vnet_mkepair) 709 710 vnet_mkjail ${j}c ${epair_c}a 711 ifconfig -j ${j}c ${epair_c}a inet 192.0.2.2/24 up 712 jexec ${j}c route add default 192.0.2.1 713 714 vnet_mkjail ${j}srv ${epair_srv}a 715 ifconfig -j ${j}srv ${epair_srv}a inet 198.51.100.2/24 up 716 717 vnet_mkjail ${j}r ${epair_c}b ${epair_srv}b 718 ifconfig -j ${j}r ${epair_c}b inet 192.0.2.1/24 up 719 ifconfig -j ${j}r ${epair_srv}b inet 198.51.100.1/24 up 720 jexec ${j}r sysctl net.inet.ip.forwarding=1 721 722 jexec ${j}r pfctl -e 723 pft_set_rules ${j}r \ 724 "nat on ${epair_srv}b inet from 192.0.2.0/24 to any -> (${epair_srv}b)" 725 726 # Sanity check 727 atf_check -s exit:0 -o ignore \ 728 jexec ${j}c ping -c 1 192.0.2.1 729 atf_check -s exit:0 -o ignore \ 730 jexec ${j}srv ping -c 1 198.51.100.1 731 atf_check -s exit:0 -o ignore \ 732 jexec ${j}c ping -c 1 198.51.100.2 733 734 # Establish state 735 # Note: use pft_ping so we always use the same ID, so pf considers all 736 # echo requests part of the same flow. 737 atf_check -s exit:0 -o ignore jexec ${j}c ${common_dir}/pft_ping.py \ 738 --sendif ${epair_c}a \ 739 --to 198.51.100.1 \ 740 --replyif ${epair_c}a 741 742 # There's NAT here, so the source IP will be 198.51.100.1 743 if ! find_state ${j}r 198.51.100.1; 744 then 745 atf_fail "Expected state not found" 746 fi 747 748 # By NAT-ed address? 749 jexec ${j}r pfctl -k nat -k 192.0.2.2 750 751 if find_state ${j}r 198.51.100.1; 752 then 753 jexec ${j}r pfctl -ss -v 754 atf_fail "Failed to remove state" 755 fi 756} 757 758nat_cleanup() 759{ 760 pft_cleanup 761} 762 763atf_init_test_cases() 764{ 765 atf_add_test_case "v4" 766 atf_add_test_case "src_dst" 767 atf_add_test_case "v6" 768 atf_add_test_case "label" 769 atf_add_test_case "multilabel" 770 atf_add_test_case "gateway" 771 atf_add_test_case "match" 772 atf_add_test_case "interface" 773 atf_add_test_case "id" 774 atf_add_test_case "key" 775 atf_add_test_case "nat" 776} 777