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