1# 2# SPDX-License-Identifier: BSD-2-Clause 3# 4# Copyright (c) 2018 Kristof Provost <kp@FreeBSD.org> 5# Copyright (c) 2025 Kajetan Staszkiewicz <ks@FreeBSD.org> 6# Copyright (c) 2021 KUROSAWA Takahiro <takahiro.kurosawa@gmail.com> 7# 8# Redistribution and use in source and binary forms, with or without 9# modification, are permitted provided that the following conditions 10# are met: 11# 1. Redistributions of source code must retain the above copyright 12# notice, this list of conditions and the following disclaimer. 13# 2. Redistributions in binary form must reproduce the above copyright 14# notice, this list of conditions and the following disclaimer in the 15# documentation and/or other materials provided with the distribution. 16# 17# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27# SUCH DAMAGE. 28 29. $(atf_get_srcdir)/utils.subr 30 31atf_test_case "exhaust" "cleanup" 32exhaust_head() 33{ 34 atf_set descr 'Test exhausting the NAT pool' 35 atf_set require.user root 36} 37 38exhaust_body() 39{ 40 pft_init 41 42 epair_nat=$(vnet_mkepair) 43 epair_echo=$(vnet_mkepair) 44 45 vnet_mkjail nat ${epair_nat}b ${epair_echo}a 46 vnet_mkjail echo ${epair_echo}b 47 48 ifconfig ${epair_nat}a 192.0.2.2/24 up 49 route add -net 198.51.100.0/24 192.0.2.1 50 51 jexec nat ifconfig ${epair_nat}b 192.0.2.1/24 up 52 jexec nat ifconfig ${epair_echo}a 198.51.100.1/24 up 53 jexec nat sysctl net.inet.ip.forwarding=1 54 55 jexec echo ifconfig ${epair_echo}b 198.51.100.2/24 up 56 jexec echo /usr/sbin/inetd -p ${PWD}/inetd-echo.pid $(atf_get_srcdir)/echo_inetd.conf 57 58 # Disable checksum offload on one of the interfaces to ensure pf handles that 59 jexec nat ifconfig ${epair_nat}a -txcsum 60 61 # Enable pf! 62 jexec nat pfctl -e 63 pft_set_rules nat \ 64 "nat pass on ${epair_echo}a inet from 192.0.2.0/24 to any -> (${epair_echo}a) port 30000:30001 sticky-address" 65 66 # Sanity check 67 atf_check -s exit:0 -o ignore ping -c 3 198.51.100.2 68 69 atf_check -s exit:0 -o match:foo* echo "foo" | nc -N 198.51.100.2 7 70 atf_check -s exit:0 -o match:foo* echo "foo" | nc -N 198.51.100.2 7 71 72 # This one will fail, but that's expected 73 echo "foo" | nc -N 198.51.100.2 7 & 74 75 sleep 1 76 77 # If the kernel is stuck in pf_get_sport() this will not succeed either. 78 timeout 2 jexec nat pfctl -sa 79 if [ $? -eq 124 ]; then 80 # Timed out 81 atf_fail "pfctl timeout" 82 fi 83} 84 85exhaust_cleanup() 86{ 87 pft_cleanup 88} 89 90atf_test_case "nested_anchor" "cleanup" 91nested_anchor_head() 92{ 93 atf_set descr 'Test setting and retrieving nested nat anchors' 94 atf_set require.user root 95} 96 97nested_anchor_body() 98{ 99 pft_init 100 101 epair=$(vnet_mkepair) 102 103 vnet_mkjail nat ${epair}a 104 105 pft_set_rules nat \ 106 "nat-anchor \"foo\"" 107 108 echo "nat-anchor \"bar\"" | jexec nat pfctl -g -a foo -f - 109 echo "nat on ${epair}a from any to any -> (${epair}a)" | jexec nat pfctl -g -a "foo/bar" -f - 110 111 atf_check -s exit:0 -o inline:"nat-anchor \"foo\" all { 112 nat-anchor \"bar\" all { 113 nat on ${epair}a all -> (${epair}a) round-robin 114 } 115} 116" jexec nat pfctl -sn -a "*" 117 118} 119 120endpoint_independent_setup() 121{ 122 pft_init 123 filter="udp and dst port 1234" # only capture udp pings 124 125 epair_client=$(vnet_mkepair) 126 epair_nat=$(vnet_mkepair) 127 epair_server1=$(vnet_mkepair) 128 epair_server2=$(vnet_mkepair) 129 bridge=$(vnet_mkbridge) 130 131 vnet_mkjail nat ${epair_client}b ${epair_nat}a 132 vnet_mkjail client ${epair_client}a 133 vnet_mkjail server1 ${epair_server1}a 134 vnet_mkjail server2 ${epair_server2}a 135 136 ifconfig ${epair_server1}b up 137 ifconfig ${epair_server2}b up 138 ifconfig ${epair_nat}b up 139 ifconfig ${bridge} \ 140 addm ${epair_server1}b \ 141 addm ${epair_server2}b \ 142 addm ${epair_nat}b \ 143 up 144 145 jexec nat ifconfig ${epair_client}b 192.0.2.1/24 up 146 jexec nat ifconfig ${epair_nat}a 198.51.100.42/24 up 147 jexec nat sysctl net.inet.ip.forwarding=1 148 149 jexec client ifconfig ${epair_client}a 192.0.2.2/24 up 150 jexec client route add default 192.0.2.1 151 152 jexec server1 ifconfig ${epair_server1}a 198.51.100.32/24 up 153 jexec server2 ifconfig ${epair_server2}a 198.51.100.22/24 up 154} 155 156endpoint_independent_common() 157{ 158 # Enable pf! 159 jexec nat pfctl -e 160 161 # validate non-endpoint independent nat rule behaviour 162 pft_set_rules nat "${1}" 163 164 jexec server1 tcpdump -i ${epair_server1}a -w ${PWD}/server1.pcap \ 165 --immediate-mode $filter & 166 server1tcppid="$!" 167 jexec server2 tcpdump -i ${epair_server2}a -w ${PWD}/server2.pcap \ 168 --immediate-mode $filter & 169 server2tcppid="$!" 170 171 # send out multiple packets 172 for i in $(seq 1 10); do 173 echo "ping" | jexec client nc -u 198.51.100.32 1234 -p 4242 -w 0 174 echo "ping" | jexec client nc -u 198.51.100.22 1234 -p 4242 -w 0 175 done 176 177 kill $server1tcppid 178 kill $server2tcppid 179 180 tuple_server1=$(tcpdump -r ${PWD}/server1.pcap | awk '{addr=$3} END {print addr}') 181 tuple_server2=$(tcpdump -r ${PWD}/server2.pcap | awk '{addr=$3} END {print addr}') 182 183 if [ -z $tuple_server1 ] 184 then 185 atf_fail "server1 did not receive connection from client (default)" 186 fi 187 188 if [ -z $tuple_server2 ] 189 then 190 atf_fail "server2 did not receive connection from client (default)" 191 fi 192 193 if [ "$tuple_server1" = "$tuple_server2" ] 194 then 195 echo "server1 tcpdump: $tuple_server1" 196 echo "server2 tcpdump: $tuple_server2" 197 atf_fail "Received same IP:port on server1 and server2 (default)" 198 fi 199 200 # validate endpoint independent nat rule behaviour 201 pft_set_rules nat "${2}" 202 203 jexec server1 tcpdump -i ${epair_server1}a -w ${PWD}/server1.pcap \ 204 --immediate-mode $filter & 205 server1tcppid="$!" 206 jexec server2 tcpdump -i ${epair_server2}a -w ${PWD}/server2.pcap \ 207 --immediate-mode $filter & 208 server2tcppid="$!" 209 210 # send out multiple packets, sometimes one fails to go through 211 for i in $(seq 1 10); do 212 echo "ping" | jexec client nc -u 198.51.100.32 1234 -p 4242 -w 0 213 echo "ping" | jexec client nc -u 198.51.100.22 1234 -p 4242 -w 0 214 done 215 216 kill $server1tcppid 217 kill $server2tcppid 218 219 tuple_server1=$(tcpdump -r ${PWD}/server1.pcap | awk '{addr=$3} END {print addr}') 220 tuple_server2=$(tcpdump -r ${PWD}/server2.pcap | awk '{addr=$3} END {print addr}') 221 222 if [ -z $tuple_server1 ] 223 then 224 atf_fail "server1 did not receive connection from client (endpoint-independent)" 225 fi 226 227 if [ -z $tuple_server2 ] 228 then 229 atf_fail "server2 did not receive connection from client (endpoint-independent)" 230 fi 231 232 if [ ! "$tuple_server1" = "$tuple_server2" ] 233 then 234 echo "server1 tcpdump: $tuple_server1" 235 echo "server2 tcpdump: $tuple_server2" 236 atf_fail "Received different IP:port on server1 than server2 (endpoint-independent)" 237 fi 238} 239 240atf_test_case "endpoint_independent_compat" "cleanup" 241endpoint_independent_compat_head() 242{ 243 atf_set descr 'Test that a client behind NAT gets the same external IP:port for different servers' 244 atf_set require.user root 245} 246 247endpoint_independent_compat_body() 248{ 249 endpoint_independent_setup # Sets ${epair_…} variables 250 251 endpoint_independent_common \ 252 "nat on ${epair_nat}a inet from ! (${epair_nat}a) to any -> (${epair_nat}a)" \ 253 "nat on ${epair_nat}a inet from ! (${epair_nat}a) to any -> (${epair_nat}a) endpoint-independent" 254} 255 256endpoint_independent_compat_cleanup() 257{ 258 pft_cleanup 259 rm -f server1.out 260 rm -f server2.out 261} 262 263atf_test_case "endpoint_independent_exhaust" "cleanup" 264endpoint_independent_exhaust_head() 265{ 266 atf_set descr 'Test that a client behind NAT gets the same external IP:port for different servers' 267 atf_set require.user root 268} 269 270endpoint_independent_exhaust_body() 271{ 272 endpoint_independent_setup # Sets ${epair_…} variables 273 274 endpoint_independent_common \ 275 "nat on ${epair_nat}a inet from ! (${epair_nat}a) to any -> (${epair_nat}a)" \ 276 "nat on ${epair_nat}a inet from ! (${epair_nat}a) to any -> (${epair_nat}a) port 3000:3001 sticky-address endpoint-independent" 277 278 # Exhaust the available nat ports 279 for i in $(seq 1 10); do 280 echo "ping" | jexec client nc -u 198.51.100.32 1234 -w 0 281 echo "ping" | jexec client nc -u 198.51.100.22 1234 -w 0 282 done 283} 284 285endpoint_independent_exhaust_cleanup() 286{ 287 pft_cleanup 288 rm -f server1.out 289 rm -f server2.out 290} 291 292atf_test_case "endpoint_independent_pass" "cleanup" 293endpoint_independent_pass_head() 294{ 295 atf_set descr 'Test that a client behind NAT gets the same external IP:port for different servers' 296 atf_set require.user root 297} 298 299endpoint_independent_pass_body() 300{ 301 endpoint_independent_setup # Sets ${epair_…} variables 302 303 endpoint_independent_common \ 304 "pass out on ${epair_nat}a inet from ! (${epair_nat}a) to any nat-to (${epair_nat}a) keep state" \ 305 "pass out on ${epair_nat}a inet from ! (${epair_nat}a) to any nat-to (${epair_nat}a) endpoint-independent keep state" 306 307} 308 309endpoint_independent_pass_cleanup() 310{ 311 pft_cleanup 312 rm -f server1.out 313 rm -f server2.out 314} 315 316nested_anchor_cleanup() 317{ 318 pft_cleanup 319} 320 321atf_test_case "nat6_nolinklocal" "cleanup" 322nat6_nolinklocal_head() 323{ 324 atf_set descr 'Ensure we do not use link-local addresses' 325 atf_set require.user root 326} 327 328nat6_nolinklocal_body() 329{ 330 pft_init 331 332 epair_nat=$(vnet_mkepair) 333 epair_echo=$(vnet_mkepair) 334 335 vnet_mkjail nat ${epair_nat}b ${epair_echo}a 336 vnet_mkjail echo ${epair_echo}b 337 338 ifconfig ${epair_nat}a inet6 2001:db8::2/64 no_dad up 339 route add -6 -net 2001:db8:1::/64 2001:db8::1 340 341 jexec nat ifconfig ${epair_nat}b inet6 2001:db8::1/64 no_dad up 342 jexec nat ifconfig ${epair_echo}a inet6 2001:db8:1::1/64 no_dad up 343 jexec nat sysctl net.inet6.ip6.forwarding=1 344 345 jexec echo ifconfig ${epair_echo}b inet6 2001:db8:1::2/64 no_dad up 346 # Ensure we can't reply to link-local pings 347 jexec echo pfctl -e 348 pft_set_rules echo \ 349 "pass" \ 350 "block in inet6 proto icmp6 from fe80::/10 to any icmp6-type echoreq" 351 352 jexec nat pfctl -e 353 pft_set_rules nat \ 354 "nat pass on ${epair_echo}a inet6 from 2001:db8::/64 to any -> (${epair_echo}a)" \ 355 "pass" 356 357 # Sanity check 358 atf_check -s exit:0 -o ignore \ 359 ping -6 -c 1 2001:db8::1 360 for i in `seq 0 10` 361 do 362 atf_check -s exit:0 -o ignore \ 363 ping -6 -c 1 2001:db8:1::2 364 done 365} 366 367nat6_nolinklocal_cleanup() 368{ 369 pft_cleanup 370} 371 372empty_table_common() 373{ 374 option=$1 375 376 pft_init 377 378 epair_wan=$(vnet_mkepair) 379 epair_lan=$(vnet_mkepair) 380 381 vnet_mkjail srv ${epair_wan}a 382 jexec srv ifconfig ${epair_wan}a 192.0.2.2/24 up 383 384 vnet_mkjail rtr ${epair_wan}b ${epair_lan}a 385 jexec rtr ifconfig ${epair_wan}b 192.0.2.1/24 up 386 jexec rtr ifconfig ${epair_lan}a 198.51.100.1/24 up 387 jexec rtr sysctl net.inet.ip.forwarding=1 388 389 ifconfig ${epair_lan}b 198.51.100.2/24 up 390 route add default 198.51.100.1 391 392 jexec rtr pfctl -e 393 pft_set_rules rtr \ 394 "table <empty>" \ 395 "nat on ${epair_wan}b inet from 198.51.100.0/24 -> <empty> ${option}" \ 396 "pass" 397 398 # Sanity checks 399 atf_check -s exit:0 -o ignore \ 400 jexec rtr ping -c 1 192.0.2.2 401 atf_check -s exit:0 -o ignore \ 402 ping -c 1 198.51.100.1 403 atf_check -s exit:0 -o ignore \ 404 ping -c 1 192.0.2.1 405 406 # Provoke divide by zero 407 ping -c 1 192.0.2.2 408 true 409} 410 411atf_test_case "empty_table_source_hash" "cleanup" 412empty_table_source_hash_head() 413{ 414 atf_set descr 'Test source-hash on an emtpy table' 415 atf_set require.user root 416} 417 418empty_table_source_hash_body() 419{ 420 empty_table_common "source-hash" 421} 422 423empty_table_source_hash_cleanup() 424{ 425 pft_cleanup 426} 427 428atf_test_case "empty_table_random" "cleanup" 429empty_table_random_head() 430{ 431 atf_set descr 'Test random on an emtpy table' 432 atf_set require.user root 433} 434 435empty_table_random_body() 436{ 437 empty_table_common "random" 438} 439 440empty_table_random_cleanup() 441{ 442 pft_cleanup 443} 444 445no_addrs_common() 446{ 447 option=$1 448 449 pft_init 450 451 epair_wan=$(vnet_mkepair) 452 epair_lan=$(vnet_mkepair) 453 454 vnet_mkjail srv ${epair_wan}a 455 jexec srv ifconfig ${epair_wan}a 192.0.2.2/24 up 456 457 vnet_mkjail rtr ${epair_wan}b ${epair_lan}a 458 jexec rtr route add -net 192.0.2.0/24 -iface ${epair_wan}b 459 jexec rtr ifconfig ${epair_lan}a 198.51.100.1/24 up 460 jexec rtr sysctl net.inet.ip.forwarding=1 461 462 ifconfig ${epair_lan}b 198.51.100.2/24 up 463 route add default 198.51.100.1 464 465 jexec rtr pfctl -e 466 pft_set_rules rtr \ 467 "nat on ${epair_wan}b inet from 198.51.100.0/24 -> (${epair_wan}b) ${option}" \ 468 "pass" 469 470 # Provoke divide by zero 471 ping -c 1 192.0.2.2 472 true 473} 474 475atf_test_case "no_addrs_source_hash" "cleanup" 476no_addrs_source_hash_head() 477{ 478 atf_set descr 'Test source-hash on an interface with no addresses' 479 atf_set require.user root 480} 481 482no_addrs_source_hash_body() 483{ 484 no_addrs_common "source-hash" 485} 486 487no_addrs_source_hash_cleanup() 488{ 489 pft_cleanup 490} 491 492atf_test_case "no_addrs_random" "cleanup" 493no_addrs_random_head() 494{ 495 atf_set descr 'Test random on an interface with no addresses' 496 atf_set require.user root 497} 498 499no_addrs_random_body() 500{ 501 no_addrs_common "random" 502} 503 504no_addrs_random_cleanup() 505{ 506 pft_cleanup 507} 508 509atf_test_case "nat_pass_in" "cleanup" 510nat_pass_in_head() 511{ 512 atf_set descr 'IPv4 NAT on inbound pass rule' 513 atf_set require.user root 514 atf_set require.progs scapy 515} 516 517nat_pass_in_body() 518{ 519 setup_router_server_ipv4 520 # Delete the route back to make sure that the traffic has been NAT-ed 521 jexec server route del -net ${net_tester} ${net_server_host_router} 522 # Provide routing back to the NAT address 523 jexec server route add 203.0.113.0/24 ${net_server_host_router} 524 jexec router route add 203.0.113.0/24 -iface ${epair_tester}b 525 526 pft_set_rules router \ 527 "block" \ 528 "pass in on ${epair_tester}b inet proto tcp nat-to 203.0.113.0 keep state" \ 529 "pass out on ${epair_server}a inet proto tcp keep state" 530 531 ping_server_check_reply exit:0 --ping-type=tcp3way --send-sport=4201 532 533 jexec router pfctl -qvvsr 534 jexec router pfctl -qvvss 535 jexec router ifconfig 536 jexec router netstat -rn 537} 538 539nat_pass_in_cleanup() 540{ 541 pft_cleanup 542} 543 544nat_pass_out_head() 545{ 546 atf_set descr 'IPv4 NAT on outbound pass rule' 547 atf_set require.user root 548 atf_set require.progs scapy 549} 550 551nat_pass_out_body() 552{ 553 setup_router_server_ipv4 554 # Delete the route back to make sure that the traffic has been NAT-ed 555 jexec server route del -net ${net_tester} ${net_server_host_router} 556 557 pft_set_rules router \ 558 "block" \ 559 "pass in on ${epair_tester}b inet proto tcp keep state" \ 560 "pass out on ${epair_server}a inet proto tcp nat-to ${epair_server}a keep state" 561 562 ping_server_check_reply exit:0 --ping-type=tcp3way --send-sport=4201 563 564 jexec router pfctl -qvvsr 565 jexec router pfctl -qvvss 566 jexec router ifconfig 567 jexec router netstat -rn 568} 569 570nat_pass_out_cleanup() 571{ 572 pft_cleanup 573} 574 575atf_test_case "nat_match" "cleanup" 576nat_match_head() 577{ 578 atf_set descr 'IPv4 NAT on match rule' 579 atf_set require.user root 580 atf_set require.progs scapy 581} 582 583nat_match_body() 584{ 585 setup_router_server_ipv4 586 # Delete the route back to make sure that the traffic has been NAT-ed 587 jexec server route del -net ${net_tester} ${net_server_host_router} 588 589 # NAT is applied during ruleset evaluation: 590 # rules after "match" match on NAT-ed address 591 pft_set_rules router \ 592 "block" \ 593 "pass in on ${epair_tester}b inet proto tcp keep state" \ 594 "match out on ${epair_server}a inet proto tcp nat-to ${epair_server}a" \ 595 "pass out on ${epair_server}a inet proto tcp from ${epair_server}a keep state" 596 597 ping_server_check_reply exit:0 --ping-type=tcp3way --send-sport=4201 598 599 jexec router pfctl -qvvsr 600 jexec router pfctl -qvvss 601 jexec router ifconfig 602 jexec router netstat -rn 603} 604 605nat_match_cleanup() 606{ 607 pft_cleanup 608} 609 610map_e_common() 611{ 612 NC_TRY_COUNT=12 613 614 pft_init 615 616 epair_map_e=$(vnet_mkepair) 617 epair_echo=$(vnet_mkepair) 618 619 vnet_mkjail map_e ${epair_map_e}b ${epair_echo}a 620 vnet_mkjail echo ${epair_echo}b 621 622 ifconfig ${epair_map_e}a 192.0.2.2/24 up 623 route add -net 198.51.100.0/24 192.0.2.1 624 625 jexec map_e ifconfig ${epair_map_e}b 192.0.2.1/24 up 626 jexec map_e ifconfig ${epair_echo}a 198.51.100.1/24 up 627 jexec map_e sysctl net.inet.ip.forwarding=1 628 629 jexec echo ifconfig ${epair_echo}b 198.51.100.2/24 up 630 jexec echo /usr/sbin/inetd -p ${PWD}/inetd-echo.pid $(atf_get_srcdir)/echo_inetd.conf 631 632 # Enable pf! 633 jexec map_e pfctl -e 634} 635 636atf_test_case "map_e_compat" "cleanup" 637map_e_compat_head() 638{ 639 atf_set descr 'map-e-portset test' 640 atf_set require.user root 641} 642 643map_e_compat_body() 644{ 645 map_e_common 646 647 pft_set_rules map_e \ 648 "nat pass on ${epair_echo}a inet from 192.0.2.0/24 to any -> (${epair_echo}a) map-e-portset 2/12/0x342" 649 650 # Only allow specified ports. 651 jexec echo pfctl -e 652 pft_set_rules echo "block return all" \ 653 "pass in on ${epair_echo}b inet proto tcp from 198.51.100.1 port 19720:19723 to (${epair_echo}b) port 7" \ 654 "pass in on ${epair_echo}b inet proto tcp from 198.51.100.1 port 36104:36107 to (${epair_echo}b) port 7" \ 655 "pass in on ${epair_echo}b inet proto tcp from 198.51.100.1 port 52488:52491 to (${epair_echo}b) port 7" \ 656 "set skip on lo" 657 658 i=0 659 while [ ${i} -lt ${NC_TRY_COUNT} ] 660 do 661 echo "foo ${i}" | timeout 2 nc -N 198.51.100.2 7 662 if [ $? -ne 0 ]; then 663 atf_fail "nc failed (${i})" 664 fi 665 i=$((${i}+1)) 666 done 667} 668 669map_e_compat_cleanup() 670{ 671 pft_cleanup 672} 673 674 675atf_test_case "map_e_pass" "cleanup" 676map_e_pass_head() 677{ 678 atf_set descr 'map-e-portset test' 679 atf_set require.user root 680} 681 682map_e_pass_body() 683{ 684 map_e_common 685 686 pft_set_rules map_e \ 687 "pass out on ${epair_echo}a inet from 192.0.2.0/24 to any nat-to (${epair_echo}a) map-e-portset 2/12/0x342 keep state" 688 689 jexec map_e pfctl -qvvsr 690 691 # Only allow specified ports. 692 jexec echo pfctl -e 693 pft_set_rules echo "block return all" \ 694 "pass in on ${epair_echo}b inet proto tcp from 198.51.100.1 port 19720:19723 to (${epair_echo}b) port 7" \ 695 "pass in on ${epair_echo}b inet proto tcp from 198.51.100.1 port 36104:36107 to (${epair_echo}b) port 7" \ 696 "pass in on ${epair_echo}b inet proto tcp from 198.51.100.1 port 52488:52491 to (${epair_echo}b) port 7" \ 697 "set skip on lo" 698 699 i=0 700 while [ ${i} -lt ${NC_TRY_COUNT} ] 701 do 702 echo "foo ${i}" | timeout 2 nc -N 198.51.100.2 7 703 if [ $? -ne 0 ]; then 704 atf_fail "nc failed (${i})" 705 fi 706 i=$((${i}+1)) 707 done 708} 709 710map_e_pass_cleanup() 711{ 712 pft_cleanup 713} 714 715atf_test_case "binat_compat" "cleanup" 716binat_compat_head() 717{ 718 atf_set descr 'IPv4 BINAT with nat ruleset' 719 atf_set require.user root 720 atf_set require.progs scapy 721} 722 723binat_compat_body() 724{ 725 setup_router_server_ipv4 726 # Delete the route back to make sure that the traffic has been NAT-ed 727 jexec server route del -net ${net_tester} ${net_server_host_router} 728 729 pft_set_rules router \ 730 "set state-policy if-bound" \ 731 "set ruleset-optimization none" \ 732 "binat on ${epair_server}a inet proto tcp from ${net_tester_host_tester} to any tag sometag -> ${epair_server}a" \ 733 "block" \ 734 "pass in on ${epair_tester}b inet proto tcp !tagged sometag keep state" \ 735 "pass out on ${epair_server}a inet proto tcp tagged sometag keep state" \ 736 "pass in on ${epair_server}a inet proto tcp tagged sometag keep state" \ 737 "pass out on ${epair_tester}b inet proto tcp tagged sometag keep state" 738 739 # Test the outbound NAT part of BINAT. 740 ping_server_check_reply exit:0 --ping-type=tcp3way --send-sport=4201 741 742 states=$(mktemp) || exit 1 743 jexec router pfctl -qvss | normalize_pfctl_s > $states 744 745 for state_regexp in \ 746 "${epair_tester}b tcp ${net_server_host_server}:9 <- ${net_tester_host_tester}:4201 .* 3:2 pkts,.* rule 1" \ 747 "${epair_server}a tcp ${net_server_host_router}:4201 \(${net_tester_host_tester}:4201\) -> ${net_server_host_server}:9 .* 3:2 pkts,.* rule 2" \ 748 ; do 749 grep -qE "${state_regexp}" $states || atf_fail "State not found for '${state_regexp}'" 750 done 751 752 # Test the inbound RDR part of BINAT. 753 # The "tester" becomes "server" and vice versa. 754 inetd_conf=$(mktemp) 755 echo "discard stream tcp nowait root internal" > $inetd_conf 756 inetd -p ${PWD}/inetd_tester.pid $inetd_conf 757 758 atf_check -s exit:0 \ 759 jexec server ${common_dir}/pft_ping.py \ 760 --ping-type=tcp3way --send-sport=4202 \ 761 --sendif ${epair_server}b \ 762 --to ${net_server_host_router} \ 763 --replyif ${epair_server}b 764 765 states=$(mktemp) || exit 1 766 jexec router pfctl -qvss | normalize_pfctl_s > $states 767 768 for state_regexp in \ 769 "${epair_server}a tcp ${net_tester_host_tester}:9 \(${net_server_host_router}:9\) <- ${net_server_host_server}:4202 .* 3:2 pkts,.* rule 3" \ 770 "${epair_tester}b tcp ${net_server_host_server}:4202 -> ${net_tester_host_tester}:9 .* 3:2 pkts,.* rule 4" \ 771 ; do 772 grep -qE "${state_regexp}" $states || atf_fail "State not found for '${state_regexp}'" 773 done 774} 775 776binat_compat_cleanup() 777{ 778 pft_cleanup 779 kill $(cat ${PWD}/inetd_tester.pid) 780} 781 782atf_test_case "binat_match" "cleanup" 783binat_match_head() 784{ 785 atf_set descr 'IPv4 BINAT with nat ruleset' 786 atf_set require.user root 787 atf_set require.progs scapy 788} 789 790binat_match_body() 791{ 792 setup_router_server_ipv4 793 # Delete the route back to make sure that the traffic has been NAT-ed 794 jexec server route del -net ${net_tester} ${net_server_host_router} 795 796 # The "binat-to" rule expands to 2 rules so the ""pass" rules start at 3! 797 pft_set_rules router \ 798 "set state-policy if-bound" \ 799 "set ruleset-optimization none" \ 800 "block" \ 801 "match on ${epair_server}a inet proto tcp from ${net_tester_host_tester} to any tag sometag binat-to ${epair_server}a" \ 802 "pass in on ${epair_tester}b inet proto tcp !tagged sometag keep state" \ 803 "pass out on ${epair_server}a inet proto tcp tagged sometag keep state" \ 804 "pass in on ${epair_server}a inet proto tcp tagged sometag keep state" \ 805 "pass out on ${epair_tester}b inet proto tcp tagged sometag keep state" 806 807 # Test the outbound NAT part of BINAT. 808 ping_server_check_reply exit:0 --ping-type=tcp3way --send-sport=4201 809 810 states=$(mktemp) || exit 1 811 jexec router pfctl -qvss | normalize_pfctl_s > $states 812 813 for state_regexp in \ 814 "${epair_tester}b tcp ${net_server_host_server}:9 <- ${net_tester_host_tester}:4201 .* 3:2 pkts,.* rule 3" \ 815 "${epair_server}a tcp ${net_server_host_router}:4201 \(${net_tester_host_tester}:4201\) -> ${net_server_host_server}:9 .* 3:2 pkts,.* rule 4" \ 816 ; do 817 grep -qE "${state_regexp}" $states || atf_fail "State not found for '${state_regexp}'" 818 done 819 820 # Test the inbound RDR part of BINAT. 821 # The "tester" becomes "server" and vice versa. 822 inetd_conf=$(mktemp) 823 echo "discard stream tcp nowait root internal" > $inetd_conf 824 inetd -p ${PWD}/inetd_tester.pid $inetd_conf 825 826 atf_check -s exit:0 \ 827 jexec server ${common_dir}/pft_ping.py \ 828 --ping-type=tcp3way --send-sport=4202 \ 829 --sendif ${epair_server}b \ 830 --to ${net_server_host_router} \ 831 --replyif ${epair_server}b 832 833 states=$(mktemp) || exit 1 834 jexec router pfctl -qvss | normalize_pfctl_s > $states 835 836 for state_regexp in \ 837 "${epair_server}a tcp ${net_tester_host_tester}:9 \(${net_server_host_router}:9\) <- ${net_server_host_server}:4202 .* 3:2 pkts,.* rule 5" \ 838 "${epair_tester}b tcp ${net_server_host_server}:4202 -> ${net_tester_host_tester}:9 .* 3:2 pkts,.* rule 6" \ 839 ; do 840 grep -qE "${state_regexp}" $states || atf_fail "State not found for '${state_regexp}'" 841 done 842} 843 844binat_match_cleanup() 845{ 846 pft_cleanup 847 kill $(cat ${PWD}/inetd_tester.pid) 848} 849 850atf_test_case "empty_pool" "cleanup" 851empty_pool_head() 852{ 853 atf_set descr 'NAT with empty pool' 854 atf_set require.user root 855 atf_set require.progs python3 scapy 856} 857 858empty_pool_body() 859{ 860 pft_init 861 setup_router_server_ipv6 862 863 864 pft_set_rules router \ 865 "block" \ 866 "pass inet6 proto icmp6 icmp6-type { neighbrsol, neighbradv }" \ 867 "pass in on ${epair_tester}b" \ 868 "pass out on ${epair_server}a inet6 from any to ${net_server_host_server} nat-to <nonexistent>" \ 869 870 # pf_map_addr_sn() won't be able to pick a target address, because 871 # the table used in redireciton pool is empty. Packet will not be 872 # forwarded, error counter will be increased. 873 ping_server_check_reply exit:1 874 # Ignore warnings about not-loaded ALTQ 875 atf_check -o "match:map-failed +1 +" -x "jexec router pfctl -qvvsi 2> /dev/null" 876} 877 878empty_pool_cleanup() 879{ 880 pft_cleanup 881} 882 883atf_test_case "dummynet_mask" "cleanup" 884dummynet_mask_head() 885{ 886 atf_set descr 'Verify that dummynet uses the pre-nat address for masking' 887 atf_set require.user root 888} 889 890dummynet_mask_body() 891{ 892 dummynet_init 893 894 epair_srv=$(vnet_mkepair) 895 epair_cl=$(vnet_mkepair) 896 897 ifconfig ${epair_cl}b 192.0.2.2/24 up 898 route add default 192.0.2.1 899 900 vnet_mkjail srv ${epair_srv}a 901 jexec srv ifconfig ${epair_srv}a 198.51.100.2/24 up 902 903 vnet_mkjail gw ${epair_srv}b ${epair_cl}a 904 jexec gw ifconfig ${epair_srv}b 198.51.100.1/24 up 905 jexec gw ifconfig ${epair_cl}a 192.0.2.1/24 up 906 jexec gw sysctl net.inet.ip.forwarding=1 907 908 jexec gw dnctl pipe 1 config delay 100 mask src-ip 0xffffff00 909 jexec gw pfctl -e 910 pft_set_rules gw \ 911 "nat on ${epair_srv}b inet from 192.0.2.0/24 to any -> (${epair_srv}b)" \ 912 "pass out dnpipe 1" 913 914 atf_check -s exit:0 -o ignore \ 915 ping -c 3 198.51.100.2 916 917 # Now check that dummynet looked at the correct address 918 atf_check -s exit:0 -o match:"ip.*192.0.2.0/0" \ 919 jexec gw dnctl pipe show 920} 921 922dummynet_mask_cleanup() 923{ 924 pft_cleanup 925} 926 927atf_init_test_cases() 928{ 929 atf_add_test_case "exhaust" 930 atf_add_test_case "nested_anchor" 931 atf_add_test_case "endpoint_independent_compat" 932 atf_add_test_case "endpoint_independent_exhaust" 933 atf_add_test_case "endpoint_independent_pass" 934 atf_add_test_case "nat6_nolinklocal" 935 atf_add_test_case "empty_table_source_hash" 936 atf_add_test_case "no_addrs_source_hash" 937 atf_add_test_case "empty_table_random" 938 atf_add_test_case "no_addrs_random" 939 atf_add_test_case "map_e_compat" 940 atf_add_test_case "map_e_pass" 941 atf_add_test_case "nat_pass_in" 942 atf_add_test_case "nat_pass_out" 943 atf_add_test_case "nat_match" 944 atf_add_test_case "binat_compat" 945 atf_add_test_case "binat_match" 946 atf_add_test_case "empty_pool" 947 atf_add_test_case "dummynet_mask" 948} 949