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