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