aboutsummaryrefslogtreecommitdiff
path: root/tests/sys/netpfil/pf
diff options
context:
space:
mode:
Diffstat (limited to 'tests/sys/netpfil/pf')
-rw-r--r--tests/sys/netpfil/pf/Makefile1
-rw-r--r--tests/sys/netpfil/pf/igmp.py6
-rw-r--r--tests/sys/netpfil/pf/ioctl/validation.c25
-rw-r--r--tests/sys/netpfil/pf/nat44.py76
-rw-r--r--tests/sys/netpfil/pf/route_to.sh716
-rw-r--r--tests/sys/netpfil/pf/sctp.sh3
-rwxr-xr-xtests/sys/netpfil/pf/src_track.sh36
-rw-r--r--tests/sys/netpfil/pf/utils.subr4
8 files changed, 836 insertions, 31 deletions
diff --git a/tests/sys/netpfil/pf/Makefile b/tests/sys/netpfil/pf/Makefile
index 616ffe560b3a..9f993eec61d0 100644
--- a/tests/sys/netpfil/pf/Makefile
+++ b/tests/sys/netpfil/pf/Makefile
@@ -61,6 +61,7 @@ ATF_TESTS_PYTEST+= header.py
ATF_TESTS_PYTEST+= icmp.py
ATF_TESTS_PYTEST+= igmp.py
ATF_TESTS_PYTEST+= mld.py
+ATF_TESTS_PYTEST+= nat44.py
ATF_TESTS_PYTEST+= nat64.py
ATF_TESTS_PYTEST+= nat66.py
ATF_TESTS_PYTEST+= return.py
diff --git a/tests/sys/netpfil/pf/igmp.py b/tests/sys/netpfil/pf/igmp.py
index b339a2825082..5d72a1c093a7 100644
--- a/tests/sys/netpfil/pf/igmp.py
+++ b/tests/sys/netpfil/pf/igmp.py
@@ -93,3 +93,9 @@ class TestIGMP(VnetTestTemplate):
options=[sp.IPOption_Router_Alert()]) \
/ sc.igmp.IGMP(type=0x11, mrcode=1)
assert not self.find_igmp_reply(pkt, ifname)
+
+ # Or with the wrong destination address
+ pkt = sp.IP(dst="224.0.0.2%%%s" % ifname, ttl=2,
+ options=[sp.IPOption_Router_Alert()]) \
+ / sc.igmp.IGMP(type=0x11, mrcode=1)
+ assert not self.find_igmp_reply(pkt, ifname)
diff --git a/tests/sys/netpfil/pf/ioctl/validation.c b/tests/sys/netpfil/pf/ioctl/validation.c
index 18fafe11c6ab..3e03163cc752 100644
--- a/tests/sys/netpfil/pf/ioctl/validation.c
+++ b/tests/sys/netpfil/pf/ioctl/validation.c
@@ -41,8 +41,6 @@
static int dev;
#define COMMON_HEAD() \
- if (modfind("pf") == -1) \
- atf_tc_skip("pf not loaded"); \
dev = open("/dev/pf", O_RDWR); \
if (dev == -1) \
atf_tc_skip("Failed to open /dev/pf");
@@ -64,6 +62,7 @@ ATF_TC_WITH_CLEANUP(addtables);
ATF_TC_HEAD(addtables, tc)
{
atf_tc_set_md_var(tc, "require.user", "root");
+ atf_tc_set_md_var(tc, "require.kmods", "pf");
}
ATF_TC_BODY(addtables, tc)
@@ -116,6 +115,7 @@ ATF_TC_WITH_CLEANUP(deltables);
ATF_TC_HEAD(deltables, tc)
{
atf_tc_set_md_var(tc, "require.user", "root");
+ atf_tc_set_md_var(tc, "require.kmods", "pf");
}
ATF_TC_BODY(deltables, tc)
@@ -159,6 +159,7 @@ ATF_TC_WITH_CLEANUP(gettables);
ATF_TC_HEAD(gettables, tc)
{
atf_tc_set_md_var(tc, "require.user", "root");
+ atf_tc_set_md_var(tc, "require.kmods", "pf");
}
ATF_TC_BODY(gettables, tc)
@@ -197,6 +198,7 @@ ATF_TC_WITH_CLEANUP(gettstats);
ATF_TC_HEAD(gettstats, tc)
{
atf_tc_set_md_var(tc, "require.user", "root");
+ atf_tc_set_md_var(tc, "require.kmods", "pf");
}
ATF_TC_BODY(gettstats, tc)
@@ -235,6 +237,7 @@ ATF_TC_WITH_CLEANUP(clrtstats);
ATF_TC_HEAD(clrtstats, tc)
{
atf_tc_set_md_var(tc, "require.user", "root");
+ atf_tc_set_md_var(tc, "require.kmods", "pf");
}
ATF_TC_BODY(clrtstats, tc)
@@ -280,6 +283,7 @@ ATF_TC_WITH_CLEANUP(settflags);
ATF_TC_HEAD(settflags, tc)
{
atf_tc_set_md_var(tc, "require.user", "root");
+ atf_tc_set_md_var(tc, "require.kmods", "pf");
}
ATF_TC_BODY(settflags, tc)
@@ -325,6 +329,7 @@ ATF_TC_WITH_CLEANUP(addaddrs);
ATF_TC_HEAD(addaddrs, tc)
{
atf_tc_set_md_var(tc, "require.user", "root");
+ atf_tc_set_md_var(tc, "require.kmods", "pf");
}
ATF_TC_BODY(addaddrs, tc)
@@ -360,6 +365,7 @@ ATF_TC_WITH_CLEANUP(deladdrs);
ATF_TC_HEAD(deladdrs, tc)
{
atf_tc_set_md_var(tc, "require.user", "root");
+ atf_tc_set_md_var(tc, "require.kmods", "pf");
}
ATF_TC_BODY(deladdrs, tc)
@@ -395,6 +401,7 @@ ATF_TC_WITH_CLEANUP(setaddrs);
ATF_TC_HEAD(setaddrs, tc)
{
atf_tc_set_md_var(tc, "require.user", "root");
+ atf_tc_set_md_var(tc, "require.kmods", "pf");
}
ATF_TC_BODY(setaddrs, tc)
@@ -430,6 +437,7 @@ ATF_TC_WITH_CLEANUP(getaddrs);
ATF_TC_HEAD(getaddrs, tc)
{
atf_tc_set_md_var(tc, "require.user", "root");
+ atf_tc_set_md_var(tc, "require.kmods", "pf");
}
ATF_TC_BODY(getaddrs, tc)
@@ -467,6 +475,7 @@ ATF_TC_WITH_CLEANUP(getastats);
ATF_TC_HEAD(getastats, tc)
{
atf_tc_set_md_var(tc, "require.user", "root");
+ atf_tc_set_md_var(tc, "require.kmods", "pf");
}
ATF_TC_BODY(getastats, tc)
@@ -504,6 +513,7 @@ ATF_TC_WITH_CLEANUP(clrastats);
ATF_TC_HEAD(clrastats, tc)
{
atf_tc_set_md_var(tc, "require.user", "root");
+ atf_tc_set_md_var(tc, "require.kmods", "pf");
}
ATF_TC_BODY(clrastats, tc)
@@ -541,6 +551,7 @@ ATF_TC_WITH_CLEANUP(tstaddrs);
ATF_TC_HEAD(tstaddrs, tc)
{
atf_tc_set_md_var(tc, "require.user", "root");
+ atf_tc_set_md_var(tc, "require.kmods", "pf");
}
ATF_TC_BODY(tstaddrs, tc)
@@ -578,6 +589,7 @@ ATF_TC_WITH_CLEANUP(inadefine);
ATF_TC_HEAD(inadefine, tc)
{
atf_tc_set_md_var(tc, "require.user", "root");
+ atf_tc_set_md_var(tc, "require.kmods", "pf");
}
ATF_TC_BODY(inadefine, tc)
@@ -615,6 +627,7 @@ ATF_TC_WITH_CLEANUP(igetifaces);
ATF_TC_HEAD(igetifaces, tc)
{
atf_tc_set_md_var(tc, "require.user", "root");
+ atf_tc_set_md_var(tc, "require.kmods", "pf");
}
ATF_TC_BODY(igetifaces, tc)
@@ -649,6 +662,7 @@ ATF_TC_WITH_CLEANUP(cxbegin);
ATF_TC_HEAD(cxbegin, tc)
{
atf_tc_set_md_var(tc, "require.user", "root");
+ atf_tc_set_md_var(tc, "require.kmods", "pf");
}
ATF_TC_BODY(cxbegin, tc)
@@ -688,6 +702,7 @@ ATF_TC_WITH_CLEANUP(cxrollback);
ATF_TC_HEAD(cxrollback, tc)
{
atf_tc_set_md_var(tc, "require.user", "root");
+ atf_tc_set_md_var(tc, "require.kmods", "pf");
}
ATF_TC_BODY(cxrollback, tc)
@@ -727,6 +742,7 @@ ATF_TC_WITH_CLEANUP(commit);
ATF_TC_HEAD(commit, tc)
{
atf_tc_set_md_var(tc, "require.user", "root");
+ atf_tc_set_md_var(tc, "require.kmods", "pf");
}
ATF_TC_BODY(commit, tc)
@@ -766,6 +782,7 @@ ATF_TC_WITH_CLEANUP(getsrcnodes);
ATF_TC_HEAD(getsrcnodes, tc)
{
atf_tc_set_md_var(tc, "require.user", "root");
+ atf_tc_set_md_var(tc, "require.kmods", "pf");
}
ATF_TC_BODY(getsrcnodes, tc)
@@ -798,6 +815,7 @@ ATF_TC_WITH_CLEANUP(tag);
ATF_TC_HEAD(tag, tc)
{
atf_tc_set_md_var(tc, "require.user", "root");
+ atf_tc_set_md_var(tc, "require.kmods", "pf");
}
ATF_TC_BODY(tag, tc)
@@ -835,6 +853,7 @@ ATF_TC_WITH_CLEANUP(rpool_mtx);
ATF_TC_HEAD(rpool_mtx, tc)
{
atf_tc_set_md_var(tc, "require.user", "root");
+ atf_tc_set_md_var(tc, "require.kmods", "pf");
}
ATF_TC_BODY(rpool_mtx, tc)
@@ -872,6 +891,7 @@ ATF_TC_WITH_CLEANUP(rpool_mtx2);
ATF_TC_HEAD(rpool_mtx2, tc)
{
atf_tc_set_md_var(tc, "require.user", "root");
+ atf_tc_set_md_var(tc, "require.kmods", "pf");
}
ATF_TC_BODY(rpool_mtx2, tc)
@@ -898,6 +918,7 @@ ATF_TC_WITH_CLEANUP(natlook);
ATF_TC_HEAD(natlook, tc)
{
atf_tc_set_md_var(tc, "require.user", "root");
+ atf_tc_set_md_var(tc, "require.kmods", "pf");
}
ATF_TC_BODY(natlook, tc)
diff --git a/tests/sys/netpfil/pf/nat44.py b/tests/sys/netpfil/pf/nat44.py
new file mode 100644
index 000000000000..d69e794a62c3
--- /dev/null
+++ b/tests/sys/netpfil/pf/nat44.py
@@ -0,0 +1,76 @@
+#
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2025 Rubicon Communications, LLC (Netgate)
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+import pytest
+from atf_python.sys.net.tools import ToolsHelper
+from atf_python.sys.net.vnet import VnetTestTemplate
+
+class TestNAT44(VnetTestTemplate):
+ REQUIRED_MODULES = [ "pf" ]
+ TOPOLOGY = {
+ "vnet1": {"ifaces": ["if1"]},
+ "vnet2": {"ifaces": ["if1", "if2"]},
+ "vnet3": {"ifaces": ["if2"]},
+ "if1": {"prefixes4": [("192.0.2.2/24", "192.0.2.1/24")]},
+ "if2": {"prefixes4": [("198.51.100.1/24", "198.51.100.2")]},
+ }
+
+ def vnet2_handler(self, vnet):
+ outifname = vnet.iface_alias_map["if2"].name
+ ToolsHelper.print_output("/sbin/sysctl net.inet.ip.forwarding=1")
+
+ ToolsHelper.print_output("/sbin/pfctl -e")
+ ToolsHelper.print_output("/sbin/pfctl -x loud")
+ ToolsHelper.pf_rules([
+ "set reassemble yes",
+ "nat on {} inet from 192.0.2.0/24 -> ({})".format(outifname, outifname),
+ "pass"])
+
+ def vnet3_handler(self, vnet):
+ pass
+
+ @pytest.mark.require_user("root")
+ @pytest.mark.require_progs(["scapy"])
+ def test_nat_igmp(self):
+ "Verify that NAT translation of !(TCP|UDP|SCTP|ICMP) doesn't panic"
+ ToolsHelper.print_output("/sbin/route add default 192.0.2.1")
+ ToolsHelper.print_output("ping -c 3 198.51.100.2")
+
+ # Import in the correct vnet, so at to not confuse Scapy
+ import scapy.all as sp
+ import scapy.contrib as sc
+ import scapy.contrib.igmp
+
+ pkt = sp.IP(dst="198.51.100.2", ttl=64) \
+ / sc.igmp.IGMP(type=0x11, mrcode=1)
+ sp.send(pkt)
+
+ # This time we'll hit an existing state
+ pkt = sp.IP(dst="198.51.100.2", ttl=64) \
+ / sc.igmp.IGMP(type=0x11, mrcode=1)
+ reply = sp.sr1(pkt, timeout=3)
+ if reply:
+ reply.show()
diff --git a/tests/sys/netpfil/pf/route_to.sh b/tests/sys/netpfil/pf/route_to.sh
index 765403dcb79c..872de0dcbb91 100644
--- a/tests/sys/netpfil/pf/route_to.sh
+++ b/tests/sys/netpfil/pf/route_to.sh
@@ -28,6 +28,75 @@
common_dir=$(atf_get_srcdir)/../common
+# We need to somehow test if the random algorithm of pf_map_addr() is working.
+# The table or prefix contains multiple IP next-hop addresses, for each one try
+# to establish up to 10 connections. Fail the test if with this many attempts
+# the "good" target has not been chosen. However this choice is random,
+# the check might still ocasionally fail.
+check_random() {
+ if [ "$1" = "IPv4" ]; then
+ ping_from="${net_clients_4}.1"
+ ping_to="${host_server_4}"
+ else
+ ping_from="${net_clients_6}::1"
+ ping_to="${host_server_6}"
+ fi
+ good_targets="$2"
+ bad_targets="$3"
+
+ port=42000
+ states=$(mktemp) || exit 1
+ for good_target in $good_targets; do
+ found="no"
+ for attempt in $(seq 1 10); do
+ port=$(( port + 1 ))
+ jexec router pfctl -Fs
+ atf_check -s exit:0 ${common_dir}/pft_ping.py \
+ --sendif ${epair_tester}a --replyif ${epair_tester}a \
+ --fromaddr ${ping_from} --to ${ping_to} \
+ --ping-type=tcp3way --send-sport=${port}
+ jexec router pfctl -qvvss | normalize_pfctl_s > $states
+ cat $states
+ if [ -n "${bad_targets}" ]; then
+ for bad_target in $bad_targets; do
+ if grep -qE "route-to: ${bad_target}@" $states; then
+ atf_fail "Bad target ${bad_target} selected!"
+ fi
+ done
+ fi;
+ if grep -qE "route-to: ${good_target}@" $states; then
+ found=yes
+ break
+ fi
+ done
+ if [ "${found}" = "no" ]; then
+ atf_fail "Target ${good_target} not selected after ${attempt} attempts!"
+ fi
+ done
+}
+
+pf_map_addr_common()
+{
+ setup_router_server_nat64
+
+ # Clients will connect from another network behind the router.
+ # This allows for using multiple source addresses.
+ jexec router route add -6 ${net_clients_6}::/${net_clients_6_mask} ${net_tester_6_host_tester}
+ jexec router route add ${net_clients_4}.0/${net_clients_4_mask} ${net_tester_4_host_tester}
+
+ # The servers are reachable over additional IP addresses for
+ # testing of tables and subnets. The addresses are noncontinougnus
+ # for pf_map_addr() counter tests.
+ for i in 0 1 4 5; do
+ a1=$((24 + i))
+ jexec server1 ifconfig ${epair_server1}b inet ${net_server1_4}.${a1}/32 alias
+ jexec server1 ifconfig ${epair_server1}b inet6 ${net_server1_6}::42:${i}/128 alias
+ a2=$((40 + i))
+ jexec server2 ifconfig ${epair_server2}b inet ${net_server2_4}.${a2}/32 alias
+ jexec server2 ifconfig ${epair_server2}b inet6 ${net_server2_6}::42:${i}/128 alias
+ done
+}
+
atf_test_case "v4" "cleanup"
v4_head()
{
@@ -893,36 +962,17 @@ empty_pool_cleanup()
pft_cleanup
}
-
atf_test_case "table_loop" "cleanup"
table_loop_head()
{
atf_set descr 'Check that iterating over tables poperly loops'
atf_set require.user root
- atf_set require.progs python3 scapy
}
table_loop_body()
{
- setup_router_server_nat64
-
- # Clients will connect from another network behind the router.
- # This allows for using multiple source addresses.
- jexec router route add -6 ${net_clients_6}::/${net_clients_6_mask} ${net_tester_6_host_tester}
- jexec router route add ${net_clients_4}.0/${net_clients_4_mask} ${net_tester_4_host_tester}
-
- # The servers are reachable over additional IP addresses for
- # testing of tables and subnets. The addresses are noncontinougnus
- # for pf_map_addr() counter tests.
- for i in 0 1 4 5; do
- a1=$((24 + i))
- jexec server1 ifconfig ${epair_server1}b inet ${net_server1_4}.${a1}/32 alias
- jexec server1 ifconfig ${epair_server1}b inet6 ${net_server1_6}::42:${i}/128 alias
- a2=$((40 + i))
- jexec server2 ifconfig ${epair_server2}b inet ${net_server2_4}.${a2}/32 alias
- jexec server2 ifconfig ${epair_server2}b inet6 ${net_server2_6}::42:${i}/128 alias
- done
+ pf_map_addr_common
jexec router pfctl -e
pft_set_rules router \
@@ -976,6 +1026,612 @@ table_loop_cleanup()
}
+atf_test_case "roundrobin" "cleanup"
+
+roundrobin_head()
+{
+ atf_set descr 'multiple gateways of mixed AF, including prefixes and tables, for IPv6 packets'
+ atf_set require.user root
+}
+
+roundrobin_body()
+{
+ pf_map_addr_common
+
+ # The rule is defined as "inet6 proto tcp" so directly given IPv4 hosts
+ # will be removed from the pool by pfctl. Tables will still be loaded
+ # and pf_map_addr() will only use IPv6 addresses from them. It will
+ # iterate over members of the pool and inside of tables and prefixes.
+
+ jexec router pfctl -e
+ pft_set_rules router \
+ "set debug loud" \
+ "set reassemble yes" \
+ "set state-policy if-bound" \
+ "table <rt_targets> { ${net_server2_4}.40/31 ${net_server2_4}.44 ${net_server2_6}::42:0/127 ${net_server2_6}::42:4 }" \
+ "pass in on ${epair_tester}b \
+ route-to { \
+ (${epair_server1}a ${net_server1_4_host_server}) \
+ (${epair_server2}a <rt_targets_empty>) \
+ (${epair_server1}a ${net_server1_6}::42:0/127) \
+ (${epair_server2}a <rt_targets_empty>) \
+ (${epair_server2}a <rt_targets>) \
+ } \
+ inet6 proto tcp \
+ keep state"
+
+ for port in $(seq 1 6); do
+ port=$((4200 + port))
+ atf_check -s exit:0 ${common_dir}/pft_ping.py \
+ --sendif ${epair_tester}a --replyif ${epair_tester}a \
+ --fromaddr ${net_clients_6}::1 --to ${host_server_6} \
+ --ping-type=tcp3way --send-sport=${port}
+ done
+
+ states=$(mktemp) || exit 1
+ jexec router pfctl -qvvss | normalize_pfctl_s > $states
+
+ for state_regexp in \
+ "${epair_tester}b tcp ${host_server_6}\[9\] <- ${net_clients_6}::1\[4201\] .* route-to: ${net_server1_6}::42:0@${epair_server1}a" \
+ "${epair_tester}b tcp ${host_server_6}\[9\] <- ${net_clients_6}::1\[4202\] .* route-to: ${net_server1_6}::42:1@${epair_server1}a" \
+ "${epair_tester}b tcp ${host_server_6}\[9\] <- ${net_clients_6}::1\[4203\] .* route-to: ${net_server2_6}::42:0@${epair_server2}a" \
+ "${epair_tester}b tcp ${host_server_6}\[9\] <- ${net_clients_6}::1\[4204\] .* route-to: ${net_server2_6}::42:1@${epair_server2}a" \
+ "${epair_tester}b tcp ${host_server_6}\[9\] <- ${net_clients_6}::1\[4205\] .* route-to: ${net_server2_6}::42:4@${epair_server2}a" \
+ "${epair_tester}b tcp ${host_server_6}\[9\] <- ${net_clients_6}::1\[4206\] .* route-to: ${net_server1_6}::42:0@${epair_server1}a" \
+ ; do
+ grep -qE "${state_regexp}" $states || atf_fail "State not found for '${state_regexp}'"
+ done
+}
+
+roundrobin_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "random_table" "cleanup"
+
+random_table_head()
+{
+ atf_set descr 'Pool with random flag and a table for IPv6'
+ atf_set require.user root
+}
+
+random_table_body()
+{
+ pf_map_addr_common
+
+ # The "random" flag will pick random hosts from the table but will
+ # not dive into prefixes, always choosing the 0th address.
+ # Proper address family will be choosen.
+
+ jexec router pfctl -e
+ pft_set_rules router \
+ "set debug loud" \
+ "set reassemble yes" \
+ "set state-policy if-bound" \
+ "table <rt_targets> { ${net_server2_4}.40/31 ${net_server2_4}.44 ${net_server2_6}::42:0/127 ${net_server2_6}::42:4 }" \
+ "pass in on ${epair_tester}b \
+ route-to { (${epair_server2}a <rt_targets>) } random \
+ inet6 proto tcp \
+ keep state"
+
+ good_targets="${net_server2_6}::42:0 ${net_server2_6}::42:4"
+ bad_targets="${net_server2_6}::42:1"
+ check_random IPv6 "${good_targets}" "${bad_targets}"
+}
+
+random_table_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "random_prefix" "cleanup"
+
+random_prefix_head()
+{
+ atf_set descr 'Pool with random flag and a table for IPv4'
+ atf_set require.user root
+}
+
+random_prefix_body()
+{
+ pf_map_addr_common
+
+ # The "random" flag will pick random hosts from given prefix.
+ # The choice being random makes testing it non-trivial. We do 10
+ # attempts to have each target chosen. Hopefully this is enough to have
+ # this test pass often enough.
+
+ jexec router pfctl -e
+ pft_set_rules router \
+ "set debug loud" \
+ "set reassemble yes" \
+ "set state-policy if-bound" \
+ "pass in on ${epair_tester}b \
+ route-to { (${epair_server2}a ${net_server2_6}::42:0/127) } random \
+ inet6 proto tcp \
+ keep state"
+
+ good_targets="${net_server2_6}::42:0 ${net_server2_6}::42:1"
+ check_random IPv6 "${good_targets}"
+}
+
+random_prefix_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "prefer_ipv6_nexthop_single_ipv4" "cleanup"
+
+prefer_ipv6_nexthop_single_ipv4_head()
+{
+ atf_set descr 'prefer-ipv6-nexthop option for a single IPv4 gateway'
+ atf_set require.user root
+}
+
+prefer_ipv6_nexthop_single_ipv4_body()
+{
+ pf_map_addr_common
+
+ # Basic forwarding test for prefer-ipv6-nexthop pool option.
+ # A single IPv4 gateway will work only for IPv4 traffic.
+
+ jexec router pfctl -e
+ pft_set_rules router \
+ "set reassemble yes" \
+ "set state-policy if-bound" \
+ "pass in on ${epair_tester}b \
+ route-to (${epair_server1}a ${net_server1_4_host_server}) prefer-ipv6-nexthop \
+ proto tcp \
+ keep state"
+
+ atf_check -s exit:0 ${common_dir}/pft_ping.py \
+ --sendif ${epair_tester}a --replyif ${epair_tester}a \
+ --fromaddr ${net_clients_4}.1 --to ${host_server_4} \
+ --ping-type=tcp3way --send-sport=4201 \
+
+ atf_check -s exit:1 ${common_dir}/pft_ping.py \
+ --sendif ${epair_tester}a --replyif ${epair_tester}a \
+ --fromaddr ${net_clients_6}::1 --to ${host_server_6} \
+ --ping-type=tcp3way --send-sport=4202
+
+ states=$(mktemp) || exit 1
+ jexec router pfctl -qvvss | normalize_pfctl_s > $states
+
+ for state_regexp in \
+ "${epair_tester}b tcp ${host_server_4}:9 <- ${net_clients_4}.1:4201 .* route-to: ${net_server1_4_host_server}@${epair_server1}a" \
+ ; do
+ grep -qE "${state_regexp}" $states || atf_fail "State not found for '${state_regexp}'"
+ done
+
+ # The IPv6 packet could not have been routed over IPv4 gateway.
+ atf_check -o "match:map-failed +1 +" -x "jexec router pfctl -qvvsi 2> /dev/null"
+}
+
+prefer_ipv6_nexthop_single_ipv4_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "prefer_ipv6_nexthop_single_ipv6" "cleanup"
+
+prefer_ipv6_nexthop_single_ipv6_head()
+{
+ atf_set descr 'prefer-ipv6-nexthop option for a single IPv6 gateway'
+ atf_set require.user root
+}
+
+prefer_ipv6_nexthop_single_ipv6_body()
+{
+ pf_map_addr_common
+
+ # Basic forwarding test for prefer-ipv6-nexthop pool option.
+ # A single IPve gateway will work both for IPv4 and IPv6 traffic.
+
+ jexec router pfctl -e
+ pft_set_rules router \
+ "set reassemble yes" \
+ "set state-policy if-bound" \
+ "pass in on ${epair_tester}b \
+ route-to (${epair_server1}a ${net_server1_6_host_server}) prefer-ipv6-nexthop \
+ proto tcp \
+ keep state"
+
+ atf_check -s exit:0 ${common_dir}/pft_ping.py \
+ --sendif ${epair_tester}a --replyif ${epair_tester}a \
+ --fromaddr ${net_clients_4}.1 --to ${host_server_4} \
+ --ping-type=tcp3way --send-sport=4201 \
+
+ atf_check -s exit:0 ${common_dir}/pft_ping.py \
+ --sendif ${epair_tester}a --replyif ${epair_tester}a \
+ --fromaddr ${net_clients_6}::1 --to ${host_server_6} \
+ --ping-type=tcp3way --send-sport=4202
+
+ states=$(mktemp) || exit 1
+ jexec router pfctl -qvvss | normalize_pfctl_s > $states
+
+ for state_regexp in \
+ "${epair_tester}b tcp ${host_server_4}:9 <- ${net_clients_4}.1:4201 .* route-to: ${net_server1_6_host_server}@${epair_server1}a" \
+ "${epair_tester}b tcp ${host_server_6}\[9\] <- ${net_clients_6}::1\[4202\] .* route-to: ${net_server1_6_host_server}@${epair_server1}a" \
+ ; do
+ grep -qE "${state_regexp}" $states || atf_fail "State not found for '${state_regexp}'"
+ done
+}
+
+prefer_ipv6_nexthop_single_ipv6_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "prefer_ipv6_nexthop_mixed_af_roundrobin_ipv4" "cleanup"
+
+prefer_ipv6_nexthop_mixed_af_roundrobin_ipv4_head()
+{
+ atf_set descr 'prefer-ipv6-nexthop option for multiple gateways of mixed AF with prefixes and tables, round robin selection, for IPv4 packets'
+ atf_set require.user root
+}
+
+prefer_ipv6_nexthop_mixed_af_roundrobin_ipv4_body()
+{
+ pf_map_addr_common
+
+ # pf_map_addr() starts iterating over hosts of the pool from the 2nd
+ # host. This behaviour was here before adding prefer-ipv6-nexthop
+ # support, we test for it. Some targets are hosts and some are tables.
+ # Those are iterated too. Finally the iteration loops back
+ # to the beginning of the pool. Send out IPv4 probes. They will use all
+ # IPv4 and IPv6 addresses from the pool.
+
+ jexec router pfctl -e
+ pft_set_rules router \
+ "set reassemble yes" \
+ "set state-policy if-bound" \
+ "table <rt_targets> { ${net_server2_4}.40/31 ${net_server2_4}.44 ${net_server2_6}::42:4/127 }" \
+ "pass in on ${epair_tester}b \
+ route-to { \
+ (${epair_server1}a ${net_server1_6_host_server}) \
+ (${epair_server1}a ${net_server1_6}::42:0/127) \
+ (${epair_server2}a ${net_server2_4_host_server}) \
+ (${epair_server2}a <rt_targets_empty>) \
+ (${epair_server1}a ${net_server1_4}.24/31) \
+ (${epair_server2}a <rt_targets>) \
+ } prefer-ipv6-nexthop \
+ proto tcp \
+ keep state"
+
+ for port in $(seq 1 12); do
+ port=$((4200 + port))
+ atf_check -s exit:0 ${common_dir}/pft_ping.py \
+ --sendif ${epair_tester}a --replyif ${epair_tester}a \
+ --fromaddr ${net_clients_4}.1 --to ${host_server_4} \
+ --ping-type=tcp3way --send-sport=${port}
+ done
+
+ states=$(mktemp) || exit 1
+ jexec router pfctl -qvvss | normalize_pfctl_s > $states
+
+ for state_regexp in \
+ "${epair_tester}b tcp ${host_server_4}:9 <- ${net_clients_4}.1:4201 .* route-to: ${net_server1_6}::42:0@${epair_server1}a" \
+ "${epair_tester}b tcp ${host_server_4}:9 <- ${net_clients_4}.1:4202 .* route-to: ${net_server1_6}::42:1@${epair_server1}a" \
+ "${epair_tester}b tcp ${host_server_4}:9 <- ${net_clients_4}.1:4203 .* route-to: ${net_server2_4_host_server}@${epair_server2}a" \
+ "${epair_tester}b tcp ${host_server_4}:9 <- ${net_clients_4}.1:4204 .* route-to: ${net_server1_4}.24@${epair_server1}a" \
+ "${epair_tester}b tcp ${host_server_4}:9 <- ${net_clients_4}.1:4205 .* route-to: ${net_server1_4}.25@${epair_server1}a" \
+ "${epair_tester}b tcp ${host_server_4}:9 <- ${net_clients_4}.1:4206 .* route-to: ${net_server2_6}::42:4@${epair_server2}a" \
+ "${epair_tester}b tcp ${host_server_4}:9 <- ${net_clients_4}.1:4207 .* route-to: ${net_server2_6}::42:5@${epair_server2}a" \
+ "${epair_tester}b tcp ${host_server_4}:9 <- ${net_clients_4}.1:4208 .* route-to: ${net_server2_4}.40@${epair_server2}a" \
+ "${epair_tester}b tcp ${host_server_4}:9 <- ${net_clients_4}.1:4209 .* route-to: ${net_server2_4}.41@${epair_server2}a" \
+ "${epair_tester}b tcp ${host_server_4}:9 <- ${net_clients_4}.1:4210 .* route-to: ${net_server2_4}.44@${epair_server2}a" \
+ "${epair_tester}b tcp ${host_server_4}:9 <- ${net_clients_4}.1:4211 .* route-to: ${net_server1_6_host_server}@${epair_server1}a" \
+ "${epair_tester}b tcp ${host_server_4}:9 <- ${net_clients_4}.1:4212 .* route-to: ${net_server1_6}::42:0@${epair_server1}a" \
+ ; do
+ grep -qE "${state_regexp}" $states || atf_fail "State not found for '${state_regexp}'"
+ done
+}
+
+prefer_ipv6_nexthop_mixed_af_roundrobin_ipv4_cleanup()
+{
+ pft_cleanup
+}
+
+prefer_ipv6_nexthop_mixed_af_roundrobin_ipv6_head()
+{
+ atf_set descr 'prefer-ipv6-nexthop option for multiple gateways of mixed AF with prefixes and tables, round-robin selection, for IPv6 packets'
+ atf_set require.user root
+}
+
+prefer_ipv6_nexthop_mixed_af_roundrobin_ipv6_body()
+{
+ pf_map_addr_common
+
+ # The "random" flag will pick random hosts from the table but will
+ # not dive into prefixes, always choosing the 0th address.
+ # Proper address family will be choosen. The choice being random makes
+ # testing it non-trivial. We do 10 attempts to have each target chosen.
+ # Hopefully this is enough to have this test pass often enough.
+
+ # pf_map_addr() starts iterating over hosts of the pool from the 2nd
+ # host. This behaviour was here before adding prefer-ipv6-nexthop
+ # support, we test for it. Some targets are hosts and some are tables.
+ # Those are iterated too. Finally the iteration loops back
+ # to the beginning of the pool. Send out IPv6 probes. They will use only
+ # IPv6 addresses from the pool.
+
+ jexec router pfctl -e
+ pft_set_rules router \
+ "set reassemble yes" \
+ "set state-policy if-bound" \
+ "table <rt_targets> { ${net_server2_4}.40/31 ${net_server2_4}.44 ${net_server2_6}::42:4/127 }" \
+ "pass in on ${epair_tester}b \
+ route-to { \
+ (${epair_server1}a ${net_server1_6_host_server}) \
+ (${epair_server1}a ${net_server1_6}::42:0/127) \
+ (${epair_server2}a ${net_server2_4_host_server}) \
+ (${epair_server2}a <rt_targets_empty>) \
+ (${epair_server1}a ${net_server1_4}.24/31) \
+ (${epair_server2}a <rt_targets>) \
+ } prefer-ipv6-nexthop \
+ proto tcp \
+ keep state"
+
+ for port in $(seq 1 6); do
+ port=$((4200 + port))
+ atf_check -s exit:0 ${common_dir}/pft_ping.py \
+ --sendif ${epair_tester}a --replyif ${epair_tester}a \
+ --fromaddr ${net_clients_6}::1 --to ${host_server_6} \
+ --ping-type=tcp3way --send-sport=${port}
+ done
+
+ states=$(mktemp) || exit 1
+ jexec router pfctl -qvvss | normalize_pfctl_s > $states
+
+ for state_regexp in \
+ "${epair_tester}b tcp ${host_server_6}\[9\] <- ${net_clients_6}::1\[4201\] .* route-to: ${net_server1_6}::42:0@${epair_server1}a" \
+ "${epair_tester}b tcp ${host_server_6}\[9\] <- ${net_clients_6}::1\[4202\] .* route-to: ${net_server1_6}::42:1@${epair_server1}a" \
+ "${epair_tester}b tcp ${host_server_6}\[9\] <- ${net_clients_6}::1\[4203\] .* route-to: ${net_server2_6}::42:4@${epair_server2}a" \
+ "${epair_tester}b tcp ${host_server_6}\[9\] <- ${net_clients_6}::1\[4204\] .* route-to: ${net_server2_6}::42:5@${epair_server2}a" \
+ "${epair_tester}b tcp ${host_server_6}\[9\] <- ${net_clients_6}::1\[4205\] .* route-to: ${net_server1_6_host_server}@${epair_server1}a" \
+ "${epair_tester}b tcp ${host_server_6}\[9\] <- ${net_clients_6}::1\[4206\] .* route-to: ${net_server1_6}::42:0@${epair_server1}a" \
+ ; do
+ grep -qE "${state_regexp}" $states || atf_fail "State not found for '${state_regexp}'"
+ done
+}
+
+prefer_ipv6_nexthop_mixed_af_roundrobin_ipv6_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "prefer_ipv6_nexthop_mixed_af_random_ipv4" "cleanup"
+
+prefer_ipv6_nexthop_mixed_af_random_table_ipv4_head()
+{
+ atf_set descr 'prefer-ipv6-nexthop option for a mixed-af table with random selection for IPv4 packets'
+ atf_set require.user root
+}
+
+prefer_ipv6_nexthop_mixed_af_random_table_ipv4_body()
+{
+ pf_map_addr_common
+
+ # Similarly to the test "random_table" the algorithm will choose
+ # IP addresses from the table not diving into prefixes.
+ # *prefer*-ipv6-nexthop means that IPv6 nexthops are preferred,
+ # so IPv4 ones will not be chosen as long as there are IPv6 ones
+ # available. With this tested there is no need for a test for IPv6-only
+ # next-hops table.
+
+ jexec router pfctl -e
+ pft_set_rules router \
+ "set reassemble yes" \
+ "set state-policy if-bound" \
+ "table <rt_targets> { ${net_server2_4}.40/31 ${net_server2_4}.44 ${net_server2_6}::42:0/127 ${net_server2_6}::42:4 }" \
+ "pass in on ${epair_tester}b \
+ route-to { (${epair_server2}a <rt_targets>) } random prefer-ipv6-nexthop \
+ proto tcp \
+ keep state"
+
+ good_targets="${net_server2_6}::42:0 ${net_server2_6}::42:4"
+ bad_targets="${net_server2_4}.40 ${net_server2_4}.41 ${net_server2_4}.44 ${net_server2_6}::42:1"
+ check_random IPv4 "${good_targets}" "${bad_targets}"
+}
+
+prefer_ipv6_nexthop_mixed_af_random_table_ipv4_cleanup()
+{
+ pft_cleanup
+}
+
+prefer_ipv6_nexthop_ipv4_random_table_ipv4_head()
+{
+ atf_set descr 'prefer-ipv6-nexthop option for an IPv4-only table with random selection for IPv4 packets'
+ atf_set require.user root
+}
+
+prefer_ipv6_nexthop_ipv4_random_table_ipv4_body()
+{
+ pf_map_addr_common
+
+ # Similarly to the test pf_map_addr:random_table the algorithm will
+ # choose IP addresses from the table not diving into prefixes.
+ # There are no IPv6 nexthops in the table, so the algorithm will
+ # fall back to IPv4.
+
+ jexec router pfctl -e
+ pft_set_rules router \
+ "set reassemble yes" \
+ "set state-policy if-bound" \
+ "table <rt_targets> { ${net_server2_4}.40/31 ${net_server2_4}.44 }" \
+ "pass in on ${epair_tester}b \
+ route-to { (${epair_server2}a <rt_targets>) } random prefer-ipv6-nexthop \
+ proto tcp \
+ keep state"
+
+ good_targets="${net_server2_4}.40 ${net_server2_4}.44"
+ bad_targets="${net_server2_4}.41"
+ check_random IPv4 "${good_targets}" "${bad_targets}"
+}
+
+prefer_ipv6_nexthop_ipv4_random_table_ipv4_cleanup()
+{
+ pft_cleanup
+}
+
+prefer_ipv6_nexthop_ipv4_random_table_ipv6_head()
+{
+ atf_set descr 'prefer-ipv6-nexthop option for an IPv4-only table with random selection for IPv6 packets'
+ atf_set require.user root
+}
+
+prefer_ipv6_nexthop_ipv4_random_table_ipv6_body()
+{
+ pf_map_addr_common
+
+ # IPv6 packets can't be forwarded over IPv4 next-hops.
+ # The failure happens in pf_map_addr() and increases the respective
+ # error counter.
+
+ jexec router pfctl -e
+ pft_set_rules router \
+ "set reassemble yes" \
+ "set state-policy if-bound" \
+ "table <rt_targets> { ${net_server2_4}.40/31 ${net_server2_4}.44 }" \
+ "pass in on ${epair_tester}b \
+ route-to { (${epair_server2}a <rt_targets>) } random prefer-ipv6-nexthop \
+ proto tcp \
+ keep state"
+
+ atf_check -s exit:1 ${common_dir}/pft_ping.py \
+ --sendif ${epair_tester}a --replyif ${epair_tester}a \
+ --fromaddr ${net_clients_6}::1 --to ${host_server_6} \
+ --ping-type=tcp3way --send-sport=4201
+
+ atf_check -o "match:map-failed +1 +" -x "jexec router pfctl -qvvsi 2> /dev/null"
+}
+
+prefer_ipv6_nexthop_ipv4_random_table_ipv6_cleanup()
+{
+ pft_cleanup
+}
+
+prefer_ipv6_nexthop_ipv6_random_prefix_ipv4_head()
+{
+ atf_set descr 'prefer-ipv6-nexthop option for an IPv6 prefix with random selection for IPv4 packets'
+ atf_set require.user root
+}
+
+prefer_ipv6_nexthop_ipv6_random_prefix_ipv4_body()
+{
+ pf_map_addr_common
+
+ jexec router pfctl -e
+ pft_set_rules router \
+ "set reassemble yes" \
+ "set state-policy if-bound" \
+ "pass in on ${epair_tester}b \
+ route-to { (${epair_server2}a ${net_server2_6}::42:0/127) } random prefer-ipv6-nexthop \
+ proto tcp \
+ keep state"
+
+ good_targets="${net_server2_6}::42:0 ${net_server2_6}::42:1"
+ check_random IPv4 "${good_targets}"
+}
+
+prefer_ipv6_nexthop_ipv6_random_prefix_ipv4_cleanup()
+{
+ pft_cleanup
+}
+
+prefer_ipv6_nexthop_ipv6_random_prefix_ipv6_head()
+{
+ atf_set descr 'prefer-ipv6-nexthop option for an IPv6 prefix with random selection for IPv6 packets'
+ atf_set require.user root
+}
+
+prefer_ipv6_nexthop_ipv6_random_prefix_ipv6_body()
+{
+ pf_map_addr_common
+
+ jexec router pfctl -e
+ pft_set_rules router \
+ "set reassemble yes" \
+ "set state-policy if-bound" \
+ "pass in on ${epair_tester}b \
+ route-to { (${epair_server2}a ${net_server2_6}::42:0/127) } random prefer-ipv6-nexthop \
+ proto tcp \
+ keep state"
+
+ good_targets="${net_server2_6}::42:0 ${net_server2_6}::42:1"
+ check_random IPv6 "${good_targets}"
+}
+
+prefer_ipv6_nexthop_ipv6_random_prefix_ipv6_cleanup()
+{
+ pft_cleanup
+}
+
+prefer_ipv6_nexthop_ipv4_random_prefix_ipv4_head()
+{
+ atf_set descr 'prefer-ipv6-nexthop option for an IPv4 prefix with random selection for IPv4 packets'
+ atf_set require.user root
+}
+
+prefer_ipv6_nexthop_ipv4_random_prefix_ipv4_body()
+{
+ pf_map_addr_common
+
+ jexec router pfctl -e
+ pft_set_rules router \
+ "set reassemble yes" \
+ "set state-policy if-bound" \
+ "pass in on ${epair_tester}b \
+ route-to { (${epair_server2}a ${net_server2_4}.40/31) } random prefer-ipv6-nexthop \
+ proto tcp \
+ keep state"
+
+ good_targets="${net_server2_4}.40 ${net_server2_4}.41"
+ check_random IPv4 "${good_targets}"
+}
+
+prefer_ipv6_nexthop_ipv4_random_prefix_ipv4_cleanup()
+{
+ pft_cleanup
+}
+
+prefer_ipv6_nexthop_ipv4_random_prefix_ipv6_head()
+{
+ atf_set descr 'prefer-ipv6-nexthop option for an IPv4 prefix with random selection for IPv6 packets'
+ atf_set require.user root
+}
+
+prefer_ipv6_nexthop_ipv4_random_prefix_ipv6_body()
+{
+ pf_map_addr_common
+
+ # IPv6 packets can't be forwarded over IPv4 next-hops.
+ # The failure happens in pf_map_addr() and increases the respective
+ # error counter.
+
+ jexec router pfctl -e
+ pft_set_rules router \
+ "set reassemble yes" \
+ "set state-policy if-bound" \
+ "pass in on ${epair_tester}b \
+ route-to { (${epair_server2}a ${net_server2_4}.40/31) } random prefer-ipv6-nexthop \
+ proto tcp \
+ keep state"
+
+ atf_check -s exit:1 ${common_dir}/pft_ping.py \
+ --sendif ${epair_tester}a --replyif ${epair_tester}a \
+ --fromaddr ${net_clients_6}::1 --to ${host_server_6} \
+ --ping-type=tcp3way --send-sport=4201
+
+ atf_check -o "match:map-failed +1 +" -x "jexec router pfctl -qvvsi 2> /dev/null"
+}
+
+prefer_ipv6_nexthop_ipv4_random_prefix_ipv6_cleanup()
+{
+ pft_cleanup
+}
+
atf_init_test_cases()
{
atf_add_test_case "v4"
@@ -995,5 +1651,25 @@ atf_init_test_cases()
atf_add_test_case "sticky"
atf_add_test_case "ttl"
atf_add_test_case "empty_pool"
+ # Tests for pf_map_addr() without prefer-ipv6-nexthop
atf_add_test_case "table_loop"
+ atf_add_test_case "roundrobin"
+ atf_add_test_case "random_table"
+ atf_add_test_case "random_prefix"
+ # Tests for pf_map_addr() without prefer-ipv6-nexthop
+ # Next hop is a Single IP address
+ atf_add_test_case "prefer_ipv6_nexthop_single_ipv4"
+ atf_add_test_case "prefer_ipv6_nexthop_single_ipv6"
+ # Next hop is tables and prefixes, accessed by the round-robin algorithm
+ atf_add_test_case "prefer_ipv6_nexthop_mixed_af_roundrobin_ipv4"
+ atf_add_test_case "prefer_ipv6_nexthop_mixed_af_roundrobin_ipv6"
+ # Next hop is a table, accessed by the random algorithm
+ atf_add_test_case "prefer_ipv6_nexthop_mixed_af_random_table_ipv4"
+ atf_add_test_case "prefer_ipv6_nexthop_ipv4_random_table_ipv4"
+ atf_add_test_case "prefer_ipv6_nexthop_ipv4_random_table_ipv6"
+ # Next hop is a prefix, accessed by the random algorithm
+ atf_add_test_case "prefer_ipv6_nexthop_ipv6_random_prefix_ipv4"
+ atf_add_test_case "prefer_ipv6_nexthop_ipv6_random_prefix_ipv6"
+ atf_add_test_case "prefer_ipv6_nexthop_ipv4_random_prefix_ipv4"
+ atf_add_test_case "prefer_ipv6_nexthop_ipv4_random_prefix_ipv6"
}
diff --git a/tests/sys/netpfil/pf/sctp.sh b/tests/sys/netpfil/pf/sctp.sh
index 57dcdad1d866..78055f5a9dd2 100644
--- a/tests/sys/netpfil/pf/sctp.sh
+++ b/tests/sys/netpfil/pf/sctp.sh
@@ -673,6 +673,9 @@ pfsync_body()
atf_fail "Initial SCTP connection failed"
fi
+ # Give pfsync some time to do its thing
+ sleep 1
+
# Verify that two has the connection too
state=$(jexec ${j}two pfctl -ss | grep sctp)
if [ -z "${state}" ];
diff --git a/tests/sys/netpfil/pf/src_track.sh b/tests/sys/netpfil/pf/src_track.sh
index ae60a5df809b..e12d0464ee8c 100755
--- a/tests/sys/netpfil/pf/src_track.sh
+++ b/tests/sys/netpfil/pf/src_track.sh
@@ -526,10 +526,12 @@ mixed_af_body()
"block" \
"pass inet6 proto icmp6 icmp6-type { neighbrsol, neighbradv }" \
"pass in on ${epair_tester}b \
- route-to { (${epair_server1}a ${net_server1_4_host_server}) \
- } sticky-address \
- inet6 proto tcp from any to 64:ff9b::/96 \
- af-to inet from ${net_clients_4}.0/${net_clients_4_mask} round-robin sticky-address"
+ route-to { \
+ (${epair_server1}a ${net_server1_4_host_server}) \
+ (${epair_server2}a ${net_server2_6_host_server}) \
+ } prefer-ipv6-nexthop sticky-address \
+ inet6 proto tcp from any to 64:ff9b::/96 \
+ af-to inet from ${net_clients_4}.0/${net_clients_4_mask} round-robin sticky-address"
atf_check -s exit:0 ${common_dir}/pft_ping.py \
--sendif ${epair_tester}a \
@@ -538,6 +540,20 @@ mixed_af_body()
--to 64:ff9b::192.0.2.100 \
--ping-type=tcp3way \
--send-sport=4201
+ atf_check -s exit:0 ${common_dir}/pft_ping.py \
+ --sendif ${epair_tester}a \
+ --replyif ${epair_tester}a \
+ --fromaddr 2001:db8:44::1 \
+ --to 64:ff9b::192.0.2.100 \
+ --ping-type=tcp3way \
+ --send-sport=4202
+ atf_check -s exit:0 ${common_dir}/pft_ping.py \
+ --sendif ${epair_tester}a \
+ --replyif ${epair_tester}a \
+ --fromaddr 2001:db8:44::2 \
+ --to 64:ff9b::192.0.2.100 \
+ --ping-type=tcp3way \
+ --send-sport=4203
states=$(mktemp) || exit 1
jexec router pfctl -qvvss | normalize_pfctl_s > $states
@@ -546,16 +562,22 @@ mixed_af_body()
# States are checked for proper route-to information.
# The route-to gateway is IPv4.
+ # FIXME: Sticky-address is broken for af-to pools!
+ # The SN is created but apparently not used, as seen in states.
for state_regexp in \
- "${epair_tester}b tcp 203.0.113.0:4201 \(2001:db8:44::1\[4201\]\) -> 192.0.2.100:9 \(64:ff9b::c000:264\[9\]\) .* route-to: 198.51.100.18@${epair_server1}a" \
+ "${epair_tester}b tcp 203.0.113.0:4201 \(2001:db8:44::1\[4201\]\) -> 192.0.2.100:9 \(64:ff9b::c000:264\[9\]\) .* route-to: 2001:db8:4202::2@${epair_server2}a" \
+ "${epair_tester}b tcp 203.0.113.1:4202 \(2001:db8:44::1\[4202\]\) -> 192.0.2.100:9 \(64:ff9b::c000:264\[9\]\) .* route-to: 2001:db8:4202::2@${epair_server2}a" \
+ "${epair_tester}b tcp 203.0.113.2:4203 \(2001:db8:44::2\[4203\]\) -> 192.0.2.100:9 \(64:ff9b::c000:264\[9\]\) .* route-to: 198.51.100.18@${epair_server1}a" \
; do
grep -qE "${state_regexp}" $states || atf_fail "State not found for '${state_regexp}'"
done
# Source nodes map IPv6 source address onto IPv4 gateway and IPv4 SNAT address.
for node_regexp in \
- '2001:db8:44::1 -> 203.0.113.0 .* states 1, .* NAT/RDR sticky-address' \
- '2001:db8:44::1 -> 198.51.100.18 .* states 1, .* route sticky-address' \
+ '2001:db8:44::2 -> 203.0.113.2 .* states 1, .* NAT/RDR sticky-address' \
+ '2001:db8:44::2 -> 198.51.100.18 .* states 1, .* route sticky-address' \
+ '2001:db8:44::1 -> 203.0.113.0 .* states 2, .* NAT/RDR sticky-address' \
+ '2001:db8:44::1 -> 2001:db8:4202::2 .* states 2, .* route sticky-address' \
; do
grep -qE "${node_regexp}" $nodes || atf_fail "Source node not found for '${node_regexp}'"
done
diff --git a/tests/sys/netpfil/pf/utils.subr b/tests/sys/netpfil/pf/utils.subr
index 3f8d437920f9..a48f26653f8c 100644
--- a/tests/sys/netpfil/pf/utils.subr
+++ b/tests/sys/netpfil/pf/utils.subr
@@ -274,8 +274,8 @@ setup_router_server_ipv6()
jexec server inetd -p ${PWD}/inetd.pid $inetd_conf
}
-# Create a router and 2 server jails for nat64 and rfc5549 test cases.
-# The router is connected to servers, both are dual-stack, and to the
+# Create a router and 2 server jails for nat64 and prefer-ipv6-nexthop test
+# cases. The router is connected to servers, both are dual-stack, and to the
# tester jail. All links are dual stack.
setup_router_server_nat64()
{