aboutsummaryrefslogtreecommitdiff
path: root/tests/sys/netpfil
diff options
context:
space:
mode:
Diffstat (limited to 'tests/sys/netpfil')
-rw-r--r--tests/sys/netpfil/pf/Makefile3
-rw-r--r--tests/sys/netpfil/pf/counters.sh831
-rw-r--r--tests/sys/netpfil/pf/ether.sh3
-rw-r--r--tests/sys/netpfil/pf/mld.py35
-rw-r--r--tests/sys/netpfil/pf/nat.sh9
-rw-r--r--tests/sys/netpfil/pf/nat64.sh30
-rw-r--r--tests/sys/netpfil/pf/rdr.sh109
-rw-r--r--tests/sys/netpfil/pf/rules_counter.sh1
-rw-r--r--tests/sys/netpfil/pf/sctp.sh18
-rw-r--r--tests/sys/netpfil/pf/syncookie.sh3
-rw-r--r--tests/sys/netpfil/pf/utils.subr3
11 files changed, 1006 insertions, 39 deletions
diff --git a/tests/sys/netpfil/pf/Makefile b/tests/sys/netpfil/pf/Makefile
index 7ddeb5369f47..b363e0b17c76 100644
--- a/tests/sys/netpfil/pf/Makefile
+++ b/tests/sys/netpfil/pf/Makefile
@@ -5,6 +5,7 @@ TESTS_SUBDIRS+= ioctl
ATF_TESTS_SH+= altq \
anchor \
+ counters \
debug \
divert-to \
dup \
@@ -71,7 +72,7 @@ ATF_TESTS_PYTEST+= tcp.py
# Allow tests to run in parallel in their own jails
TEST_METADATA+= execenv="jail"
-TEST_METADATA+= execenv_jail_params="vnet allow.raw_sockets"
+TEST_METADATA+= execenv_jail_params="vnet allow.raw_sockets allow.read_msgbuf"
${PACKAGE}FILES+= \
bsnmpd.conf \
diff --git a/tests/sys/netpfil/pf/counters.sh b/tests/sys/netpfil/pf/counters.sh
new file mode 100644
index 000000000000..20d7dc3c6d89
--- /dev/null
+++ b/tests/sys/netpfil/pf/counters.sh
@@ -0,0 +1,831 @@
+#
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2025 Kajetan Staszkiewicz
+#
+# 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.
+
+. $(atf_get_srcdir)/utils.subr
+
+get_counters()
+{
+ echo " === rules ==="
+ rules=$(mktemp) || exit
+ (jexec router pfctl -qvvsn ; jexec router pfctl -qvvsr) | normalize_pfctl_s > $rules
+ cat $rules
+
+ echo " === tables ==="
+ tables=$(mktemp) || exit 1
+ jexec router pfctl -qvvsT > $tables
+ cat $tables
+
+ echo " === states ==="
+ states=$(mktemp) || exit 1
+ jexec router pfctl -qvvss | normalize_pfctl_s > $states
+ cat $states
+
+ echo " === nodes ==="
+ nodes=$(mktemp) || exit 1
+ jexec router pfctl -qvvsS | normalize_pfctl_s > $nodes
+ cat $nodes
+}
+
+atf_test_case "match_pass_state" "cleanup"
+match_pass_state_head()
+{
+ atf_set descr 'Counters on match and pass rules'
+ atf_set require.user root
+}
+
+match_pass_state_body()
+{
+ setup_router_server_ipv6
+
+ # Thest counters for a statefull firewall. Expose the behaviour of
+ # increasing table counters if a table is used multiple times.
+ # The table "tbl_in" is used both in match and pass rule. It's counters
+ # are incremented twice. The tables "tbl_out_match" and "tbl_out_pass"
+ # are used only once and have their countes increased only once.
+ # Test source node counters for this simple scenario too.
+ pft_set_rules router \
+ "set state-policy if-bound" \
+ "table <tbl_in> { ${net_tester_host_tester} }" \
+ "table <tbl_out_pass> { ${net_server_host_server} }" \
+ "table <tbl_out_match> { ${net_server_host_server} }" \
+ "block" \
+ "pass inet6 proto icmp6 icmp6-type { neighbrsol, neighbradv }" \
+ "match in on ${epair_tester}b inet6 proto tcp from <tbl_in> scrub (random-id)" \
+ "pass in on ${epair_tester}b inet6 proto tcp from <tbl_in> keep state (max-src-states 3 source-track rule)" \
+ "match out on ${epair_server}a inet6 proto tcp to <tbl_out_match> scrub (random-id)" \
+ "pass out on ${epair_server}a inet6 proto tcp to <tbl_out_pass> keep state"
+
+ # Use a real TCP connection so that it will be properly closed, guaranteeing the amount of packets.
+ atf_check -s exit:0 -o match:"This is a test" -x \
+ "echo 'This is a test' | nc -w3 ${net_server_host_server} echo"
+ # Let FINs pass through.
+ sleep 1
+ get_counters
+
+ for rule_regexp in \
+ "@3 match in on ${epair_tester}b .* Packets: 10 Bytes: 766 States: 1 " \
+ "@4 pass in on ${epair_tester}b .* Packets: 10 Bytes: 766 States: 1 " \
+ "@5 match out on ${epair_server}a .* Packets: 10 Bytes: 766 States: 1 " \
+ "@6 pass out on ${epair_server}a .* Packets: 10 Bytes: 766 States: 1 " \
+ ; do
+ grep -qE "${rule_regexp}" $rules || atf_fail "Rule regexp not found for '${rule_regexp}'"
+ done
+
+ table_counters_single="Evaluations: NoMatch: 0 Match: 1 In/Block: Packets: 0 Bytes: 0 In/Pass: Packets: 4 Bytes: 311 In/XPass: Packets: 0 Bytes: 0 Out/Block: Packets: 0 Bytes: 0 Out/Pass: Packets: 6 Bytes: 455 Out/XPass: Packets: 0 Bytes: 0"
+ table_counters_double="Evaluations: NoMatch: 0 Match: 2 In/Block: Packets: 0 Bytes: 0 In/Pass: Packets: 12 Bytes: 910 In/XPass: Packets: 0 Bytes: 0 Out/Block: Packets: 0 Bytes: 0 Out/Pass: Packets: 8 Bytes: 622 Out/XPass: Packets: 0 Bytes: 0"
+ for table_test in \
+ "tbl_in___${table_counters_double}" \
+ "tbl_out_match___${table_counters_single}" \
+ "tbl_out_pass___${table_counters_single}" \
+ ; do
+ table_name=${table_test%%___*}
+ table_regexp=${table_test##*___}
+ table=$(mktemp) || exit 1
+ cat $tables | grep -A10 $table_name | tr '\n' ' ' | awk '{gsub("[\\[\\]]", " ", $0); gsub("[[:blank:]]+"," ",$0); print $0}' > ${table}
+ grep -qE "${table_regexp}" ${table} || atf_fail "Bad counters for table ${table_name}"
+ done;
+
+ for state_regexp in \
+ "${epair_tester}b tcp ${net_server_host_server}.* <- ${net_tester_host_tester}.* 6:4 pkts, 455:311 bytes, rule 4," \
+ "${epair_server}a tcp ${net_server_host_tester}.* -> ${net_server_host_server}.* 6:4 pkts, 455:311 bytes, rule 6," \
+ ; do
+ grep -qE "${state_regexp}" $states || atf_fail "State not found for '${state_regexp}'"
+ done
+
+ for node_regexp in \
+ "${net_tester_host_tester} -> :: .* 10 pkts, 766 bytes, filter rule 4, limit source-track"\
+ ; do
+ grep -qE "${node_regexp}" $nodes || atf_fail "Source node not found for '${node_regexp}'"
+ done
+}
+
+match_pass_state_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "match_pass_no_state" "cleanup"
+match_pass_no_state_head()
+{
+ atf_set descr 'Counters on match and pass rules without keep state'
+ atf_set require.user root
+}
+
+match_pass_no_state_body()
+{
+ setup_router_server_ipv6
+
+ # Test counters for a stateless firewall.
+ # The table "tbl_in" is used both in match and pass rule in the inbound
+ # direction. The "In/Pass" counter is incremented twice. The table
+ # "tbl_inout" matches the same host on inbound and outbound direction.
+ # It will also be incremented twice. The tables "tbl_out_match" and
+ # "tbl_out_pass" will have their counters increased only once.
+ pft_set_rules router \
+ "table <tbl_in> { ${net_tester_host_tester} }" \
+ "table <tbl_inout> { ${net_tester_host_tester} }" \
+ "table <tbl_out_match> { ${net_server_host_server} }" \
+ "table <tbl_out_pass> { ${net_server_host_server} }" \
+ "block" \
+ "pass inet6 proto icmp6 icmp6-type { neighbrsol, neighbradv }" \
+ "match in on ${epair_tester}b inet6 proto tcp from <tbl_inout>" \
+ "match in on ${epair_tester}b inet6 proto tcp from <tbl_in>" \
+ "pass in on ${epair_tester}b inet6 proto tcp from <tbl_in> no state" \
+ "pass out on ${epair_tester}b inet6 proto tcp to <tbl_in> no state" \
+ "match in on ${epair_server}a inet6 proto tcp from <tbl_out_match>" \
+ "pass in on ${epair_server}a inet6 proto tcp from <tbl_out_pass> no state" \
+ "match out on ${epair_server}a inet6 proto tcp from <tbl_inout> no state" \
+ "pass out on ${epair_server}a inet6 proto tcp to <tbl_out_pass> no state"
+
+ # Use a real TCP connection so that it will be properly closed, guaranteeing the amount of packets.
+ atf_check -s exit:0 -o match:"This is a test" -x \
+ "echo 'This is a test' | nc -w3 ${net_server_host_server} echo"
+ sleep 1
+ get_counters
+
+ for rule_regexp in \
+ "@3 match in on ${epair_tester}b .* Packets: 6 Bytes: 455 " \
+ "@4 match in on ${epair_tester}b .* Packets: 6 Bytes: 455 " \
+ "@5 pass in on ${epair_tester}b .* Packets: 6 Bytes: 455 " \
+ "@6 pass out on ${epair_tester}b .* Packets: 4 Bytes: 311 " \
+ "@7 match in on ${epair_server}a .* Packets: 4 Bytes: 311 " \
+ "@8 pass in on ${epair_server}a .* Packets: 4 Bytes: 311 " \
+ "@10 pass out on ${epair_server}a .* Packets: 6 Bytes: 455 " \
+ ; do
+ grep -qE "${rule_regexp}" $rules || atf_fail "Rule regexp not found for '${rule_regexp}'"
+ done
+
+ for table_test in \
+ "tbl_in___Evaluations: NoMatch: 0 Match: 16 In/Block: Packets: 0 Bytes: 0 In/Pass: Packets: 12 Bytes: 910 In/XPass: Packets: 0 Bytes: 0 Out/Block: Packets: 0 Bytes: 0 Out/Pass: Packets: 4 Bytes: 311 Out/XPass: Packets: 0 Bytes: 0" \
+ "tbl_out_match___Evaluations: NoMatch: 0 Match: 4 In/Block: Packets: 0 Bytes: 0 In/Pass: Packets: 4 Bytes: 311 In/XPass: Packets: 0 Bytes: 0 Out/Block: Packets: 0 Bytes: 0 Out/Pass: Packets: 0 Bytes: 0 Out/XPass: Packets: 0 Bytes: 0" \
+ "tbl_out_pass___Evaluations: NoMatch: 0 Match: 10 In/Block: Packets: 0 Bytes: 0 In/Pass: Packets: 4 Bytes: 311 In/XPass: Packets: 0 Bytes: 0 Out/Block: Packets: 0 Bytes: 0 Out/Pass: Packets: 6 Bytes: 455 Out/XPass: Packets: 0 Bytes: 0" \
+ "tbl_inout___Evaluations: NoMatch: 0 Match: 12 In/Block: Packets: 0 Bytes: 0 In/Pass: Packets: 6 Bytes: 455 In/XPass: Packets: 0 Bytes: 0 Out/Block: Packets: 0 Bytes: 0 Out/Pass: Packets: 6 Bytes: 455 Out/XPass: Packets: 0 Bytes: 0" \
+ ; do
+ table_name=${table_test%%___*}
+ table_regexp=${table_test##*___}
+ table=$(mktemp) || exit 1
+ cat $tables | grep -A10 $table_name | tr '\n' ' ' | awk '{gsub("[\\[\\]]", " ", $0); gsub("[[:blank:]]+"," ",$0); print $0}' > ${table}
+ grep -qE "${table_regexp}" ${table} || atf_fail "Bad counters for table ${table_name}"
+ done;
+}
+
+match_pass_no_state_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "match_block" "cleanup"
+match_block_head()
+{
+ atf_set descr 'Counters on match and block rules'
+ atf_set require.user root
+}
+
+match_block_body()
+{
+ setup_router_server_ipv6
+
+ # Stateful firewall with a blocking rule. The rule will have its
+ # counters increased because it matches and applies correctly.
+ # The "match" rule before the "pass" rule will have its counters
+ # increased for blocked traffic too.
+ pft_set_rules router \
+ "set state-policy if-bound" \
+ "table <tbl_in_match> { ${net_server_host_server} }" \
+ "table <tbl_in_block> { ${net_server_host_server} }" \
+ "block" \
+ "pass inet6 proto icmp6 icmp6-type { neighbrsol, neighbradv }" \
+ "match in on ${epair_tester}b inet6 proto tcp to <tbl_in_match> scrub (random-id)" \
+ "block in on ${epair_tester}b inet6 proto tcp to <tbl_in_block>" \
+ "pass out on ${epair_server}a inet6 proto tcp keep state"
+
+ # Wait 3 seconds, that will cause 2 SYNs to be sent out.
+ echo 'This is a test' | nc -w3 ${net_server_host_server} echo
+ sleep 1
+ get_counters
+
+ for rule_regexp in \
+ "@3 match in on ${epair_tester}b .* Packets: 2 Bytes: 160 States: 0 " \
+ "@4 block drop in on ${epair_tester}b .* Packets: 2 Bytes: 160 States: 0 " \
+ ; do
+ grep -qE "${rule_regexp}" $rules || atf_fail "Rule regexp not found for '${rule_regexp}'"
+ done
+
+ # OpenBSD has (In|Out)/Match. We don't (yet) have it in FreeBSD
+ # so we follow the action of the "pass" rule ("block" for this test)
+ # in "match" rules.
+ for table_test in \
+ "tbl_in_match___Evaluations: NoMatch: 0 Match: 2 In/Block: Packets: 2 Bytes: 160 In/Pass: Packets: 0 Bytes: 0 In/XPass: Packets: 0 Bytes: 0 Out/Block: Packets: 0 Bytes: 0 Out/Pass: Packets: 0 Bytes: 0 Out/XPass: Packets: 0 Bytes: 0" \
+ "tbl_in_block___Evaluations: NoMatch: 0 Match: 2 In/Block: Packets: 2 Bytes: 160 In/Pass: Packets: 0 Bytes: 0 In/XPass: Packets: 0 Bytes: 0 Out/Block: Packets: 0 Bytes: 0 Out/Pass: Packets: 0 Bytes: 0 Out/XPass: Packets: 0 Bytes: 0" \
+ ; do
+ table_name=${table_test%%___*}
+ table_regexp=${table_test##*___}
+ table=$(mktemp) || exit 1
+ cat $tables | grep -A10 $table_name | tr '\n' ' ' | awk '{gsub("[\\[\\]]", " ", $0); gsub("[[:blank:]]+"," ",$0); print $0}' > ${table}
+ grep -qE "${table_regexp}" ${table} || atf_fail "Bad counters for table ${table_name}"
+ done;
+}
+
+match_block_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "match_fail" "cleanup"
+match_fail_head()
+{
+ atf_set descr 'Counters on match and failing pass rules'
+ atf_set require.user root
+}
+
+match_fail_body()
+{
+ setup_router_server_ipv6
+
+ # Statefull firewall with a failing "pass" rule.
+ # When the rule can't apply it will not have its counters increased.
+ pft_set_rules router \
+ "set state-policy if-bound" \
+ "table <tbl_in_match> { ${net_server_host_server} }" \
+ "table <tbl_in_fail> { ${net_server_host_server} }" \
+ "block" \
+ "pass inet6 proto icmp6 icmp6-type { neighbrsol, neighbradv }" \
+ "match in on ${epair_tester}b inet6 proto tcp to <tbl_in_match> scrub (random-id)" \
+ "pass in on ${epair_tester}b inet6 proto tcp to <tbl_in_fail> keep state (max 1)" \
+ "pass out on ${epair_server}a inet6 proto tcp keep state"
+
+ # The first test will pass and increase the counters for all rules.
+ echo 'This is a test' | nc -w3 ${net_server_host_server} echo
+ # The second test will go through the "match" rules but fail
+ # on the "pass" rule due to 'keep state (max 1)'.
+ # Wait 3 seconds, that will cause 2 SYNs to be sent out.
+ echo 'This is a test' | nc -w3 ${net_server_host_server} echo
+ sleep 1
+ get_counters
+
+ for rule_regexp in \
+ "@3 match in on ${epair_tester}b .* Packets: 10 Bytes: 766 States: 1 " \
+ "@4 pass in on ${epair_tester}b .* Packets: 10 Bytes: 766 States: 1 " \
+ ; do
+ grep -qE "${rule_regexp}" $rules || atf_fail "Rule regexp not found for '${rule_regexp}'"
+ done
+
+ $table_counters_single="Evaluations: NoMatch: 0 Match: 3 In/Block: Packets: 0 Bytes: 0 In/Pass: Packets: 6 Bytes: 455 In/XPass: Packets: 0 Bytes: 0 Out/Block: Packets: 0 Bytes: 0 Out/Pass: Packets: 4 Bytes: 311 Out/XPass: Packets: 0 Bytes: 0"
+ for table_test in \
+ "tbl_in_match___${table_counters_single}" \
+ "tbl_in_fail___${table_counters_single}" \
+ ; do
+ table_name=${table_test%%___*}
+ table_regexp=${table_test##*___}
+ table=$(mktemp) || exit 1
+ cat $tables | grep -A10 $table_name | tr '\n' ' ' | awk '{gsub("[\\[\\]]", " ", $0); gsub("[[:blank:]]+"," ",$0); print $0}' > ${table}
+ grep -qE "${table_regexp}" ${table} || atf_fail "Bad counters for table ${table_name}"
+ done;
+}
+
+match_fail_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "nat_natonly" "cleanup"
+nat_natonly_head()
+{
+ atf_set descr 'Counters on only a NAT rule creating state'
+ atf_set require.user root
+}
+
+nat_natonly_body()
+{
+ setup_router_server_ipv6
+
+ # NAT is applied on the "nat" rule.
+ # The "nat" rule matches on pre-NAT addresses. There is no separate
+ # "pass" rule so the "nat" rule creates the state.
+ pft_set_rules router \
+ "set state-policy if-bound" \
+ "table <tbl_src_nat> { ${net_tester_host_tester} }" \
+ "table <tbl_dst_nat> { ${net_server_host_server} }" \
+ "nat on ${epair_server}a inet6 proto tcp from <tbl_src_nat> to <tbl_dst_nat> -> ${net_server_host_router}"
+
+ # Use a real TCP connection so that it will be properly closed, guaranteeing the amount of packets.
+ atf_check -s exit:0 -o match:"This is a test" -x \
+ "echo 'This is a test' | nc -w3 ${net_server_host_server} echo"
+ sleep 1
+ get_counters
+
+ for rule_regexp in \
+ "@0 nat on ${epair_server}a .* Packets: 10 Bytes: 766 States: 1 " \
+ ; do
+ grep -qE "${rule_regexp}" $rules || atf_fail "Rule regexp not found for '${rule_regexp}'"
+ done
+
+ # All tables have counters increased for In/Pass and Out/Pass, not XPass.
+ table_counters="Evaluations: NoMatch: 0 Match: 1 In/Block: Packets: 0 Bytes: 0 In/Pass: Packets: 4 Bytes: 311 In/XPass: Packets: 0 Bytes: 0 Out/Block: Packets: 0 Bytes: 0 Out/Pass: Packets: 6 Bytes: 455 Out/XPass: Packets: 0 Bytes: 0"
+ for table_test in \
+ "tbl_src_nat___${table_counters}" \
+ "tbl_dst_nat___${table_counters}" \
+ ; do
+ table_name=${table_test%%___*}
+ table_regexp=${table_test##*___}
+ table=$(mktemp) || exit 1
+ cat $tables | grep -A10 $table_name | tr '\n' ' ' | awk '{gsub("[\\[\\]]", " ", $0); gsub("[[:blank:]]+"," ",$0); print $0}' > ${table}
+ grep -qE "${table_regexp}" ${table} || atf_fail "Bad counters for table ${table_name}"
+ done;
+
+ for state_regexp in \
+ "all tcp ${net_server_host_router}.* -> ${net_server_host_server}.* 6:4 pkts, 455:311 bytes" \
+ ; do
+ grep -qE "${state_regexp}" $states || atf_fail "State not found for '${state_regexp}'"
+ done
+}
+
+nat_natonly_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "nat_nat" "cleanup"
+nat_nat_head()
+{
+ atf_set descr 'Counters on NAT, match and pass rules with keep state'
+ atf_set require.user root
+}
+
+nat_nat_body()
+{
+ setup_router_server_ipv6
+
+ # NAT is applied in the NAT ruleset.
+ # The "nat" rule matches on pre-NAT addresses.
+ # The "match" rule matches on post-NAT addresses.
+ # The "pass" rule matches on post-NAT addresses and creates the state.
+ pft_set_rules router \
+ "set state-policy if-bound" \
+ "table <tbl_src_nat> { ${net_tester_host_tester} }" \
+ "table <tbl_dst_nat> { ${net_server_host_server} }" \
+ "table <tbl_src_match> { ${net_server_host_router} }" \
+ "table <tbl_dst_match> { ${net_server_host_server} }" \
+ "table <tbl_src_pass> { ${net_server_host_router} }" \
+ "table <tbl_dst_pass> { ${net_server_host_server} }" \
+ "nat on ${epair_server}a inet6 proto tcp from <tbl_src_nat> to <tbl_dst_nat> -> ${net_server_host_router}" \
+ "block" \
+ "pass inet6 proto icmp6 icmp6-type { neighbrsol, neighbradv }" \
+ "pass in on ${epair_tester}b inet6 proto tcp keep state" \
+ "match out on ${epair_server}a inet6 proto tcp from <tbl_src_match> to <tbl_dst_match> scrub (random-id)" \
+ "pass out on ${epair_server}a inet6 proto tcp from <tbl_src_pass> to <tbl_dst_pass> keep state"
+
+ # Use a real TCP connection so that it will be properly closed, guaranteeing the amount of packets.
+ atf_check -s exit:0 -o match:"This is a test" -x \
+ "echo 'This is a test' | nc -w3 ${net_server_host_server} echo"
+ sleep 1
+ get_counters
+
+ for rule_regexp in \
+ "@0 nat on ${epair_server}a .* Packets: 10 Bytes: 766 States: 1 " \
+ "@4 match out on ${epair_server}a .* Packets: 10 Bytes: 766 States: 1 " \
+ "@5 pass out on ${epair_server}a .* Packets: 10 Bytes: 766 States: 1 " \
+ ; do
+ grep -qE "${rule_regexp}" $rules || atf_fail "Rule regexp not found for '${rule_regexp}'"
+ done
+
+ # All tables have counters increased for In/Pass and Out/Pass, not XPass nor Block.
+ table_counters="Evaluations: NoMatch: 0 Match: 1 In/Block: Packets: 0 Bytes: 0 In/Pass: Packets: 4 Bytes: 311 In/XPass: Packets: 0 Bytes: 0 Out/Block: Packets: 0 Bytes: 0 Out/Pass: Packets: 6 Bytes: 455 Out/XPass: Packets: 0 Bytes: 0"
+ for table_test in \
+ "tbl_src_nat___${table_counters}" \
+ "tbl_dst_nat___${table_counters}" \
+ "tbl_src_match___${table_counters}" \
+ "tbl_dst_match___${table_counters}" \
+ "tbl_src_pass___${table_counters}" \
+ "tbl_dst_pass___${table_counters}" \
+ ; do
+ table_name=${table_test%%___*}
+ table_regexp=${table_test##*___}
+ table=$(mktemp) || exit 1
+ cat $tables | grep -A10 $table_name | tr '\n' ' ' | awk '{gsub("[\\[\\]]", " ", $0); gsub("[[:blank:]]+"," ",$0); print $0}' > ${table}
+ grep -qE "${table_regexp}" ${table} || atf_fail "Bad counters for table ${table_name}"
+ done;
+
+ for state_regexp in \
+ "${epair_server}a tcp ${net_server_host_router}.* -> ${net_server_host_server}.* 6:4 pkts, 455:311 bytes, rule 5," \
+ ; do
+ grep -qE "${state_regexp}" $states || atf_fail "State not found for '${state_regexp}'"
+ done
+}
+
+nat_nat_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "nat_match" "cleanup"
+nat_match_head()
+{
+ atf_set descr 'Counters on match with NAT and pass rules'
+ atf_set require.user root
+}
+
+nat_match_body()
+{
+ setup_router_server_ipv6
+
+ # NAT is applied on the "match" rule.
+ # The "match" rule up to and including the NAT rule match on pre-NAT addresses.
+ # The "match" rule after NAT matches on post-NAT addresses.
+ # The "pass" rule matches on post-NAT addresses and creates the state.
+ pft_set_rules router \
+ "set state-policy if-bound" \
+ "table <tbl_src_match1> { ${net_tester_host_tester} }" \
+ "table <tbl_dst_match1> { ${net_server_host_server} }" \
+ "table <tbl_src_match2> { ${net_tester_host_tester} }" \
+ "table <tbl_dst_match2> { ${net_server_host_server} }" \
+ "table <tbl_src_match3> { ${net_server_host_router} }" \
+ "table <tbl_dst_match3> { ${net_server_host_server} }" \
+ "table <tbl_src_pass> { ${net_server_host_router} }" \
+ "table <tbl_dst_pass> { ${net_server_host_server} }" \
+ "block" \
+ "pass inet6 proto icmp6 icmp6-type { neighbrsol, neighbradv }" \
+ "pass in on ${epair_tester}b inet6 proto tcp keep state" \
+ "match out on ${epair_server}a inet6 proto tcp from <tbl_src_match1> to <tbl_dst_match1> scrub (random-id)" \
+ "match out on ${epair_server}a inet6 proto tcp from <tbl_src_match2> to <tbl_dst_match2> nat-to ${net_server_host_router}" \
+ "match out on ${epair_server}a inet6 proto tcp from <tbl_src_match3> to <tbl_dst_match3> scrub (random-id)" \
+ "pass out on ${epair_server}a inet6 proto tcp from <tbl_src_pass> to <tbl_dst_pass> keep state"
+
+ # Use a real TCP connection so that it will be properly closed, guaranteeing the amount of packets.
+ atf_check -s exit:0 -o match:"This is a test" -x \
+ "echo 'This is a test' | nc -w3 ${net_server_host_server} echo"
+ sleep 1
+ get_counters
+
+ for rule_regexp in \
+ "@4 match out on ${epair_server}a .* Packets: 10 Bytes: 766 States: 1 " \
+ "@5 match out on ${epair_server}a .* Packets: 10 Bytes: 766 States: 1 " \
+ "@6 match out on ${epair_server}a .* Packets: 10 Bytes: 766 States: 1 " \
+ "@7 pass out on ${epair_server}a .* Packets: 10 Bytes: 766 States: 1 " \
+ ; do
+ grep -qE "${rule_regexp}" $rules || atf_fail "Rule regexp not found for '${rule_regexp}'"
+ done
+
+ # All tables have counters increased for In/Pass and Out/Pass, not XPass nor Block.
+ table_counters="Evaluations: NoMatch: 0 Match: 1 In/Block: Packets: 0 Bytes: 0 In/Pass: Packets: 4 Bytes: 311 In/XPass: Packets: 0 Bytes: 0 Out/Block: Packets: 0 Bytes: 0 Out/Pass: Packets: 6 Bytes: 455 Out/XPass: Packets: 0 Bytes: 0"
+ for table_test in \
+ "tbl_src_match1___${table_counters}" \
+ "tbl_dst_match1___${table_counters}" \
+ "tbl_src_match2___${table_counters}" \
+ "tbl_dst_match2___${table_counters}" \
+ "tbl_src_match3___${table_counters}" \
+ "tbl_dst_match3___${table_counters}" \
+ "tbl_src_pass___${table_counters}" \
+ "tbl_dst_pass___${table_counters}" \
+ ; do
+ table_name=${table_test%%___*}
+ table_regexp=${table_test##*___}
+ table=$(mktemp) || exit 1
+ cat $tables | grep -A10 $table_name | tr '\n' ' ' | awk '{gsub("[\\[\\]]", " ", $0); gsub("[[:blank:]]+"," ",$0); print $0}' > ${table}
+ grep -qE "${table_regexp}" ${table} || atf_fail "Bad counters for table ${table_name}"
+ done;
+
+ for state_regexp in \
+ "${epair_server}a tcp ${net_server_host_tester}.* -> ${net_server_host_server}.* 6:4 pkts, 455:311 bytes, rule 7, " \
+ ; do
+ grep -qE "${state_regexp}" $states || atf_fail "State not found for '${state_regexp}'"
+ done
+}
+
+nat_match_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "nat_pass" "cleanup"
+nat_pass_head()
+{
+ atf_set descr 'Counters on match, and pass with NAT rules'
+ atf_set require.user root
+}
+
+nat_pass_body()
+{
+ setup_router_server_ipv6
+
+ # NAT is applied on the "pass" rule which also creates the state.
+ # All rules match on pre-NAT addresses.
+ pft_set_rules router \
+ "set state-policy if-bound" \
+ "table <tbl_src_match> { ${net_tester_host_tester} }" \
+ "table <tbl_dst_match> { ${net_server_host_server} }" \
+ "table <tbl_src_pass> { ${net_tester_host_tester} }" \
+ "table <tbl_dst_pass> { ${net_server_host_server} }" \
+ "block" \
+ "pass inet6 proto icmp6 icmp6-type { neighbrsol, neighbradv }" \
+ "pass in on ${epair_tester}b inet6 proto tcp keep state" \
+ "match out on ${epair_server}a inet6 proto tcp from <tbl_src_match> to <tbl_dst_match> scrub (random-id)" \
+ "pass out on ${epair_server}a inet6 proto tcp from <tbl_src_pass> to <tbl_dst_pass> nat-to ${net_server_host_router} keep state"
+
+ # Use a real TCP connection so that it will be properly closed, guaranteeing the amount of packets.
+ atf_check -s exit:0 -o match:"This is a test" -x \
+ "echo 'This is a test' | nc -w3 ${net_server_host_server} echo"
+ sleep 1
+ get_counters
+
+ for rule_regexp in \
+ "@4 match out on ${epair_server}a .* Packets: 10 Bytes: 766 States: 1 " \
+ "@5 pass out on ${epair_server}a .* Packets: 10 Bytes: 766 States: 1 " \
+ ; do
+ grep -qE "${rule_regexp}" $rules || atf_fail "Rule regexp not found for '${rule_regexp}'"
+ done
+
+ table_counters="Evaluations: NoMatch: 0 Match: 1 In/Block: Packets: 0 Bytes: 0 In/Pass: Packets: 4 Bytes: 311 In/XPass: Packets: 0 Bytes: 0 Out/Block: Packets: 0 Bytes: 0 Out/Pass: Packets: 6 Bytes: 455 Out/XPass: Packets: 0 Bytes: 0"
+ for table_test in \
+ "tbl_src_match___${table_counters}" \
+ "tbl_dst_match___${table_counters}" \
+ "tbl_src_pass___${table_counters}" \
+ "tbl_dst_pass___${table_counters}" \
+ ; do
+ table_name=${table_test%%___*}
+ table_regexp=${table_test##*___}
+ table=$(mktemp) || exit 1
+ cat $tables | grep -A10 $table_name | tr '\n' ' ' | awk '{gsub("[\\[\\]]", " ", $0); gsub("[[:blank:]]+"," ",$0); print $0}' > ${table}
+ grep -qE "${table_regexp}" ${table} || atf_fail "Bad counters for table ${table_name}"
+ done;
+
+ for state_regexp in \
+ "${epair_server}a tcp ${net_server_host_router}.* -> ${net_server_host_server}.* 6:4 pkts, 455:311 bytes, rule 5," \
+ ; do
+ grep -qE "${state_regexp}" $states || atf_fail "State not found for '${state_regexp}'"
+ done
+}
+
+nat_pass_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "rdr_match" "cleanup"
+rdr_match_head()
+{
+ atf_set descr 'Counters on match with RDR and pass rules'
+ atf_set require.user root
+}
+
+rdr_match_body()
+{
+ setup_router_server_ipv6
+
+ # Similar to the nat_match test but for the RDR action.
+ # Hopefully we don't need all other tests duplicated for RDR.
+ # Send traffic to a non-existing host, RDR it to the server.
+ #
+ # The "match" rule up to and including the RDR rule match on pre-RDR dst address.
+ # The "match" rule after NAT matches on post-RDR dst address.
+ # The "pass" rule matches on post-RDR dst address.
+ net_server_host_notserver=${net_server_host_server%%::*}::3
+ pft_set_rules router \
+ "set state-policy if-bound" \
+ "table <tbl_src_match1> { ${net_tester_host_tester} }" \
+ "table <tbl_dst_match1> { ${net_server_host_notserver} }" \
+ "table <tbl_src_match2> { ${net_tester_host_tester} }" \
+ "table <tbl_dst_match2> { ${net_server_host_notserver} }" \
+ "table <tbl_src_match3> { ${net_tester_host_tester} }" \
+ "table <tbl_dst_match3> { ${net_server_host_server} }" \
+ "table <tbl_src_pass> { ${net_tester_host_tester} }" \
+ "table <tbl_dst_pass> { ${net_server_host_server} }" \
+ "block" \
+ "pass inet6 proto icmp6 icmp6-type { neighbrsol, neighbradv }" \
+ "pass out on ${epair_server}a inet6 proto tcp keep state" \
+ "match in on ${epair_tester}b inet6 proto tcp from <tbl_src_match1> to <tbl_dst_match1> scrub (random-id)" \
+ "match in on ${epair_tester}b inet6 proto tcp from <tbl_src_match2> to <tbl_dst_match2> rdr-to ${net_server_host_server}" \
+ "match in on ${epair_tester}b inet6 proto tcp from <tbl_src_match3> to <tbl_dst_match3> scrub (random-id)" \
+ "pass in on ${epair_tester}b inet6 proto tcp from <tbl_src_pass> to <tbl_dst_pass> keep state"
+
+ # Use a real TCP connection so that it will be properly closed, guaranteeing the amount of packets.
+ atf_check -s exit:0 -o match:"This is a test" -x \
+ "echo 'This is a test' | nc -w3 ${net_server_host_notserver} echo"
+ sleep 1
+ get_counters
+
+ for rule_regexp in \
+ "@4 match in on ${epair_tester}b .* Packets: 10 Bytes: 766 States: 1 " \
+ "@5 match in on ${epair_tester}b .* Packets: 10 Bytes: 766 States: 1 " \
+ "@6 match in on ${epair_tester}b .* Packets: 10 Bytes: 766 States: 1 " \
+ "@7 pass in on ${epair_tester}b .* Packets: 10 Bytes: 766 States: 1 " \
+ ; do
+ grep -qE "${rule_regexp}" $rules || atf_fail "Rule regexp not found for '${rule_regexp}'"
+ done
+
+ # All tables have counters increased for In/Pass and Out/Pass, not XPass nor Block.
+ table_counters="Evaluations: NoMatch: 0 Match: 1 In/Block: Packets: 0 Bytes: 0 In/Pass: Packets: 6 Bytes: 455 In/XPass: Packets: 0 Bytes: 0 Out/Block: Packets: 0 Bytes: 0 Out/Pass: Packets: 4 Bytes: 311 Out/XPass: Packets: 0 Bytes: 0"
+ for table_test in \
+ "tbl_src_match1___${table_counters}" \
+ "tbl_dst_match1___${table_counters}" \
+ "tbl_src_match2___${table_counters}" \
+ "tbl_dst_match2___${table_counters}" \
+ "tbl_src_match3___${table_counters}" \
+ "tbl_dst_match3___${table_counters}" \
+ "tbl_src_pass___${table_counters}" \
+ "tbl_dst_pass___${table_counters}" \
+ ; do
+ table_name=${table_test%%___*}
+ table_regexp=${table_test##*___}
+ table=$(mktemp) || exit 1
+ cat $tables | grep -A10 $table_name | tr '\n' ' ' | awk '{gsub("[\\[\\]]", " ", $0); gsub("[[:blank:]]+"," ",$0); print $0}' > ${table}
+ grep -qE "${table_regexp}" ${table} || atf_fail "Bad counters for table ${table_name}"
+ done;
+
+ for state_regexp in \
+ "${epair_tester}b tcp ${net_server_host_server}.* 6:4 pkts, 455:311 bytes, rule 7, " \
+ ; do
+ grep -qE "${state_regexp}" $states || atf_fail "State not found for '${state_regexp}'"
+ done
+}
+
+rdr_match_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "nat64_in" "cleanup"
+nat64_in_head()
+{
+ atf_set descr 'Counters on match and inbound af-to rules'
+ atf_set require.user root
+}
+
+nat64_in_body()
+{
+ setup_router_server_nat64
+
+ pft_set_rules router \
+ "set state-policy if-bound" \
+ "table <tbl_src_match> { ${net_tester_6_host_tester} }" \
+ "table <tbl_dst_match> { 64:ff9b::${net_server1_4_host_server} }" \
+ "table <tbl_src_pass> { ${net_tester_6_host_tester} }" \
+ "table <tbl_dst_pass> { 64:ff9b::${net_server1_4_host_server} }" \
+ "block log" \
+ "pass inet6 proto icmp6 icmp6-type { neighbrsol, neighbradv }" \
+ "match in on ${epair_tester}b inet6 proto tcp from <tbl_src_match> to <tbl_dst_match> scrub (random-id)" \
+ "pass in on ${epair_tester}b inet6 proto tcp from <tbl_src_pass> to <tbl_dst_pass> \
+ af-to inet from (${epair_server1}a) \
+ keep state"
+
+ # Use a real TCP connection so that it will be properly closed, guaranteeing the amount of packets.
+ atf_check -s exit:0 -o match:"This is a test" -x \
+ "echo 'This is a test' | nc -w3 64:ff9b::${net_server1_4_host_server} echo"
+ sleep 1
+ get_counters
+
+ # The amount of packets is counted properly but sizes are not because
+ # pd->tot_len is always post-nat, even when updating pre-nat counters.
+ for rule_regexp in \
+ "@3 match in on ${epair_tester}b .* Packets: 10 Bytes: 686 States: 1 " \
+ "@4 pass in on ${epair_tester}b .* Packets: 10 Bytes: 686 States: 1 " \
+ ; do
+ grep -qE "${rule_regexp}" $rules || atf_fail "Rule regexp not found for '${rule_regexp}'"
+ done
+
+ # All tables have counters increased for In/Pass and Out/Pass, not XPass nor Block.
+ table_counters="Evaluations: NoMatch: 0 Match: 1 In/Block: Packets: 0 Bytes: 0 In/Pass: Packets: 4 Bytes: 231 In/XPass: Packets: 0 Bytes: 0 Out/Block: Packets: 0 Bytes: 0 Out/Pass: Packets: 6 Bytes: 455 Out/XPass: Packets: 0 Bytes: 0"
+ for table_test in \
+ "tbl_src_match___${table_counters}" \
+ "tbl_dst_match___${table_counters}" \
+ "tbl_src_pass___${table_counters}" \
+ "tbl_dst_pass___${table_counters}" \
+ ; do
+ table_name=${table_test%%___*}
+ table_regexp=${table_test##*___}
+ table=$(mktemp) || exit 1
+ cat $tables | grep -A10 $table_name | tr '\n' ' ' | awk '{gsub("[\\[\\]]", " ", $0); gsub("[[:blank:]]+"," ",$0); print $0}' > ${table}
+ grep -qE "${table_regexp}" ${table} || atf_fail "Bad counters for table ${table_name}"
+ done;
+
+ for state_regexp in \
+ "${epair_server1}a tcp ${net_server_host_tester}.* 6:4 pkts, 455:231 bytes, rule 4, " \
+ ; do
+ grep -qE "${state_regexp}" $states || atf_fail "State not found for '${state_regexp}'"
+ done
+
+ echo " === interfaces === "
+ echo " === tester === "
+ jexec router pfctl -qvvsI -i ${epair_tester}b
+ echo " === server === "
+ jexec router pfctl -qvvsI -i ${epair_server1}a
+ echo " === "
+}
+
+nat64_in_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "nat64_out" "cleanup"
+nat64_out_head()
+{
+ atf_set descr 'Counters on match and outbound af-to rules'
+ atf_set require.user root
+}
+
+nat64_out_body()
+{
+ setup_router_server_nat64
+
+ # af-to in outbound path requires routes for the pre-af-to traffic.
+ jexec router route add -inet6 64:ff9b::/96 -iface ${epair_server1}a
+
+ pft_set_rules router \
+ "set state-policy if-bound" \
+ "table <tbl_src_match> { ${net_tester_6_host_tester} }" \
+ "table <tbl_dst_match> { 64:ff9b::${net_server1_4_host_server} }" \
+ "table <tbl_src_pass> { ${net_tester_6_host_tester} }" \
+ "table <tbl_dst_pass> { 64:ff9b::${net_server1_4_host_server} }" \
+ "block log " \
+ "pass inet6 proto icmp6 icmp6-type { neighbrsol, neighbradv }" \
+ "pass in on ${epair_tester}b inet6 proto tcp keep state" \
+ "match out on ${epair_server1}a inet6 proto tcp from <tbl_src_match> to <tbl_dst_match> scrub (random-id)" \
+ "pass out on ${epair_server1}a inet6 proto tcp from <tbl_src_pass> to <tbl_dst_pass> \
+ af-to inet from (${epair_server1}a) \
+ keep state"
+
+ # Use a real TCP connection so that it will be properly closed, guaranteeing the amount of packets.
+ atf_check -s exit:0 -o match:"This is a test" -x \
+ "echo 'This is a test' | nc -w3 64:ff9b::${net_server1_4_host_server} echo"
+ sleep 1
+ get_counters
+
+ for rule_regexp in \
+ "@4 match out on ${epair_server1}a .* Packets: 10 Bytes: 686 States: 1 " \
+ "@5 pass out on ${epair_server1}a .* Packets: 10 Bytes: 686 States: 1 " \
+ ; do
+ grep -qE "${rule_regexp}" $rules || atf_fail "Rule regexp not found for '${rule_regexp}'"
+ done
+
+ # All tables have counters increased for In/Pass and Out/Pass, not XPass nor Block.
+ table_counters="Evaluations: NoMatch: 0 Match: 1 In/Block: Packets: 0 Bytes: 0 In/Pass: Packets: 4 Bytes: 231 In/XPass: Packets: 0 Bytes: 0 Out/Block: Packets: 0 Bytes: 0 Out/Pass: Packets: 6 Bytes: 455 Out/XPass: Packets: 0 Bytes: 0"
+ for table_test in \
+ "tbl_src_match___${table_counters}" \
+ "tbl_dst_match___${table_counters}" \
+ "tbl_src_pass___${table_counters}" \
+ "tbl_dst_pass___${table_counters}" \
+ ; do
+ table_name=${table_test%%___*}
+ table_regexp=${table_test##*___}
+ table=$(mktemp) || exit 1
+ cat $tables | grep -A10 $table_name | tr '\n' ' ' | awk '{gsub("[\\[\\]]", " ", $0); gsub("[[:blank:]]+"," ",$0); print $0}' > ${table}
+ grep -qE "${table_regexp}" ${table} || atf_fail "Bad counters for table ${table_name}"
+ done;
+
+ for state_regexp in \
+ "${epair_server1}a tcp 198.51.100.17:[0-9]+ \(64:ff9b::c633:6412\[7\]\) -> 198.51.100.18:7 \(2001:db8:4200::2\[[0-9]+\]\) .* 6:4 pkts, 455:231 bytes, rule 5," \
+ ; do
+ grep -qE "${state_regexp}" $states || atf_fail "State not found for '${state_regexp}'"
+ done
+
+ echo " === interfaces === "
+ echo " === tester === "
+ jexec router pfctl -qvvsI -i ${epair_tester}b
+ echo " === server === "
+ jexec router pfctl -qvvsI -i ${epair_server1}a
+ echo " === "
+}
+
+nat64_out_cleanup()
+{
+ pft_cleanup
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case "match_pass_state"
+ atf_add_test_case "match_pass_no_state"
+ atf_add_test_case "match_block"
+ atf_add_test_case "match_fail"
+ atf_add_test_case "nat_natonly"
+ atf_add_test_case "nat_nat"
+ atf_add_test_case "nat_match"
+ atf_add_test_case "nat_pass"
+ atf_add_test_case "rdr_match"
+ atf_add_test_case "nat64_in"
+ atf_add_test_case "nat64_out"
+}
diff --git a/tests/sys/netpfil/pf/ether.sh b/tests/sys/netpfil/pf/ether.sh
index f0fdce50a7d3..f15dff06f9cd 100644
--- a/tests/sys/netpfil/pf/ether.sh
+++ b/tests/sys/netpfil/pf/ether.sh
@@ -287,6 +287,7 @@ captive_body()
# Run the echo server only on the gw, so we know we've redirectly
# correctly if we get an echo message.
jexec gw /usr/sbin/inetd -p ${PWD}/echo_inetd.pid $(atf_get_srcdir)/echo_inetd.conf
+ sleep 1
# Confirm that we're getting redirected
atf_check -s exit:0 -o match:"^foo$" -x "echo foo | nc -N 198.51.100.2 7"
@@ -305,6 +306,7 @@ captive_body()
# Start a server in srv
jexec srv /usr/sbin/inetd -p ${PWD}/echo_inetd.pid $(atf_get_srcdir)/echo_inetd.conf
+ sleep 1
# And now we can talk to that one.
atf_check -s exit:0 -o match:"^foo$" -x "echo foo | nc -N 198.51.100.2 7"
@@ -364,6 +366,7 @@ captive_long_body()
jexec gw /usr/sbin/inetd -p ${PWD}/gw.pid $(atf_get_srcdir)/echo_inetd.conf
jexec srv /usr/sbin/inetd -p ${PWD}/srv.pid $(atf_get_srcdir)/daytime_inetd.conf
+ sleep p1
echo foo | nc -N 198.51.100.2 13
diff --git a/tests/sys/netpfil/pf/mld.py b/tests/sys/netpfil/pf/mld.py
index d118a34c8a7d..b3ef6c21b3de 100644
--- a/tests/sys/netpfil/pf/mld.py
+++ b/tests/sys/netpfil/pf/mld.py
@@ -32,23 +32,22 @@ from atf_python.sys.net.vnet import VnetTestTemplate
class TestMLD(VnetTestTemplate):
REQUIRED_MODULES = [ "pf" ]
TOPOLOGY = {
- "vnet1": {"ifaces": ["if1"]},
+ "vnet1": {"ifaces": ["if1"], "opts": ["allow.read_msgbuf"]},
"vnet2": {"ifaces": ["if1"]},
"if1": {"prefixes6": [("2001:db8::2/64", "2001:db8::1/64")]},
}
def vnet2_handler(self, vnet):
ifname = vnet.iface_alias_map["if1"].name
- #ToolsHelper.print_output("/sbin/pfctl -e")
+ ToolsHelper.print_output("/sbin/pfctl -e")
ToolsHelper.pf_rules([
"pass",
])
ToolsHelper.print_output("/sbin/pfctl -x loud")
- #ToolsHelper.print_output("echo \"j 230.0.0.1 %s\ns 3600\nq\" | /usr/sbin/mtest" % ifname)
def find_mld_reply(self, pkt, ifname):
pkt.show()
- s = DelayedSend(pkt)
+ s = DelayedSend(pkt, ifname)
found = False
packets = self.sp.sniff(iface=ifname, timeout=5)
@@ -66,7 +65,6 @@ class TestMLD(VnetTestTemplate):
def test_router_alert(self):
"""Verify that we allow MLD packets with router alert extension header"""
ifname = self.vnet.iface_alias_map["if1"].name
- #ToolsHelper.print_output("/sbin/ifconfig %s inet6 -ifdisable" % ifname)
ToolsHelper.print_output("/sbin/ifconfig")
# Import in the correct vnet, so at to not confuse Scapy
@@ -76,20 +74,17 @@ class TestMLD(VnetTestTemplate):
self.sp = sp
self.sc = sc
- # A correct MLD query gets a reply
- pkt = sp.IPv6(src="fe80::1%%%s" % ifname, dst="ff02::1", hlim=1) \
- / sp.RouterAlert(value=0) \
+ # MLD packets with an incorrect hop limit get dropped.
+ pkt = sp.Ether() \
+ / sp.IPv6(src="fe80::1%%%s" % ifname, dst="ff02::1", hlim=2) \
+ / sp.IPv6ExtHdrHopByHop(options=[ \
+ sp.RouterAlert(value=0) \
+ ]) \
/ sp.ICMPv6MLQuery2()
- assert self.find_mld_reply(pkt, ifname)
+ # We can't reliably test this by checking for a reply, because
+ # the other jail may just send a spontaneous MLD reply.
+ self.find_mld_reply(pkt, ifname)
- # The wrong extension header does not
- pkt = sp.IPv6(src="fe80::1%%%s" % ifname, dst="ff02::1", hlim=1) \
- / sp.IPv6ExtHdrRouting() \
- / sp.ICMPv6MLQuery2()
- assert not self.find_mld_reply(pkt, ifname)
-
- # Neither does an incorrect hop limit
- pkt = sp.IPv6(src="fe80::1%%%s" % ifname, dst="ff02::1", hlim=2) \
- / sp.RouterAlert(value=0) \
- / sp.ICMPv6MLQuery2()
- assert not self.find_mld_reply(pkt, ifname)
+ # Check if we logged dropping the MLD paacket
+ dmesg = ToolsHelper.get_output("/sbin/dmesg")
+ assert dmesg.find("Invalid MLD") != -1
diff --git a/tests/sys/netpfil/pf/nat.sh b/tests/sys/netpfil/pf/nat.sh
index 170d813d57fe..1ef87cee3598 100644
--- a/tests/sys/netpfil/pf/nat.sh
+++ b/tests/sys/netpfil/pf/nat.sh
@@ -55,6 +55,9 @@ exhaust_body()
jexec echo ifconfig ${epair_echo}b 198.51.100.2/24 up
jexec echo /usr/sbin/inetd -p ${PWD}/inetd-echo.pid $(atf_get_srcdir)/echo_inetd.conf
+ # Disable checksum offload on one of the interfaces to ensure pf handles that
+ jexec nat ifconfig ${epair_nat}a -txcsum
+
# Enable pf!
jexec nat pfctl -e
pft_set_rules nat \
@@ -474,6 +477,7 @@ no_addrs_random_cleanup()
pft_cleanup
}
+atf_test_case "nat_pass" "cleanup"
nat_pass_head()
{
atf_set descr 'IPv4 NAT on pass rule'
@@ -505,6 +509,7 @@ nat_pass_cleanup()
pft_cleanup
}
+atf_test_case "nat_match" "cleanup"
nat_match_head()
{
atf_set descr 'IPv4 NAT on match rule'
@@ -644,6 +649,7 @@ map_e_pass_cleanup()
pft_cleanup
}
+atf_test_case "binat_compat" "cleanup"
binat_compat_head()
{
atf_set descr 'IPv4 BINAT with nat ruleset'
@@ -710,6 +716,7 @@ binat_compat_cleanup()
kill $(cat ${PWD}/inetd_tester.pid)
}
+atf_test_case "binat_match" "cleanup"
binat_match_head()
{
atf_set descr 'IPv4 BINAT with nat ruleset'
@@ -838,7 +845,7 @@ dummynet_mask_body()
jexec gw dnctl pipe 1 config delay 100 mask src-ip 0xffffff00
jexec gw pfctl -e
pft_set_rules gw \
- "nat pass on ${epair_srv}b inet from 192.0.2.0/24 to any -> (${epair_srv}b)" \
+ "nat on ${epair_srv}b inet from 192.0.2.0/24 to any -> (${epair_srv}b)" \
"pass out dnpipe 1"
atf_check -s exit:0 -o ignore \
diff --git a/tests/sys/netpfil/pf/nat64.sh b/tests/sys/netpfil/pf/nat64.sh
index d930e2ee5763..6631e3eca2c7 100644
--- a/tests/sys/netpfil/pf/nat64.sh
+++ b/tests/sys/netpfil/pf/nat64.sh
@@ -213,12 +213,14 @@ tcp_in_if_bound_body()
atf_fail "Failed to connect to TCP server"
fi
+ sleep 1
+
# Interfaces of the state are reversed when doing inbound NAT64!
- # FIXME: Packets counters seem wrong!
+ # FIXME: Packets from both directions are counted only on the inbound direction!
states=$(mktemp) || exit 1
jexec rtr pfctl -qvvss | normalize_pfctl_s > $states
for state_regexp in \
- "${epair_link}a tcp 192.0.2.1:[0-9]+ \(2001:db8::2\[[0-9]+\]\) -> 192.0.2.2:1234 \(64:ff9b::c000:202\[1234\]\) .* 9:9 pkts.* rule 3 .* origif: ${epair}b" \
+ "${epair_link}a tcp 192.0.2.1:[0-9]+ \(2001:db8::2\[[0-9]+\]\) -> 192.0.2.2:1234 \(64:ff9b::c000:202\[1234\]\) .* 5:4 pkts.* rule 3 .* origif: ${epair}b" \
; do
grep -qE "${state_regexp}" $states || atf_fail "State not found for '${state_regexp}'"
done
@@ -254,6 +256,8 @@ tcp_out_if_bound_body()
atf_fail "Failed to connect to TCP server"
fi
+ sleep 1
+
# Origif is not printed when identical as if.
states=$(mktemp) || exit 1
jexec rtr pfctl -qvvss | normalize_pfctl_s > $states
@@ -295,12 +299,14 @@ tcp_in_floating_body()
atf_fail "Failed to connect to TCP server"
fi
+ sleep 1
+
# Interfaces of the state are reversed when doing inbound NAT64!
- # FIXME: Packets counters seem wrong!
+ # FIXME: Packets from both directions are counted only on the inbound direction!
states=$(mktemp) || exit 1
jexec rtr pfctl -qvvss | normalize_pfctl_s > $states
for state_regexp in \
- "all tcp 192.0.2.1:[0-9]+ \(2001:db8::2\[[0-9]+\]\) -> 192.0.2.2:1234 \(64:ff9b::c000:202\[1234\]\).* 9:9 pkts.* rule 3 .* origif: ${epair}b" \
+ "all tcp 192.0.2.1:[0-9]+ \(2001:db8::2\[[0-9]+\]\) -> 192.0.2.2:1234 \(64:ff9b::c000:202\[1234\]\).* 5:4 pkts.* rule 3 .* origif: ${epair}b" \
; do
grep -qE "${state_regexp}" $states || atf_fail "State not found for '${state_regexp}'"
done
@@ -336,6 +342,8 @@ tcp_out_floating_body()
atf_fail "Failed to connect to TCP server"
fi
+ sleep 1
+
# Origif is not printed when identical as if.
states=$(mktemp) || exit 1
jexec rtr pfctl -qvvss | normalize_pfctl_s > $states
@@ -1011,20 +1019,19 @@ route_to_body()
pft_init
epair_link=$(vnet_mkepair)
- epair_null=$(vnet_mkepair)
epair=$(vnet_mkepair)
ifconfig ${epair}a inet6 2001:db8::2/64 up no_dad
route -6 add default 2001:db8::1
- vnet_mkjail rtr ${epair}b ${epair_link}a ${epair_null}a
+ vnet_mkjail rtr ${epair}b ${epair_link}a
jexec rtr ifconfig ${epair}b inet6 2001:db8::1/64 up no_dad
- jexec rtr ifconfig ${epair_null}a 192.0.2.3/24 up
jexec rtr ifconfig ${epair_link}a 192.0.2.1/24 up
vnet_mkjail dst ${epair_link}b
jexec dst ifconfig ${epair_link}b 192.0.2.2/24 up
- jexec dst route add default 192.0.2.1
+ jexec dst ifconfig lo0 203.0.113.1/32 alias
+ jexec dst route add default 192.0.2.2
# Sanity checks
atf_check -s exit:0 -o ignore \
@@ -1033,8 +1040,9 @@ route_to_body()
jexec rtr pfctl -e
pft_set_rules rtr \
"set reassemble yes" \
+ "set debug loud" \
"set state-policy if-bound" \
- "block" \
+ "block log (all)" \
"pass inet6 proto icmp6 icmp6-type { neighbrsol, neighbradv }" \
"pass in on ${epair}b route-to (${epair_link}a 192.0.2.2) inet6 from any to 64:ff9b::/96 af-to inet from (${epair_link}a)"
@@ -1044,9 +1052,11 @@ route_to_body()
states=$(mktemp) || exit 1
jexec rtr pfctl -qvvss | normalize_pfctl_s > $states
+ cat $states
+ # Interfaces of the state are reversed when doing inbound NAT64!
for state_regexp in \
- "${epair_link}a ipv6-icmp 192.0.2.1:.* \(2001:db8::2\[[0-9]+\]\) -> 192.0.2.2:8 \(64:ff9b::c000:202\[[0-9]+\]\).*6:6 pkts.*route-to: 192.0.2.2@${epair_link}a origif: ${epair}b" \
+ "${epair_link}a ipv6-icmp 192.0.2.1:.* \(2001:db8::2\[[0-9]+\]\) -> 192.0.2.2:8 \(64:ff9b::c000:202\[[0-9]+\]\).* 3:3 pkts.*route-to: 192.0.2.2@${epair_link}a origif: ${epair}b" \
; do
grep -qE "${state_regexp}" $states || atf_fail "State not found for '${state_regexp}'"
done
diff --git a/tests/sys/netpfil/pf/rdr.sh b/tests/sys/netpfil/pf/rdr.sh
index f7c920bbfa8f..b0f0e6d13d0f 100644
--- a/tests/sys/netpfil/pf/rdr.sh
+++ b/tests/sys/netpfil/pf/rdr.sh
@@ -281,10 +281,119 @@ srcport_pass_cleanup()
pft_cleanup
}
+atf_test_case "natpass" "cleanup"
+natpass_head()
+{
+ atf_set descr 'Test rdr pass'
+ atf_set require.user root
+}
+
+natpass_body()
+{
+ pft_init
+
+ epair=$(vnet_mkepair)
+ epair_link=$(vnet_mkepair)
+
+ ifconfig ${epair}a 192.0.2.2/24 up
+
+ vnet_mkjail alcatraz ${epair}b ${epair_link}a
+ jexec alcatraz ifconfig lo0 inet 127.0.0.1/8 up
+ jexec alcatraz ifconfig ${epair}b inet 192.0.2.1/24 up
+ jexec alcatraz ifconfig ${epair_link}a 198.51.100.1/24 up
+ jexec alcatraz sysctl net.inet.ip.forwarding=1
+
+ vnet_mkjail srv ${epair_link}b
+ jexec srv ifconfig ${epair_link}b inet 198.51.100.2/24 up
+ jexec srv route add default 198.51.100.1
+
+ # Sanity check
+ atf_check -s exit:0 -o ignore \
+ ping -c 1 192.0.2.1
+ atf_check -s exit:0 -o ignore \
+ jexec alcatraz ping -c 1 198.51.100.2
+
+ jexec alcatraz pfctl -e
+ pft_set_rules alcatraz \
+ "rdr pass on ${epair}b proto udp from any to 192.0.2.1 port 80 -> 198.51.100.2" \
+ "nat on ${epair}b inet from 198.51.100.0/24 to any -> 192.0.2.1" \
+ "block in proto udp from any to any port 80" \
+ "pass in proto icmp"
+
+ echo "foo" | jexec srv nc -u -l 80 &
+ sleep 1 # Give the above a moment to start
+
+ out=$(echo 1 | nc -u -w 1 192.0.2.1 80)
+ echo "out ${out}"
+ if [ "${out}" != "foo" ];
+ then
+ jexec alcatraz pfctl -sn -vv
+ jexec alcatraz pfctl -ss -vv
+ atf_fail "rdr failed"
+ fi
+}
+
+natpass_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "pr290177" "cleanup"
+pr290177_head()
+{
+ atf_set descr 'Test PR290177'
+ atf_set require.user root
+}
+
+pr290177_body()
+{
+ pft_init
+
+ epair=$(vnet_mkepair)
+
+ ifconfig ${epair}a 192.0.2.2/24 up
+ ifconfig ${epair}a inet alias 192.0.2.3/24 up
+
+ vnet_mkjail alcatraz ${epair}b
+ jexec alcatraz ifconfig ${epair}b 192.0.2.1/24 up
+ jexec alcatraz ifconfig lo0 127.0.0.1/8 up
+
+ # Sanity check
+ atf_check -s exit:0 -o ignore \
+ ping -c 1 192.0.2.1
+
+ jexec alcatraz pfctl -e
+ pft_set_rules alcatraz \
+ "table <white> { 192.0.2.2 }" \
+ "no rdr inet proto tcp from <white> to any port 25" \
+ "rdr pass inet proto tcp from any to any port 25 -> 127.0.0.1 port 2500"
+
+ echo foo | jexec alcatraz nc -N -l 2500 &
+ sleep 1
+
+ reply=$(nc -w 3 -s 192.0.2.2 192.0.2.1 25)
+ if [ "${reply}" == "foo" ]
+ then
+ atf_fail "no rdr rule failed"
+ fi
+ reply=$(nc -w 3 -s 192.0.2.3 192.0.2.1 25)
+ if [ "${reply}" != "foo" ]
+ then
+ atf_fail "rdr rule failed"
+ fi
+}
+
+pr290177_cleanup()
+{
+ pft_cleanup
+}
+
atf_init_test_cases()
{
+ atf_add_test_case "natpass"
atf_add_test_case "tcp_v6_compat"
atf_add_test_case "tcp_v6_pass"
atf_add_test_case "srcport_compat"
atf_add_test_case "srcport_pass"
+ atf_add_test_case "pr290177"
}
diff --git a/tests/sys/netpfil/pf/rules_counter.sh b/tests/sys/netpfil/pf/rules_counter.sh
index 98f96a7adca1..e80a46e9d6c6 100644
--- a/tests/sys/netpfil/pf/rules_counter.sh
+++ b/tests/sys/netpfil/pf/rules_counter.sh
@@ -153,6 +153,7 @@ atf_test_case "4G" "cleanup"
{
atf_set descr 'Test keepcounter for values above 32 bits'
atf_set require.user root
+ atf_set timeout 900
}
4G_body()
diff --git a/tests/sys/netpfil/pf/sctp.sh b/tests/sys/netpfil/pf/sctp.sh
index 78055f5a9dd2..47bf40181b1b 100644
--- a/tests/sys/netpfil/pf/sctp.sh
+++ b/tests/sys/netpfil/pf/sctp.sh
@@ -29,9 +29,6 @@
sctp_init()
{
pft_init
- if ! kldstat -q -m sctp; then
- atf_skip "This test requires SCTP"
- fi
}
atf_test_case "basic_v4" "cleanup"
@@ -39,6 +36,7 @@ basic_v4_head()
{
atf_set descr 'Basic SCTP connection over IPv4 passthrough'
atf_set require.user root
+ atf_set require.kmods sctp
}
basic_v4_body()
@@ -112,6 +110,7 @@ basic_v6_head()
{
atf_set descr 'Basic SCTP connection over IPv6'
atf_set require.user root
+ atf_set require.kmods sctp
}
basic_v6_body()
@@ -186,6 +185,7 @@ reuse_head()
{
atf_set descr 'Test handling dumb clients that reuse source ports'
atf_set require.user root
+ atf_set require.kmods sctp
}
reuse_body()
@@ -244,6 +244,7 @@ abort_v4_head()
{
atf_set descr 'Test sending ABORT messages'
atf_set require.user root
+ atf_set require.kmods sctp
}
abort_v4_body()
@@ -302,6 +303,7 @@ abort_v6_head()
{
atf_set descr 'Test sending ABORT messages over IPv6'
atf_set require.user root
+ atf_set require.kmods sctp
}
abort_v6_body()
@@ -360,6 +362,7 @@ nat_v4_head()
{
atf_set descr 'Test NAT-ing SCTP over IPv4'
atf_set require.user root
+ atf_set require.kmods sctp
}
nat_v4_body()
@@ -412,6 +415,7 @@ nat_v6_head()
{
atf_set descr 'Test NAT-ing SCTP over IPv6'
atf_set require.user root
+ atf_set require.kmods sctp
}
nat_v6_body()
@@ -464,6 +468,7 @@ rdr_v4_head()
{
atf_set descr 'Test rdr SCTP over IPv4'
atf_set require.user root
+ atf_set require.kmods sctp
}
rdr_v4_body()
@@ -531,6 +536,7 @@ pfsync_head()
{
atf_set descr 'Test pfsync-ing SCTP connections'
atf_set require.user root
+ atf_set require.kmods carp sctp
}
pfsync_body()
@@ -563,10 +569,6 @@ pfsync_body()
sctp_init
pfsynct_init
vnet_init_bridge
- if ! kldstat -q -m carp
- then
- atf_skip "This test requires carp"
- fi
j="sctp:pfsync"
@@ -722,6 +724,7 @@ timeout_head()
{
atf_set descr 'Test setting and retrieving timeout values'
atf_set require.user root
+ atf_set require.kmods sctp
}
timeout_body()
@@ -753,6 +756,7 @@ related_icmp_head()
{
atf_set descr 'Verify that ICMP messages related to an SCTP connection are allowed'
atf_set require.user root
+ atf_set require.kmods sctp
}
related_icmp_body()
diff --git a/tests/sys/netpfil/pf/syncookie.sh b/tests/sys/netpfil/pf/syncookie.sh
index fad90f3b2618..598ac17c67f5 100644
--- a/tests/sys/netpfil/pf/syncookie.sh
+++ b/tests/sys/netpfil/pf/syncookie.sh
@@ -253,6 +253,9 @@ Creativity, no.
__EOF__
nc -l $addr $port >out &
+ # Give the background nc time to start
+ sleep 1
+
atf_check nc -N $addr $port < in
atf_check -o file:in cat out
diff --git a/tests/sys/netpfil/pf/utils.subr b/tests/sys/netpfil/pf/utils.subr
index a48f26653f8c..8b3b06bf3bba 100644
--- a/tests/sys/netpfil/pf/utils.subr
+++ b/tests/sys/netpfil/pf/utils.subr
@@ -217,6 +217,7 @@ setup_router_server_ipv4()
jexec server route add -net ${net_tester} ${net_server_host_router}
inetd_conf=$(mktemp)
echo "discard stream tcp nowait root internal" > $inetd_conf
+ echo "echo stream tcp nowait root internal" >> $inetd_conf
jexec server inetd -p ${PWD}/inetd.pid $inetd_conf
}
@@ -271,6 +272,7 @@ setup_router_server_ipv6()
jexec server route add -6 ${net_tester} ${net_server_host_router}
inetd_conf=$(mktemp)
echo "discard stream tcp6 nowait root internal" > $inetd_conf
+ echo "echo stream tcp6 nowait root internal" >> $inetd_conf
jexec server inetd -p ${PWD}/inetd.pid $inetd_conf
}
@@ -353,6 +355,7 @@ setup_router_server_nat64()
inetd_conf=$(mktemp)
echo "discard stream tcp46 nowait root internal" >> $inetd_conf
+ echo "echo stream tcp46 nowait root internal" >> $inetd_conf
vnet_mkjail server1 ${epair_server1}b
jexec server1 /etc/rc.d/netif start lo0