1# 2# SPDX-License-Identifier: BSD-2-Clause 3# 4# Copyright (c) 2018 Kristof Provost <kp@FreeBSD.org> 5# 6# Redistribution and use in source and binary forms, with or without 7# modification, are permitted provided that the following conditions 8# are met: 9# 1. Redistributions of source code must retain the above copyright 10# notice, this list of conditions and the following disclaimer. 11# 2. Redistributions in binary form must reproduce the above copyright 12# notice, this list of conditions and the following disclaimer in the 13# documentation and/or other materials provided with the distribution. 14# 15# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25# SUCH DAMAGE. 26 27. $(atf_get_srcdir)/utils.subr 28 29common_dir=$(atf_get_srcdir)/../common 30 31# We need to somehow test if the random algorithm of pf_map_addr() is working. 32# The table or prefix contains multiple IP next-hop addresses, for each one try 33# to establish up to 10 connections. Fail the test if with this many attempts 34# the "good" target has not been chosen. However this choice is random, 35# the check might still ocasionally fail. 36check_random() { 37 if [ "$1" = "IPv4" ]; then 38 ping_from="${net_clients_4}.1" 39 ping_to="${host_server_4}" 40 else 41 ping_from="${net_clients_6}::1" 42 ping_to="${host_server_6}" 43 fi 44 good_targets="$2" 45 bad_targets="$3" 46 47 port=42000 48 states=$(mktemp) || exit 1 49 for good_target in $good_targets; do 50 found="no" 51 for attempt in $(seq 1 10); do 52 port=$(( port + 1 )) 53 jexec router pfctl -Fs 54 atf_check -s exit:0 ${common_dir}/pft_ping.py \ 55 --sendif ${epair_tester}a --replyif ${epair_tester}a \ 56 --fromaddr ${ping_from} --to ${ping_to} \ 57 --ping-type=tcp3way --send-sport=${port} 58 jexec router pfctl -qvvss | normalize_pfctl_s > $states 59 cat $states 60 if [ -n "${bad_targets}" ]; then 61 for bad_target in $bad_targets; do 62 if grep -qE "route-to: ${bad_target}@" $states; then 63 atf_fail "Bad target ${bad_target} selected!" 64 fi 65 done 66 fi; 67 if grep -qE "route-to: ${good_target}@" $states; then 68 found=yes 69 break 70 fi 71 done 72 if [ "${found}" = "no" ]; then 73 atf_fail "Target ${good_target} not selected after ${attempt} attempts!" 74 fi 75 done 76} 77 78pf_map_addr_common() 79{ 80 setup_router_server_nat64 81 82 # Clients will connect from another network behind the router. 83 # This allows for using multiple source addresses. 84 jexec router route add -6 ${net_clients_6}::/${net_clients_6_mask} ${net_tester_6_host_tester} 85 jexec router route add ${net_clients_4}.0/${net_clients_4_mask} ${net_tester_4_host_tester} 86 87 # The servers are reachable over additional IP addresses for 88 # testing of tables and subnets. The addresses are noncontinougnus 89 # for pf_map_addr() counter tests. 90 for i in 0 1 4 5; do 91 a1=$((24 + i)) 92 jexec server1 ifconfig ${epair_server1}b inet ${net_server1_4}.${a1}/32 alias 93 jexec server1 ifconfig ${epair_server1}b inet6 ${net_server1_6}::42:${i}/128 alias 94 a2=$((40 + i)) 95 jexec server2 ifconfig ${epair_server2}b inet ${net_server2_4}.${a2}/32 alias 96 jexec server2 ifconfig ${epair_server2}b inet6 ${net_server2_6}::42:${i}/128 alias 97 done 98} 99 100atf_test_case "v4" "cleanup" 101v4_head() 102{ 103 atf_set descr 'Basic route-to test' 104 atf_set require.user root 105} 106 107v4_body() 108{ 109 pft_init 110 111 epair_send=$(vnet_mkepair) 112 ifconfig ${epair_send}a 192.0.2.1/24 up 113 epair_route=$(vnet_mkepair) 114 ifconfig ${epair_route}a 203.0.113.1/24 up 115 116 vnet_mkjail alcatraz ${epair_send}b ${epair_route}b 117 jexec alcatraz ifconfig ${epair_send}b 192.0.2.2/24 up 118 jexec alcatraz ifconfig ${epair_route}b 203.0.113.2/24 up 119 jexec alcatraz route add -net 198.51.100.0/24 192.0.2.1 120 jexec alcatraz pfctl -e 121 122 # Attempt to provoke PR 228782 123 pft_set_rules alcatraz "block all" "pass user 2" \ 124 "pass out route-to (${epair_route}b 203.0.113.1) from 192.0.2.2 to 198.51.100.1 no state" 125 jexec alcatraz nc -w 3 -s 192.0.2.2 198.51.100.1 22 126 127 # atf wants us to not return an error, but our netcat will fail 128 true 129} 130 131v4_cleanup() 132{ 133 pft_cleanup 134} 135 136atf_test_case "v6" "cleanup" 137v6_head() 138{ 139 atf_set descr 'Basic route-to test (IPv6)' 140 atf_set require.user root 141} 142 143v6_body() 144{ 145 pft_init 146 147 epair_send=$(vnet_mkepair) 148 ifconfig ${epair_send}a inet6 2001:db8:42::1/64 up no_dad -ifdisabled 149 epair_route=$(vnet_mkepair) 150 ifconfig ${epair_route}a inet6 2001:db8:43::1/64 up no_dad -ifdisabled 151 152 vnet_mkjail alcatraz ${epair_send}b ${epair_route}b 153 jexec alcatraz ifconfig ${epair_send}b inet6 2001:db8:42::2/64 up no_dad 154 jexec alcatraz ifconfig ${epair_route}b inet6 2001:db8:43::2/64 up no_dad 155 jexec alcatraz route add -6 2001:db8:666::/64 2001:db8:42::2 156 jexec alcatraz pfctl -e 157 158 # Attempt to provoke PR 228782 159 pft_set_rules alcatraz "block all" "pass user 2" \ 160 "pass out route-to (${epair_route}b 2001:db8:43::1) from 2001:db8:42::2 to 2001:db8:666::1 no state" 161 jexec alcatraz nc -6 -w 3 -s 2001:db8:42::2 2001:db8:666::1 22 162 163 # atf wants us to not return an error, but our netcat will fail 164 true 165} 166 167v6_cleanup() 168{ 169 pft_cleanup 170} 171 172atf_test_case "multiwan" "cleanup" 173multiwan_head() 174{ 175 atf_set descr 'Multi-WAN redirection / reply-to test' 176 atf_set require.user root 177} 178 179multiwan_body() 180{ 181 pft_init 182 183 epair_one=$(vnet_mkepair) 184 epair_two=$(vnet_mkepair) 185 epair_cl_one=$(vnet_mkepair) 186 epair_cl_two=$(vnet_mkepair) 187 188 vnet_mkjail srv ${epair_one}b ${epair_two}b 189 vnet_mkjail wan_one ${epair_one}a ${epair_cl_one}b 190 vnet_mkjail wan_two ${epair_two}a ${epair_cl_two}b 191 vnet_mkjail client ${epair_cl_one}a ${epair_cl_two}a 192 193 jexec client ifconfig ${epair_cl_one}a 203.0.113.1/25 194 jexec wan_one ifconfig ${epair_cl_one}b 203.0.113.2/25 195 jexec wan_one ifconfig ${epair_one}a 192.0.2.1/24 up 196 jexec wan_one sysctl net.inet.ip.forwarding=1 197 jexec srv ifconfig ${epair_one}b 192.0.2.2/24 up 198 jexec client route add 192.0.2.0/24 203.0.113.2 199 200 jexec client ifconfig ${epair_cl_two}a 203.0.113.128/25 201 jexec wan_two ifconfig ${epair_cl_two}b 203.0.113.129/25 202 jexec wan_two ifconfig ${epair_two}a 198.51.100.1/24 up 203 jexec wan_two sysctl net.inet.ip.forwarding=1 204 jexec srv ifconfig ${epair_two}b 198.51.100.2/24 up 205 jexec client route add 198.51.100.0/24 203.0.113.129 206 207 jexec srv ifconfig lo0 127.0.0.1/8 up 208 jexec srv route add default 192.0.2.1 209 jexec srv sysctl net.inet.ip.forwarding=1 210 211 # Run echo server in srv jail 212 jexec srv /usr/sbin/inetd -p ${PWD}/multiwan.pid $(atf_get_srcdir)/echo_inetd.conf 213 214 jexec srv pfctl -e 215 pft_set_rules srv \ 216 "nat on ${epair_one}b inet from 127.0.0.0/8 to any -> (${epair_one}b)" \ 217 "nat on ${epair_two}b inet from 127.0.0.0/8 to any -> (${epair_two}b)" \ 218 "rdr on ${epair_one}b inet proto tcp from any to 192.0.2.2 port 7 -> 127.0.0.1 port 7" \ 219 "rdr on ${epair_two}b inet proto tcp from any to 198.51.100.2 port 7 -> 127.0.0.1 port 7" \ 220 "block in" \ 221 "block out" \ 222 "pass in quick on ${epair_one}b reply-to (${epair_one}b 192.0.2.1) inet proto tcp from any to 127.0.0.1 port 7" \ 223 "pass in quick on ${epair_two}b reply-to (${epair_two}b 198.51.100.1) inet proto tcp from any to 127.0.0.1 port 7" 224 225 # These will always succeed, because we don't change interface to route 226 # correctly here. 227 result=$(echo "one" | jexec wan_one nc -N -w 3 192.0.2.2 7) 228 if [ "${result}" != "one" ]; then 229 atf_fail "Redirect on one failed" 230 fi 231 result=$(echo "two" | jexec wan_two nc -N -w 3 198.51.100.2 7) 232 if [ "${result}" != "two" ]; then 233 atf_fail "Redirect on two failed" 234 fi 235 236 result=$(echo "one" | jexec client nc -N -w 3 192.0.2.2 7) 237 if [ "${result}" != "one" ]; then 238 atf_fail "Redirect from client on one failed" 239 fi 240 241 # This should trigger the issue fixed in 829a69db855b48ff7e8242b95e193a0783c489d9 242 result=$(echo "two" | jexec client nc -N -w 3 198.51.100.2 7) 243 if [ "${result}" != "two" ]; then 244 atf_fail "Redirect from client on two failed" 245 fi 246} 247 248multiwan_cleanup() 249{ 250 pft_cleanup 251} 252 253atf_test_case "multiwanlocal" "cleanup" 254multiwanlocal_head() 255{ 256 atf_set descr 'Multi-WAN local origin source-based redirection / route-to test' 257 atf_set require.user root 258} 259 260multiwanlocal_body() 261{ 262 pft_init 263 264 epair_one=$(vnet_mkepair) 265 epair_two=$(vnet_mkepair) 266 epair_cl_one=$(vnet_mkepair) 267 epair_cl_two=$(vnet_mkepair) 268 269 vnet_mkjail srv1 ${epair_one}b 270 vnet_mkjail srv2 ${epair_two}b 271 vnet_mkjail wan_one ${epair_one}a ${epair_cl_one}b 272 vnet_mkjail wan_two ${epair_two}a ${epair_cl_two}b 273 vnet_mkjail client ${epair_cl_one}a ${epair_cl_two}a 274 275 jexec client ifconfig ${epair_cl_one}a 203.0.113.1/25 276 jexec wan_one ifconfig ${epair_cl_one}b 203.0.113.2/25 277 jexec wan_one ifconfig ${epair_one}a 192.0.2.1/24 up 278 jexec wan_one sysctl net.inet.ip.forwarding=1 279 jexec srv1 ifconfig ${epair_one}b 192.0.2.2/24 up 280 281 jexec client ifconfig ${epair_cl_two}a 203.0.113.128/25 282 jexec wan_two ifconfig ${epair_cl_two}b 203.0.113.129/25 283 jexec wan_two ifconfig ${epair_two}a 198.51.100.1/24 up 284 jexec wan_two sysctl net.inet.ip.forwarding=1 285 jexec srv2 ifconfig ${epair_two}b 198.51.100.2/24 up 286 287 jexec client route add default 203.0.113.2 288 jexec srv1 route add default 192.0.2.1 289 jexec srv2 route add default 198.51.100.1 290 291 # Run data source in srv1 and srv2 292 jexec srv1 sh -c 'dd if=/dev/zero bs=1024 count=100 | nc -l 7 -w 2 -N &' 293 jexec srv2 sh -c 'dd if=/dev/zero bs=1024 count=100 | nc -l 7 -w 2 -N &' 294 295 jexec client pfctl -e 296 pft_set_rules client \ 297 "block in" \ 298 "block out" \ 299 "pass out quick route-to (${epair_cl_two}a 203.0.113.129) inet proto tcp from 203.0.113.128 to any port 7" \ 300 "pass out on ${epair_cl_one}a inet proto tcp from any to any port 7" \ 301 "set skip on lo" 302 303 # This should work 304 result=$(jexec client nc -N -w 1 192.0.2.2 7 | wc -c) 305 if [ ${result} -ne 102400 ]; then 306 jexec client pfctl -ss 307 atf_fail "Redirect from client on one failed: ${result}" 308 fi 309 310 # This should trigger the issue 311 result=$(jexec client nc -N -w 1 -s 203.0.113.128 198.51.100.2 7 | wc -c) 312 jexec client pfctl -ss 313 if [ ${result} -ne 102400 ]; then 314 atf_fail "Redirect from client on two failed: ${result}" 315 fi 316} 317 318multiwanlocal_cleanup() 319{ 320 pft_cleanup 321} 322 323atf_test_case "icmp_nat" "cleanup" 324icmp_nat_head() 325{ 326 atf_set descr 'Test that ICMP packets are correct for route-to + NAT' 327 atf_set require.user root 328 atf_set require.progs python3 scapy 329} 330 331icmp_nat_body() 332{ 333 pft_init 334 335 epair_one=$(vnet_mkepair) 336 epair_two=$(vnet_mkepair) 337 epair_three=$(vnet_mkepair) 338 339 vnet_mkjail gw ${epair_one}b ${epair_two}a ${epair_three}a 340 vnet_mkjail srv ${epair_two}b 341 vnet_mkjail srv2 ${epair_three}b 342 343 ifconfig ${epair_one}a 192.0.2.2/24 up 344 route add -net 198.51.100.0/24 192.0.2.1 345 jexec gw sysctl net.inet.ip.forwarding=1 346 jexec gw ifconfig ${epair_one}b 192.0.2.1/24 up 347 jexec gw ifconfig ${epair_two}a 198.51.100.1/24 up 348 jexec gw ifconfig ${epair_three}a 203.0.113.1/24 up mtu 500 349 jexec srv ifconfig ${epair_two}b 198.51.100.2/24 up 350 jexec srv route add default 198.51.100.1 351 jexec srv2 ifconfig ${epair_three}b 203.0.113.2/24 up mtu 500 352 jexec srv2 route add default 203.0.113.1 353 354 # Sanity check 355 atf_check -s exit:0 -o ignore ping -c 1 198.51.100.2 356 357 jexec gw pfctl -e 358 pft_set_rules gw \ 359 "nat on ${epair_two}a inet from 192.0.2.0/24 to any -> (${epair_two}a)" \ 360 "nat on ${epair_three}a inet from 192.0.2.0/24 to any -> (${epair_three}a)" \ 361 "pass out route-to (${epair_three}a 203.0.113.2) proto icmp icmp-type echoreq" 362 363 # Now ensure that we get an ICMP error with the correct IP addresses in it. 364 atf_check -s exit:0 ${common_dir}/pft_icmp_check.py \ 365 --to 198.51.100.2 \ 366 --fromaddr 192.0.2.2 \ 367 --recvif ${epair_one}a \ 368 --sendif ${epair_one}a 369 370 # ping reports the ICMP error, so check of that too. 371 atf_check -s exit:2 -o match:'frag needed and DF set' \ 372 ping -D -c 1 -s 1000 198.51.100.2 373} 374 375icmp_nat_cleanup() 376{ 377 pft_cleanup 378} 379 380atf_test_case "dummynet" "cleanup" 381dummynet_head() 382{ 383 atf_set descr 'Test that dummynet applies to route-to packets' 384 atf_set require.user root 385} 386 387dummynet_body() 388{ 389 dummynet_init 390 391 epair_srv=$(vnet_mkepair) 392 epair_gw=$(vnet_mkepair) 393 394 vnet_mkjail srv ${epair_srv}a 395 jexec srv ifconfig ${epair_srv}a 192.0.2.1/24 up 396 jexec srv route add default 192.0.2.2 397 398 vnet_mkjail gw ${epair_srv}b ${epair_gw}a 399 jexec gw ifconfig ${epair_srv}b 192.0.2.2/24 up 400 jexec gw ifconfig ${epair_gw}a 198.51.100.1/24 up 401 jexec gw sysctl net.inet.ip.forwarding=1 402 403 ifconfig ${epair_gw}b 198.51.100.2/24 up 404 route add -net 192.0.2.0/24 198.51.100.1 405 406 # Sanity check 407 atf_check -s exit:0 -o ignore ping -c 1 -t 1 192.0.2.1 408 409 jexec gw dnctl pipe 1 config delay 1200 410 pft_set_rules gw \ 411 "pass out route-to (${epair_srv}b 192.0.2.1) to 192.0.2.1 dnpipe 1" 412 jexec gw pfctl -e 413 414 # The ping request will pass, but take 1.2 seconds 415 # So this works: 416 atf_check -s exit:0 -o ignore ping -c 1 -t 2 192.0.2.1 417 # But this times out: 418 atf_check -s exit:2 -o ignore ping -c 1 -t 1 192.0.2.1 419 420 # return path dummynet 421 pft_set_rules gw \ 422 "pass out route-to (${epair_srv}b 192.0.2.1) to 192.0.2.1 dnpipe (0, 1)" 423 424 # The ping request will pass, but take 1.2 seconds 425 # So this works: 426 atf_check -s exit:0 -o ignore ping -c 1 -t 2 192.0.2.1 427 # But this times out: 428 atf_check -s exit:2 -o ignore ping -c 1 -t 1 192.0.2.1 429} 430 431dummynet_cleanup() 432{ 433 pft_cleanup 434} 435 436atf_test_case "dummynet_in" "cleanup" 437dummynet_in_head() 438{ 439 atf_set descr 'Thest that dummynet works as expected on pass in route-to packets' 440 atf_set require.user root 441} 442 443dummynet_in_body() 444{ 445 dummynet_init 446 447 epair_srv=$(vnet_mkepair) 448 epair_gw=$(vnet_mkepair) 449 450 vnet_mkjail srv ${epair_srv}a 451 jexec srv ifconfig ${epair_srv}a 192.0.2.1/24 up 452 jexec srv route add default 192.0.2.2 453 454 vnet_mkjail gw ${epair_srv}b ${epair_gw}a 455 jexec gw ifconfig ${epair_srv}b 192.0.2.2/24 up 456 jexec gw ifconfig ${epair_gw}a 198.51.100.1/24 up 457 jexec gw sysctl net.inet.ip.forwarding=1 458 459 ifconfig ${epair_gw}b 198.51.100.2/24 up 460 route add -net 192.0.2.0/24 198.51.100.1 461 462 # Sanity check 463 atf_check -s exit:0 -o ignore ping -c 1 -t 1 192.0.2.1 464 465 jexec gw dnctl pipe 1 config delay 1200 466 pft_set_rules gw \ 467 "pass in route-to (${epair_srv}b 192.0.2.1) to 192.0.2.1 dnpipe 1" 468 jexec gw pfctl -e 469 470 # The ping request will pass, but take 1.2 seconds 471 # So this works: 472 echo "Expect 1.2 s" 473 ping -c 1 192.0.2.1 474 atf_check -s exit:0 -o ignore ping -c 1 -t 2 192.0.2.1 475 # But this times out: 476 atf_check -s exit:2 -o ignore ping -c 1 -t 1 192.0.2.1 477 478 # return path dummynet 479 pft_set_rules gw \ 480 "pass in route-to (${epair_srv}b 192.0.2.1) to 192.0.2.1 dnpipe (0, 1)" 481 482 # The ping request will pass, but take 1.2 seconds 483 # So this works: 484 echo "Expect 1.2 s" 485 ping -c 1 192.0.2.1 486 atf_check -s exit:0 -o ignore ping -c 1 -t 2 192.0.2.1 487 # But this times out: 488 atf_check -s exit:2 -o ignore ping -c 1 -t 1 192.0.2.1 489} 490 491dummynet_in_cleanup() 492{ 493 pft_cleanup 494} 495 496atf_test_case "ifbound" "cleanup" 497ifbound_head() 498{ 499 atf_set descr 'Test that route-to states bind the expected interface' 500 atf_set require.user root 501} 502 503ifbound_body() 504{ 505 pft_init 506 507 j="route_to:ifbound" 508 509 epair_one=$(vnet_mkepair) 510 epair_two=$(vnet_mkepair) 511 ifconfig ${epair_one}b up 512 513 vnet_mkjail ${j}2 ${epair_two}b 514 jexec ${j}2 ifconfig ${epair_two}b inet 198.51.100.2/24 up 515 jexec ${j}2 ifconfig ${epair_two}b inet alias 203.0.113.1/24 516 jexec ${j}2 route add default 198.51.100.1 517 518 vnet_mkjail $j ${epair_one}a ${epair_two}a 519 jexec $j ifconfig ${epair_one}a 192.0.2.1/24 up 520 jexec $j ifconfig ${epair_two}a 198.51.100.1/24 up 521 jexec $j route add default 192.0.2.2 522 523 jexec $j pfctl -e 524 pft_set_rules $j \ 525 "set state-policy if-bound" \ 526 "block" \ 527 "pass out route-to (${epair_two}a 198.51.100.2)" 528 529 atf_check -s exit:0 -o ignore \ 530 jexec $j ping -c 3 203.0.113.1 531} 532 533ifbound_cleanup() 534{ 535 pft_cleanup 536} 537 538atf_test_case "ifbound_v6" "cleanup" 539ifbound_v6_head() 540{ 541 atf_set descr 'Test that route-to states for IPv6 bind the expected interface' 542 atf_set require.user root 543} 544 545ifbound_v6_body() 546{ 547 pft_init 548 549 j="route_to:ifbound_v6" 550 551 epair_one=$(vnet_mkepair) 552 epair_two=$(vnet_mkepair) 553 ifconfig ${epair_one}b up 554 555 vnet_mkjail ${j}2 ${epair_two}b 556 jexec ${j}2 ifconfig ${epair_two}b inet6 2001:db8:1::2/64 up no_dad 557 jexec ${j}2 ifconfig ${epair_two}b inet6 alias 2001:db8:2::1/64 no_dad 558 jexec ${j}2 route -6 add default 2001:db8:1::1 559 560 vnet_mkjail $j ${epair_one}a ${epair_two}a 561 jexec $j ifconfig ${epair_one}a inet6 2001:db8::1/64 up no_dad 562 jexec $j ifconfig ${epair_two}a inet6 2001:db8:1::1/64 up no_dad 563 jexec $j route -6 add default 2001:db8::2 564 565 jexec $j ping6 -c 3 2001:db8:1::2 566 567 jexec $j pfctl -e 568 pft_set_rules $j \ 569 "set state-policy if-bound" \ 570 "block" \ 571 "pass inet6 proto icmp6 icmp6-type { neighbrsol, neighbradv }" \ 572 "pass out route-to (${epair_two}a 2001:db8:1::2)" 573 574 atf_check -s exit:0 -o ignore \ 575 jexec $j ping6 -c 3 2001:db8:2::1 576} 577 578ifbound_v6_cleanup() 579{ 580 pft_cleanup 581} 582 583atf_test_case "ifbound_reply_to" "cleanup" 584ifbound_reply_to_head() 585{ 586 atf_set descr 'Test that reply-to states bind to the expected interface' 587 atf_set require.user root 588 atf_set require.progs python3 scapy 589} 590 591ifbound_reply_to_body() 592{ 593 pft_init 594 595 j="route_to:ifbound_reply_to" 596 597 epair_one=$(vnet_mkepair) 598 epair_two=$(vnet_mkepair) 599 ifconfig ${epair_one}b inet 192.0.2.2/24 up 600 ifconfig ${epair_two}b up 601 602 vnet_mkjail $j ${epair_one}a ${epair_two}a 603 jexec $j ifconfig ${epair_one}a 192.0.2.1/24 up 604 jexec $j ifconfig ${epair_two}a 198.51.100.1/24 up 605 jexec $j route add default 198.51.100.254 606 607 jexec $j pfctl -e 608 pft_set_rules $j \ 609 "set state-policy if-bound" \ 610 "block" \ 611 "pass in on ${epair_one}a reply-to (${epair_one}a 192.0.2.2) inet from any to 192.0.2.0/24 keep state" 612 613 atf_check -s exit:0 -o ignore \ 614 ping -c 3 192.0.2.1 615 616 atf_check -s exit:0 \ 617 ${common_dir}/pft_ping.py \ 618 --to 192.0.2.1 \ 619 --from 203.0.113.2 \ 620 --sendif ${epair_one}b \ 621 --replyif ${epair_one}b 622 623 # pft_ping uses the same ID every time, so this will look like more traffic in the same state 624 atf_check -s exit:0 \ 625 ${common_dir}/pft_ping.py \ 626 --to 192.0.2.1 \ 627 --from 203.0.113.2 \ 628 --sendif ${epair_one}b \ 629 --replyif ${epair_one}b 630 631 jexec $j pfctl -ss -vv 632} 633 634ifbound_reply_to_cleanup() 635{ 636 pft_cleanup 637} 638 639atf_test_case "ifbound_reply_to_v6" "cleanup" 640ifbound_reply_to_v6_head() 641{ 642 atf_set descr 'Test that reply-to states bind to the expected interface for IPv6' 643 atf_set require.user root 644 atf_set require.progs python3 scapy 645} 646 647ifbound_reply_to_v6_body() 648{ 649 pft_init 650 651 j="route_to:ifbound_reply_to_v6" 652 653 epair_one=$(vnet_mkepair) 654 epair_two=$(vnet_mkepair) 655 656 vnet_mkjail ${j}s ${epair_one}b ${epair_two}b 657 jexec ${j}s ifconfig ${epair_one}b inet6 2001:db8::2/64 up no_dad 658 jexec ${j}s ifconfig ${epair_two}b up 659 #jexec ${j}s route -6 add default 2001:db8::1 660 661 vnet_mkjail $j ${epair_one}a ${epair_two}a 662 jexec $j ifconfig ${epair_one}a inet6 2001:db8::1/64 up no_dad 663 jexec $j ifconfig ${epair_two}a inet6 2001:db8:1::1/64 up no_dad 664 jexec $j route -6 add default 2001:db8:1::254 665 666 jexec $j pfctl -e 667 pft_set_rules $j \ 668 "set state-policy if-bound" \ 669 "block" \ 670 "pass quick inet6 proto icmp6 icmp6-type { neighbrsol, neighbradv }" \ 671 "pass in on ${epair_one}a reply-to (${epair_one}a 2001:db8::2) inet6 from any to 2001:db8::/64 keep state" 672 673 atf_check -s exit:0 -o ignore \ 674 jexec ${j}s ping6 -c 3 2001:db8::1 675 676 atf_check -s exit:0 \ 677 jexec ${j}s ${common_dir}/pft_ping.py \ 678 --to 2001:db8::1 \ 679 --from 2001:db8:2::2 \ 680 --sendif ${epair_one}b \ 681 --replyif ${epair_one}b 682 683 # pft_ping uses the same ID every time, so this will look like more traffic in the same state 684 atf_check -s exit:0 \ 685 jexec ${j}s ${common_dir}/pft_ping.py \ 686 --to 2001:db8::1 \ 687 --from 2001:db8:2::2 \ 688 --sendif ${epair_one}b \ 689 --replyif ${epair_one}b 690 691 jexec $j pfctl -ss -vv 692} 693 694ifbound_reply_to_v6_cleanup() 695{ 696 pft_cleanup 697} 698 699atf_test_case "ifbound_reply_to_rdr_dummynet" "cleanup" 700ifbound_reply_to_rdr_dummynet_head() 701{ 702 atf_set descr 'Test that reply-to states bind to the expected non-default-route interface after rdr and dummynet' 703 atf_set require.user root 704 atf_set require.progs python3 scapy 705} 706 707ifbound_reply_to_rdr_dummynet_body() 708{ 709 dummynet_init 710 711 j="route_to:ifbound_reply_to_rdr_dummynet" 712 713 epair_one=$(vnet_mkepair) 714 epair_two=$(vnet_mkepair) 715 ifconfig ${epair_one}b inet 192.0.2.2/24 up 716 ifconfig ${epair_two}b up 717 718 vnet_mkjail $j ${epair_one}a ${epair_two}a 719 jexec $j ifconfig lo0 inet 127.0.0.1/8 up 720 jexec $j ifconfig ${epair_one}a 192.0.2.1/24 up 721 jexec $j ifconfig ${epair_two}a 198.51.100.1/24 up 722 jexec $j route add default 198.51.100.254 723 724 jexec $j pfctl -e 725 jexec $j dnctl pipe 1 config delay 1 726 pft_set_rules $j \ 727 "set state-policy if-bound" \ 728 "rdr on ${epair_one}a proto icmp from any to 192.0.2.1 -> 127.0.0.1" \ 729 "rdr on ${epair_two}a proto icmp from any to 198.51.100.1 -> 127.0.0.1" \ 730 "match in on ${epair_one}a inet all dnpipe (1, 1)" \ 731 "pass in on ${epair_one}a reply-to (${epair_one}a 192.0.2.2) inet from any to 127.0.0.1 keep state" 732 733 atf_check -s exit:0 -o ignore \ 734 ping -c 3 192.0.2.1 735 736 atf_check -s exit:0 \ 737 ${common_dir}/pft_ping.py \ 738 --to 192.0.2.1 \ 739 --from 203.0.113.2 \ 740 --sendif ${epair_one}b \ 741 --replyif ${epair_one}b 742 743 # pft_ping uses the same ID every time, so this will look like more traffic in the same state 744 atf_check -s exit:0 \ 745 ${common_dir}/pft_ping.py \ 746 --to 192.0.2.1 \ 747 --from 203.0.113.2 \ 748 --sendif ${epair_one}b \ 749 --replyif ${epair_one}b 750 751 jexec $j pfctl -sr -vv 752 jexec $j pfctl -ss -vv 753} 754 755ifbound_reply_to_rdr_dummynet_cleanup() 756{ 757 pft_cleanup 758} 759 760atf_test_case "dummynet_frag" "cleanup" 761dummynet_frag_head() 762{ 763 atf_set descr 'Test fragmentation with route-to and dummynet' 764 atf_set require.user root 765} 766 767dummynet_frag_body() 768{ 769 pft_init 770 dummynet_init 771 772 epair_one=$(vnet_mkepair) 773 epair_two=$(vnet_mkepair) 774 775 ifconfig ${epair_one}a 192.0.2.1/24 up 776 777 vnet_mkjail alcatraz ${epair_one}b ${epair_two}a 778 jexec alcatraz ifconfig ${epair_one}b 192.0.2.2/24 up 779 jexec alcatraz ifconfig ${epair_two}a 198.51.100.1/24 up 780 jexec alcatraz sysctl net.inet.ip.forwarding=1 781 782 vnet_mkjail singsing ${epair_two}b 783 jexec singsing ifconfig ${epair_two}b 198.51.100.2/24 up 784 jexec singsing route add default 198.51.100.1 785 786 route add 198.51.100.0/24 192.0.2.2 787 788 jexec alcatraz dnctl pipe 1 config bw 1000Byte/s burst 4500 789 jexec alcatraz dnctl pipe 2 config 790 # This second pipe ensures that the pf_test(PF_OUT) call in pf_route() doesn't 791 # delay packets in dummynet (by inheriting pipe 1 from the input rule). 792 793 jexec alcatraz pfctl -e 794 pft_set_rules alcatraz \ 795 "set reassemble yes" \ 796 "pass in route-to (${epair_two}a 198.51.100.2) inet proto icmp all icmp-type echoreq dnpipe 1" \ 797 "pass out dnpipe 2" 798 799 800 atf_check -s exit:0 -o ignore ping -c 1 198.51.100.2 801 atf_check -s exit:0 -o ignore ping -c 1 -s 4000 198.51.100.2 802} 803 804dummynet_frag_cleanup() 805{ 806 pft_cleanup 807} 808 809atf_test_case "dummynet_double" "cleanup" 810dummynet_double_head() 811{ 812 atf_set descr 'Ensure dummynet is not applied multiple times' 813 atf_set require.user root 814} 815 816dummynet_double_body() 817{ 818 pft_init 819 dummynet_init 820 821 epair_one=$(vnet_mkepair) 822 epair_two=$(vnet_mkepair) 823 824 ifconfig ${epair_one}a 192.0.2.1/24 up 825 826 vnet_mkjail alcatraz ${epair_one}b ${epair_two}a 827 jexec alcatraz ifconfig ${epair_one}b 192.0.2.2/24 up 828 jexec alcatraz ifconfig ${epair_two}a 198.51.100.1/24 up 829 jexec alcatraz sysctl net.inet.ip.forwarding=1 830 831 vnet_mkjail singsing ${epair_two}b 832 jexec singsing ifconfig ${epair_two}b 198.51.100.2/24 up 833 jexec singsing route add default 198.51.100.1 834 835 route add 198.51.100.0/24 192.0.2.2 836 837 jexec alcatraz dnctl pipe 1 config delay 800 838 839 jexec alcatraz pfctl -e 840 pft_set_rules alcatraz \ 841 "set reassemble yes" \ 842 "nat on ${epair_two}a from 192.0.2.0/24 -> (${epair_two}a)" \ 843 "pass in route-to (${epair_two}a 198.51.100.2) inet proto icmp all icmp-type echoreq dnpipe (1, 1)" \ 844 "pass out route-to (${epair_two}a 198.51.100.2) inet proto icmp all icmp-type echoreq" 845 846 ping -c 1 198.51.100.2 847 jexec alcatraz pfctl -sr -vv 848 jexec alcatraz pfctl -ss -vv 849 850 # We expect to be delayed 1.6 seconds, so timeout of two seconds passes, but 851 # timeout of 1 does not. 852 atf_check -s exit:0 -o ignore ping -t 2 -c 1 198.51.100.2 853 atf_check -s exit:2 -o ignore ping -t 1 -c 1 198.51.100.2 854} 855 856dummynet_double_cleanup() 857{ 858 pft_cleanup 859} 860 861atf_test_case "sticky" "cleanup" 862sticky_head() 863{ 864 atf_set descr 'Set and retrieve a rule with sticky-address' 865 atf_set require.user root 866} 867 868sticky_body() 869{ 870 pft_init 871 872 vnet_mkjail alcatraz 873 874 pft_set_rules alcatraz \ 875 "pass in quick log on n_test_h_rtr route-to (n_srv_h_rtr <change_dst>) sticky-address from any to <dst> keep state" 876 877 jexec alcatraz pfctl -qvvsr 878} 879 880sticky_cleanup() 881{ 882 pft_cleanup 883} 884 885atf_test_case "ttl" "cleanup" 886ttl_head() 887{ 888 atf_set descr 'Ensure we decrement TTL on route-to' 889 atf_set require.user root 890} 891 892ttl_body() 893{ 894 pft_init 895 896 epair_one=$(vnet_mkepair) 897 epair_two=$(vnet_mkepair) 898 ifconfig ${epair_one}b 192.0.2.2/24 up 899 route add default 192.0.2.1 900 901 vnet_mkjail alcatraz ${epair_one}a ${epair_two}a 902 jexec alcatraz ifconfig ${epair_one}a 192.0.2.1/24 up 903 jexec alcatraz ifconfig ${epair_two}a 198.51.100.1/24 up 904 jexec alcatraz sysctl net.inet.ip.forwarding=1 905 906 vnet_mkjail singsing ${epair_two}b 907 jexec singsing ifconfig ${epair_two}b 198.51.100.2/24 up 908 jexec singsing route add default 198.51.100.1 909 910 # Sanity check 911 atf_check -s exit:0 -o ignore \ 912 ping -c 3 198.51.100.2 913 914 jexec alcatraz pfctl -e 915 pft_set_rules alcatraz \ 916 "pass out" \ 917 "pass in route-to (${epair_two}a 198.51.100.2)" 918 919 atf_check -s exit:0 -o ignore \ 920 ping -c 3 198.51.100.2 921 922 atf_check -s exit:2 -o ignore \ 923 ping -m 1 -c 3 198.51.100.2 924} 925 926ttl_cleanup() 927{ 928 pft_cleanup 929} 930 931 932atf_test_case "empty_pool" "cleanup" 933empty_pool_head() 934{ 935 atf_set descr 'Route-to with empty pool' 936 atf_set require.user root 937 atf_set require.progs python3 scapy 938} 939 940empty_pool_body() 941{ 942 pft_init 943 setup_router_server_ipv6 944 945 946 pft_set_rules router \ 947 "block" \ 948 "pass inet6 proto icmp6 icmp6-type { neighbrsol, neighbradv }" \ 949 "pass in on ${epair_tester}b route-to (${epair_server}a <nonexistent>) inet6 from any to ${net_server_host_server}" \ 950 "pass out on ${epair_server}a" 951 952 # pf_map_addr_sn() won't be able to pick a target address, because 953 # the table used in redireciton pool is empty. Packet will not be 954 # forwarded, error counter will be increased. 955 ping_server_check_reply exit:1 956 # Ignore warnings about not-loaded ALTQ 957 atf_check -o "match:map-failed +1 +" -x "jexec router pfctl -qvvsi 2> /dev/null" 958} 959 960empty_pool_cleanup() 961{ 962 pft_cleanup 963} 964 965atf_test_case "table_loop" "cleanup" 966 967table_loop_head() 968{ 969 atf_set descr 'Check that iterating over tables poperly loops' 970 atf_set require.user root 971 atf_set require.progs python3 scapy 972} 973 974table_loop_body() 975{ 976 pf_map_addr_common 977 978 jexec router pfctl -e 979 pft_set_rules router \ 980 "set debug loud" \ 981 "set reassemble yes" \ 982 "set state-policy if-bound" \ 983 "table <rt_targets_1> { ${net_server1_6}::42:4/127 ${net_server1_6}::42:0/127 }" \ 984 "table <rt_targets_2> { ${net_server2_6}::42:4/127 }" \ 985 "pass in on ${epair_tester}b \ 986 route-to { \ 987 (${epair_server1}a <rt_targets_1>) \ 988 (${epair_server2}a <rt_targets_2_empty>) \ 989 (${epair_server2}a <rt_targets_2>) \ 990 } \ 991 inet6 proto tcp \ 992 keep state" 993 994 # Both hosts of the pool are tables. Each table gets iterated over once, 995 # then the pool iterates to the next host, which is also iterated, 996 # then the pool loops back to the 1st host. If an empty table is found, 997 # it is skipped. Unless that's the only table, that is tested by 998 # the "empty_pool" test. 999 for port in $(seq 1 7); do 1000 port=$((4200 + port)) 1001 atf_check -s exit:0 ${common_dir}/pft_ping.py \ 1002 --sendif ${epair_tester}a --replyif ${epair_tester}a \ 1003 --fromaddr ${net_clients_6}::1 --to ${host_server_6} \ 1004 --ping-type=tcp3way --send-sport=${port} 1005 done 1006 1007 states=$(mktemp) || exit 1 1008 jexec router pfctl -qvvss | normalize_pfctl_s > $states 1009 cat $states 1010 1011 for state_regexp in \ 1012 "${epair_tester}b tcp ${host_server_6}\[9\] <- ${net_clients_6}::1\[4201\] .* route-to: ${net_server1_6}::42:0@${epair_server1}a" \ 1013 "${epair_tester}b tcp ${host_server_6}\[9\] <- ${net_clients_6}::1\[4202\] .* route-to: ${net_server1_6}::42:1@${epair_server1}a" \ 1014 "${epair_tester}b tcp ${host_server_6}\[9\] <- ${net_clients_6}::1\[4203\] .* route-to: ${net_server1_6}::42:4@${epair_server1}a" \ 1015 "${epair_tester}b tcp ${host_server_6}\[9\] <- ${net_clients_6}::1\[4204\] .* route-to: ${net_server1_6}::42:5@${epair_server1}a" \ 1016 "${epair_tester}b tcp ${host_server_6}\[9\] <- ${net_clients_6}::1\[4205\] .* route-to: ${net_server2_6}::42:4@${epair_server2}a" \ 1017 "${epair_tester}b tcp ${host_server_6}\[9\] <- ${net_clients_6}::1\[4206\] .* route-to: ${net_server2_6}::42:5@${epair_server2}a" \ 1018 "${epair_tester}b tcp ${host_server_6}\[9\] <- ${net_clients_6}::1\[4207\] .* route-to: ${net_server1_6}::42:0@${epair_server1}a" \ 1019 ; do 1020 grep -qE "${state_regexp}" $states || atf_fail "State not found for '${state_regexp}'" 1021 done 1022} 1023 1024table_loop_cleanup() 1025{ 1026 pft_cleanup 1027} 1028 1029 1030atf_test_case "roundrobin" "cleanup" 1031 1032roundrobin_head() 1033{ 1034 atf_set descr 'multiple gateways of mixed AF, including prefixes and tables, for IPv6 packets' 1035 atf_set require.user root 1036 atf_set require.progs python3 scapy 1037} 1038 1039roundrobin_body() 1040{ 1041 pf_map_addr_common 1042 1043 # The rule is defined as "inet6 proto tcp" so directly given IPv4 hosts 1044 # will be removed from the pool by pfctl. Tables will still be loaded 1045 # and pf_map_addr() will only use IPv6 addresses from them. It will 1046 # iterate over members of the pool and inside of tables and prefixes. 1047 1048 jexec router pfctl -e 1049 pft_set_rules router \ 1050 "set debug loud" \ 1051 "set reassemble yes" \ 1052 "set state-policy if-bound" \ 1053 "table <rt_targets> { ${net_server2_4}.40/31 ${net_server2_4}.44 ${net_server2_6}::42:0/127 ${net_server2_6}::42:4 }" \ 1054 "pass in on ${epair_tester}b \ 1055 route-to { \ 1056 (${epair_server1}a ${net_server1_4_host_server}) \ 1057 (${epair_server2}a <rt_targets_empty>) \ 1058 (${epair_server1}a ${net_server1_6}::42:0/127) \ 1059 (${epair_server2}a <rt_targets_empty>) \ 1060 (${epair_server2}a <rt_targets>) \ 1061 } \ 1062 inet6 proto tcp \ 1063 keep state" 1064 1065 for port in $(seq 1 6); do 1066 port=$((4200 + port)) 1067 atf_check -s exit:0 ${common_dir}/pft_ping.py \ 1068 --sendif ${epair_tester}a --replyif ${epair_tester}a \ 1069 --fromaddr ${net_clients_6}::1 --to ${host_server_6} \ 1070 --ping-type=tcp3way --send-sport=${port} 1071 done 1072 1073 states=$(mktemp) || exit 1 1074 jexec router pfctl -qvvss | normalize_pfctl_s > $states 1075 1076 for state_regexp in \ 1077 "${epair_tester}b tcp ${host_server_6}\[9\] <- ${net_clients_6}::1\[4201\] .* route-to: ${net_server1_6}::42:0@${epair_server1}a" \ 1078 "${epair_tester}b tcp ${host_server_6}\[9\] <- ${net_clients_6}::1\[4202\] .* route-to: ${net_server1_6}::42:1@${epair_server1}a" \ 1079 "${epair_tester}b tcp ${host_server_6}\[9\] <- ${net_clients_6}::1\[4203\] .* route-to: ${net_server2_6}::42:0@${epair_server2}a" \ 1080 "${epair_tester}b tcp ${host_server_6}\[9\] <- ${net_clients_6}::1\[4204\] .* route-to: ${net_server2_6}::42:1@${epair_server2}a" \ 1081 "${epair_tester}b tcp ${host_server_6}\[9\] <- ${net_clients_6}::1\[4205\] .* route-to: ${net_server2_6}::42:4@${epair_server2}a" \ 1082 "${epair_tester}b tcp ${host_server_6}\[9\] <- ${net_clients_6}::1\[4206\] .* route-to: ${net_server1_6}::42:0@${epair_server1}a" \ 1083 ; do 1084 grep -qE "${state_regexp}" $states || atf_fail "State not found for '${state_regexp}'" 1085 done 1086} 1087 1088roundrobin_cleanup() 1089{ 1090 pft_cleanup 1091} 1092 1093atf_test_case "random_table" "cleanup" 1094 1095random_table_head() 1096{ 1097 atf_set descr 'Pool with random flag and a table for IPv6' 1098 atf_set require.user root 1099 atf_set require.progs python3 scapy 1100} 1101 1102random_table_body() 1103{ 1104 pf_map_addr_common 1105 1106 # The "random" flag will pick random hosts from the table but will 1107 # not dive into prefixes, always choosing the 0th address. 1108 # Proper address family will be choosen. 1109 1110 jexec router pfctl -e 1111 pft_set_rules router \ 1112 "set debug loud" \ 1113 "set reassemble yes" \ 1114 "set state-policy if-bound" \ 1115 "table <rt_targets> { ${net_server2_4}.40/31 ${net_server2_4}.44 ${net_server2_6}::42:0/127 ${net_server2_6}::42:4 }" \ 1116 "pass in on ${epair_tester}b \ 1117 route-to { (${epair_server2}a <rt_targets>) } random \ 1118 inet6 proto tcp \ 1119 keep state" 1120 1121 good_targets="${net_server2_6}::42:0 ${net_server2_6}::42:4" 1122 bad_targets="${net_server2_6}::42:1" 1123 check_random IPv6 "${good_targets}" "${bad_targets}" 1124} 1125 1126random_table_cleanup() 1127{ 1128 pft_cleanup 1129} 1130 1131atf_test_case "random_prefix" "cleanup" 1132 1133random_prefix_head() 1134{ 1135 atf_set descr 'Pool with random flag and a table for IPv4' 1136 atf_set require.user root 1137 atf_set require.progs python3 scapy 1138} 1139 1140random_prefix_body() 1141{ 1142 pf_map_addr_common 1143 1144 # The "random" flag will pick random hosts from given prefix. 1145 # The choice being random makes testing it non-trivial. We do 10 1146 # attempts to have each target chosen. Hopefully this is enough to have 1147 # this test pass often enough. 1148 1149 jexec router pfctl -e 1150 pft_set_rules router \ 1151 "set debug loud" \ 1152 "set reassemble yes" \ 1153 "set state-policy if-bound" \ 1154 "pass in on ${epair_tester}b \ 1155 route-to { (${epair_server2}a ${net_server2_6}::42:0/127) } random \ 1156 inet6 proto tcp \ 1157 keep state" 1158 1159 good_targets="${net_server2_6}::42:0 ${net_server2_6}::42:1" 1160 check_random IPv6 "${good_targets}" 1161} 1162 1163random_prefix_cleanup() 1164{ 1165 pft_cleanup 1166} 1167 1168atf_test_case "prefer_ipv6_nexthop_single_ipv4" "cleanup" 1169 1170prefer_ipv6_nexthop_single_ipv4_head() 1171{ 1172 atf_set descr 'prefer-ipv6-nexthop option for a single IPv4 gateway' 1173 atf_set require.user root 1174 atf_set require.progs python3 scapy 1175} 1176 1177prefer_ipv6_nexthop_single_ipv4_body() 1178{ 1179 pf_map_addr_common 1180 1181 # Basic forwarding test for prefer-ipv6-nexthop pool option. 1182 # A single IPv4 gateway will work only for IPv4 traffic. 1183 1184 jexec router pfctl -e 1185 pft_set_rules router \ 1186 "set reassemble yes" \ 1187 "set state-policy if-bound" \ 1188 "pass in on ${epair_tester}b \ 1189 route-to (${epair_server1}a ${net_server1_4_host_server}) prefer-ipv6-nexthop \ 1190 proto tcp \ 1191 keep state" 1192 1193 atf_check -s exit:0 ${common_dir}/pft_ping.py \ 1194 --sendif ${epair_tester}a --replyif ${epair_tester}a \ 1195 --fromaddr ${net_clients_4}.1 --to ${host_server_4} \ 1196 --ping-type=tcp3way --send-sport=4201 \ 1197 1198 atf_check -s exit:1 ${common_dir}/pft_ping.py \ 1199 --sendif ${epair_tester}a --replyif ${epair_tester}a \ 1200 --fromaddr ${net_clients_6}::1 --to ${host_server_6} \ 1201 --ping-type=tcp3way --send-sport=4202 1202 1203 states=$(mktemp) || exit 1 1204 jexec router pfctl -qvvss | normalize_pfctl_s > $states 1205 1206 for state_regexp in \ 1207 "${epair_tester}b tcp ${host_server_4}:9 <- ${net_clients_4}.1:4201 .* route-to: ${net_server1_4_host_server}@${epair_server1}a" \ 1208 ; do 1209 grep -qE "${state_regexp}" $states || atf_fail "State not found for '${state_regexp}'" 1210 done 1211 1212 # The IPv6 packet could not have been routed over IPv4 gateway. 1213 atf_check -o "match:map-failed +1 +" -x "jexec router pfctl -qvvsi 2> /dev/null" 1214} 1215 1216prefer_ipv6_nexthop_single_ipv4_cleanup() 1217{ 1218 pft_cleanup 1219} 1220 1221atf_test_case "prefer_ipv6_nexthop_single_ipv6" "cleanup" 1222 1223prefer_ipv6_nexthop_single_ipv6_head() 1224{ 1225 atf_set descr 'prefer-ipv6-nexthop option for a single IPv6 gateway' 1226 atf_set require.user root 1227 atf_set require.progs python3 scapy 1228} 1229 1230prefer_ipv6_nexthop_single_ipv6_body() 1231{ 1232 pf_map_addr_common 1233 1234 # Basic forwarding test for prefer-ipv6-nexthop pool option. 1235 # A single IPve gateway will work both for IPv4 and IPv6 traffic. 1236 1237 jexec router pfctl -e 1238 pft_set_rules router \ 1239 "set reassemble yes" \ 1240 "set state-policy if-bound" \ 1241 "pass in on ${epair_tester}b \ 1242 route-to (${epair_server1}a ${net_server1_6_host_server}) prefer-ipv6-nexthop \ 1243 proto tcp \ 1244 keep state" 1245 1246 atf_check -s exit:0 ${common_dir}/pft_ping.py \ 1247 --sendif ${epair_tester}a --replyif ${epair_tester}a \ 1248 --fromaddr ${net_clients_4}.1 --to ${host_server_4} \ 1249 --ping-type=tcp3way --send-sport=4201 \ 1250 1251 atf_check -s exit:0 ${common_dir}/pft_ping.py \ 1252 --sendif ${epair_tester}a --replyif ${epair_tester}a \ 1253 --fromaddr ${net_clients_6}::1 --to ${host_server_6} \ 1254 --ping-type=tcp3way --send-sport=4202 1255 1256 states=$(mktemp) || exit 1 1257 jexec router pfctl -qvvss | normalize_pfctl_s > $states 1258 1259 for state_regexp in \ 1260 "${epair_tester}b tcp ${host_server_4}:9 <- ${net_clients_4}.1:4201 .* route-to: ${net_server1_6_host_server}@${epair_server1}a" \ 1261 "${epair_tester}b tcp ${host_server_6}\[9\] <- ${net_clients_6}::1\[4202\] .* route-to: ${net_server1_6_host_server}@${epair_server1}a" \ 1262 ; do 1263 grep -qE "${state_regexp}" $states || atf_fail "State not found for '${state_regexp}'" 1264 done 1265} 1266 1267prefer_ipv6_nexthop_single_ipv6_cleanup() 1268{ 1269 pft_cleanup 1270} 1271 1272atf_test_case "prefer_ipv6_nexthop_mixed_af_roundrobin_ipv4" "cleanup" 1273 1274prefer_ipv6_nexthop_mixed_af_roundrobin_ipv4_head() 1275{ 1276 atf_set descr 'prefer-ipv6-nexthop option for multiple gateways of mixed AF with prefixes and tables, round robin selection, for IPv4 packets' 1277 atf_set require.user root 1278 atf_set require.progs python3 scapy 1279} 1280 1281prefer_ipv6_nexthop_mixed_af_roundrobin_ipv4_body() 1282{ 1283 pf_map_addr_common 1284 1285 # pf_map_addr() starts iterating over hosts of the pool from the 2nd 1286 # host. This behaviour was here before adding prefer-ipv6-nexthop 1287 # support, we test for it. Some targets are hosts and some are tables. 1288 # Those are iterated too. Finally the iteration loops back 1289 # to the beginning of the pool. Send out IPv4 probes. They will use all 1290 # IPv4 and IPv6 addresses from the pool. 1291 1292 jexec router pfctl -e 1293 pft_set_rules router \ 1294 "set reassemble yes" \ 1295 "set state-policy if-bound" \ 1296 "table <rt_targets> { ${net_server2_4}.40/31 ${net_server2_4}.44 ${net_server2_6}::42:4/127 }" \ 1297 "pass in on ${epair_tester}b \ 1298 route-to { \ 1299 (${epair_server1}a ${net_server1_6_host_server}) \ 1300 (${epair_server1}a ${net_server1_6}::42:0/127) \ 1301 (${epair_server2}a ${net_server2_4_host_server}) \ 1302 (${epair_server2}a <rt_targets_empty>) \ 1303 (${epair_server1}a ${net_server1_4}.24/31) \ 1304 (${epair_server2}a <rt_targets>) \ 1305 } prefer-ipv6-nexthop \ 1306 proto tcp \ 1307 keep state" 1308 1309 for port in $(seq 1 12); do 1310 port=$((4200 + port)) 1311 atf_check -s exit:0 ${common_dir}/pft_ping.py \ 1312 --sendif ${epair_tester}a --replyif ${epair_tester}a \ 1313 --fromaddr ${net_clients_4}.1 --to ${host_server_4} \ 1314 --ping-type=tcp3way --send-sport=${port} 1315 done 1316 1317 states=$(mktemp) || exit 1 1318 jexec router pfctl -qvvss | normalize_pfctl_s > $states 1319 1320 for state_regexp in \ 1321 "${epair_tester}b tcp ${host_server_4}:9 <- ${net_clients_4}.1:4201 .* route-to: ${net_server1_6}::42:0@${epair_server1}a" \ 1322 "${epair_tester}b tcp ${host_server_4}:9 <- ${net_clients_4}.1:4202 .* route-to: ${net_server1_6}::42:1@${epair_server1}a" \ 1323 "${epair_tester}b tcp ${host_server_4}:9 <- ${net_clients_4}.1:4203 .* route-to: ${net_server2_4_host_server}@${epair_server2}a" \ 1324 "${epair_tester}b tcp ${host_server_4}:9 <- ${net_clients_4}.1:4204 .* route-to: ${net_server1_4}.24@${epair_server1}a" \ 1325 "${epair_tester}b tcp ${host_server_4}:9 <- ${net_clients_4}.1:4205 .* route-to: ${net_server1_4}.25@${epair_server1}a" \ 1326 "${epair_tester}b tcp ${host_server_4}:9 <- ${net_clients_4}.1:4206 .* route-to: ${net_server2_6}::42:4@${epair_server2}a" \ 1327 "${epair_tester}b tcp ${host_server_4}:9 <- ${net_clients_4}.1:4207 .* route-to: ${net_server2_6}::42:5@${epair_server2}a" \ 1328 "${epair_tester}b tcp ${host_server_4}:9 <- ${net_clients_4}.1:4208 .* route-to: ${net_server2_4}.40@${epair_server2}a" \ 1329 "${epair_tester}b tcp ${host_server_4}:9 <- ${net_clients_4}.1:4209 .* route-to: ${net_server2_4}.41@${epair_server2}a" \ 1330 "${epair_tester}b tcp ${host_server_4}:9 <- ${net_clients_4}.1:4210 .* route-to: ${net_server2_4}.44@${epair_server2}a" \ 1331 "${epair_tester}b tcp ${host_server_4}:9 <- ${net_clients_4}.1:4211 .* route-to: ${net_server1_6_host_server}@${epair_server1}a" \ 1332 "${epair_tester}b tcp ${host_server_4}:9 <- ${net_clients_4}.1:4212 .* route-to: ${net_server1_6}::42:0@${epair_server1}a" \ 1333 ; do 1334 grep -qE "${state_regexp}" $states || atf_fail "State not found for '${state_regexp}'" 1335 done 1336} 1337 1338prefer_ipv6_nexthop_mixed_af_roundrobin_ipv4_cleanup() 1339{ 1340 pft_cleanup 1341} 1342 1343prefer_ipv6_nexthop_mixed_af_roundrobin_ipv6_head() 1344{ 1345 atf_set descr 'prefer-ipv6-nexthop option for multiple gateways of mixed AF with prefixes and tables, round-robin selection, for IPv6 packets' 1346 atf_set require.user root 1347 atf_set require.progs python3 scapy 1348} 1349 1350prefer_ipv6_nexthop_mixed_af_roundrobin_ipv6_body() 1351{ 1352 pf_map_addr_common 1353 1354 # The "random" flag will pick random hosts from the table but will 1355 # not dive into prefixes, always choosing the 0th address. 1356 # Proper address family will be choosen. The choice being random makes 1357 # testing it non-trivial. We do 10 attempts to have each target chosen. 1358 # Hopefully this is enough to have this test pass often enough. 1359 1360 # pf_map_addr() starts iterating over hosts of the pool from the 2nd 1361 # host. This behaviour was here before adding prefer-ipv6-nexthop 1362 # support, we test for it. Some targets are hosts and some are tables. 1363 # Those are iterated too. Finally the iteration loops back 1364 # to the beginning of the pool. Send out IPv6 probes. They will use only 1365 # IPv6 addresses from the pool. 1366 1367 jexec router pfctl -e 1368 pft_set_rules router \ 1369 "set reassemble yes" \ 1370 "set state-policy if-bound" \ 1371 "table <rt_targets> { ${net_server2_4}.40/31 ${net_server2_4}.44 ${net_server2_6}::42:4/127 }" \ 1372 "pass in on ${epair_tester}b \ 1373 route-to { \ 1374 (${epair_server1}a ${net_server1_6_host_server}) \ 1375 (${epair_server1}a ${net_server1_6}::42:0/127) \ 1376 (${epair_server2}a ${net_server2_4_host_server}) \ 1377 (${epair_server2}a <rt_targets_empty>) \ 1378 (${epair_server1}a ${net_server1_4}.24/31) \ 1379 (${epair_server2}a <rt_targets>) \ 1380 } prefer-ipv6-nexthop \ 1381 proto tcp \ 1382 keep state" 1383 1384 for port in $(seq 1 6); do 1385 port=$((4200 + port)) 1386 atf_check -s exit:0 ${common_dir}/pft_ping.py \ 1387 --sendif ${epair_tester}a --replyif ${epair_tester}a \ 1388 --fromaddr ${net_clients_6}::1 --to ${host_server_6} \ 1389 --ping-type=tcp3way --send-sport=${port} 1390 done 1391 1392 states=$(mktemp) || exit 1 1393 jexec router pfctl -qvvss | normalize_pfctl_s > $states 1394 1395 for state_regexp in \ 1396 "${epair_tester}b tcp ${host_server_6}\[9\] <- ${net_clients_6}::1\[4201\] .* route-to: ${net_server1_6}::42:0@${epair_server1}a" \ 1397 "${epair_tester}b tcp ${host_server_6}\[9\] <- ${net_clients_6}::1\[4202\] .* route-to: ${net_server1_6}::42:1@${epair_server1}a" \ 1398 "${epair_tester}b tcp ${host_server_6}\[9\] <- ${net_clients_6}::1\[4203\] .* route-to: ${net_server2_6}::42:4@${epair_server2}a" \ 1399 "${epair_tester}b tcp ${host_server_6}\[9\] <- ${net_clients_6}::1\[4204\] .* route-to: ${net_server2_6}::42:5@${epair_server2}a" \ 1400 "${epair_tester}b tcp ${host_server_6}\[9\] <- ${net_clients_6}::1\[4205\] .* route-to: ${net_server1_6_host_server}@${epair_server1}a" \ 1401 "${epair_tester}b tcp ${host_server_6}\[9\] <- ${net_clients_6}::1\[4206\] .* route-to: ${net_server1_6}::42:0@${epair_server1}a" \ 1402 ; do 1403 grep -qE "${state_regexp}" $states || atf_fail "State not found for '${state_regexp}'" 1404 done 1405} 1406 1407prefer_ipv6_nexthop_mixed_af_roundrobin_ipv6_cleanup() 1408{ 1409 pft_cleanup 1410} 1411 1412atf_test_case "prefer_ipv6_nexthop_mixed_af_random_ipv4" "cleanup" 1413 1414prefer_ipv6_nexthop_mixed_af_random_table_ipv4_head() 1415{ 1416 atf_set descr 'prefer-ipv6-nexthop option for a mixed-af table with random selection for IPv4 packets' 1417 atf_set require.user root 1418 atf_set require.progs python3 scapy 1419} 1420 1421prefer_ipv6_nexthop_mixed_af_random_table_ipv4_body() 1422{ 1423 pf_map_addr_common 1424 1425 # Similarly to the test "random_table" the algorithm will choose 1426 # IP addresses from the table not diving into prefixes. 1427 # *prefer*-ipv6-nexthop means that IPv6 nexthops are preferred, 1428 # so IPv4 ones will not be chosen as long as there are IPv6 ones 1429 # available. With this tested there is no need for a test for IPv6-only 1430 # next-hops table. 1431 1432 jexec router pfctl -e 1433 pft_set_rules router \ 1434 "set reassemble yes" \ 1435 "set state-policy if-bound" \ 1436 "table <rt_targets> { ${net_server2_4}.40/31 ${net_server2_4}.44 ${net_server2_6}::42:0/127 ${net_server2_6}::42:4 }" \ 1437 "pass in on ${epair_tester}b \ 1438 route-to { (${epair_server2}a <rt_targets>) } random prefer-ipv6-nexthop \ 1439 proto tcp \ 1440 keep state" 1441 1442 good_targets="${net_server2_6}::42:0 ${net_server2_6}::42:4" 1443 bad_targets="${net_server2_4}.40 ${net_server2_4}.41 ${net_server2_4}.44 ${net_server2_6}::42:1" 1444 check_random IPv4 "${good_targets}" "${bad_targets}" 1445} 1446 1447prefer_ipv6_nexthop_mixed_af_random_table_ipv4_cleanup() 1448{ 1449 pft_cleanup 1450} 1451 1452prefer_ipv6_nexthop_ipv4_random_table_ipv4_head() 1453{ 1454 atf_set descr 'prefer-ipv6-nexthop option for an IPv4-only table with random selection for IPv4 packets' 1455 atf_set require.user root 1456 atf_set require.progs python3 scapy 1457} 1458 1459prefer_ipv6_nexthop_ipv4_random_table_ipv4_body() 1460{ 1461 pf_map_addr_common 1462 1463 # Similarly to the test pf_map_addr:random_table the algorithm will 1464 # choose IP addresses from the table not diving into prefixes. 1465 # There are no IPv6 nexthops in the table, so the algorithm will 1466 # fall back to IPv4. 1467 1468 jexec router pfctl -e 1469 pft_set_rules router \ 1470 "set reassemble yes" \ 1471 "set state-policy if-bound" \ 1472 "table <rt_targets> { ${net_server2_4}.40/31 ${net_server2_4}.44 }" \ 1473 "pass in on ${epair_tester}b \ 1474 route-to { (${epair_server2}a <rt_targets>) } random prefer-ipv6-nexthop \ 1475 proto tcp \ 1476 keep state" 1477 1478 good_targets="${net_server2_4}.40 ${net_server2_4}.44" 1479 bad_targets="${net_server2_4}.41" 1480 check_random IPv4 "${good_targets}" "${bad_targets}" 1481} 1482 1483prefer_ipv6_nexthop_ipv4_random_table_ipv4_cleanup() 1484{ 1485 pft_cleanup 1486} 1487 1488prefer_ipv6_nexthop_ipv4_random_table_ipv6_head() 1489{ 1490 atf_set descr 'prefer-ipv6-nexthop option for an IPv4-only table with random selection for IPv6 packets' 1491 atf_set require.user root 1492 atf_set require.progs python3 scapy 1493} 1494 1495prefer_ipv6_nexthop_ipv4_random_table_ipv6_body() 1496{ 1497 pf_map_addr_common 1498 1499 # IPv6 packets can't be forwarded over IPv4 next-hops. 1500 # The failure happens in pf_map_addr() and increases the respective 1501 # error counter. 1502 1503 jexec router pfctl -e 1504 pft_set_rules router \ 1505 "set reassemble yes" \ 1506 "set state-policy if-bound" \ 1507 "table <rt_targets> { ${net_server2_4}.40/31 ${net_server2_4}.44 }" \ 1508 "pass in on ${epair_tester}b \ 1509 route-to { (${epair_server2}a <rt_targets>) } random prefer-ipv6-nexthop \ 1510 proto tcp \ 1511 keep state" 1512 1513 atf_check -s exit:1 ${common_dir}/pft_ping.py \ 1514 --sendif ${epair_tester}a --replyif ${epair_tester}a \ 1515 --fromaddr ${net_clients_6}::1 --to ${host_server_6} \ 1516 --ping-type=tcp3way --send-sport=4201 1517 1518 atf_check -o "match:map-failed +1 +" -x "jexec router pfctl -qvvsi 2> /dev/null" 1519} 1520 1521prefer_ipv6_nexthop_ipv4_random_table_ipv6_cleanup() 1522{ 1523 pft_cleanup 1524} 1525 1526prefer_ipv6_nexthop_ipv6_random_prefix_ipv4_head() 1527{ 1528 atf_set descr 'prefer-ipv6-nexthop option for an IPv6 prefix with random selection for IPv4 packets' 1529 atf_set require.user root 1530 atf_set require.progs python3 scapy 1531} 1532 1533prefer_ipv6_nexthop_ipv6_random_prefix_ipv4_body() 1534{ 1535 pf_map_addr_common 1536 1537 jexec router pfctl -e 1538 pft_set_rules router \ 1539 "set reassemble yes" \ 1540 "set state-policy if-bound" \ 1541 "pass in on ${epair_tester}b \ 1542 route-to { (${epair_server2}a ${net_server2_6}::42:0/127) } random prefer-ipv6-nexthop \ 1543 proto tcp \ 1544 keep state" 1545 1546 good_targets="${net_server2_6}::42:0 ${net_server2_6}::42:1" 1547 check_random IPv4 "${good_targets}" 1548} 1549 1550prefer_ipv6_nexthop_ipv6_random_prefix_ipv4_cleanup() 1551{ 1552 pft_cleanup 1553} 1554 1555prefer_ipv6_nexthop_ipv6_random_prefix_ipv6_head() 1556{ 1557 atf_set descr 'prefer-ipv6-nexthop option for an IPv6 prefix with random selection for IPv6 packets' 1558 atf_set require.user root 1559 atf_set require.progs python3 scapy 1560} 1561 1562prefer_ipv6_nexthop_ipv6_random_prefix_ipv6_body() 1563{ 1564 pf_map_addr_common 1565 1566 jexec router pfctl -e 1567 pft_set_rules router \ 1568 "set reassemble yes" \ 1569 "set state-policy if-bound" \ 1570 "pass in on ${epair_tester}b \ 1571 route-to { (${epair_server2}a ${net_server2_6}::42:0/127) } random prefer-ipv6-nexthop \ 1572 proto tcp \ 1573 keep state" 1574 1575 good_targets="${net_server2_6}::42:0 ${net_server2_6}::42:1" 1576 check_random IPv6 "${good_targets}" 1577} 1578 1579prefer_ipv6_nexthop_ipv6_random_prefix_ipv6_cleanup() 1580{ 1581 pft_cleanup 1582} 1583 1584prefer_ipv6_nexthop_ipv4_random_prefix_ipv4_head() 1585{ 1586 atf_set descr 'prefer-ipv6-nexthop option for an IPv4 prefix with random selection for IPv4 packets' 1587 atf_set require.user root 1588 atf_set require.progs python3 scapy 1589} 1590 1591prefer_ipv6_nexthop_ipv4_random_prefix_ipv4_body() 1592{ 1593 pf_map_addr_common 1594 1595 jexec router pfctl -e 1596 pft_set_rules router \ 1597 "set reassemble yes" \ 1598 "set state-policy if-bound" \ 1599 "pass in on ${epair_tester}b \ 1600 route-to { (${epair_server2}a ${net_server2_4}.40/31) } random prefer-ipv6-nexthop \ 1601 proto tcp \ 1602 keep state" 1603 1604 good_targets="${net_server2_4}.40 ${net_server2_4}.41" 1605 check_random IPv4 "${good_targets}" 1606} 1607 1608prefer_ipv6_nexthop_ipv4_random_prefix_ipv4_cleanup() 1609{ 1610 pft_cleanup 1611} 1612 1613prefer_ipv6_nexthop_ipv4_random_prefix_ipv6_head() 1614{ 1615 atf_set descr 'prefer-ipv6-nexthop option for an IPv4 prefix with random selection for IPv6 packets' 1616 atf_set require.user root 1617 atf_set require.progs python3 scapy 1618} 1619 1620prefer_ipv6_nexthop_ipv4_random_prefix_ipv6_body() 1621{ 1622 pf_map_addr_common 1623 1624 # IPv6 packets can't be forwarded over IPv4 next-hops. 1625 # The failure happens in pf_map_addr() and increases the respective 1626 # error counter. 1627 1628 jexec router pfctl -e 1629 pft_set_rules router \ 1630 "set reassemble yes" \ 1631 "set state-policy if-bound" \ 1632 "pass in on ${epair_tester}b \ 1633 route-to { (${epair_server2}a ${net_server2_4}.40/31) } random prefer-ipv6-nexthop \ 1634 proto tcp \ 1635 keep state" 1636 1637 atf_check -s exit:1 ${common_dir}/pft_ping.py \ 1638 --sendif ${epair_tester}a --replyif ${epair_tester}a \ 1639 --fromaddr ${net_clients_6}::1 --to ${host_server_6} \ 1640 --ping-type=tcp3way --send-sport=4201 1641 1642 atf_check -o "match:map-failed +1 +" -x "jexec router pfctl -qvvsi 2> /dev/null" 1643} 1644 1645prefer_ipv6_nexthop_ipv4_random_prefix_ipv6_cleanup() 1646{ 1647 pft_cleanup 1648} 1649 1650atf_init_test_cases() 1651{ 1652 atf_add_test_case "v4" 1653 atf_add_test_case "v6" 1654 atf_add_test_case "multiwan" 1655 atf_add_test_case "multiwanlocal" 1656 atf_add_test_case "icmp_nat" 1657 atf_add_test_case "dummynet" 1658 atf_add_test_case "dummynet_in" 1659 atf_add_test_case "ifbound" 1660 atf_add_test_case "ifbound_v6" 1661 atf_add_test_case "ifbound_reply_to" 1662 atf_add_test_case "ifbound_reply_to_v6" 1663 atf_add_test_case "ifbound_reply_to_rdr_dummynet" 1664 atf_add_test_case "dummynet_frag" 1665 atf_add_test_case "dummynet_double" 1666 atf_add_test_case "sticky" 1667 atf_add_test_case "ttl" 1668 atf_add_test_case "empty_pool" 1669 # Tests for pf_map_addr() without prefer-ipv6-nexthop 1670 atf_add_test_case "table_loop" 1671 atf_add_test_case "roundrobin" 1672 atf_add_test_case "random_table" 1673 atf_add_test_case "random_prefix" 1674 # Tests for pf_map_addr() without prefer-ipv6-nexthop 1675 # Next hop is a Single IP address 1676 atf_add_test_case "prefer_ipv6_nexthop_single_ipv4" 1677 atf_add_test_case "prefer_ipv6_nexthop_single_ipv6" 1678 # Next hop is tables and prefixes, accessed by the round-robin algorithm 1679 atf_add_test_case "prefer_ipv6_nexthop_mixed_af_roundrobin_ipv4" 1680 atf_add_test_case "prefer_ipv6_nexthop_mixed_af_roundrobin_ipv6" 1681 # Next hop is a table, accessed by the random algorithm 1682 atf_add_test_case "prefer_ipv6_nexthop_mixed_af_random_table_ipv4" 1683 atf_add_test_case "prefer_ipv6_nexthop_ipv4_random_table_ipv4" 1684 atf_add_test_case "prefer_ipv6_nexthop_ipv4_random_table_ipv6" 1685 # Next hop is a prefix, accessed by the random algorithm 1686 atf_add_test_case "prefer_ipv6_nexthop_ipv6_random_prefix_ipv4" 1687 atf_add_test_case "prefer_ipv6_nexthop_ipv6_random_prefix_ipv6" 1688 atf_add_test_case "prefer_ipv6_nexthop_ipv4_random_prefix_ipv4" 1689 atf_add_test_case "prefer_ipv6_nexthop_ipv4_random_prefix_ipv6" 1690} 1691