aboutsummaryrefslogtreecommitdiff
path: root/tests/sys
diff options
context:
space:
mode:
Diffstat (limited to 'tests/sys')
-rw-r--r--tests/sys/cam/ctl/ctl.subr9
-rw-r--r--tests/sys/fs/fusefs/Makefile3
-rw-r--r--tests/sys/kern/Makefile2
-rw-r--r--tests/sys/kern/exterr_test.c108
-rw-r--r--tests/sys/kern/getdirentries_test.c172
-rw-r--r--tests/sys/kern/inotify_test.c6
-rw-r--r--tests/sys/mac/bsdextended/Makefile1
-rw-r--r--tests/sys/mac/bsdextended/matches_test.sh3
-rw-r--r--tests/sys/mac/portacl/Makefile1
-rwxr-xr-xtests/sys/net/if_bridge_test.sh28
-rw-r--r--tests/sys/net/if_ovpn/if_ovpn.sh258
-rwxr-xr-xtests/sys/net/if_vlan.sh27
-rwxr-xr-xtests/sys/netinet6/addr6.sh25
-rw-r--r--tests/sys/netpfil/common/dummynet.sh4
-rw-r--r--tests/sys/netpfil/pf/Makefile2
-rw-r--r--tests/sys/netpfil/pf/anchor.sh61
-rw-r--r--tests/sys/netpfil/pf/forward.sh4
-rw-r--r--tests/sys/netpfil/pf/header.py23
-rw-r--r--tests/sys/netpfil/pf/icmp.py29
-rw-r--r--tests/sys/netpfil/pf/igmp.py95
-rw-r--r--tests/sys/netpfil/pf/ioctl/validation.c35
-rw-r--r--tests/sys/netpfil/pf/killstate.sh4
-rw-r--r--tests/sys/netpfil/pf/mbuf.sh6
-rw-r--r--tests/sys/netpfil/pf/mld.py95
-rw-r--r--tests/sys/netpfil/pf/nat.sh33
-rw-r--r--tests/sys/netpfil/pf/nat64.py56
-rw-r--r--tests/sys/netpfil/pf/pfsync.sh85
-rw-r--r--tests/sys/netpfil/pf/rdr.sh2
-rw-r--r--tests/sys/netpfil/pf/route_to.sh117
-rw-r--r--tests/sys/netpfil/pf/set_tos.sh4
-rw-r--r--tests/sys/netpfil/pf/table.sh29
-rw-r--r--tests/sys/netpfil/pf/utils.subr101
32 files changed, 1372 insertions, 56 deletions
diff --git a/tests/sys/cam/ctl/ctl.subr b/tests/sys/cam/ctl/ctl.subr
index 5da441b806f0..6cc02d774bdb 100644
--- a/tests/sys/cam/ctl/ctl.subr
+++ b/tests/sys/cam/ctl/ctl.subr
@@ -25,15 +25,6 @@
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-load_modules() {
- if ! kldstat -q -m ctl; then
- kldload ctl || atf_skip "could not load ctl kernel mod"
- fi
- if ! ctladm port -o on -p 0; then
- atf_skip "could not enable the camsim frontend"
- fi
-}
-
find_device() {
LUN=$1
diff --git a/tests/sys/fs/fusefs/Makefile b/tests/sys/fs/fusefs/Makefile
index b11f11bdfa98..a21512798597 100644
--- a/tests/sys/fs/fusefs/Makefile
+++ b/tests/sys/fs/fusefs/Makefile
@@ -70,7 +70,8 @@ TEST_METADATA.nfs+= required_user="root"
TEST_METADATA.ctl+= is_exclusive="true"
TEST_METADATA.ctl+= required_user="root"
-TEST_METADATA+= timeout=10
+TEST_METADATA+= timeout=10
+TEST_METADATA+= required_kmods="fusefs"
FUSEFS= ${SRCTOP}/sys/fs/fuse
# Suppress warnings that GCC generates for the libc++ and gtest headers.
diff --git a/tests/sys/kern/Makefile b/tests/sys/kern/Makefile
index 26c0013696c7..336e73f29835 100644
--- a/tests/sys/kern/Makefile
+++ b/tests/sys/kern/Makefile
@@ -17,7 +17,9 @@ ATF_TESTS_C+= kern_copyin
ATF_TESTS_C+= kern_descrip_test
# One test modifies the maxfiles limit, which can cause spurious test failures.
TEST_METADATA.kern_descrip_test+= is_exclusive="true"
+ATF_TESTS_C+= exterr_test
ATF_TESTS_C+= fdgrowtable_test
+ATF_TESTS_C+= getdirentries_test
ATF_TESTS_C+= jail_lookup_root
ATF_TESTS_C+= inotify_test
ATF_TESTS_C+= kill_zombie
diff --git a/tests/sys/kern/exterr_test.c b/tests/sys/kern/exterr_test.c
new file mode 100644
index 000000000000..17c84c1f8ed4
--- /dev/null
+++ b/tests/sys/kern/exterr_test.c
@@ -0,0 +1,108 @@
+/*-
+ * Copyright (C) 2025 ConnectWise, LLC. All rights reserved.
+ *
+ * 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.
+ */
+
+#include <sys/exterrvar.h>
+#include <sys/mman.h>
+
+#include <atf-c.h>
+#include <errno.h>
+#include <exterr.h>
+#include <stdio.h>
+
+ATF_TC(gettext_extended);
+ATF_TC_HEAD(gettext_extended, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Retrieve an extended error message");
+}
+ATF_TC_BODY(gettext_extended, tc)
+{
+ char exterr[UEXTERROR_MAXLEN];
+ int r;
+
+ /*
+ * Use an invalid call to mmap() because it supports extended error
+ * messages, requires no special resources, and does not need root.
+ */
+ ATF_CHECK_ERRNO(ENOTSUP,
+ mmap(NULL, 0, PROT_MAX(PROT_READ) | PROT_WRITE, 0, -1, 0));
+ r = uexterr_gettext(exterr, sizeof(exterr));
+ ATF_CHECK_EQ(0, r);
+ printf("Extended error: %s\n", exterr);
+ /* Note: error string may need to be updated due to kernel changes */
+ ATF_CHECK(strstr(exterr, "prot is not subset of max_prot") != 0);
+}
+
+ATF_TC(gettext_noextended);
+ATF_TC_HEAD(gettext_noextended, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Fail to retrieve an extended error message because none exists");
+}
+ATF_TC_BODY(gettext_noextended, tc)
+{
+ char exterr[UEXTERROR_MAXLEN];
+ int r;
+
+ ATF_CHECK_ERRNO(EINVAL, exterrctl(EXTERRCTL_UD, 0, NULL));
+ r = uexterr_gettext(exterr, sizeof(exterr));
+ ATF_CHECK_EQ(0, r);
+ ATF_CHECK_STREQ(exterr, "");
+}
+
+ATF_TC(gettext_noextended_after_extended);
+ATF_TC_HEAD(gettext_noextended_after_extended, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "uexterr_gettext should not return a stale extended error message");
+}
+ATF_TC_BODY(gettext_noextended_after_extended, tc)
+{
+ char exterr[UEXTERROR_MAXLEN];
+ int r;
+
+ /*
+ * First do something that will create an extended error message, but
+ * ignore it.
+ */
+ ATF_CHECK_ERRNO(ENOTSUP,
+ mmap(NULL, 0, PROT_MAX(PROT_READ) | PROT_WRITE, 0, -1, 0));
+
+ /* Then do something that won't create an extended error message */
+ ATF_CHECK_ERRNO(EINVAL, exterrctl(EXTERRCTL_UD, 0, NULL));
+
+ /* Hopefully we won't see the stale extended error message */
+ r = uexterr_gettext(exterr, sizeof(exterr));
+ ATF_CHECK_EQ(0, r);
+ ATF_CHECK_STREQ(exterr, "");
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, gettext_extended);
+ ATF_TP_ADD_TC(tp, gettext_noextended);
+ ATF_TP_ADD_TC(tp, gettext_noextended_after_extended);
+
+ return (atf_no_error());
+}
diff --git a/tests/sys/kern/getdirentries_test.c b/tests/sys/kern/getdirentries_test.c
new file mode 100644
index 000000000000..e66872ffe5b6
--- /dev/null
+++ b/tests/sys/kern/getdirentries_test.c
@@ -0,0 +1,172 @@
+/*-
+ * Copyright (c) 2025 Klara, Inc.
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <sys/stat.h>
+#include <sys/mount.h>
+
+#include <dirent.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <stdint.h>
+
+#include <atf-c.h>
+
+ATF_TC(getdirentries_ok);
+ATF_TC_HEAD(getdirentries_ok, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Successfully read a directory.");
+}
+ATF_TC_BODY(getdirentries_ok, tc)
+{
+ char dbuf[4096];
+ struct dirent *d;
+ off_t base;
+ ssize_t ret;
+ int dd, n;
+
+ ATF_REQUIRE_EQ(0, mkdir("dir", 0755));
+ ATF_REQUIRE((dd = open("dir", O_DIRECTORY | O_RDONLY)) >= 0);
+ ATF_REQUIRE((ret = getdirentries(dd, dbuf, sizeof(dbuf), &base)) > 0);
+ ATF_REQUIRE_EQ(0, getdirentries(dd, dbuf, sizeof(dbuf), &base));
+ ATF_REQUIRE_EQ(base, lseek(dd, 0, SEEK_CUR));
+ ATF_CHECK_EQ(0, close(dd));
+ for (n = 0, d = (struct dirent *)dbuf;
+ d < (struct dirent *)(dbuf + ret);
+ d = (struct dirent *)((char *)d + d->d_reclen), n++)
+ /* nothing */ ;
+ ATF_CHECK_EQ((struct dirent *)(dbuf + ret), d);
+ ATF_CHECK_EQ(2, n);
+}
+
+ATF_TC(getdirentries_ebadf);
+ATF_TC_HEAD(getdirentries_ebadf, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Attempt to read a directory "
+ "from an invalid descriptor.");
+}
+ATF_TC_BODY(getdirentries_ebadf, tc)
+{
+ char dbuf[4096];
+ off_t base;
+ int fd;
+
+ ATF_REQUIRE((fd = open("file", O_CREAT | O_WRONLY, 0644)) >= 0);
+ ATF_REQUIRE_EQ(-1, getdirentries(fd, dbuf, sizeof(dbuf), &base));
+ ATF_CHECK_EQ(EBADF, errno);
+ ATF_REQUIRE_EQ(0, close(fd));
+ ATF_REQUIRE_EQ(-1, getdirentries(fd, dbuf, sizeof(dbuf), &base));
+ ATF_CHECK_EQ(EBADF, errno);
+}
+
+ATF_TC(getdirentries_efault);
+ATF_TC_HEAD(getdirentries_efault, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Attempt to read a directory "
+ "to an invalid buffer.");
+}
+ATF_TC_BODY(getdirentries_efault, tc)
+{
+ char dbuf[4096];
+ off_t base, *basep;
+ int dd;
+
+ ATF_REQUIRE_EQ(0, mkdir("dir", 0755));
+ ATF_REQUIRE((dd = open("dir", O_DIRECTORY | O_RDONLY)) >= 0);
+ ATF_REQUIRE_EQ(-1, getdirentries(dd, NULL, sizeof(dbuf), &base));
+ ATF_CHECK_EQ(EFAULT, errno);
+ basep = NULL;
+ basep++;
+ ATF_REQUIRE_EQ(-1, getdirentries(dd, dbuf, sizeof(dbuf), basep));
+ ATF_CHECK_EQ(EFAULT, errno);
+ ATF_CHECK_EQ(0, close(dd));
+}
+
+ATF_TC(getdirentries_einval);
+ATF_TC_HEAD(getdirentries_einval, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Attempt to read a directory "
+ "with various invalid parameters.");
+}
+ATF_TC_BODY(getdirentries_einval, tc)
+{
+ struct statfs fsb;
+ char dbuf[4096];
+ off_t base;
+ ssize_t ret;
+ int dd;
+
+ ATF_REQUIRE_EQ(0, mkdir("dir", 0755));
+ ATF_REQUIRE((dd = open("dir", O_DIRECTORY | O_RDONLY)) >= 0);
+ ATF_REQUIRE_EQ(0, fstatfs(dd, &fsb));
+ /* nbytes too small */
+ ATF_REQUIRE_EQ(-1, getdirentries(dd, dbuf, 8, &base));
+ ATF_CHECK_EQ(EINVAL, errno);
+ /* nbytes too big */
+ ATF_REQUIRE_EQ(-1, getdirentries(dd, dbuf, SIZE_MAX, &base));
+ ATF_CHECK_EQ(EINVAL, errno);
+ /* invalid position */
+ ATF_REQUIRE((ret = getdirentries(dd, dbuf, sizeof(dbuf), &base)) > 0);
+ ATF_REQUIRE_EQ(0, getdirentries(dd, dbuf, sizeof(dbuf), &base));
+ ATF_REQUIRE(base > 0);
+ ATF_REQUIRE_EQ(base + 3, lseek(dd, 3, SEEK_CUR));
+ /* known to fail on ufs (FFS2) and zfs, and work on tmpfs */
+ if (strcmp(fsb.f_fstypename, "ufs") == 0 ||
+ strcmp(fsb.f_fstypename, "zfs") == 0) {
+ atf_tc_expect_fail("incorrectly returns 0 instead of EINVAL "
+ "on %s", fsb.f_fstypename);
+ }
+ ATF_REQUIRE_EQ(-1, getdirentries(dd, dbuf, sizeof(dbuf), &base));
+ ATF_CHECK_EQ(EINVAL, errno);
+ ATF_CHECK_EQ(0, close(dd));
+}
+
+ATF_TC(getdirentries_enoent);
+ATF_TC_HEAD(getdirentries_enoent, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Attempt to read a directory "
+ "after it is deleted.");
+}
+ATF_TC_BODY(getdirentries_enoent, tc)
+{
+ char dbuf[4096];
+ off_t base;
+ int dd;
+
+ ATF_REQUIRE_EQ(0, mkdir("dir", 0755));
+ ATF_REQUIRE((dd = open("dir", O_DIRECTORY | O_RDONLY)) >= 0);
+ ATF_REQUIRE_EQ(0, rmdir("dir"));
+ ATF_REQUIRE_EQ(-1, getdirentries(dd, dbuf, sizeof(dbuf), &base));
+ ATF_CHECK_EQ(ENOENT, errno);
+}
+
+ATF_TC(getdirentries_enotdir);
+ATF_TC_HEAD(getdirentries_enotdir, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Attempt to read a directory "
+ "from a descriptor not associated with a directory.");
+}
+ATF_TC_BODY(getdirentries_enotdir, tc)
+{
+ char dbuf[4096];
+ off_t base;
+ int fd;
+
+ ATF_REQUIRE((fd = open("file", O_CREAT | O_RDWR, 0644)) >= 0);
+ ATF_REQUIRE_EQ(-1, getdirentries(fd, dbuf, sizeof(dbuf), &base));
+ ATF_CHECK_EQ(ENOTDIR, errno);
+ ATF_CHECK_EQ(0, close(fd));
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, getdirentries_ok);
+ ATF_TP_ADD_TC(tp, getdirentries_ebadf);
+ ATF_TP_ADD_TC(tp, getdirentries_efault);
+ ATF_TP_ADD_TC(tp, getdirentries_einval);
+ ATF_TP_ADD_TC(tp, getdirentries_enoent);
+ ATF_TP_ADD_TC(tp, getdirentries_enotdir);
+ return (atf_no_error());
+}
diff --git a/tests/sys/kern/inotify_test.c b/tests/sys/kern/inotify_test.c
index ed7cef5d148c..713db55afc22 100644
--- a/tests/sys/kern/inotify_test.c
+++ b/tests/sys/kern/inotify_test.c
@@ -79,7 +79,8 @@ close_inotify(int fd)
}
static uint32_t
-consume_event_cookie(int ifd, int wd, int event, int flags, const char *name)
+consume_event_cookie(int ifd, int wd, unsigned int event, unsigned int flags,
+ const char *name)
{
struct inotify_event *ev;
size_t evsz, namelen;
@@ -118,7 +119,8 @@ consume_event_cookie(int ifd, int wd, int event, int flags, const char *name)
* matches the expected values.
*/
static void
-consume_event(int ifd, int wd, int event, int flags, const char *name)
+consume_event(int ifd, int wd, unsigned int event, unsigned int flags,
+ const char *name)
{
(void)consume_event_cookie(ifd, wd, event, flags, name);
}
diff --git a/tests/sys/mac/bsdextended/Makefile b/tests/sys/mac/bsdextended/Makefile
index 69cd27c0e321..cc3a3f8ea534 100644
--- a/tests/sys/mac/bsdextended/Makefile
+++ b/tests/sys/mac/bsdextended/Makefile
@@ -9,5 +9,6 @@ TEST_METADATA.ugidfw_test+= required_user="root"
# Each test case of matches_test reuses the same ruleset number, so they cannot
# be run simultaneously
TEST_METADATA.matches_test+= is_exclusive=true
+TEST_METADATA+= required_kmods="mac_bsdextended"
.include <bsd.test.mk>
diff --git a/tests/sys/mac/bsdextended/matches_test.sh b/tests/sys/mac/bsdextended/matches_test.sh
index 2a28be0f231b..41fa04f221e3 100644
--- a/tests/sys/mac/bsdextended/matches_test.sh
+++ b/tests/sys/mac/bsdextended/matches_test.sh
@@ -12,9 +12,6 @@ gidoutrange="daemon" # We expect $uidinrange in this group
check_ko()
{
- if ! sysctl -N security.mac.bsdextended >/dev/null 2>&1; then
- atf_skip "mac_bsdextended(4) support isn't available"
- fi
if [ $(sysctl -n security.mac.bsdextended.enabled) = "0" ]; then
# The kernel module is loaded but disabled. Enable it for the
# duration of the test.
diff --git a/tests/sys/mac/portacl/Makefile b/tests/sys/mac/portacl/Makefile
index c9fb6bbaae3e..856a85d331d5 100644
--- a/tests/sys/mac/portacl/Makefile
+++ b/tests/sys/mac/portacl/Makefile
@@ -10,6 +10,7 @@ TAP_TESTS_SH+= root_test
.for t in ${TAP_TESTS_SH}
TEST_METADATA.$t+= required_user="root"
TEST_METADATA.$t+= timeout="450"
+TEST_METADATA.$t+= is_exclusive="true"
.endfor
.include <bsd.test.mk>
diff --git a/tests/sys/net/if_bridge_test.sh b/tests/sys/net/if_bridge_test.sh
index cc0b212aebd2..c0c085f22273 100755
--- a/tests/sys/net/if_bridge_test.sh
+++ b/tests/sys/net/if_bridge_test.sh
@@ -537,7 +537,7 @@ get_mtu()
{
intf=$1
- ifconfig ${intf} ether | awk '$5 == "mtu" { print $6 }'
+ ifconfig ${intf} | awk '$5 == "mtu" { print $6 }'
}
check_mtu()
@@ -546,7 +546,7 @@ check_mtu()
expected=$2
mtu=$(get_mtu $intf)
- if [ $mtu -ne $expected ];
+ if [ "$mtu" -ne "$expected" ];
then
atf_fail "Expected MTU of $expected on $intf but found $mtu"
fi
@@ -1221,6 +1221,29 @@ vlan_qinq_cleanup()
vnet_cleanup
}
+# Adding a bridge SVI to a bridge should not be allowed.
+atf_test_case "bridge_svi_in_bridge" "cleanup"
+bridge_svi_in_bridge_head()
+{
+ atf_set descr 'adding a bridge SVI to a bridge is not allowed (1)'
+ atf_set require.user root
+}
+
+bridge_svi_in_bridge_body()
+{
+ vnet_init
+ vnet_init_bridge
+
+ bridge=$(vnet_mkbridge)
+ atf_check -s exit:0 ifconfig ${bridge}.1 create
+ atf_check -s exit:1 -e ignore ifconfig ${bridge} addm ${bridge}.1
+}
+
+bridge_svi_in_bridge_cleanup()
+{
+ vnet_cleanup
+}
+
atf_init_test_cases()
{
atf_add_test_case "bridge_transmit_ipv4_unicast"
@@ -1247,4 +1270,5 @@ atf_init_test_cases()
atf_add_test_case "vlan_ifconfig_tagged"
atf_add_test_case "vlan_svi"
atf_add_test_case "vlan_qinq"
+ atf_add_test_case "bridge_svi_in_bridge"
}
diff --git a/tests/sys/net/if_ovpn/if_ovpn.sh b/tests/sys/net/if_ovpn/if_ovpn.sh
index 2138e0f666ec..c42344da1a3b 100644
--- a/tests/sys/net/if_ovpn/if_ovpn.sh
+++ b/tests/sys/net/if_ovpn/if_ovpn.sh
@@ -1149,6 +1149,261 @@ destroy_unused_cleanup()
ovpn_cleanup
}
+atf_test_case "multihome4" "cleanup"
+multihome4_head()
+{
+ atf_set descr 'Test multihome IPv4 with OpenVPN'
+ atf_set require.user root
+ atf_set require.progs openvpn
+}
+
+multihome4_body()
+{
+ pft_init
+ ovpn_init
+
+ l=$(vnet_mkepair)
+
+ vnet_mkjail a ${l}a
+ atf_check jexec a ifconfig ${l}a inet 192.0.2.1/24
+ atf_check jexec a ifconfig ${l}a alias 192.0.2.2/24
+ vnet_mkjail b ${l}b
+ atf_check jexec b ifconfig ${l}b inet 192.0.2.3/24
+
+ # Sanity check
+ atf_check -s exit:0 -o ignore jexec b ping -c 1 192.0.2.1
+ atf_check -s exit:0 -o ignore jexec b ping -c 1 192.0.2.2
+
+ ovpn_start a "
+ dev ovpn0
+ dev-type tun
+ proto udp4
+
+ cipher AES-256-GCM
+ auth SHA256
+
+ multihome
+ server 198.51.100.0 255.255.255.0
+ ca $(atf_get_srcdir)/ca.crt
+ cert $(atf_get_srcdir)/server.crt
+ key $(atf_get_srcdir)/server.key
+ dh $(atf_get_srcdir)/dh.pem
+
+ mode server
+ script-security 2
+ auth-user-pass-verify /usr/bin/true via-env
+ topology subnet
+
+ keepalive 100 600
+ "
+ ovpn_start b "
+ dev tun0
+ dev-type tun
+
+ client
+
+ remote 192.0.2.2
+ auth-user-pass $(atf_get_srcdir)/user.pass
+
+ ca $(atf_get_srcdir)/ca.crt
+ cert $(atf_get_srcdir)/client.crt
+ key $(atf_get_srcdir)/client.key
+ dh $(atf_get_srcdir)/dh.pem
+
+ keepalive 100 600
+ "
+
+ # Block packets from the primary address, openvpn should only use the
+ # configured remote address.
+ jexec b pfctl -e
+ pft_set_rules b \
+ "block in quick from 192.0.2.1 to any" \
+ "pass all"
+
+ # Give the tunnel time to come up
+ sleep 10
+
+ atf_check -s exit:0 -o ignore jexec b ping -c 3 198.51.100.1
+}
+
+multihome4_cleanup()
+{
+ ovpn_cleanup
+ pft_cleanup
+}
+
+multihome6_head()
+{
+ atf_set descr 'Test multihome IPv6 with OpenVPN'
+ atf_set require.user root
+ atf_set require.progs openvpn
+}
+
+multihome6_body()
+{
+ ovpn_init
+
+ l=$(vnet_mkepair)
+
+ vnet_mkjail a ${l}a
+ atf_check jexec a ifconfig ${l}a inet6 2001:db8::1/64 no_dad
+ atf_check jexec a ifconfig ${l}a inet6 alias 2001:db8::2/64 no_dad
+ vnet_mkjail b ${l}b
+ atf_check jexec b ifconfig ${l}b inet6 2001:db8::3/64 no_dad
+
+ # Sanity check
+ atf_check -s exit:0 -o ignore jexec b ping6 -c 1 2001:db8::1
+ atf_check -s exit:0 -o ignore jexec b ping6 -c 1 2001:db8::2
+
+ ovpn_start a "
+ dev ovpn0
+ dev-type tun
+ proto udp6
+
+ cipher AES-256-GCM
+ auth SHA256
+
+ multihome
+ server-ipv6 2001:db8:1::/64
+
+ ca $(atf_get_srcdir)/ca.crt
+ cert $(atf_get_srcdir)/server.crt
+ key $(atf_get_srcdir)/server.key
+ dh $(atf_get_srcdir)/dh.pem
+
+ mode server
+ script-security 2
+ auth-user-pass-verify /usr/bin/true via-env
+ topology subnet
+
+ keepalive 100 600
+ "
+ ovpn_start b "
+ dev tun0
+ dev-type tun
+
+ client
+
+ remote 2001:db8::2
+ auth-user-pass $(atf_get_srcdir)/user.pass
+
+ ca $(atf_get_srcdir)/ca.crt
+ cert $(atf_get_srcdir)/client.crt
+ key $(atf_get_srcdir)/client.key
+ dh $(atf_get_srcdir)/dh.pem
+
+ keepalive 100 600
+ "
+
+ # Block packets from the primary address, openvpn should only use the
+ # configured remote address.
+ jexec b pfctl -e
+ pft_set_rules b \
+ "block in quick from 2001:db8::1 to any" \
+ "pass all"
+
+ # Give the tunnel time to come up
+ sleep 10
+
+ atf_check -s exit:0 -o ignore jexec b ping6 -c 3 2001:db8:1::1
+ atf_check -s exit:0 -o ignore jexec b ping6 -c 3 -z 16 2001:db8:1::1
+}
+
+multihome6_cleanup()
+{
+ ovpn_cleanup
+}
+
+atf_test_case "float" "cleanup"
+float_head()
+{
+ atf_set descr 'Test peer float notification'
+ atf_set require.user root
+}
+
+float_body()
+{
+ ovpn_init
+
+ l=$(vnet_mkepair)
+
+ vnet_mkjail a ${l}a
+ jexec a ifconfig ${l}a 192.0.2.1/24 up
+ jexec a ifconfig lo0 127.0.0.1/8 up
+ vnet_mkjail b ${l}b
+ jexec b ifconfig ${l}b 192.0.2.2/24 up
+
+ # Sanity check
+ atf_check -s exit:0 -o ignore jexec a ping -c 1 192.0.2.2
+
+ ovpn_start a "
+ dev ovpn0
+ dev-type tun
+ proto udp4
+
+ cipher AES-256-GCM
+ auth SHA256
+
+ local 192.0.2.1
+ server 198.51.100.0 255.255.255.0
+ ca $(atf_get_srcdir)/ca.crt
+ cert $(atf_get_srcdir)/server.crt
+ key $(atf_get_srcdir)/server.key
+ dh $(atf_get_srcdir)/dh.pem
+
+ mode server
+ script-security 2
+ auth-user-pass-verify /usr/bin/true via-env
+ topology subnet
+
+ keepalive 2 10
+
+ management 192.0.2.1 1234
+ "
+ ovpn_start b "
+ dev tun0
+ dev-type tun
+
+ client
+
+ remote 192.0.2.1
+ auth-user-pass $(atf_get_srcdir)/user.pass
+
+ ca $(atf_get_srcdir)/ca.crt
+ cert $(atf_get_srcdir)/client.crt
+ key $(atf_get_srcdir)/client.key
+ dh $(atf_get_srcdir)/dh.pem
+
+ keepalive 2 10
+ "
+
+ # Give the tunnel time to come up
+ sleep 10
+
+ atf_check -s exit:0 -o ignore jexec b ping -c 3 198.51.100.1
+
+ # We expect the client on 192.0.2.2
+ if ! echo "status" | jexec a nc -N 192.0.2.1 1234 | grep 192.0.2.2; then
+ atf_fail "Client not found in status list!"
+ fi
+
+ # Now change the client IP
+ jexec b ifconfig ${l}b 192.0.2.3/24 up
+
+ # And wait for keepalives to trigger the float notification
+ sleep 5
+
+ # So the client now has the new address in userspace
+ if ! echo "status" | jexec a nc -N 192.0.2.1 1234 | grep 192.0.2.3; then
+ atf_fail "Client not found in status list!"
+ fi
+}
+
+float_cleanup()
+{
+ ovpn_cleanup
+}
+
atf_init_test_cases()
{
atf_add_test_case "4in4"
@@ -1165,4 +1420,7 @@ atf_init_test_cases()
atf_add_test_case "chacha"
atf_add_test_case "gcm_128"
atf_add_test_case "destroy_unused"
+ atf_add_test_case "multihome4"
+ atf_add_test_case "multihome6"
+ atf_add_test_case "float"
}
diff --git a/tests/sys/net/if_vlan.sh b/tests/sys/net/if_vlan.sh
index 424eac705b94..8122203337e2 100755
--- a/tests/sys/net/if_vlan.sh
+++ b/tests/sys/net/if_vlan.sh
@@ -333,6 +333,32 @@ conflict_id_cleanup()
}
+# If a vlan interface is in a bridge, changing the vlandev to refer to
+# a bridge should not be allowed.
+atf_test_case "bridge_vlandev" "cleanup"
+bridge_vlandev_head()
+{
+ atf_set descr 'transforming a bridge member vlan into an SVI is not allowed'
+ atf_set require.user root
+}
+
+bridge_vlandev_body()
+{
+ vnet_init
+ vnet_init_bridge
+
+ bridge=$(vnet_mkbridge)
+ vlan=$(vnet_mkvlan)
+
+ atf_check -s exit:0 ifconfig ${bridge} addm ${vlan}
+ atf_check -s exit:1 -e ignore ifconfig ${vlan} vlan 1 vlandev ${bridge}
+}
+
+bridge_vlandev_cleanup()
+{
+ vnet_cleanup
+}
+
atf_init_test_cases()
{
atf_add_test_case "basic"
@@ -343,4 +369,5 @@ atf_init_test_cases()
atf_add_test_case "qinq_setflags"
atf_add_test_case "bpf_pcp"
atf_add_test_case "conflict_id"
+ atf_add_test_case "bridge_vlandev"
}
diff --git a/tests/sys/netinet6/addr6.sh b/tests/sys/netinet6/addr6.sh
index 38e4bb152240..6fd66d5aa0c7 100755
--- a/tests/sys/netinet6/addr6.sh
+++ b/tests/sys/netinet6/addr6.sh
@@ -39,7 +39,32 @@ addr6_invalid_addr_cleanup()
vnet_cleanup
}
+atf_test_case "anycast_raw_addr" "cleanup"
+anycast_raw_addr_head()
+{
+ atf_set descr "a raw socket can bind to an anycast address"
+ atf_set require.user root
+}
+
+anycast_raw_addr_body()
+{
+ # lo0 needs to be up in the test jail for this test to work
+ ifconfig lo0 up
+
+ netif=$(ifconfig lo create)
+ echo $netif >netif
+ atf_check -s exit:0 ifconfig $netif inet6 2001:db8::1/128 up
+ atf_check -s exit:0 ifconfig $netif inet6 2001:db8::2/128 anycast
+ atf_check -s exit:0 -o ignore ping -c1 -S 2001:db8::2 2001:db8::1
+}
+
+anycast_raw_addr_cleanup()
+{
+ ifconfig $(cat netif) destroy
+}
+
atf_init_test_cases()
{
atf_add_test_case "addr6_invalid_addr"
+ atf_add_test_case "anycast_raw_addr"
}
diff --git a/tests/sys/netpfil/common/dummynet.sh b/tests/sys/netpfil/common/dummynet.sh
index b77b2df84010..66736fbecdb7 100644
--- a/tests/sys/netpfil/common/dummynet.sh
+++ b/tests/sys/netpfil/common/dummynet.sh
@@ -265,10 +265,6 @@ queue_body()
{
fw=$1
- if [ $fw = "ipfw" ] && [ "$(atf_config_get ci false)" = "true" ]; then
- atf_skip "https://bugs.freebsd.org/264805"
- fi
-
firewall_init $fw
dummynet_init $fw
diff --git a/tests/sys/netpfil/pf/Makefile b/tests/sys/netpfil/pf/Makefile
index 3adaef09ddbd..404d5adfb07a 100644
--- a/tests/sys/netpfil/pf/Makefile
+++ b/tests/sys/netpfil/pf/Makefile
@@ -58,6 +58,8 @@ ATF_TESTS_SH+= altq \
ATF_TESTS_PYTEST+= frag6.py
ATF_TESTS_PYTEST+= header.py
ATF_TESTS_PYTEST+= icmp.py
+ATF_TESTS_PYTEST+= igmp.py
+ATF_TESTS_PYTEST+= mld.py
ATF_TESTS_PYTEST+= nat64.py
ATF_TESTS_PYTEST+= nat66.py
ATF_TESTS_PYTEST+= return.py
diff --git a/tests/sys/netpfil/pf/anchor.sh b/tests/sys/netpfil/pf/anchor.sh
index b4b52d7a24d6..64ca84b34c3d 100644
--- a/tests/sys/netpfil/pf/anchor.sh
+++ b/tests/sys/netpfil/pf/anchor.sh
@@ -350,9 +350,9 @@ nat_body()
jexec alcatraz pfctl -sn -a "foo/bar"
jexec alcatraz pfctl -sn -a "foo/baz"
- atf_check -s exit:0 -o match:"nat log on epair0a inet from 192.0.2.0/24 to any port = domain -> 192.0.2.1" \
+ atf_check -s exit:0 -o match:"nat log on ${epair}a inet from 192.0.2.0/24 to any port = domain -> 192.0.2.1" \
jexec alcatraz pfctl -sn -a "*"
- atf_check -s exit:0 -o match:"rdr on epair0a inet proto tcp from any to any port = echo -> 127.0.0.1 port 7" \
+ atf_check -s exit:0 -o match:"rdr on ${epair}a inet proto tcp from any to any port = echo -> 127.0.0.1 port 7" \
jexec alcatraz pfctl -sn -a "*"
}
@@ -437,6 +437,62 @@ quick_cleanup()
pft_cleanup
}
+atf_test_case "recursive_flush" "cleanup"
+recursive_flush_head()
+{
+ atf_set descr 'Test recursive flushing of rules'
+ atf_set require.user root
+}
+
+recursive_flush_body()
+{
+ pft_init
+
+ epair=$(vnet_mkepair)
+ vnet_mkjail alcatraz ${epair}a
+
+ ifconfig ${epair}b 192.0.2.2/24 up
+ jexec alcatraz ifconfig ${epair}a 192.0.2.1/24 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 \
+ "block" \
+ "anchor \"foo\" {\n\
+ pass\n\
+ }"
+
+ # We can ping thanks to the pass rule in foo
+ atf_check -s exit:0 -o ignore ping -c 1 192.0.2.1
+
+ # Only reset the main rules. I.e. not a recursive flush
+ pft_set_rules alcatraz \
+ "block" \
+ "anchor \"foo\""
+
+ # "foo" still has the pass rule, so this works
+ jexec alcatraz pfctl -a "*" -sr
+ atf_check -s exit:0 -o ignore ping -c 1 192.0.2.1
+
+ # Now do a recursive flush
+ atf_check -s exit:0 -e ignore -o ignore \
+ jexec alcatraz pfctl -a "*" -Fr
+ pft_set_rules alcatraz \
+ "block" \
+ "anchor \"foo\""
+
+ # So this fails
+ jexec alcatraz pfctl -a "*" -sr
+ atf_check -s exit:2 -o ignore ping -c 1 192.0.2.1
+}
+
+recursive_flush_cleanup()
+{
+ pft_cleanup
+}
+
atf_init_test_cases()
{
atf_add_test_case "pr183198"
@@ -450,4 +506,5 @@ atf_init_test_cases()
atf_add_test_case "nat"
atf_add_test_case "include"
atf_add_test_case "quick"
+ atf_add_test_case "recursive_flush"
}
diff --git a/tests/sys/netpfil/pf/forward.sh b/tests/sys/netpfil/pf/forward.sh
index 5d7d48a5dd9a..e9539bc9d278 100644
--- a/tests/sys/netpfil/pf/forward.sh
+++ b/tests/sys/netpfil/pf/forward.sh
@@ -101,10 +101,6 @@ v6_body()
{
pft_init
- if [ "$(atf_config_get ci false)" = "true" ]; then
- atf_skip "https://bugs.freebsd.org/260460"
- fi
-
epair_send=$(vnet_mkepair)
epair_recv=$(vnet_mkepair)
diff --git a/tests/sys/netpfil/pf/header.py b/tests/sys/netpfil/pf/header.py
index 6832cfe6d42b..a5e36bc85d14 100644
--- a/tests/sys/netpfil/pf/header.py
+++ b/tests/sys/netpfil/pf/header.py
@@ -53,10 +53,9 @@ class TestHeader(VnetTestTemplate):
def test_too_many(self):
"Verify that we drop packets with silly numbers of headers."
- sendif = self.vnet.iface_alias_map["if1"].name
+ sendif = self.vnet.iface_alias_map["if1"]
recvif = self.vnet.iface_alias_map["if2"].name
- gw_mac = ToolsHelper.get_output("/sbin/ifconfig %s ether | awk '/ether/ { print $2; }'" % sendif)
- gw_mac = re.sub("0a$", "0b", gw_mac)
+ gw_mac = sendif.epairb.ether
ToolsHelper.print_output("/sbin/route add default 192.0.2.1")
@@ -67,7 +66,7 @@ class TestHeader(VnetTestTemplate):
pkt = sp.Ether(dst=gw_mac) \
/ sp.IP(dst="198.51.100.3") \
/ sp.ICMP(type='echo-request')
- s = DelayedSend(pkt, sendif)
+ s = DelayedSend(pkt, sendif.name)
reply = sp.sniff(iface=recvif, timeout=3)
print(reply)
@@ -89,7 +88,7 @@ class TestHeader(VnetTestTemplate):
pkt = pkt / sp.AH(nh=51, payloadlen=1)
pkt = pkt / sp.AH(nh=1, payloadlen=1) / sp.ICMP(type='echo-request')
- s = DelayedSend(pkt, sendif)
+ s = DelayedSend(pkt, sendif.name)
reply = sp.sniff(iface=recvif, timeout=3)
print(reply)
found = False
@@ -109,7 +108,7 @@ class TestHeader(VnetTestTemplate):
pkt = pkt / sp.AH(nh=51, payloadlen=1)
pkt = pkt / sp.AH(nh=1, payloadlen=1) / sp.ICMP(type='echo-request')
- s = DelayedSend(pkt, sendif)
+ s = DelayedSend(pkt, sendif.name)
reply = sp.sniff(iface=recvif, timeout=3)
print(reply)
@@ -148,10 +147,10 @@ class TestHeader6(VnetTestTemplate):
"Verify that we drop packets with silly numbers of headers."
ToolsHelper.print_output("/sbin/ifconfig")
- sendif = self.vnet.iface_alias_map["if1"].name
+ sendif = self.vnet.iface_alias_map["if1"]
recvif = self.vnet.iface_alias_map["if2"].name
- our_mac = ToolsHelper.get_output("/sbin/ifconfig %s ether | awk '/ether/ { print $2; }'" % sendif)
- gw_mac = re.sub("0a$", "0b", our_mac)
+ our_mac = sendif.ether
+ gw_mac = sendif.epairb.ether
ToolsHelper.print_output("/sbin/route -6 add default 2001:db8::1")
@@ -162,7 +161,7 @@ class TestHeader6(VnetTestTemplate):
pkt = sp.Ether(src=our_mac, dst=gw_mac) \
/ sp.IPv6(src="2001:db8::2", dst="2001:db8:1::3") \
/ sp.ICMPv6EchoRequest()
- s = DelayedSend(pkt, sendif)
+ s = DelayedSend(pkt, sendif.name)
reply = sp.sniff(iface=recvif, timeout=3)
print(reply)
@@ -182,7 +181,7 @@ class TestHeader6(VnetTestTemplate):
for i in range(0, 18):
pkt = pkt / sp.AH(nh=51, payloadlen=1)
pkt = pkt / sp.AH(nh=58, payloadlen=1) / sp.ICMPv6EchoRequest()
- s = DelayedSend(pkt, sendif)
+ s = DelayedSend(pkt, sendif.name)
reply = sp.sniff(iface=recvif, timeout=3)
print(reply)
@@ -202,7 +201,7 @@ class TestHeader6(VnetTestTemplate):
for i in range(0, 19):
pkt = pkt / sp.AH(nh=51, payloadlen=1)
pkt = pkt / sp.AH(nh=58, payloadlen=1) / sp.ICMPv6EchoRequest()
- s = DelayedSend(pkt, sendif)
+ s = DelayedSend(pkt, sendif.name)
reply = sp.sniff(iface=recvif, timeout=3)
print(reply)
diff --git a/tests/sys/netpfil/pf/icmp.py b/tests/sys/netpfil/pf/icmp.py
index 83096886691e..c5e945d60e99 100644
--- a/tests/sys/netpfil/pf/icmp.py
+++ b/tests/sys/netpfil/pf/icmp.py
@@ -91,10 +91,10 @@ class TestICMP(VnetTestTemplate):
def test_inner_match(self):
vnet = self.vnet_map["vnet1"]
dst_vnet = self.vnet_map["vnet3"]
- sendif = vnet.iface_alias_map["if1"].name
+ sendif = vnet.iface_alias_map["if1"]
- our_mac = ToolsHelper.get_output("/sbin/ifconfig %s ether | awk '/ether/ { print $2; }'" % sendif)
- dst_mac = re.sub("0a$", "0b", our_mac)
+ our_mac = sendif.ether
+ dst_mac = sendif.epairb.ether
# Import in the correct vnet, so at to not confuse Scapy
import scapy.all as sp
@@ -111,7 +111,7 @@ class TestICMP(VnetTestTemplate):
/ sp.IP(src="192.0.2.2", dst="198.51.100.2") \
/ sp.ICMP(type='echo-request') \
/ "PAYLOAD"
- sp.sendp(pkt, sendif, verbose=False)
+ sp.sendp(pkt, sendif.name, verbose=False)
# Now try to pass an ICMP error message piggy-backing on that state, but
# use a different source address
@@ -120,7 +120,7 @@ class TestICMP(VnetTestTemplate):
/ sp.ICMP(type='dest-unreach') \
/ sp.IP(src="198.51.100.2", dst="192.0.2.2") \
/ sp.ICMP(type='echo-reply')
- sp.sendp(pkt, sendif, verbose=False)
+ sp.sendp(pkt, sendif.name, verbose=False)
try:
rcvd = self.wait_object(dst_vnet.pipe, timeout=1)
@@ -136,8 +136,7 @@ class TestICMP(VnetTestTemplate):
/ sp.ICMP(type='echo-request') \
/ sp.raw(bytes.fromhex('f0') * payload_size)
- p = sp.sr1(packet, iface=self.vnet.iface_alias_map["if1"].name,
- timeout=3)
+ p = sp.sr1(packet, timeout=3)
p.show()
ip = p.getlayer(sp.IP)
@@ -176,6 +175,22 @@ class TestICMP(VnetTestTemplate):
self.check_icmp_echo(sp, 1464)
self.check_icmp_echo(sp, 1468)
+ @pytest.mark.require_user("root")
+ @pytest.mark.require_progs(["scapy"])
+ def test_truncated_opts(self):
+ ToolsHelper.print_output("/sbin/route add default 192.0.2.1")
+
+ # Import in the correct vnet, so at to not confuse Scapy
+ import scapy.all as sp
+
+ packet = sp.IP(dst="198.51.100.2", flags="DF") \
+ / sp.ICMP(type='dest-unreach', length=108) \
+ / sp.IP(src="198.51.100.2", dst="192.0.2.2", len=1000, \
+ ihl=(120 >> 2), options=[ \
+ sp.IPOption_Security(length=100)])
+ packet.show()
+ sp.sr1(packet, timeout=3)
+
class TestICMP_NAT(VnetTestTemplate):
REQUIRED_MODULES = [ "pf" ]
TOPOLOGY = {
diff --git a/tests/sys/netpfil/pf/igmp.py b/tests/sys/netpfil/pf/igmp.py
new file mode 100644
index 000000000000..b339a2825082
--- /dev/null
+++ b/tests/sys/netpfil/pf/igmp.py
@@ -0,0 +1,95 @@
+#
+# 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 utils import DelayedSend
+from atf_python.sys.net.tools import ToolsHelper
+from atf_python.sys.net.vnet import VnetTestTemplate
+
+class TestIGMP(VnetTestTemplate):
+ REQUIRED_MODULES = [ "pf" ]
+ TOPOLOGY = {
+ "vnet1": {"ifaces": ["if1"]},
+ "vnet2": {"ifaces": ["if1"]},
+ "if1": {"prefixes4": [("192.0.2.2/24", "192.0.2.1/24")]},
+ }
+
+ def vnet2_handler(self, vnet):
+ ifname = vnet.iface_alias_map["if1"].name
+ 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_igmp_reply(self, pkt, ifname):
+ pkt.show()
+ s = DelayedSend(pkt)
+
+ found = False
+ packets = self.sp.sniff(iface=ifname, timeout=5)
+ for r in packets:
+ r.show()
+ igmp = r.getlayer(self.sc.igmp.IGMP)
+ if not igmp:
+ continue
+ igmp.show()
+ if not igmp.gaddr == "230.0.0.1":
+ continue
+ found = True
+ return found
+
+ @pytest.mark.require_user("root")
+ @pytest.mark.require_progs(["scapy"])
+ def test_ip_opts(self):
+ """Verify that we allow IGMP packets with IP options"""
+ ifname = self.vnet.iface_alias_map["if1"].name
+
+ # 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
+ self.sp = sp
+ self.sc = sc
+
+ # We allow IGMP packets with the router alert option
+ pkt = sp.IP(dst="224.0.0.1%%%s" % ifname, ttl=1,
+ options=[sp.IPOption_Router_Alert()]) \
+ / sc.igmp.IGMP(type=0x11, mrcode=1)
+ assert self.find_igmp_reply(pkt, ifname)
+
+ # But not with other options
+ pkt = sp.IP(dst="224.0.0.1%%%s" % ifname, ttl=1,
+ options=[sp.IPOption_NOP()]) \
+ / sc.igmp.IGMP(type=0x11, mrcode=1)
+ assert not self.find_igmp_reply(pkt, ifname)
+
+ # Or with the wrong TTL
+ pkt = sp.IP(dst="224.0.0.1%%%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 1ce8999dcb91..18fafe11c6ab 100644
--- a/tests/sys/netpfil/pf/ioctl/validation.c
+++ b/tests/sys/netpfil/pf/ioctl/validation.c
@@ -32,6 +32,7 @@
#include <net/if.h>
#include <net/pfvar.h>
+#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
@@ -893,6 +894,39 @@ ATF_TC_CLEANUP(rpool_mtx2, tc)
COMMON_CLEANUP();
}
+ATF_TC_WITH_CLEANUP(natlook);
+ATF_TC_HEAD(natlook, tc)
+{
+ atf_tc_set_md_var(tc, "require.user", "root");
+}
+
+ATF_TC_BODY(natlook, tc)
+{
+ struct pfioc_natlook nl = { 0 };
+
+ COMMON_HEAD();
+
+ nl.af = AF_INET;
+ nl.proto = IPPROTO_ICMP;
+ nl.saddr.v4.s_addr = 0x01020304;
+ nl.daddr.v4.s_addr = 0x05060708;
+
+ /* Invalid direction */
+ nl.direction = 42;
+
+ ATF_CHECK_ERRNO(EINVAL, ioctl(dev, DIOCNATLOOK, &nl) == -1);
+
+ /* Invalid af */
+ nl.direction = PF_IN;
+ nl.af = 99;
+
+ ATF_CHECK_ERRNO(EAFNOSUPPORT, ioctl(dev, DIOCNATLOOK, &nl) == -1);
+}
+
+ATF_TC_CLEANUP(natlook, tc)
+{
+ COMMON_CLEANUP();
+}
ATF_TP_ADD_TCS(tp)
{
@@ -918,6 +952,7 @@ ATF_TP_ADD_TCS(tp)
ATF_TP_ADD_TC(tp, tag);
ATF_TP_ADD_TC(tp, rpool_mtx);
ATF_TP_ADD_TC(tp, rpool_mtx2);
+ ATF_TP_ADD_TC(tp, natlook);
return (atf_no_error());
}
diff --git a/tests/sys/netpfil/pf/killstate.sh b/tests/sys/netpfil/pf/killstate.sh
index 447a4e388f11..0d98db822535 100644
--- a/tests/sys/netpfil/pf/killstate.sh
+++ b/tests/sys/netpfil/pf/killstate.sh
@@ -117,10 +117,6 @@ v6_body()
{
pft_init
- if [ "$(atf_config_get ci false)" = "true" ]; then
- atf_skip "https://bugs.freebsd.org/260458"
- fi
-
epair=$(vnet_mkepair)
ifconfig ${epair}a inet6 2001:db8::1/64 up no_dad
diff --git a/tests/sys/netpfil/pf/mbuf.sh b/tests/sys/netpfil/pf/mbuf.sh
index d845f793a969..e3f138bb73b9 100644
--- a/tests/sys/netpfil/pf/mbuf.sh
+++ b/tests/sys/netpfil/pf/mbuf.sh
@@ -105,6 +105,12 @@ inet6_in_mbuf_len_body()
epair=$(vnet_mkepair)
ifconfig ${epair}a inet6 2001:db8::1/64 up no_dad
+ # Ensure we don't unintentionally send MLD packets to alcatraz
+ pfctl -e
+ echo "block
+ pass out inet6 proto icmp6 icmp6-type { neighbrsol, neighbradv, echoreq, echorep }
+ " | pfctl -g -f -
+
# Set up a simple jail with one interface
vnet_mkjail alcatraz ${epair}b
jexec alcatraz ifconfig ${epair}b inet6 2001:db8::2/64 up no_dad
diff --git a/tests/sys/netpfil/pf/mld.py b/tests/sys/netpfil/pf/mld.py
new file mode 100644
index 000000000000..d118a34c8a7d
--- /dev/null
+++ b/tests/sys/netpfil/pf/mld.py
@@ -0,0 +1,95 @@
+#
+# 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 utils import DelayedSend
+from atf_python.sys.net.tools import ToolsHelper
+from atf_python.sys.net.vnet import VnetTestTemplate
+
+class TestMLD(VnetTestTemplate):
+ REQUIRED_MODULES = [ "pf" ]
+ TOPOLOGY = {
+ "vnet1": {"ifaces": ["if1"]},
+ "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.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)
+
+ found = False
+ packets = self.sp.sniff(iface=ifname, timeout=5)
+ for r in packets:
+ r.show()
+ mld = r.getlayer(self.sp.ICMPv6MLReport2)
+ if not mld:
+ continue
+ mld.show()
+ found = True
+ return found
+
+ @pytest.mark.require_user("root")
+ @pytest.mark.require_progs(["scapy"])
+ 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
+ import scapy.all as sp
+ import scapy.contrib as sc
+ import scapy.contrib.igmp
+ 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) \
+ / sp.ICMPv6MLQuery2()
+ assert 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)
diff --git a/tests/sys/netpfil/pf/nat.sh b/tests/sys/netpfil/pf/nat.sh
index f1fdf6405d97..16c981f97399 100644
--- a/tests/sys/netpfil/pf/nat.sh
+++ b/tests/sys/netpfil/pf/nat.sh
@@ -777,6 +777,38 @@ binat_match_cleanup()
kill $(cat ${PWD}/inetd_tester.pid)
}
+atf_test_case "empty_pool" "cleanup"
+empty_pool_head()
+{
+ atf_set descr 'NAT with empty pool'
+ atf_set require.user root
+}
+
+empty_pool_body()
+{
+ pft_init
+ setup_router_server_ipv6
+
+
+ pft_set_rules router \
+ "block" \
+ "pass inet6 proto icmp6 icmp6-type { neighbrsol, neighbradv }" \
+ "pass in on ${epair_tester}b" \
+ "pass out on ${epair_server}a inet6 from any to ${net_server_host_server} nat-to <nonexistent>" \
+
+ # pf_map_addr_sn() won't be able to pick a target address, because
+ # the table used in redireciton pool is empty. Packet will not be
+ # forwarded, error counter will be increased.
+ ping_server_check_reply exit:1
+ # Ignore warnings about not-loaded ALTQ
+ atf_check -o "match:map-failed +1 +" -x "jexec router pfctl -qvvsi 2> /dev/null"
+}
+
+empty_pool_cleanup()
+{
+ pft_cleanup
+}
+
atf_init_test_cases()
{
atf_add_test_case "exhaust"
@@ -794,4 +826,5 @@ atf_init_test_cases()
atf_add_test_case "nat_match"
atf_add_test_case "binat_compat"
atf_add_test_case "binat_match"
+ atf_add_test_case "empty_pool"
}
diff --git a/tests/sys/netpfil/pf/nat64.py b/tests/sys/netpfil/pf/nat64.py
index adae2489ce5e..a5890fc4a161 100644
--- a/tests/sys/netpfil/pf/nat64.py
+++ b/tests/sys/netpfil/pf/nat64.py
@@ -33,7 +33,7 @@ from atf_python.sys.net.tools import ToolsHelper
from atf_python.sys.net.vnet import VnetTestTemplate
class TestNAT64(VnetTestTemplate):
- REQUIRED_MODULES = [ "pf" ]
+ REQUIRED_MODULES = [ "pf", "pflog" ]
TOPOLOGY = {
"vnet1": {"ifaces": ["if1"]},
"vnet2": {"ifaces": ["if1", "if2"]},
@@ -92,12 +92,15 @@ class TestNAT64(VnetTestTemplate):
def vnet2_handler(self, vnet):
ifname = vnet.iface_alias_map["if1"].name
+ ToolsHelper.print_output("/sbin/sysctl net.inet6.ip6.forwarding=1")
ToolsHelper.print_output("/sbin/route add default 192.0.2.2")
ToolsHelper.print_output("/sbin/pfctl -e")
ToolsHelper.pf_rules([
"pass inet6 proto icmp6",
"pass in on %s inet6 af-to inet from 192.0.2.1" % ifname])
+ vnet.pipe.send(socket.if_nametoindex("pflog0"))
+
@pytest.mark.require_user("root")
@pytest.mark.require_progs(["scapy"])
def test_tcp_rst(self):
@@ -272,3 +275,54 @@ class TestNAT64(VnetTestTemplate):
reply = self.common_test_source_addr(packet)
icmp = reply.getlayer(sp.ICMPv6EchoRequest)
assert icmp
+
+ @pytest.mark.require_user("root")
+ @pytest.mark.require_progs(["scapy"])
+ def test_bad_len(self):
+ """
+ PR 288224: we can panic if the IPv6 plen is longer than the packet length.
+ """
+ ToolsHelper.print_output("/sbin/route -6 add default 2001:db8::1")
+ import scapy.all as sp
+
+ packet = sp.IPv6(dst="64:ff9b::198.51.100.2", hlim=2, plen=512) \
+ / sp.ICMPv6EchoRequest() / sp.Raw("foo")
+ reply = sp.sr1(packet, timeout=3)
+ # We don't expect a reply to a corrupted packet
+ assert not reply
+
+ @pytest.mark.require_user("root")
+ @pytest.mark.require_progs(["scapy"])
+ def test_noip6(self):
+ """
+ PR 288263: link-local target address in icmp6 ADVERT can cause NULL deref
+ """
+ ifname = self.vnet.iface_alias_map["if1"].name
+ gw_mac = self.vnet.iface_alias_map["if1"].epairb.ether
+ scopeid = self.wait_object(self.vnet_map["vnet2"].pipe)
+ ToolsHelper.print_output("/sbin/route -6 add default 2001:db8::1")
+
+ import scapy.all as sp
+
+ pkt = sp.Ether(dst=gw_mac) \
+ / sp.IPv6(dst="64:ff9b::203.0.113.2") \
+ / sp.ICMPv6ND_NA(tgt="FFA2:%x:2821:125F:1D27:B3B2:3F6F:C43C" % scopeid)
+ pkt.show()
+ sp.hexdump(pkt)
+ s = DelayedSend(pkt, sendif=ifname)
+
+ packets = sp.sniff(iface=ifname, timeout=5)
+ for r in packets:
+ r.show()
+
+ # Try scope id that likely doesn't have an interface at all
+ pkt = sp.Ether(dst=gw_mac) \
+ / sp.IPv6(dst="64:ff9b::203.0.113.2") \
+ / sp.ICMPv6ND_NA(tgt="FFA2:%x:2821:125F:1D27:B3B2:3F6F:C43C" % 255)
+ pkt.show()
+ sp.hexdump(pkt)
+ s = DelayedSend(pkt, sendif=ifname)
+
+ packets = sp.sniff(iface=ifname, timeout=5)
+ for r in packets:
+ r.show()
diff --git a/tests/sys/netpfil/pf/pfsync.sh b/tests/sys/netpfil/pf/pfsync.sh
index 7f545b43a066..3be4a3024393 100644
--- a/tests/sys/netpfil/pf/pfsync.sh
+++ b/tests/sys/netpfil/pf/pfsync.sh
@@ -835,6 +835,90 @@ basic_ipv6_cleanup()
pfsynct_cleanup
}
+atf_test_case "rtable" "cleanup"
+rtable_head()
+{
+ atf_set descr 'Test handling of invalid rtableid'
+ atf_set require.user root
+}
+
+rtable_body()
+{
+ pfsynct_init
+
+ epair_sync=$(vnet_mkepair)
+ epair_one=$(vnet_mkepair)
+ epair_two=$(vnet_mkepair)
+
+ vnet_mkjail one ${epair_one}a ${epair_sync}a
+ vnet_mkjail two ${epair_two}a ${epair_sync}b
+
+ # pfsync interface
+ jexec one ifconfig ${epair_sync}a 192.0.2.1/24 up
+ jexec one ifconfig ${epair_one}a 198.51.100.1/24 up
+ jexec one ifconfig pfsync0 \
+ syncdev ${epair_sync}a \
+ maxupd 1 \
+ up
+ jexec two ifconfig ${epair_two}a 198.51.100.1/24 up
+ jexec two ifconfig ${epair_sync}b 192.0.2.2/24 up
+ jexec two ifconfig pfsync0 \
+ syncdev ${epair_sync}b \
+ maxupd 1 \
+ up
+
+ # Make life easy, give ${epair_two}a the same mac addrss as ${epair_one}a
+ mac=$(jexec one ifconfig ${epair_one}a | awk '/ether/ { print($2); }')
+ jexec two ifconfig ${epair_two}a ether ${mac}
+
+ # Enable pf!
+ jexec one /sbin/sysctl net.fibs=8
+ jexec one pfctl -e
+ pft_set_rules one \
+ "set skip on ${epair_sync}a" \
+ "pass rtable 3 keep state"
+ # No extra fibs in two
+ jexec two pfctl -e
+ pft_set_rules two \
+ "set skip on ${epair_sync}b" \
+ "pass keep state"
+
+ ifconfig ${epair_one}b 198.51.100.254/24 up
+ ifconfig ${epair_two}b 198.51.100.253/24 up
+
+ # Create a new state
+ env PYTHONPATH=${common_dir} \
+ ${common_dir}/pft_ping.py \
+ --sendif ${epair_one}b \
+ --fromaddr 198.51.100.254 \
+ --to 198.51.100.1 \
+ --recvif ${epair_one}b
+
+ # Now
+ jexec one pfctl -ss -vv
+ sleep 2
+
+ # Now try to use that state on jail two
+ env PYTHONPATH=${common_dir} \
+ ${common_dir}/pft_ping.py \
+ --sendif ${epair_two}b \
+ --fromaddr 198.51.100.254 \
+ --to 198.51.100.1 \
+ --recvif ${epair_two}b
+
+ echo one
+ jexec one pfctl -ss -vv
+ jexec one pfctl -sr -vv
+ echo two
+ jexec two pfctl -ss -vv
+ jexec two pfctl -sr -vv
+}
+
+rtable_cleanup()
+{
+ pfsynct_cleanup
+}
+
route_to_common_head()
{
pfsync_version=$1
@@ -1134,6 +1218,7 @@ atf_init_test_cases()
atf_add_test_case "timeout"
atf_add_test_case "basic_ipv6_unicast"
atf_add_test_case "basic_ipv6"
+ atf_add_test_case "rtable"
atf_add_test_case "route_to_1301"
atf_add_test_case "route_to_1301_bad_ruleset"
atf_add_test_case "route_to_1301_bad_rpool"
diff --git a/tests/sys/netpfil/pf/rdr.sh b/tests/sys/netpfil/pf/rdr.sh
index 4c08b4973891..f7c920bbfa8f 100644
--- a/tests/sys/netpfil/pf/rdr.sh
+++ b/tests/sys/netpfil/pf/rdr.sh
@@ -142,7 +142,7 @@ tcp_v6_pass_body()
{
tcp_v6_setup # Sets ${epair_…} variables
tcp_v6_common \
- "rdr on ${epair_one}a proto tcp from any to any port 80 -> 2001:db8:b::2 port 8000"
+ "pass in on ${epair_one}a proto tcp from any to any port 80 rdr-to 2001:db8:b::2 port 8000"
}
tcp_v6_pass_cleanup()
diff --git a/tests/sys/netpfil/pf/route_to.sh b/tests/sys/netpfil/pf/route_to.sh
index 5c0d355b8ea1..fd1653cce311 100644
--- a/tests/sys/netpfil/pf/route_to.sh
+++ b/tests/sys/netpfil/pf/route_to.sh
@@ -859,6 +859,121 @@ ttl_cleanup()
pft_cleanup
}
+
+atf_test_case "empty_pool" "cleanup"
+empty_pool_head()
+{
+ atf_set descr 'Route-to with empty pool'
+ atf_set require.user root
+}
+
+empty_pool_body()
+{
+ pft_init
+ setup_router_server_ipv6
+
+
+ pft_set_rules router \
+ "block" \
+ "pass inet6 proto icmp6 icmp6-type { neighbrsol, neighbradv }" \
+ "pass in on ${epair_tester}b route-to (${epair_server}a <nonexistent>) inet6 from any to ${net_server_host_server}" \
+ "pass out on ${epair_server}a"
+
+ # pf_map_addr_sn() won't be able to pick a target address, because
+ # the table used in redireciton pool is empty. Packet will not be
+ # forwarded, error counter will be increased.
+ ping_server_check_reply exit:1
+ # Ignore warnings about not-loaded ALTQ
+ atf_check -o "match:map-failed +1 +" -x "jexec router pfctl -qvvsi 2> /dev/null"
+}
+
+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
+}
+
+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
+
+ jexec router pfctl -e
+ pft_set_rules router \
+ "set debug loud" \
+ "set reassemble yes" \
+ "set state-policy if-bound" \
+ "table <rt_targets_1> { ${net_server1_6}::42:4/127 ${net_server1_6}::42:0/127 }" \
+ "table <rt_targets_2> { ${net_server2_6}::42:4/127 }" \
+ "pass in on ${epair_tester}b \
+ route-to { \
+ (${epair_server1}a <rt_targets_1>) \
+ (${epair_server2}a <rt_targets_2_empty>) \
+ (${epair_server2}a <rt_targets_2>) \
+ } \
+ inet6 proto tcp \
+ keep state"
+
+ # Both hosts of the pool are tables. Each table gets iterated over once,
+ # then the pool iterates to the next host, which is also iterated,
+ # then the pool loops back to the 1st host. If an empty table is found,
+ # it is skipped. Unless that's the only table, that is tested by
+ # the "empty_pool" test.
+ for port in $(seq 1 7); 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
+ cat $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_server1_6}::42:4@${epair_server1}a" \
+ "${epair_tester}b tcp ${host_server_6}\[9\] <- ${net_clients_6}::1\[4204\] .* route-to: ${net_server1_6}::42:5@${epair_server1}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_server2_6}::42:5@${epair_server2}a" \
+ "${epair_tester}b tcp ${host_server_6}\[9\] <- ${net_clients_6}::1\[4207\] .* 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
+}
+
+table_loop_cleanup()
+{
+ pft_cleanup
+}
+
+
atf_init_test_cases()
{
atf_add_test_case "v4"
@@ -877,4 +992,6 @@ atf_init_test_cases()
atf_add_test_case "dummynet_double"
atf_add_test_case "sticky"
atf_add_test_case "ttl"
+ atf_add_test_case "empty_pool"
+ atf_add_test_case "table_loop"
}
diff --git a/tests/sys/netpfil/pf/set_tos.sh b/tests/sys/netpfil/pf/set_tos.sh
index 75b96edbab6e..842377ee97c6 100644
--- a/tests/sys/netpfil/pf/set_tos.sh
+++ b/tests/sys/netpfil/pf/set_tos.sh
@@ -129,10 +129,6 @@ v6_body()
{
pft_init
- if [ "$(atf_config_get ci false)" = "true" ]; then
- atf_skip "https://bugs.freebsd.org/260459"
- fi
-
epair=$(vnet_mkepair)
ifconfig ${epair}a inet6 add 2001:db8:192::1
vnet_mkjail alcatraz ${epair}b
diff --git a/tests/sys/netpfil/pf/table.sh b/tests/sys/netpfil/pf/table.sh
index 78320375db7c..5e5fccdaca20 100644
--- a/tests/sys/netpfil/pf/table.sh
+++ b/tests/sys/netpfil/pf/table.sh
@@ -582,6 +582,34 @@ anchor_cleanup()
pft_cleanup
}
+atf_test_case "flush" "cleanup"
+flush_head()
+{
+ atf_set descr 'Test flushing addresses from tables'
+ atf_set require.user root
+}
+
+flush_body()
+{
+ pft_init
+
+ vnet_mkjail alcatraz
+
+ atf_check -s exit:0 -e match:"1/1 addresses added." \
+ jexec alcatraz pfctl -t foo -T add 1.2.3.4
+ atf_check -s exit:0 -o match:" 1.2.3.4" \
+ jexec alcatraz pfctl -t foo -T show
+ atf_check -s exit:0 -e match:"1 addresses deleted." \
+ jexec alcatraz pfctl -t foo -T flush
+ atf_check -s exit:0 -o not-match:"1.2.3.4" \
+ jexec alcatraz pfctl -t foo -T show
+}
+
+flush_cleanup()
+{
+ pft_cleanup
+}
+
atf_init_test_cases()
{
atf_add_test_case "v4_counters"
@@ -596,4 +624,5 @@ atf_init_test_cases()
atf_add_test_case "pr259689"
atf_add_test_case "precreate"
atf_add_test_case "anchor"
+ atf_add_test_case "flush"
}
diff --git a/tests/sys/netpfil/pf/utils.subr b/tests/sys/netpfil/pf/utils.subr
index 6af10e80390d..3f8d437920f9 100644
--- a/tests/sys/netpfil/pf/utils.subr
+++ b/tests/sys/netpfil/pf/utils.subr
@@ -274,6 +274,107 @@ 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
+# tester jail. All links are dual stack.
+setup_router_server_nat64()
+{
+ pft_init
+
+ epair_tester=$(vnet_mkepair)
+ epair_server1=$(vnet_mkepair)
+ epair_server2=$(vnet_mkepair)
+
+ # Funny how IPv4 address space is to small to even assign nice /24
+ # prefixes on all needed networks. On IPv6 we have a separate /64 for
+ # each link, loopback server, and client/SNAT pool. On IPv4 we must
+ # use small /28 prefixes, so even though we define all networks
+ # as variables we can't easily use them in tests if additional addresses
+ # are needed.
+
+ # IP addresses which can be used by the tester jail.
+ # Can be used as SNAT or as source with pft_ping.py. It is up to
+ # the test code to make them accessible from router.
+ net_clients_4=203.0.113
+ net_clients_4_mask=24
+ net_clients_6=2001:db8:44
+ net_clients_6_mask=64
+
+ # IP addresses on loopback interfaces of both servers. They can be
+ # accessed using the route-to targtet.
+ host_server_4=192.0.2.100
+ host_server_6=2001:db8:4203::100
+
+ net_tester_4=198.51.100
+ net_tester_4_mask=28
+ net_tester_4_host_router=198.51.100.1
+ net_tester_4_host_tester=198.51.100.2
+
+ net_tester_6=2001:db8:4200
+ net_tester_6_mask=64
+ net_tester_6_host_router=2001:db8:4200::1
+ net_tester_6_host_tester=2001:db8:4200::2
+
+ net_server1_4=198.51.100
+ net_server1_4_mask=28
+ net_server1_4_host_router=198.51.100.17
+ net_server1_4_host_server=198.51.100.18
+
+ net_server1_6=2001:db8:4201
+ net_server1_6_mask=64
+ net_server1_6_host_router=2001:db8:4201::1
+ net_server1_6_host_server=2001:db8:4201::2
+
+ net_server2_4=198.51.100
+ net_server2_4_mask=28
+ net_server2_4_host_router=198.51.100.33
+ net_server2_4_host_server=198.51.100.34
+
+ net_server2_6=2001:db8:4202
+ net_server2_6_mask=64
+ net_server2_6_host_router=2001:db8:4202::1
+ net_server2_6_host_server=2001:db8:4202::2
+
+ vnet_mkjail router ${epair_tester}b ${epair_server1}a ${epair_server2}a
+ jexec router ifconfig ${epair_tester}b inet ${net_tester_4_host_router}/${net_tester_4_mask} up
+ jexec router ifconfig ${epair_tester}b inet6 ${net_tester_6_host_router}/${net_tester_6_mask} up no_dad
+ jexec router ifconfig ${epair_server1}a inet ${net_server1_4_host_router}/${net_server1_4_mask} up
+ jexec router ifconfig ${epair_server1}a inet6 ${net_server1_6_host_router}/${net_server1_6_mask} up no_dad
+ jexec router ifconfig ${epair_server2}a inet ${net_server2_4_host_router}/${net_server2_4_mask} up
+ jexec router ifconfig ${epair_server2}a inet6 ${net_server2_6_host_router}/${net_server2_6_mask} up no_dad
+ jexec router sysctl net.inet.ip.forwarding=1
+ jexec router sysctl net.inet6.ip6.forwarding=1
+ jexec router pfctl -e
+
+ ifconfig ${epair_tester}a inet ${net_tester_4_host_tester}/${net_tester_4_mask} up
+ ifconfig ${epair_tester}a inet6 ${net_tester_6_host_tester}/${net_tester_6_mask} up no_dad
+ route add 0.0.0.0/0 ${net_tester_4_host_router}
+ route add -6 ::/0 ${net_tester_6_host_router}
+
+ inetd_conf=$(mktemp)
+ echo "discard stream tcp46 nowait root internal" >> $inetd_conf
+
+ vnet_mkjail server1 ${epair_server1}b
+ jexec server1 /etc/rc.d/netif start lo0
+ jexec server1 ifconfig ${epair_server1}b inet ${net_server1_4_host_server}/${net_server1_4_mask} up
+ jexec server1 ifconfig ${epair_server1}b inet6 ${net_server1_6_host_server}/${net_server1_6_mask} up no_dad
+ jexec server1 ifconfig lo0 ${host_server_4}/32 alias
+ jexec server1 ifconfig lo0 inet6 ${host_server_6}/128 alias
+ jexec server1 inetd -p ${PWD}/inetd_1.pid $inetd_conf
+ jexec server1 route add 0.0.0.0/0 ${net_server1_4_host_router}
+
+ jexec server1 route add -6 ::/0 ${net_server1_6_host_router}
+ vnet_mkjail server2 ${epair_server2}b
+ jexec server2 /etc/rc.d/netif start lo0
+ jexec server2 ifconfig ${epair_server2}b inet ${net_server2_4_host_server}/${net_server2_4_mask} up
+ jexec server2 ifconfig ${epair_server2}b inet6 ${net_server2_6_host_server}/${net_server2_6_mask} up no_dad
+ jexec server2 ifconfig lo0 ${host_server_4}/32 alias
+ jexec server2 ifconfig lo0 inet6 ${host_server_6}/128 alias
+ jexec server2 inetd -p ${PWD}/inetd_2.pid $inetd_conf
+ jexec server2 route add 0.0.0.0/0 ${net_server2_4_host_router}
+ jexec server2 route add -6 ::/0 ${net_server2_6_host_router}
+}
+
# Ping the dummy static NDP target.
# Check for pings being forwarded through the router towards the target.
ping_dummy_check_request()