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_pass" "cleanup" 264endpoint_independent_pass_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_pass_body() 271{ 272 endpoint_independent_setup # Sets ${epair_…} variables 273 274 endpoint_independent_common \ 275 "pass out on ${epair_nat}a inet from ! (${epair_nat}a) to any nat-to (${epair_nat}a) keep state" \ 276 "pass out on ${epair_nat}a inet from ! (${epair_nat}a) to any nat-to (${epair_nat}a) endpoint-independent keep state" 277 278} 279 280endpoint_independent_pass_cleanup() 281{ 282 pft_cleanup 283 rm -f server1.out 284 rm -f server2.out 285} 286 287nested_anchor_cleanup() 288{ 289 pft_cleanup 290} 291 292atf_test_case "nat6_nolinklocal" "cleanup" 293nat6_nolinklocal_head() 294{ 295 atf_set descr 'Ensure we do not use link-local addresses' 296 atf_set require.user root 297} 298 299nat6_nolinklocal_body() 300{ 301 pft_init 302 303 epair_nat=$(vnet_mkepair) 304 epair_echo=$(vnet_mkepair) 305 306 vnet_mkjail nat ${epair_nat}b ${epair_echo}a 307 vnet_mkjail echo ${epair_echo}b 308 309 ifconfig ${epair_nat}a inet6 2001:db8::2/64 no_dad up 310 route add -6 -net 2001:db8:1::/64 2001:db8::1 311 312 jexec nat ifconfig ${epair_nat}b inet6 2001:db8::1/64 no_dad up 313 jexec nat ifconfig ${epair_echo}a inet6 2001:db8:1::1/64 no_dad up 314 jexec nat sysctl net.inet6.ip6.forwarding=1 315 316 jexec echo ifconfig ${epair_echo}b inet6 2001:db8:1::2/64 no_dad up 317 # Ensure we can't reply to link-local pings 318 jexec echo pfctl -e 319 pft_set_rules echo \ 320 "pass" \ 321 "block in inet6 proto icmp6 from fe80::/10 to any icmp6-type echoreq" 322 323 jexec nat pfctl -e 324 pft_set_rules nat \ 325 "nat pass on ${epair_echo}a inet6 from 2001:db8::/64 to any -> (${epair_echo}a)" \ 326 "pass" 327 328 # Sanity check 329 atf_check -s exit:0 -o ignore \ 330 ping -6 -c 1 2001:db8::1 331 for i in `seq 0 10` 332 do 333 atf_check -s exit:0 -o ignore \ 334 ping -6 -c 1 2001:db8:1::2 335 done 336} 337 338nat6_nolinklocal_cleanup() 339{ 340 pft_cleanup 341} 342 343empty_table_common() 344{ 345 option=$1 346 347 pft_init 348 349 epair_wan=$(vnet_mkepair) 350 epair_lan=$(vnet_mkepair) 351 352 vnet_mkjail srv ${epair_wan}a 353 jexec srv ifconfig ${epair_wan}a 192.0.2.2/24 up 354 355 vnet_mkjail rtr ${epair_wan}b ${epair_lan}a 356 jexec rtr ifconfig ${epair_wan}b 192.0.2.1/24 up 357 jexec rtr ifconfig ${epair_lan}a 198.51.100.1/24 up 358 jexec rtr sysctl net.inet.ip.forwarding=1 359 360 ifconfig ${epair_lan}b 198.51.100.2/24 up 361 route add default 198.51.100.1 362 363 jexec rtr pfctl -e 364 pft_set_rules rtr \ 365 "table <empty>" \ 366 "nat on ${epair_wan}b inet from 198.51.100.0/24 -> <empty> ${option}" \ 367 "pass" 368 369 # Sanity checks 370 atf_check -s exit:0 -o ignore \ 371 jexec rtr ping -c 1 192.0.2.2 372 atf_check -s exit:0 -o ignore \ 373 ping -c 1 198.51.100.1 374 atf_check -s exit:0 -o ignore \ 375 ping -c 1 192.0.2.1 376 377 # Provoke divide by zero 378 ping -c 1 192.0.2.2 379 true 380} 381 382atf_test_case "empty_table_source_hash" "cleanup" 383empty_table_source_hash_head() 384{ 385 atf_set descr 'Test source-hash on an emtpy table' 386 atf_set require.user root 387} 388 389empty_table_source_hash_body() 390{ 391 empty_table_common "source-hash" 392} 393 394empty_table_source_hash_cleanup() 395{ 396 pft_cleanup 397} 398 399atf_test_case "empty_table_random" "cleanup" 400empty_table_random_head() 401{ 402 atf_set descr 'Test random on an emtpy table' 403 atf_set require.user root 404} 405 406empty_table_random_body() 407{ 408 empty_table_common "random" 409} 410 411empty_table_random_cleanup() 412{ 413 pft_cleanup 414} 415 416no_addrs_common() 417{ 418 option=$1 419 420 pft_init 421 422 epair_wan=$(vnet_mkepair) 423 epair_lan=$(vnet_mkepair) 424 425 vnet_mkjail srv ${epair_wan}a 426 jexec srv ifconfig ${epair_wan}a 192.0.2.2/24 up 427 428 vnet_mkjail rtr ${epair_wan}b ${epair_lan}a 429 jexec rtr route add -net 192.0.2.0/24 -iface ${epair_wan}b 430 jexec rtr ifconfig ${epair_lan}a 198.51.100.1/24 up 431 jexec rtr sysctl net.inet.ip.forwarding=1 432 433 ifconfig ${epair_lan}b 198.51.100.2/24 up 434 route add default 198.51.100.1 435 436 jexec rtr pfctl -e 437 pft_set_rules rtr \ 438 "nat on ${epair_wan}b inet from 198.51.100.0/24 -> (${epair_wan}b) ${option}" \ 439 "pass" 440 441 # Provoke divide by zero 442 ping -c 1 192.0.2.2 443 true 444} 445 446atf_test_case "no_addrs_source_hash" "cleanup" 447no_addrs_source_hash_head() 448{ 449 atf_set descr 'Test source-hash on an interface with no addresses' 450 atf_set require.user root 451} 452 453no_addrs_source_hash_body() 454{ 455 no_addrs_common "source-hash" 456} 457 458no_addrs_source_hash_cleanup() 459{ 460 pft_cleanup 461} 462 463atf_test_case "no_addrs_random" "cleanup" 464no_addrs_random_head() 465{ 466 atf_set descr 'Test random on an interface with no addresses' 467 atf_set require.user root 468} 469 470no_addrs_random_body() 471{ 472 no_addrs_common "random" 473} 474 475no_addrs_random_cleanup() 476{ 477 pft_cleanup 478} 479 480atf_test_case "nat_pass_in" "cleanup" 481nat_pass_in_head() 482{ 483 atf_set descr 'IPv4 NAT on inbound pass rule' 484 atf_set require.user root 485 atf_set require.progs scapy 486} 487 488nat_pass_in_body() 489{ 490 setup_router_server_ipv4 491 # Delete the route back to make sure that the traffic has been NAT-ed 492 jexec server route del -net ${net_tester} ${net_server_host_router} 493 # Provide routing back to the NAT address 494 jexec server route add 203.0.113.0/24 ${net_server_host_router} 495 jexec router route add 203.0.113.0/24 -iface ${epair_tester}b 496 497 pft_set_rules router \ 498 "block" \ 499 "pass in on ${epair_tester}b inet proto tcp nat-to 203.0.113.0 keep state" \ 500 "pass out on ${epair_server}a inet proto tcp keep state" 501 502 ping_server_check_reply exit:0 --ping-type=tcp3way --send-sport=4201 503 504 jexec router pfctl -qvvsr 505 jexec router pfctl -qvvss 506 jexec router ifconfig 507 jexec router netstat -rn 508} 509 510nat_pass_in_cleanup() 511{ 512 pft_cleanup 513} 514 515nat_pass_out_head() 516{ 517 atf_set descr 'IPv4 NAT on outbound pass rule' 518 atf_set require.user root 519 atf_set require.progs scapy 520} 521 522nat_pass_out_body() 523{ 524 setup_router_server_ipv4 525 # Delete the route back to make sure that the traffic has been NAT-ed 526 jexec server route del -net ${net_tester} ${net_server_host_router} 527 528 pft_set_rules router \ 529 "block" \ 530 "pass in on ${epair_tester}b inet proto tcp keep state" \ 531 "pass out on ${epair_server}a inet proto tcp nat-to ${epair_server}a keep state" 532 533 ping_server_check_reply exit:0 --ping-type=tcp3way --send-sport=4201 534 535 jexec router pfctl -qvvsr 536 jexec router pfctl -qvvss 537 jexec router ifconfig 538 jexec router netstat -rn 539} 540 541nat_pass_out_cleanup() 542{ 543 pft_cleanup 544} 545 546atf_test_case "nat_match" "cleanup" 547nat_match_head() 548{ 549 atf_set descr 'IPv4 NAT on match rule' 550 atf_set require.user root 551 atf_set require.progs scapy 552} 553 554nat_match_body() 555{ 556 setup_router_server_ipv4 557 # Delete the route back to make sure that the traffic has been NAT-ed 558 jexec server route del -net ${net_tester} ${net_server_host_router} 559 560 # NAT is applied during ruleset evaluation: 561 # rules after "match" match on NAT-ed address 562 pft_set_rules router \ 563 "block" \ 564 "pass in on ${epair_tester}b inet proto tcp keep state" \ 565 "match out on ${epair_server}a inet proto tcp nat-to ${epair_server}a" \ 566 "pass out on ${epair_server}a inet proto tcp from ${epair_server}a keep state" 567 568 ping_server_check_reply exit:0 --ping-type=tcp3way --send-sport=4201 569 570 jexec router pfctl -qvvsr 571 jexec router pfctl -qvvss 572 jexec router ifconfig 573 jexec router netstat -rn 574} 575 576nat_match_cleanup() 577{ 578 pft_cleanup 579} 580 581map_e_common() 582{ 583 NC_TRY_COUNT=12 584 585 pft_init 586 587 epair_map_e=$(vnet_mkepair) 588 epair_echo=$(vnet_mkepair) 589 590 vnet_mkjail map_e ${epair_map_e}b ${epair_echo}a 591 vnet_mkjail echo ${epair_echo}b 592 593 ifconfig ${epair_map_e}a 192.0.2.2/24 up 594 route add -net 198.51.100.0/24 192.0.2.1 595 596 jexec map_e ifconfig ${epair_map_e}b 192.0.2.1/24 up 597 jexec map_e ifconfig ${epair_echo}a 198.51.100.1/24 up 598 jexec map_e sysctl net.inet.ip.forwarding=1 599 600 jexec echo ifconfig ${epair_echo}b 198.51.100.2/24 up 601 jexec echo /usr/sbin/inetd -p ${PWD}/inetd-echo.pid $(atf_get_srcdir)/echo_inetd.conf 602 603 # Enable pf! 604 jexec map_e pfctl -e 605} 606 607atf_test_case "map_e_compat" "cleanup" 608map_e_compat_head() 609{ 610 atf_set descr 'map-e-portset test' 611 atf_set require.user root 612} 613 614map_e_compat_body() 615{ 616 map_e_common 617 618 pft_set_rules map_e \ 619 "nat pass on ${epair_echo}a inet from 192.0.2.0/24 to any -> (${epair_echo}a) map-e-portset 2/12/0x342" 620 621 # Only allow specified ports. 622 jexec echo pfctl -e 623 pft_set_rules echo "block return all" \ 624 "pass in on ${epair_echo}b inet proto tcp from 198.51.100.1 port 19720:19723 to (${epair_echo}b) port 7" \ 625 "pass in on ${epair_echo}b inet proto tcp from 198.51.100.1 port 36104:36107 to (${epair_echo}b) port 7" \ 626 "pass in on ${epair_echo}b inet proto tcp from 198.51.100.1 port 52488:52491 to (${epair_echo}b) port 7" \ 627 "set skip on lo" 628 629 i=0 630 while [ ${i} -lt ${NC_TRY_COUNT} ] 631 do 632 echo "foo ${i}" | timeout 2 nc -N 198.51.100.2 7 633 if [ $? -ne 0 ]; then 634 atf_fail "nc failed (${i})" 635 fi 636 i=$((${i}+1)) 637 done 638} 639 640map_e_compat_cleanup() 641{ 642 pft_cleanup 643} 644 645 646atf_test_case "map_e_pass" "cleanup" 647map_e_pass_head() 648{ 649 atf_set descr 'map-e-portset test' 650 atf_set require.user root 651} 652 653map_e_pass_body() 654{ 655 map_e_common 656 657 pft_set_rules map_e \ 658 "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" 659 660 jexec map_e pfctl -qvvsr 661 662 # Only allow specified ports. 663 jexec echo pfctl -e 664 pft_set_rules echo "block return all" \ 665 "pass in on ${epair_echo}b inet proto tcp from 198.51.100.1 port 19720:19723 to (${epair_echo}b) port 7" \ 666 "pass in on ${epair_echo}b inet proto tcp from 198.51.100.1 port 36104:36107 to (${epair_echo}b) port 7" \ 667 "pass in on ${epair_echo}b inet proto tcp from 198.51.100.1 port 52488:52491 to (${epair_echo}b) port 7" \ 668 "set skip on lo" 669 670 i=0 671 while [ ${i} -lt ${NC_TRY_COUNT} ] 672 do 673 echo "foo ${i}" | timeout 2 nc -N 198.51.100.2 7 674 if [ $? -ne 0 ]; then 675 atf_fail "nc failed (${i})" 676 fi 677 i=$((${i}+1)) 678 done 679} 680 681map_e_pass_cleanup() 682{ 683 pft_cleanup 684} 685 686atf_test_case "binat_compat" "cleanup" 687binat_compat_head() 688{ 689 atf_set descr 'IPv4 BINAT with nat ruleset' 690 atf_set require.user root 691 atf_set require.progs scapy 692} 693 694binat_compat_body() 695{ 696 setup_router_server_ipv4 697 # Delete the route back to make sure that the traffic has been NAT-ed 698 jexec server route del -net ${net_tester} ${net_server_host_router} 699 700 pft_set_rules router \ 701 "set state-policy if-bound" \ 702 "set ruleset-optimization none" \ 703 "binat on ${epair_server}a inet proto tcp from ${net_tester_host_tester} to any tag sometag -> ${epair_server}a" \ 704 "block" \ 705 "pass in on ${epair_tester}b inet proto tcp !tagged sometag keep state" \ 706 "pass out on ${epair_server}a inet proto tcp tagged sometag keep state" \ 707 "pass in on ${epair_server}a inet proto tcp tagged sometag keep state" \ 708 "pass out on ${epair_tester}b inet proto tcp tagged sometag keep state" 709 710 # Test the outbound NAT part of BINAT. 711 ping_server_check_reply exit:0 --ping-type=tcp3way --send-sport=4201 712 713 states=$(mktemp) || exit 1 714 jexec router pfctl -qvss | normalize_pfctl_s > $states 715 716 for state_regexp in \ 717 "${epair_tester}b tcp ${net_server_host_server}:9 <- ${net_tester_host_tester}:4201 .* 3:2 pkts,.* rule 1" \ 718 "${epair_server}a tcp ${net_server_host_router}:4201 \(${net_tester_host_tester}:4201\) -> ${net_server_host_server}:9 .* 3:2 pkts,.* rule 2" \ 719 ; do 720 grep -qE "${state_regexp}" $states || atf_fail "State not found for '${state_regexp}'" 721 done 722 723 # Test the inbound RDR part of BINAT. 724 # The "tester" becomes "server" and vice versa. 725 inetd_conf=$(mktemp) 726 echo "discard stream tcp nowait root internal" > $inetd_conf 727 inetd -p ${PWD}/inetd_tester.pid $inetd_conf 728 729 atf_check -s exit:0 \ 730 jexec server ${common_dir}/pft_ping.py \ 731 --ping-type=tcp3way --send-sport=4202 \ 732 --sendif ${epair_server}b \ 733 --to ${net_server_host_router} \ 734 --replyif ${epair_server}b 735 736 states=$(mktemp) || exit 1 737 jexec router pfctl -qvss | normalize_pfctl_s > $states 738 739 for state_regexp in \ 740 "${epair_server}a tcp ${net_tester_host_tester}:9 \(${net_server_host_router}:9\) <- ${net_server_host_server}:4202 .* 3:2 pkts,.* rule 3" \ 741 "${epair_tester}b tcp ${net_server_host_server}:4202 -> ${net_tester_host_tester}:9 .* 3:2 pkts,.* rule 4" \ 742 ; do 743 grep -qE "${state_regexp}" $states || atf_fail "State not found for '${state_regexp}'" 744 done 745} 746 747binat_compat_cleanup() 748{ 749 pft_cleanup 750 kill $(cat ${PWD}/inetd_tester.pid) 751} 752 753atf_test_case "binat_match" "cleanup" 754binat_match_head() 755{ 756 atf_set descr 'IPv4 BINAT with nat ruleset' 757 atf_set require.user root 758 atf_set require.progs scapy 759} 760 761binat_match_body() 762{ 763 setup_router_server_ipv4 764 # Delete the route back to make sure that the traffic has been NAT-ed 765 jexec server route del -net ${net_tester} ${net_server_host_router} 766 767 # The "binat-to" rule expands to 2 rules so the ""pass" rules start at 3! 768 pft_set_rules router \ 769 "set state-policy if-bound" \ 770 "set ruleset-optimization none" \ 771 "block" \ 772 "match on ${epair_server}a inet proto tcp from ${net_tester_host_tester} to any tag sometag binat-to ${epair_server}a" \ 773 "pass in on ${epair_tester}b inet proto tcp !tagged sometag keep state" \ 774 "pass out on ${epair_server}a inet proto tcp tagged sometag keep state" \ 775 "pass in on ${epair_server}a inet proto tcp tagged sometag keep state" \ 776 "pass out on ${epair_tester}b inet proto tcp tagged sometag keep state" 777 778 # Test the outbound NAT part of BINAT. 779 ping_server_check_reply exit:0 --ping-type=tcp3way --send-sport=4201 780 781 states=$(mktemp) || exit 1 782 jexec router pfctl -qvss | normalize_pfctl_s > $states 783 784 for state_regexp in \ 785 "${epair_tester}b tcp ${net_server_host_server}:9 <- ${net_tester_host_tester}:4201 .* 3:2 pkts,.* rule 3" \ 786 "${epair_server}a tcp ${net_server_host_router}:4201 \(${net_tester_host_tester}:4201\) -> ${net_server_host_server}:9 .* 3:2 pkts,.* rule 4" \ 787 ; do 788 grep -qE "${state_regexp}" $states || atf_fail "State not found for '${state_regexp}'" 789 done 790 791 # Test the inbound RDR part of BINAT. 792 # The "tester" becomes "server" and vice versa. 793 inetd_conf=$(mktemp) 794 echo "discard stream tcp nowait root internal" > $inetd_conf 795 inetd -p ${PWD}/inetd_tester.pid $inetd_conf 796 797 atf_check -s exit:0 \ 798 jexec server ${common_dir}/pft_ping.py \ 799 --ping-type=tcp3way --send-sport=4202 \ 800 --sendif ${epair_server}b \ 801 --to ${net_server_host_router} \ 802 --replyif ${epair_server}b 803 804 states=$(mktemp) || exit 1 805 jexec router pfctl -qvss | normalize_pfctl_s > $states 806 807 for state_regexp in \ 808 "${epair_server}a tcp ${net_tester_host_tester}:9 \(${net_server_host_router}:9\) <- ${net_server_host_server}:4202 .* 3:2 pkts,.* rule 5" \ 809 "${epair_tester}b tcp ${net_server_host_server}:4202 -> ${net_tester_host_tester}:9 .* 3:2 pkts,.* rule 6" \ 810 ; do 811 grep -qE "${state_regexp}" $states || atf_fail "State not found for '${state_regexp}'" 812 done 813} 814 815binat_match_cleanup() 816{ 817 pft_cleanup 818 kill $(cat ${PWD}/inetd_tester.pid) 819} 820 821atf_test_case "empty_pool" "cleanup" 822empty_pool_head() 823{ 824 atf_set descr 'NAT with empty pool' 825 atf_set require.user root 826 atf_set require.progs python3 scapy 827} 828 829empty_pool_body() 830{ 831 pft_init 832 setup_router_server_ipv6 833 834 835 pft_set_rules router \ 836 "block" \ 837 "pass inet6 proto icmp6 icmp6-type { neighbrsol, neighbradv }" \ 838 "pass in on ${epair_tester}b" \ 839 "pass out on ${epair_server}a inet6 from any to ${net_server_host_server} nat-to <nonexistent>" \ 840 841 # pf_map_addr_sn() won't be able to pick a target address, because 842 # the table used in redireciton pool is empty. Packet will not be 843 # forwarded, error counter will be increased. 844 ping_server_check_reply exit:1 845 # Ignore warnings about not-loaded ALTQ 846 atf_check -o "match:map-failed +1 +" -x "jexec router pfctl -qvvsi 2> /dev/null" 847} 848 849empty_pool_cleanup() 850{ 851 pft_cleanup 852} 853 854atf_test_case "dummynet_mask" "cleanup" 855dummynet_mask_head() 856{ 857 atf_set descr 'Verify that dummynet uses the pre-nat address for masking' 858 atf_set require.user root 859} 860 861dummynet_mask_body() 862{ 863 dummynet_init 864 865 epair_srv=$(vnet_mkepair) 866 epair_cl=$(vnet_mkepair) 867 868 ifconfig ${epair_cl}b 192.0.2.2/24 up 869 route add default 192.0.2.1 870 871 vnet_mkjail srv ${epair_srv}a 872 jexec srv ifconfig ${epair_srv}a 198.51.100.2/24 up 873 874 vnet_mkjail gw ${epair_srv}b ${epair_cl}a 875 jexec gw ifconfig ${epair_srv}b 198.51.100.1/24 up 876 jexec gw ifconfig ${epair_cl}a 192.0.2.1/24 up 877 jexec gw sysctl net.inet.ip.forwarding=1 878 879 jexec gw dnctl pipe 1 config delay 100 mask src-ip 0xffffff00 880 jexec gw pfctl -e 881 pft_set_rules gw \ 882 "nat on ${epair_srv}b inet from 192.0.2.0/24 to any -> (${epair_srv}b)" \ 883 "pass out dnpipe 1" 884 885 atf_check -s exit:0 -o ignore \ 886 ping -c 3 198.51.100.2 887 888 # Now check that dummynet looked at the correct address 889 atf_check -s exit:0 -o match:"ip.*192.0.2.0/0" \ 890 jexec gw dnctl pipe show 891} 892 893dummynet_mask_cleanup() 894{ 895 pft_cleanup 896} 897 898atf_init_test_cases() 899{ 900 atf_add_test_case "exhaust" 901 atf_add_test_case "nested_anchor" 902 atf_add_test_case "endpoint_independent_compat" 903 atf_add_test_case "endpoint_independent_pass" 904 atf_add_test_case "nat6_nolinklocal" 905 atf_add_test_case "empty_table_source_hash" 906 atf_add_test_case "no_addrs_source_hash" 907 atf_add_test_case "empty_table_random" 908 atf_add_test_case "no_addrs_random" 909 atf_add_test_case "map_e_compat" 910 atf_add_test_case "map_e_pass" 911 atf_add_test_case "nat_pass_in" 912 atf_add_test_case "nat_pass_out" 913 atf_add_test_case "nat_match" 914 atf_add_test_case "binat_compat" 915 atf_add_test_case "binat_match" 916 atf_add_test_case "empty_pool" 917 atf_add_test_case "dummynet_mask" 918} 919