1# 2# SPDX-License-Identifier: BSD-2-Clause 3# 4# Copyright (c) 2024 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 29nat64_setup_base() 30{ 31 pft_init 32 33 epair_link=$(vnet_mkepair) 34 epair=$(vnet_mkepair) 35 36 ifconfig ${epair}a inet6 2001:db8::2/64 up no_dad 37 route -6 add default 2001:db8::1 38 39 vnet_mkjail rtr ${epair}b ${epair_link}a 40 jexec rtr ifconfig ${epair}b inet6 2001:db8::1/64 up no_dad 41 jexec rtr ifconfig ${epair_link}a 192.0.2.1/24 up 42 43 vnet_mkjail dst ${epair_link}b 44 jexec dst ifconfig ${epair_link}b 192.0.2.2/24 up 45 jexec dst route add default 192.0.2.1 46 47 # Sanity checks 48 atf_check -s exit:0 -o ignore \ 49 ping6 -c 1 2001:db8::1 50 atf_check -s exit:0 -o ignore \ 51 jexec dst ping -c 1 192.0.2.1 52 53 jexec rtr pfctl -e 54} 55 56nat64_setup_in() 57{ 58 state_policy="${1:-if-bound}" 59 nat64_setup_base 60 pft_set_rules rtr \ 61 "set reassemble yes" \ 62 "set state-policy ${state_policy}" \ 63 "block" \ 64 "pass inet6 proto icmp6 icmp6-type { neighbrsol, neighbradv }" \ 65 "pass in on ${epair}b inet6 from any to 64:ff9b::/96 af-to inet from (${epair_link}a)" 66} 67 68nat64_setup_out() 69{ 70 state_policy="${1:-if-bound}" 71 nat64_setup_base 72 jexec rtr sysctl net.inet6.ip6.forwarding=1 73 # AF translation happens post-routing, traffic must be directed 74 # towards the outbound interface using routes for the original AF. 75 # jexec rtr ifconfig ${epair_link}a inet6 2001:db8:2::1/64 up no_dad 76 jexec rtr route add -inet6 64:ff9b::/96 -iface ${epair_link}a; 77 pft_set_rules rtr \ 78 "set reassemble yes" \ 79 "set state-policy ${state_policy}" \ 80 "block" \ 81 "pass inet6 proto icmp6 icmp6-type { neighbrsol, neighbradv }" \ 82 "pass in on ${epair}b from any to 64:ff9b::/96" \ 83 "pass out on ${epair_link}a from any to 64:ff9b::/96 af-to inet from (${epair_link}a)" 84} 85 86atf_test_case "icmp_echo_in" "cleanup" 87icmp_echo_in_head() 88{ 89 atf_set descr 'Basic NAT64 ICMP echo test on inbound interface' 90 atf_set require.user root 91} 92 93icmp_echo_in_body() 94{ 95 nat64_setup_in 96 97 # One ping 98 atf_check -s exit:0 -o ignore \ 99 ping6 -c 1 64:ff9b::192.0.2.2 100 101 # Make sure packets make it even when state is established 102 atf_check -s exit:0 \ 103 -o match:'5 packets transmitted, 5 packets received, 0.0% packet loss' \ 104 ping6 -c 5 64:ff9b::192.0.2.2 105} 106 107icmp_echo_in_cleanup() 108{ 109 pft_cleanup 110} 111 112atf_test_case "icmp_echo_out" "cleanup" 113icmp_echo_out_head() 114{ 115 atf_set descr 'Basic NAT64 ICMP echo test on outbound interface' 116 atf_set require.user root 117} 118 119icmp_echo_out_body() 120{ 121 nat64_setup_out 122 123 # One ping 124 atf_check -s exit:0 -o ignore \ 125 ping6 -c 1 64:ff9b::192.0.2.2 126 127 # Make sure packets make it even when state is established 128 atf_check -s exit:0 \ 129 -o match:'5 packets transmitted, 5 packets received, 0.0% packet loss' \ 130 ping6 -c 5 64:ff9b::192.0.2.2 131} 132 133icmp_echo_out_cleanup() 134{ 135 pft_cleanup 136} 137 138atf_test_case "fragmentation_in" "cleanup" 139fragmentation_in_head() 140{ 141 atf_set descr 'Test fragmented packets on inbound interface' 142 atf_set require.user root 143} 144 145fragmentation_in_body() 146{ 147 nat64_setup_in 148 149 atf_check -s exit:0 -o ignore \ 150 ping6 -c 1 -s 1280 64:ff9b::192.0.2.2 151 152 atf_check -s exit:0 \ 153 -o match:'3 packets transmitted, 3 packets received, 0.0% packet loss' \ 154 ping6 -c 3 -s 2000 64:ff9b::192.0.2.2 155 atf_check -s exit:0 \ 156 -o match:'3 packets transmitted, 3 packets received, 0.0% packet loss' \ 157 ping6 -c 3 -s 10000 -b 20000 64:ff9b::192.0.2.2 158} 159 160fragmentation_in_cleanup() 161{ 162 pft_cleanup 163} 164 165atf_test_case "fragmentation_out" "cleanup" 166fragmentation_out_head() 167{ 168 atf_set descr 'Test fragmented packets on outbound interface' 169 atf_set require.user root 170} 171 172fragmentation_out_body() 173{ 174 nat64_setup_out 175 176 atf_check -s exit:0 -o ignore \ 177 ping6 -c 1 -s 1280 64:ff9b::192.0.2.2 178 179 atf_check -s exit:0 \ 180 -o match:'3 packets transmitted, 3 packets received, 0.0% packet loss' \ 181 ping6 -c 3 -s 2000 64:ff9b::192.0.2.2 182 atf_check -s exit:0 \ 183 -o match:'3 packets transmitted, 3 packets received, 0.0% packet loss' \ 184 ping6 -c 3 -s 10000 -b 20000 64:ff9b::192.0.2.2 185} 186 187fragmentation_out_cleanup() 188{ 189 pft_cleanup 190} 191 192atf_test_case "tcp_in_if_bound" "cleanup" 193tcp_in_if_bound_head() 194{ 195 atf_set descr 'TCP NAT64 test on inbound interface, if-bound states' 196 atf_set require.user root 197} 198 199tcp_in_if_bound_body() 200{ 201 nat64_setup_in 202 203 echo "foo" | jexec dst nc -l 1234 & 204 205 # Sanity check & delay for nc startup 206 atf_check -s exit:0 -o ignore \ 207 ping6 -c 3 64:ff9b::192.0.2.2 208 209 rcv=$(nc -w 3 -6 64:ff9b::c000:202 1234) 210 if [ "${rcv}" != "foo" ]; 211 then 212 echo "rcv=${rcv}" 213 atf_fail "Failed to connect to TCP server" 214 fi 215 216 # Interfaces of the state are reversed when doing inbound NAT64! 217 # FIXME: Packets counters seem wrong! 218 states=$(mktemp) || exit 1 219 jexec rtr pfctl -qvvss | normalize_pfctl_s > $states 220 for state_regexp in \ 221 "${epair_link}a tcp 192.0.2.1:[0-9]+ \(2001:db8::2\[[0-9]+\]\) -> 192.0.2.2:1234 \(64:ff9b::c000:202\[1234\]\) .* 9:9 pkts.* rule 3 .* origif: ${epair}b" \ 222 ; do 223 grep -qE "${state_regexp}" $states || atf_fail "State not found for '${state_regexp}'" 224 done 225 [ $(cat $states | grep tcp | wc -l) -eq 1 ] || atf_fail "Not exactly 1 state found!" 226} 227 228tcp_in_if_bound_cleanup() 229{ 230 pft_cleanup 231} 232 233atf_test_case "tcp_out_if_bound" "cleanup" 234tcp_out_if_bound_head() 235{ 236 atf_set descr 'TCP NAT64 test on outbound interface, if-bound states' 237 atf_set require.user root 238} 239 240tcp_out_if_bound_body() 241{ 242 nat64_setup_out 243 244 echo "foo" | jexec dst nc -l 1234 & 245 246 # Sanity check & delay for nc startup 247 atf_check -s exit:0 -o ignore \ 248 ping6 -c 3 64:ff9b::192.0.2.2 249 250 rcv=$(nc -w 3 -6 64:ff9b::c000:202 1234) 251 if [ "${rcv}" != "foo" ]; 252 then 253 echo "rcv=${rcv}" 254 atf_fail "Failed to connect to TCP server" 255 fi 256 257 # Origif is not printed when identical as if. 258 states=$(mktemp) || exit 1 259 jexec rtr pfctl -qvvss | normalize_pfctl_s > $states 260 for state_regexp in \ 261 "${epair}b tcp 64:ff9b::c000:202\[1234\] <- 2001:db8::2\[[0-9]+\] .* 5:4 pkts.* rule 3 .*creatorid" \ 262 "${epair_link}a tcp 192.0.2.1:[0-9]+ \(64:ff9b::c000:202\[1234\]\) -> 192.0.2.2:1234 \(2001:db8::2\[[0-9]+\]\).* 5:4 pkts.* rule 4 .*creatorid" \ 263 ; do 264 grep -qE "${state_regexp}" $states || atf_fail "State not found for '${state_regexp}'" 265 done 266 [ $(cat $states | grep tcp | wc -l) -eq 2 ] || atf_fail "Not exactly 2 states found!" 267} 268 269tcp_out_if_bound_cleanup() 270{ 271 pft_cleanup 272} 273 274atf_test_case "tcp_in_floating" "cleanup" 275tcp_in_floating_head() 276{ 277 atf_set descr 'TCP NAT64 test on inbound interface, floating states' 278 atf_set require.user root 279} 280 281tcp_in_floating_body() 282{ 283 nat64_setup_in "floating" 284 285 echo "foo" | jexec dst nc -l 1234 & 286 287 # Sanity check & delay for nc startup 288 atf_check -s exit:0 -o ignore \ 289 ping6 -c 3 64:ff9b::192.0.2.2 290 291 rcv=$(nc -w 3 -6 64:ff9b::c000:202 1234) 292 if [ "${rcv}" != "foo" ]; 293 then 294 echo "rcv=${rcv}" 295 atf_fail "Failed to connect to TCP server" 296 fi 297 298 # Interfaces of the state are reversed when doing inbound NAT64! 299 # FIXME: Packets counters seem wrong! 300 states=$(mktemp) || exit 1 301 jexec rtr pfctl -qvvss | normalize_pfctl_s > $states 302 for state_regexp in \ 303 "all tcp 192.0.2.1:[0-9]+ \(2001:db8::2\[[0-9]+\]\) -> 192.0.2.2:1234 \(64:ff9b::c000:202\[1234\]\).* 9:9 pkts.* rule 3 .* origif: ${epair}b" \ 304 ; do 305 grep -qE "${state_regexp}" $states || atf_fail "State not found for '${state_regexp}'" 306 done 307 [ $(cat $states | grep tcp | wc -l) -eq 1 ] || atf_fail "Not exactly 1 state found!" 308} 309 310tcp_in_floating_cleanup() 311{ 312 pft_cleanup 313} 314 315atf_test_case "tcp_out_floating" "cleanup" 316tcp_out_floating_head() 317{ 318 atf_set descr 'TCP NAT64 test on outbound interface, floating states' 319 atf_set require.user root 320} 321 322tcp_out_floating_body() 323{ 324 nat64_setup_out "floating" 325 326 echo "foo" | jexec dst nc -l 1234 & 327 328 # Sanity check & delay for nc startup 329 atf_check -s exit:0 -o ignore \ 330 ping6 -c 3 64:ff9b::192.0.2.2 331 332 rcv=$(nc -w 3 -6 64:ff9b::c000:202 1234) 333 if [ "${rcv}" != "foo" ]; 334 then 335 echo "rcv=${rcv}" 336 atf_fail "Failed to connect to TCP server" 337 fi 338 339 # Origif is not printed when identical as if. 340 states=$(mktemp) || exit 1 341 jexec rtr pfctl -qvvss | normalize_pfctl_s > $states 342 for state_regexp in \ 343 "all tcp 64:ff9b::c000:202\[1234\] <- 2001:db8::2\[[0-9]+\] .* 5:4 pkts,.* rule 3 .*creatorid"\ 344 "all tcp 192.0.2.1:[0-9]+ \(64:ff9b::c000:202\[1234\]\) -> 192.0.2.2:1234 \(2001:db8::2\[[0-9]+\]\) .* 5:4 pkts,.* rule 4 .*creatorid"\ 345 ; do 346 grep -qE "${state_regexp}" $states || atf_fail "State not found for '${state_regexp}'" 347 done 348 [ $(cat $states | grep tcp | wc -l) -eq 2 ] || atf_fail "Not exactly 2 states found!" 349} 350 351tcp_out_floating_cleanup() 352{ 353 pft_cleanup 354} 355 356atf_test_case "udp_in" "cleanup" 357udp_in_head() 358{ 359 atf_set descr 'UDP NAT64 test on inbound interface' 360 atf_set require.user root 361} 362 363udp_in_body() 364{ 365 nat64_setup_in 366 367 echo "foo" | jexec dst nc -u -l 1234 & 368 369 # Sanity check & delay for nc startup 370 atf_check -s exit:0 -o ignore \ 371 ping6 -c 3 64:ff9b::192.0.2.2 372 373 rcv=$(echo bar | nc -w 3 -6 -u 64:ff9b::c000:202 1234) 374 if [ "${rcv}" != "foo" ]; 375 then 376 echo "rcv=${rcv}" 377 atf_fail "Failed to connect to UDP server" 378 fi 379} 380 381udp_in_cleanup() 382{ 383 pft_cleanup 384} 385 386atf_test_case "udp_out" "cleanup" 387udp_out_head() 388{ 389 atf_set descr 'UDP NAT64 test on outbound interface' 390 atf_set require.user root 391} 392 393udp_out_body() 394{ 395 nat64_setup_out 396 397 echo "foo" | jexec dst nc -u -l 1234 & 398 399 # Sanity check & delay for nc startup 400 atf_check -s exit:0 -o ignore \ 401 ping6 -c 3 64:ff9b::192.0.2.2 402 403 rcv=$(echo bar | nc -w 3 -6 -u 64:ff9b::c000:202 1234) 404 if [ "${rcv}" != "foo" ]; 405 then 406 echo "rcv=${rcv}" 407 atf_fail "Failed to connect to UDP server" 408 fi 409} 410 411udp_out_cleanup() 412{ 413 pft_cleanup 414} 415 416atf_test_case "sctp_in" "cleanup" 417sctp_in_head() 418{ 419 atf_set descr 'SCTP NAT64 test on inbound interface' 420 atf_set require.user root 421} 422 423sctp_in_body() 424{ 425 nat64_setup_in 426 if ! kldstat -q -m sctp; then 427 atf_skip "This test requires SCTP" 428 fi 429 430 echo "foo" | jexec dst nc --sctp -N -l 1234 & 431 432 # Sanity check & delay for nc startup 433 atf_check -s exit:0 -o ignore \ 434 ping6 -c 3 64:ff9b::192.0.2.2 435 436 rcv=$(echo bar | nc --sctp -w 3 -6 64:ff9b::c000:202 1234) 437 if [ "${rcv}" != "foo" ]; 438 then 439 echo "rcv=${rcv}" 440 atf_fail "Failed to connect to SCTP server" 441 fi 442} 443 444sctp_in_cleanup() 445{ 446 pft_cleanup 447} 448 449atf_test_case "sctp_out" "cleanup" 450sctp_out_head() 451{ 452 atf_set descr 'SCTP NAT64 test on outbound interface' 453 atf_set require.user root 454} 455 456sctp_out_body() 457{ 458 nat64_setup_out 459 if ! kldstat -q -m sctp; then 460 atf_skip "This test requires SCTP" 461 fi 462 463 echo "foo" | jexec dst nc --sctp -N -l 1234 & 464 465 # Sanity check & delay for nc startup 466 atf_check -s exit:0 -o ignore \ 467 ping6 -c 3 64:ff9b::192.0.2.2 468 469 rcv=$(echo bar | nc --sctp -w 3 -6 64:ff9b::c000:202 1234) 470 if [ "${rcv}" != "foo" ]; 471 then 472 echo "rcv=${rcv}" 473 atf_fail "Failed to connect to SCTP server" 474 fi 475} 476 477sctp_out_cleanup() 478{ 479 pft_cleanup 480} 481 482atf_test_case "tos" "cleanup" 483tos_head() 484{ 485 atf_set descr 'ToS translation test' 486 atf_set require.user root 487} 488 489tos_body() 490{ 491 nat64_setup_in 492 493 # Ensure we can distinguish ToS on the destination 494 jexec dst pfctl -e 495 pft_set_rules dst \ 496 "pass" \ 497 "block in inet tos 8" 498 499 atf_check -s exit:0 -o ignore \ 500 ping6 -c 1 -z 4 64:ff9b::192.0.2.2 501 atf_check -s exit:2 -o ignore \ 502 ping6 -c 1 -z 8 64:ff9b::192.0.2.2 503 atf_check -s exit:0 -o ignore \ 504 ping6 -c 1 -z 16 64:ff9b::192.0.2.2 505 506 jexec dst pfctl -sr -vv 507} 508 509tos_cleanup() 510{ 511 pft_cleanup 512} 513 514atf_test_case "no_v4" "cleanup" 515no_v4_head() 516{ 517 atf_set descr 'Test error handling when there is no IPv4 address to translate to' 518 atf_set require.user root 519} 520 521no_v4_body() 522{ 523 pft_init 524 525 epair_link=$(vnet_mkepair) 526 epair=$(vnet_mkepair) 527 528 ifconfig ${epair}a inet6 2001:db8::2/64 up no_dad 529 route -6 add default 2001:db8::1 530 531 vnet_mkjail rtr ${epair}b ${epair_link}a 532 jexec rtr ifconfig ${epair}b inet6 2001:db8::1/64 up no_dad 533 534 vnet_mkjail dst ${epair_link}b 535 jexec dst ifconfig ${epair_link}b 192.0.2.2/24 up 536 jexec dst route add default 192.0.2.1 537 538 # Sanity check 539 atf_check -s exit:0 -o ignore \ 540 ping6 -c 1 2001:db8::1 541 542 jexec rtr pfctl -e 543 pft_set_rules rtr \ 544 "block" \ 545 "pass inet6 proto icmp6 icmp6-type { neighbrsol, neighbradv }" \ 546 "pass in on ${epair}b inet6 from any to 64:ff9b::/96 af-to inet from (${epair_link}a)" \ 547 548 atf_check -s exit:2 -o ignore \ 549 ping6 -c 3 64:ff9b::192.0.2.2 550} 551 552no_v4_cleanup() 553{ 554 pft_cleanup 555} 556 557atf_test_case "range" "cleanup" 558range_head() 559{ 560 atf_set descr 'Test using an address range for the IPv4 side' 561 atf_set require.user root 562} 563 564range_body() 565{ 566 pft_init 567 568 epair_link=$(vnet_mkepair) 569 epair=$(vnet_mkepair) 570 571 ifconfig ${epair}a inet6 2001:db8::2/64 up no_dad 572 route -6 add default 2001:db8::1 573 574 vnet_mkjail rtr ${epair}b ${epair_link}a 575 jexec rtr ifconfig ${epair}b inet6 2001:db8::1/64 up no_dad 576 jexec rtr ifconfig ${epair_link}a 192.0.2.2/24 up 577 jexec rtr ifconfig ${epair_link}a inet alias 192.0.2.3/24 up 578 579 vnet_mkjail dst ${epair_link}b 580 jexec dst ifconfig ${epair_link}b 192.0.2.254/24 up 581 jexec dst route add default 192.0.2.2 582 583 # Sanity checks 584 atf_check -s exit:0 -o ignore \ 585 jexec rtr ping -c 1 192.0.2.254 586 atf_check -s exit:0 -o ignore \ 587 ping6 -c 1 2001:db8::1 588 atf_check -s exit:0 -o ignore \ 589 jexec dst ping -c 1 192.0.2.2 590 atf_check -s exit:0 -o ignore \ 591 jexec dst ping -c 1 192.0.2.3 592 593 jexec rtr pfctl -e 594 pft_set_rules rtr \ 595 "set reassemble yes" \ 596 "set state-policy if-bound" \ 597 "block" \ 598 "pass inet6 proto icmp6 icmp6-type { neighbrsol, neighbradv }" \ 599 "pass in on ${epair}b inet6 from any to 64:ff9b::/96 af-to inet from 192.0.2.2/31 round-robin" \ 600 601 # Use pf to count sources 602 jexec dst pfctl -e 603 pft_set_rules dst \ 604 "pass" 605 606 atf_check -s exit:0 -o ignore \ 607 ping6 -c 1 64:ff9b::192.0.2.254 608 atf_check -s exit:0 -o ignore \ 609 ping6 -c 1 64:ff9b::192.0.2.254 610 611 # Verify on dst that we saw different source addresses 612 atf_check -s exit:0 -o match:".*192.0.2.2.*" \ 613 jexec dst pfctl -ss 614 atf_check -s exit:0 -o match:".*192.0.2.3.*" \ 615 jexec dst pfctl -ss 616} 617 618range_cleanup() 619{ 620 pft_cleanup 621} 622 623atf_test_case "pool" "cleanup" 624pool_head() 625{ 626 atf_set descr 'Use a pool of IPv4 addresses' 627 atf_set require.user root 628} 629 630pool_body() 631{ 632 pft_init 633 634 epair_link=$(vnet_mkepair) 635 epair=$(vnet_mkepair) 636 637 ifconfig ${epair}a inet6 2001:db8::2/64 up no_dad 638 route -6 add default 2001:db8::1 639 640 vnet_mkjail rtr ${epair}b ${epair_link}a 641 jexec rtr ifconfig ${epair}b inet6 2001:db8::1/64 up no_dad 642 jexec rtr ifconfig ${epair_link}a 192.0.2.1/24 up 643 jexec rtr ifconfig ${epair_link}a inet alias 192.0.2.3/24 up 644 jexec rtr ifconfig ${epair_link}a inet alias 192.0.2.4/24 up 645 646 vnet_mkjail dst ${epair_link}b 647 jexec dst ifconfig ${epair_link}b 192.0.2.2/24 up 648 jexec dst route add default 192.0.2.1 649 650 # Sanity checks 651 atf_check -s exit:0 -o ignore \ 652 ping6 -c 1 2001:db8::1 653 atf_check -s exit:0 -o ignore \ 654 jexec dst ping -c 1 192.0.2.1 655 656 jexec rtr pfctl -e 657 pft_set_rules rtr \ 658 "set reassemble yes" \ 659 "set state-policy if-bound" \ 660 "block" \ 661 "pass inet6 proto icmp6 icmp6-type { neighbrsol, neighbradv }" \ 662 "pass in on ${epair}b inet6 from any to 64:ff9b::/96 af-to inet from { 192.0.2.1, 192.0.2.3, 192.0.2.4 } round-robin" 663 664 # Use pf to count sources 665 jexec dst pfctl -e 666 pft_set_rules dst \ 667 "pass" 668 669 atf_check -s exit:0 -o ignore \ 670 ping6 -c 1 64:ff9b::192.0.2.2 671 atf_check -s exit:0 -o ignore \ 672 ping6 -c 1 64:ff9b::192.0.2.2 673 atf_check -s exit:0 -o ignore \ 674 ping6 -c 1 64:ff9b::192.0.2.2 675 676 # Verify on dst that we saw different source addresses 677 atf_check -s exit:0 -o match:".*192.0.2.1.*" \ 678 jexec dst pfctl -ss 679 atf_check -s exit:0 -o match:".*192.0.2.3.*" \ 680 jexec dst pfctl -ss 681 atf_check -s exit:0 -o match:".*192.0.2.4.*" \ 682 jexec dst pfctl -ss 683} 684 685pool_cleanup() 686{ 687 pft_cleanup 688} 689 690 691atf_test_case "table" 692table_head() 693{ 694 atf_set descr 'Check table restrictions' 695 atf_set require.user root 696} 697 698table_body() 699{ 700 pft_init 701 702 # Round-robin and random are allowed 703 echo "pass in on epair inet6 from any to 64:ff9b::/96 af-to inet from <wanaddr> round-robin" | \ 704 atf_check -s exit:0 \ 705 pfctl -f - 706 echo "pass in on epair inet6 from any to 64:ff9b::/96 af-to inet from <wanaddr> random" | \ 707 atf_check -s exit:0 \ 708 pfctl -f - 709 710 # bitmask is not 711 echo "pass in on epair inet6 from any to 64:ff9b::/96 af-to inet from <wanaddr> bitmask" | \ 712 atf_check -s exit:1 \ 713 -e match:"tables are not supported by pool type" \ 714 pfctl -f - 715} 716 717table_cleanup() 718{ 719 pft_cleanup 720} 721 722atf_test_case "table_range" "cleanup" 723table_range_head() 724{ 725 atf_set descr 'Test using an address range within a table for the IPv4 side' 726 atf_set require.user root 727} 728 729table_range_body() 730{ 731 pft_init 732 733 epair_link=$(vnet_mkepair) 734 epair=$(vnet_mkepair) 735 736 ifconfig ${epair}a inet6 2001:db8::2/64 up no_dad 737 route -6 add default 2001:db8::1 738 739 vnet_mkjail rtr ${epair}b ${epair_link}a 740 jexec rtr ifconfig ${epair}b inet6 2001:db8::1/64 up no_dad 741 jexec rtr ifconfig ${epair_link}a 192.0.2.2/24 up 742 jexec rtr ifconfig ${epair_link}a inet alias 192.0.2.3/24 up 743 744 vnet_mkjail dst ${epair_link}b 745 jexec dst ifconfig ${epair_link}b 192.0.2.254/24 up 746 jexec dst route add default 192.0.2.2 747 748 # Sanity checks 749 atf_check -s exit:0 -o ignore \ 750 ping6 -c 1 2001:db8::1 751 atf_check -s exit:0 -o ignore \ 752 jexec dst ping -c 1 192.0.2.2 753 754 jexec rtr pfctl -e 755 pft_set_rules rtr \ 756 "set reassemble yes" \ 757 "set state-policy if-bound" \ 758 "table <wanaddrs> { 192.0.2.2/31 }" \ 759 "block" \ 760 "pass inet6 proto icmp6 icmp6-type { neighbrsol, neighbradv }" \ 761 "pass in on ${epair}b inet6 from any to 64:ff9b::/96 af-to inet from <wanaddrs> round-robin" 762 763 # Use pf to count sources 764 jexec dst pfctl -e 765 pft_set_rules dst \ 766 "pass" 767 768 atf_check -s exit:0 -o ignore \ 769 ping6 -c 1 64:ff9b::192.0.2.254 770 atf_check -s exit:0 -o ignore \ 771 ping6 -c 1 64:ff9b::192.0.2.254 772 773 # Verify on dst that we saw different source addresses 774 atf_check -s exit:0 -o match:".*192.0.2.2.*" \ 775 jexec dst pfctl -ss 776 atf_check -s exit:0 -o match:".*192.0.2.3.*" \ 777 jexec dst pfctl -ss 778} 779 780table_range_cleanup() 781{ 782 pft_cleanup 783} 784 785table_common_body() 786{ 787 pool_type=$1 788 789 pft_init 790 791 epair_link=$(vnet_mkepair) 792 epair=$(vnet_mkepair) 793 794 ifconfig ${epair}a inet6 2001:db8::2/64 up no_dad 795 route -6 add default 2001:db8::1 796 797 vnet_mkjail rtr ${epair}b ${epair_link}a 798 jexec rtr ifconfig ${epair}b inet6 2001:db8::1/64 up no_dad 799 jexec rtr ifconfig ${epair_link}a 192.0.2.1/24 up 800 jexec rtr ifconfig ${epair_link}a inet alias 192.0.2.3/24 up 801 jexec rtr ifconfig ${epair_link}a inet alias 192.0.2.4/24 up 802 803 vnet_mkjail dst ${epair_link}b 804 jexec dst ifconfig ${epair_link}b 192.0.2.2/24 up 805 jexec dst route add default 192.0.2.1 806 807 # Sanity checks 808 atf_check -s exit:0 -o ignore \ 809 ping6 -c 1 2001:db8::1 810 atf_check -s exit:0 -o ignore \ 811 jexec dst ping -c 1 192.0.2.1 812 813 jexec rtr pfctl -e 814 pft_set_rules rtr \ 815 "set reassemble yes" \ 816 "set state-policy if-bound" \ 817 "table <wanaddrs> { 192.0.2.1, 192.0.2.3, 192.0.2.4 }" \ 818 "block" \ 819 "pass inet6 proto icmp6 icmp6-type { neighbrsol, neighbradv }" \ 820 "pass in on ${epair}b inet6 from any to 64:ff9b::/96 af-to inet from <wanaddrs> ${pool_type}" 821 822 # Use pf to count sources 823 jexec dst pfctl -e 824 pft_set_rules dst \ 825 "pass" 826 827 atf_check -s exit:0 -o ignore \ 828 ping6 -c 1 64:ff9b::192.0.2.2 829 atf_check -s exit:0 -o ignore \ 830 ping6 -c 1 64:ff9b::192.0.2.2 831 atf_check -s exit:0 -o ignore \ 832 ping6 -c 1 64:ff9b::192.0.2.2 833 834 # XXX We can't reasonably check pool type random because it's random. It may end 835 # up choosing the same source IP for all three connections. 836 if [ "${pool_type}" == "round-robin" ]; 837 then 838 # Verify on dst that we saw different source addresses 839 atf_check -s exit:0 -o match:".*192.0.2.1.*" \ 840 jexec dst pfctl -ss 841 atf_check -s exit:0 -o match:".*192.0.2.3.*" \ 842 jexec dst pfctl -ss 843 atf_check -s exit:0 -o match:".*192.0.2.4.*" \ 844 jexec dst pfctl -ss 845 fi 846} 847 848atf_test_case "table_round_robin" "cleanup" 849table_round_robin_head() 850{ 851 atf_set descr 'Use a table of IPv4 addresses in round-robin mode' 852 atf_set require.user root 853} 854 855table_round_robin_body() 856{ 857 table_common_body round-robin 858} 859 860table_round_robin_cleanup() 861{ 862 pft_cleanup 863} 864 865atf_test_case "table_random" "cleanup" 866table_random_head() 867{ 868 atf_set descr 'Use a table of IPv4 addresses in random mode' 869 atf_set require.user root 870} 871 872table_random_body() 873{ 874 table_common_body random 875} 876 877table_random_cleanup() 878{ 879 pft_cleanup 880} 881 882atf_test_case "dummynet" "cleanup" 883dummynet_head() 884{ 885 atf_set descr 'Test dummynet on af-to rules' 886 atf_set require.user root 887} 888 889dummynet_body() 890{ 891 pft_init 892 dummynet_init 893 894 epair_link=$(vnet_mkepair) 895 epair=$(vnet_mkepair) 896 897 ifconfig ${epair}a inet6 2001:db8::2/64 up no_dad 898 route -6 add default 2001:db8::1 899 900 vnet_mkjail rtr ${epair}b ${epair_link}a 901 jexec rtr ifconfig ${epair}b inet6 2001:db8::1/64 up no_dad 902 jexec rtr ifconfig ${epair_link}a 192.0.2.1/24 up 903 904 vnet_mkjail dst ${epair_link}b 905 jexec dst ifconfig ${epair_link}b 192.0.2.2/24 up 906 jexec dst route add default 192.0.2.1 907 908 # Sanity checks 909 atf_check -s exit:0 -o ignore \ 910 ping6 -c 1 2001:db8::1 911 atf_check -s exit:0 -o ignore \ 912 jexec dst ping -c 1 192.0.2.1 913 914 jexec rtr pfctl -e 915 jexec rtr dnctl pipe 1 config delay 600 916 pft_set_rules rtr \ 917 "set reassemble yes" \ 918 "set state-policy if-bound" \ 919 "block" \ 920 "pass inet6 proto icmp6 icmp6-type { neighbrsol, neighbradv }" \ 921 "pass in on ${epair}b inet6 from any to 64:ff9b::/96 dnpipe 1 af-to inet from (${epair_link}a)" 922 923 # The ping request will pass, but take 1.2 seconds (.6 in, .6 out) 924 # So this works: 925 atf_check -s exit:0 -o ignore \ 926 ping6 -c 1 -t 2 64:ff9b::192.0.2.2 927 928 # But this times out: 929 atf_check -s exit:2 -o ignore \ 930 ping6 -c 1 -t 1 64:ff9b::192.0.2.2 931} 932 933dummynet_cleanup() 934{ 935 pft_cleanup 936} 937 938atf_test_case "gateway6" "cleanup" 939gateway6_head() 940{ 941 atf_set descr 'NAT64 with a routing hop on the v6 side' 942 atf_set require.user root 943} 944 945gateway6_body() 946{ 947 pft_init 948 949 epair_lan_link=$(vnet_mkepair) 950 epair_link=$(vnet_mkepair) 951 epair=$(vnet_mkepair) 952 953 ifconfig ${epair}a inet6 2001:db8:1::2/64 up no_dad 954 route -6 add default 2001:db8:1::1 955 956 vnet_mkjail lan_rtr ${epair}b ${epair_lan_link}a 957 jexec lan_rtr ifconfig ${epair}b inet6 2001:db8:1::1/64 up no_dad 958 jexec lan_rtr ifconfig ${epair_lan_link}a inet6 2001:db8::2/64 up no_dad 959 jexec lan_rtr route -6 add default 2001:db8::1 960 jexec lan_rtr sysctl net.inet6.ip6.forwarding=1 961 962 vnet_mkjail rtr ${epair_lan_link}b ${epair_link}a 963 jexec rtr ifconfig ${epair_lan_link}b inet6 2001:db8::1/64 up no_dad 964 jexec rtr ifconfig ${epair_link}a 192.0.2.1/24 up 965 jexec rtr route -6 add default 2001:db8::2 966 967 vnet_mkjail dst ${epair_link}b 968 jexec dst ifconfig ${epair_link}b 192.0.2.2/24 up 969 jexec dst route add default 192.0.2.1 970 971 # Sanity checks 972 atf_check -s exit:0 -o ignore \ 973 ping6 -c 1 2001:db8:1::1 974 atf_check -s exit:0 -o ignore \ 975 ping6 -c 1 2001:db8::1 976 atf_check -s exit:0 -o ignore \ 977 jexec dst ping -c 1 192.0.2.1 978 979 jexec rtr pfctl -e 980 pft_set_rules rtr \ 981 "set reassemble yes" \ 982 "set state-policy if-bound" \ 983 "block" \ 984 "pass inet6 proto icmp6 icmp6-type { neighbrsol, neighbradv }" \ 985 "pass in on ${epair_lan_link}b inet6 from any to 64:ff9b::/96 af-to inet from (${epair_link}a)" 986 987 # One ping 988 atf_check -s exit:0 -o ignore \ 989 ping6 -c 1 64:ff9b::192.0.2.2 990 991 # Make sure packets make it even when state is established 992 atf_check -s exit:0 \ 993 -o match:'5 packets transmitted, 5 packets received, 0.0% packet loss' \ 994 ping6 -c 5 64:ff9b::192.0.2.2 995} 996 997gateway6_cleanup() 998{ 999 pft_cleanup 1000} 1001 1002atf_test_case "route_to" "cleanup" 1003route_to_head() 1004{ 1005 atf_set descr 'Test route-to on af-to rules' 1006 atf_set require.user root 1007} 1008 1009route_to_body() 1010{ 1011 pft_init 1012 1013 epair_link=$(vnet_mkepair) 1014 epair_null=$(vnet_mkepair) 1015 epair=$(vnet_mkepair) 1016 1017 ifconfig ${epair}a inet6 2001:db8::2/64 up no_dad 1018 route -6 add default 2001:db8::1 1019 1020 vnet_mkjail rtr ${epair}b ${epair_link}a ${epair_null}a 1021 jexec rtr ifconfig ${epair}b inet6 2001:db8::1/64 up no_dad 1022 jexec rtr ifconfig ${epair_null}a 192.0.2.3/24 up 1023 jexec rtr ifconfig ${epair_link}a 192.0.2.1/24 up 1024 1025 vnet_mkjail dst ${epair_link}b 1026 jexec dst ifconfig ${epair_link}b 192.0.2.2/24 up 1027 jexec dst route add default 192.0.2.1 1028 1029 # Sanity checks 1030 atf_check -s exit:0 -o ignore \ 1031 ping6 -c 1 2001:db8::1 1032 1033 jexec rtr pfctl -e 1034 pft_set_rules rtr \ 1035 "set reassemble yes" \ 1036 "set state-policy if-bound" \ 1037 "block" \ 1038 "pass inet6 proto icmp6 icmp6-type { neighbrsol, neighbradv }" \ 1039 "pass in on ${epair}b route-to (${epair_link}a 192.0.2.2) inet6 from any to 64:ff9b::/96 af-to inet from (${epair_link}a)" 1040 1041 atf_check -s exit:0 -o ignore \ 1042 ping6 -c 3 64:ff9b::192.0.2.2 1043 1044 states=$(mktemp) || exit 1 1045 jexec rtr pfctl -qvvss | normalize_pfctl_s > $states 1046 1047 for state_regexp in \ 1048 "${epair}b ipv6-icmp 192.0.2.1:.* \(2001:db8::2\[[0-9]+\]\) -> 192.0.2.2:8 \(64:ff9b::c000:202\[[0-9]+\]\).*4:2 pkts.*route-to: 192.0.2.2@${epair_link}a" \ 1049 ; do 1050 grep -qE "${state_regexp}" $states || atf_fail "State not found for '${state_regexp}'" 1051 done 1052} 1053 1054route_to_cleanup() 1055{ 1056 pft_cleanup 1057} 1058 1059atf_test_case "reply_to" "cleanup" 1060reply_to_head() 1061{ 1062 atf_set descr 'Test reply-to on af-to rules' 1063 atf_set require.user root 1064} 1065 1066reply_to_body() 1067{ 1068 pft_init 1069 1070 epair_link=$(vnet_mkepair) 1071 epair=$(vnet_mkepair) 1072 1073 ifconfig ${epair}a inet6 2001:db8::2/64 up no_dad 1074 route -6 add default 2001:db8::1 1075 1076 vnet_mkjail rtr ${epair}b ${epair_link}a 1077 jexec rtr ifconfig ${epair}b inet6 2001:db8::1/64 up no_dad 1078 jexec rtr ifconfig ${epair_link}a 192.0.2.1/24 up 1079 1080 vnet_mkjail dst ${epair_link}b 1081 jexec dst ifconfig ${epair_link}b 192.0.2.2/24 up 1082 jexec dst route add default 192.0.2.1 1083 1084 # Sanity checks 1085 atf_check -s exit:0 -o ignore \ 1086 ping6 -c 1 2001:db8::1 1087 1088 jexec rtr pfctl -e 1089 pft_set_rules rtr \ 1090 "set reassemble yes" \ 1091 "set state-policy if-bound" \ 1092 "block" \ 1093 "pass inet6 proto icmp6 icmp6-type { neighbrsol, neighbradv }" \ 1094 "pass in on ${epair}b reply-to (${epair}b 2001:db8::2) inet6 from any to 64:ff9b::/96 af-to inet from 192.0.2.1" 1095 1096 atf_check -s exit:0 -o ignore \ 1097 ping6 -c 3 64:ff9b::192.0.2.2 1098} 1099 1100reply_to_cleanup() 1101{ 1102 pft_cleanup 1103} 1104 1105atf_test_case "v6_gateway" "cleanup" 1106v6_gateway_head() 1107{ 1108 atf_set descr 'nat64 when the IPv4 gateway is given by an IPv6 address' 1109 atf_set require.user root 1110} 1111 1112v6_gateway_body() 1113{ 1114 pft_init 1115 1116 epair_wan_two=$(vnet_mkepair) 1117 epair_wan_one=$(vnet_mkepair) 1118 epair_lan=$(vnet_mkepair) 1119 1120 ifconfig ${epair_lan}a inet6 2001:db8::2/64 up no_dad 1121 route -6 add default 2001:db8::1 1122 1123 vnet_mkjail rtr ${epair_lan}b ${epair_wan_one}a 1124 jexec rtr ifconfig ${epair_lan}b inet6 2001:db8::1/64 up no_dad 1125 jexec rtr ifconfig ${epair_wan_one}a 192.0.2.1/24 up 1126 jexec rtr ifconfig ${epair_wan_one}a inet6 -ifdisabled 1127 jexec rtr route add default -inet6 fe80::1%${epair_wan_one}a 1128 #jexec rtr route add default 192.0.2.2 1129 1130 vnet_mkjail wan_one ${epair_wan_one}b ${epair_wan_two}a 1131 jexec wan_one ifconfig ${epair_wan_one}b 192.0.2.2/24 up 1132 jexec wan_one ifconfig ${epair_wan_one}b inet6 fe80::1/64 1133 jexec wan_one ifconfig ${epair_wan_two}a 198.51.100.2/24 up 1134 jexec wan_one route add default 192.0.2.1 1135 jexec wan_one sysctl net.inet.ip.forwarding=1 1136 1137 vnet_mkjail wan_two ${epair_wan_two}b 1138 jexec wan_two ifconfig ${epair_wan_two}b 198.51.100.1/24 up 1139 jexec wan_two route add default 198.51.100.2 1140 1141 # Sanity checks 1142 atf_check -s exit:0 -o ignore \ 1143 ping6 -c 1 2001:db8::1 1144 atf_check -s exit:0 -o ignore \ 1145 jexec rtr ping -c 1 192.0.2.2 1146 atf_check -s exit:0 -o ignore \ 1147 jexec rtr ping -c 1 198.51.100.1 1148 1149 jexec rtr pfctl -e 1150 pft_set_rules rtr \ 1151 "set reassemble yes" \ 1152 "set state-policy if-bound" \ 1153 "block" \ 1154 "pass inet6 proto icmp6 icmp6-type { neighbrsol, neighbradv }" \ 1155 "pass in on ${epair_lan}b inet6 from any to 64:ff9b::/96 af-to inet from (${epair_wan_one}a)" 1156 1157 atf_check -s exit:0 -o ignore \ 1158 ping6 -c 3 64:ff9b::192.0.2.2 1159 atf_check -s exit:0 -o ignore \ 1160 ping6 -c 3 64:ff9b::198.51.100.1 1161} 1162 1163v6_gateway_cleanup() 1164{ 1165 pft_cleanup 1166} 1167 1168atf_init_test_cases() 1169{ 1170 atf_add_test_case "icmp_echo_in" 1171 atf_add_test_case "icmp_echo_out" 1172 atf_add_test_case "fragmentation_in" 1173 atf_add_test_case "fragmentation_out" 1174 atf_add_test_case "tcp_in_if_bound" 1175 atf_add_test_case "tcp_out_if_bound" 1176 atf_add_test_case "tcp_in_floating" 1177 atf_add_test_case "tcp_out_floating" 1178 atf_add_test_case "udp_in" 1179 atf_add_test_case "udp_out" 1180 atf_add_test_case "sctp_in" 1181 atf_add_test_case "sctp_out" 1182 atf_add_test_case "tos" 1183 atf_add_test_case "no_v4" 1184 atf_add_test_case "range" 1185 atf_add_test_case "pool" 1186 atf_add_test_case "table" 1187 atf_add_test_case "table_range" 1188 atf_add_test_case "table_round_robin" 1189 atf_add_test_case "table_random" 1190 atf_add_test_case "dummynet" 1191 atf_add_test_case "gateway6" 1192 atf_add_test_case "route_to" 1193 atf_add_test_case "reply_to" 1194 atf_add_test_case "v6_gateway" 1195} 1196