aboutsummaryrefslogtreecommitdiff
path: root/tests/sys
diff options
context:
space:
mode:
Diffstat (limited to 'tests/sys')
-rw-r--r--tests/sys/aio/Makefile1
-rw-r--r--tests/sys/aio/aio_kqueue_test.c2
-rw-r--r--tests/sys/aio/aio_test.c33
-rw-r--r--tests/sys/aio/lio_kqueue_test.c2
-rw-r--r--tests/sys/aio/lio_test.c2
-rw-r--r--tests/sys/common/vnet.subr5
-rw-r--r--tests/sys/file/Makefile2
-rw-r--r--tests/sys/file/fcntlflags_test.c143
-rw-r--r--tests/sys/fs/fusefs/last_local_modify.cc12
-rw-r--r--tests/sys/fs/fusefs/mockfs.cc2
-rw-r--r--tests/sys/fs/fusefs/release.cc54
-rw-r--r--tests/sys/fs/fusefs/write.cc73
-rw-r--r--tests/sys/kern/socket_accf.c32
-rw-r--r--tests/sys/kern/unix_stream.c24
-rw-r--r--tests/sys/mqueue/mqtest1.c4
-rw-r--r--tests/sys/mqueue/mqtest2.c6
-rw-r--r--tests/sys/mqueue/mqtest3.c4
-rw-r--r--tests/sys/mqueue/mqtest4.c4
-rw-r--r--tests/sys/mqueue/mqtest5.c4
-rw-r--r--tests/sys/mqueue/mqueue_test.sh14
-rw-r--r--tests/sys/net/Makefile1
-rwxr-xr-xtests/sys/net/if_bridge_test.sh19
-rw-r--r--tests/sys/net/if_epair.c4
-rw-r--r--tests/sys/net/if_ovpn/if_ovpn.sh2
-rw-r--r--tests/sys/net/if_ovpn/if_ovpn_c.c5
-rw-r--r--tests/sys/net/if_ovpn/utils.subr19
-rwxr-xr-xtests/sys/net/if_tun_test.sh22
-rw-r--r--tests/sys/net/transient_tuntap.c54
-rw-r--r--tests/sys/netinet/Makefile2
-rwxr-xr-xtests/sys/netinet/divert.sh11
-rw-r--r--tests/sys/netinet/multicast-receive.c130
-rw-r--r--tests/sys/netinet/multicast-send.c (renamed from tests/sys/netinet/sendto-IP_MULTICAST_IF.c)62
-rwxr-xr-x[-rw-r--r--]tests/sys/netinet/multicast.sh126
-rw-r--r--tests/sys/netinet/tcp_md5_getsockopt.c5
-rwxr-xr-xtests/sys/netinet6/ndp.sh39
-rw-r--r--tests/sys/netlink/netlink_socket.c45
-rw-r--r--tests/sys/netlink/test_snl.c19
-rw-r--r--tests/sys/netlink/test_snl_generic.c16
-rw-r--r--tests/sys/netmap/Makefile1
-rw-r--r--tests/sys/netmap/ctrl-api-test.c7
-rw-r--r--tests/sys/netpfil/pf/Makefile5
-rw-r--r--tests/sys/netpfil/pf/counters.sh831
-rw-r--r--tests/sys/netpfil/pf/fragmentation_pass.sh67
-rw-r--r--tests/sys/netpfil/pf/igmp.py6
-rw-r--r--tests/sys/netpfil/pf/ioctl/validation.c25
-rw-r--r--tests/sys/netpfil/pf/killstate.sh63
-rw-r--r--tests/sys/netpfil/pf/limits.sh35
-rw-r--r--tests/sys/netpfil/pf/mbuf.sh26
-rw-r--r--tests/sys/netpfil/pf/mld.py35
-rw-r--r--tests/sys/netpfil/pf/nat.sh45
-rw-r--r--tests/sys/netpfil/pf/nat44.py76
-rw-r--r--tests/sys/netpfil/pf/nat64.sh34
-rw-r--r--tests/sys/netpfil/pf/once.sh132
-rw-r--r--tests/sys/netpfil/pf/pflog.sh59
-rw-r--r--tests/sys/netpfil/pf/pfsync.sh466
-rw-r--r--tests/sys/netpfil/pf/rdr.sh58
-rw-r--r--tests/sys/netpfil/pf/route_to.sh729
-rw-r--r--tests/sys/netpfil/pf/sctp.sh3
-rwxr-xr-xtests/sys/netpfil/pf/src_track.sh36
-rw-r--r--tests/sys/netpfil/pf/table.sh100
-rw-r--r--tests/sys/netpfil/pf/utils.subr7
-rw-r--r--tests/sys/opencrypto/blake2_test.c42
-rw-r--r--tests/sys/opencrypto/poly1305_test.c13
-rw-r--r--tests/sys/sound/sndstat.c13
-rw-r--r--tests/sys/sys/bitstring_test.c14
-rw-r--r--tests/sys/vmm/vmm_cred_jail.sh8
66 files changed, 3578 insertions, 362 deletions
diff --git a/tests/sys/aio/Makefile b/tests/sys/aio/Makefile
index 5cddb28c27a6..578d16b7bf32 100644
--- a/tests/sys/aio/Makefile
+++ b/tests/sys/aio/Makefile
@@ -2,6 +2,7 @@ TESTSDIR= ${TESTSBASE}/sys/aio
ATF_TESTS_C+= aio_test
ATF_TESTS_C+= lio_test
+TEST_METADATA+= required_kmods="aio"
TEST_METADATA.aio_test+= timeout="30"
TEST_METADATA.lio_test+= timeout="10"
# Some lio_test testcases involve system resource limitations, so cannot run concurrently
diff --git a/tests/sys/aio/aio_kqueue_test.c b/tests/sys/aio/aio_kqueue_test.c
index 5e5cb40d0752..43a7ebf91f96 100644
--- a/tests/sys/aio/aio_kqueue_test.c
+++ b/tests/sys/aio/aio_kqueue_test.c
@@ -45,7 +45,6 @@
#include <string.h>
#include <unistd.h>
-#include "freebsd_test_suite/macros.h"
#include "local.h"
#define PATH_TEMPLATE "aio.XXXXXXXXXX"
@@ -70,7 +69,6 @@ main (int argc, char *argv[])
int tmp_file = 0;
int i, j;
- PLAIN_REQUIRE_KERNEL_MODULE("aio", 0);
PLAIN_REQUIRE_UNSAFE_AIO(0);
max_queue_per_proc_size = sizeof(max_queue_per_proc);
diff --git a/tests/sys/aio/aio_test.c b/tests/sys/aio/aio_test.c
index 64939825ec66..b9f8e7062203 100644
--- a/tests/sys/aio/aio_test.c
+++ b/tests/sys/aio/aio_test.c
@@ -63,7 +63,6 @@
#include <atf-c.h>
-#include "freebsd_test_suite/macros.h"
#include "local.h"
/*
@@ -452,7 +451,6 @@ aio_file_test(completion comp, struct sigevent *sev, bool vectored)
struct aio_context ac;
int fd;
- ATF_REQUIRE_KERNEL_MODULE("aio");
ATF_REQUIRE_UNSAFE_AIO();
fd = open(FILE_PATHNAME, O_RDWR | O_CREAT, 0600);
@@ -514,7 +512,6 @@ aio_fifo_test(completion comp, struct sigevent *sev)
int error, read_fd = -1, write_fd = -1;
struct aio_context ac;
- ATF_REQUIRE_KERNEL_MODULE("aio");
ATF_REQUIRE_UNSAFE_AIO();
ATF_REQUIRE_MSG(mkfifo(FIFO_PATHNAME, 0600) != -1,
@@ -588,8 +585,6 @@ aio_unix_socketpair_test(completion comp, struct sigevent *sev, bool vectored)
struct rusage ru_before, ru_after;
int sockets[2];
- ATF_REQUIRE_KERNEL_MODULE("aio");
-
ATF_REQUIRE_MSG(socketpair(PF_UNIX, SOCK_STREAM, 0, sockets) != -1,
"socketpair failed: %s", strerror(errno));
@@ -662,7 +657,6 @@ aio_pty_test(completion comp, struct sigevent *sev)
struct termios ts;
int error;
- ATF_REQUIRE_KERNEL_MODULE("aio");
ATF_REQUIRE_UNSAFE_AIO();
ATF_REQUIRE_MSG(openpty(&read_fd, &write_fd, NULL, NULL, NULL) == 0,
@@ -732,7 +726,6 @@ aio_pipe_test(completion comp, struct sigevent *sev)
struct aio_context ac;
int pipes[2];
- ATF_REQUIRE_KERNEL_MODULE("aio");
ATF_REQUIRE_UNSAFE_AIO();
ATF_REQUIRE_MSG(pipe(pipes) != -1,
@@ -793,8 +786,6 @@ aio_md_setup(void)
struct md_ioctl mdio;
char buf[80];
- ATF_REQUIRE_KERNEL_MODULE("aio");
-
mdctl_fd = open("/dev/" MDCTL_NAME, O_RDWR, 0);
ATF_REQUIRE_MSG(mdctl_fd != -1,
"opening /dev/%s failed: %s", MDCTL_NAME, strerror(errno));
@@ -985,9 +976,6 @@ aio_zvol_setup(const char *unique)
char zvol_name[160];
char devname[160];
- ATF_REQUIRE_KERNEL_MODULE("aio");
- ATF_REQUIRE_KERNEL_MODULE("zfs");
-
pid = getpid();
snprintf(vdev_name, sizeof(vdev_name), "%s", ZVOL_VDEV_PATHNAME);
snprintf(pool_name, sizeof(pool_name), "%s_%s.%d", POOL_NAME, unique,
@@ -1057,7 +1045,6 @@ ATF_TC_BODY(aio_large_read_test, tc)
int clamped;
#endif
- ATF_REQUIRE_KERNEL_MODULE("aio");
ATF_REQUIRE_UNSAFE_AIO();
#ifdef __LP64__
@@ -1133,7 +1120,6 @@ ATF_TC_BODY(aio_socket_two_reads, tc)
int s[2];
char c;
- ATF_REQUIRE_KERNEL_MODULE("aio");
#if __FreeBSD_version < 1100101
aft_tc_skip("kernel version %d is too old (%d required)",
__FreeBSD_version, 1100101);
@@ -1187,8 +1173,6 @@ aio_socket_blocking_short_write_test(bool vectored)
socklen_t len;
int s[2];
- ATF_REQUIRE_KERNEL_MODULE("aio");
-
ATF_REQUIRE(socketpair(PF_UNIX, SOCK_STREAM, 0, s) != -1);
len = sizeof(sb_size);
@@ -1356,8 +1340,6 @@ ATF_TC_BODY(aio_socket_short_write_cancel, tc)
socklen_t len;
int s[2];
- ATF_REQUIRE_KERNEL_MODULE("aio");
-
ATF_REQUIRE(socketpair(PF_UNIX, SOCK_STREAM, 0, s) != -1);
len = sizeof(sb_size);
@@ -1423,8 +1405,6 @@ ATF_TC_BODY(aio_socket_shutdown, tc)
size_t bsz;
int error, s[2];
- ATF_REQUIRE_KERNEL_MODULE("aio");
-
ATF_REQUIRE(socketpair(PF_UNIX, SOCK_STREAM, 0, s) != -1);
bsz = 1024;
@@ -1485,7 +1465,6 @@ ATF_TC_BODY(aio_fsync_errors, tc)
int fd;
struct aiocb iocb;
- ATF_REQUIRE_KERNEL_MODULE("aio");
ATF_REQUIRE_UNSAFE_AIO();
fd = open(FILE_PATHNAME, O_RDWR | O_CREAT, 0600);
@@ -1529,7 +1508,6 @@ aio_fsync_test(int op)
unsigned i;
int fd;
- ATF_REQUIRE_KERNEL_MODULE("aio");
ATF_REQUIRE_UNSAFE_AIO();
fd = open(FILE_PATHNAME, O_RDWR | O_CREAT, 0600);
@@ -1618,7 +1596,6 @@ ATF_TC_BODY(aio_writev_dos_iov_len, tc)
ssize_t r;
int fd;
- ATF_REQUIRE_KERNEL_MODULE("aio");
ATF_REQUIRE_UNSAFE_AIO();
fd = open("testfile", O_RDWR | O_CREAT, 0600);
@@ -1656,7 +1633,6 @@ ATF_TC_BODY(aio_writev_dos_iovcnt, tc)
ssize_t len;
int fd;
- ATF_REQUIRE_KERNEL_MODULE("aio");
ATF_REQUIRE_UNSAFE_AIO();
fd = open("testfile", O_RDWR | O_CREAT, 0600);
@@ -1693,7 +1669,6 @@ ATF_TC_BODY(aio_writev_efault, tc)
long seed;
int fd;
- ATF_REQUIRE_KERNEL_MODULE("aio");
ATF_REQUIRE_UNSAFE_AIO();
fd = aio_md_setup();
@@ -1728,7 +1703,6 @@ ATF_TC_BODY(aio_writev_empty_file_poll, tc)
struct aiocb aio;
int fd;
- ATF_REQUIRE_KERNEL_MODULE("aio");
ATF_REQUIRE_UNSAFE_AIO();
fd = open("testfile", O_RDWR | O_CREAT, 0600);
@@ -1751,7 +1725,6 @@ ATF_TC_BODY(aio_writev_empty_file_signal, tc)
struct aiocb aio;
int fd;
- ATF_REQUIRE_KERNEL_MODULE("aio");
ATF_REQUIRE_UNSAFE_AIO();
fd = open("testfile", O_RDWR | O_CREAT, 0600);
@@ -1781,8 +1754,6 @@ ATF_TC_BODY(ev_oneshot, tc)
struct kevent events[1];
struct timespec timeout;
- ATF_REQUIRE_KERNEL_MODULE("aio");
-
kq = kqueue();
ATF_REQUIRE(kq >= 0);
@@ -1844,7 +1815,6 @@ ATF_TC_BODY(vectored_big_iovcnt, tc)
int fd, i;
ssize_t sysctl_len = sizeof(max_buf_aio);
- ATF_REQUIRE_KERNEL_MODULE("aio");
ATF_REQUIRE_UNSAFE_AIO();
if (sysctlbyname(oid, &max_buf_aio, &sysctl_len, NULL, 0) == -1)
@@ -1946,6 +1916,7 @@ ATF_TC_HEAD(vectored_unaligned, tc)
"Vectored AIO should still work even if the iov contains elements "
"that aren't a multiple of the sector size.");
atf_tc_set_md_var(tc, "require.user", "root");
+ atf_tc_set_md_var(tc, "require.kmods", "zfs");
}
ATF_TC_BODY(vectored_unaligned, tc)
{
@@ -1958,7 +1929,6 @@ ATF_TC_BODY(vectored_unaligned, tc)
if (atf_tc_get_config_var_as_bool_wd(tc, "ci", false))
atf_tc_skip("https://bugs.freebsd.org/258766");
- ATF_REQUIRE_KERNEL_MODULE("aio");
ATF_REQUIRE_UNSAFE_AIO();
/*
@@ -2045,6 +2015,7 @@ ATF_TC_WITH_CLEANUP(vectored_zvol_poll);
ATF_TC_HEAD(vectored_zvol_poll, tc)
{
atf_tc_set_md_var(tc, "require.user", "root");
+ atf_tc_set_md_var(tc, "require.kmods", "zfs");
}
ATF_TC_BODY(vectored_zvol_poll, tc)
{
diff --git a/tests/sys/aio/lio_kqueue_test.c b/tests/sys/aio/lio_kqueue_test.c
index f891ab95f3ca..6ac99af9254c 100644
--- a/tests/sys/aio/lio_kqueue_test.c
+++ b/tests/sys/aio/lio_kqueue_test.c
@@ -40,7 +40,6 @@
#include <string.h>
#include <unistd.h>
-#include "freebsd_test_suite/macros.h"
#include "local.h"
#define PATH_TEMPLATE "aio.XXXXXXXXXX"
@@ -68,7 +67,6 @@ main(int argc, char *argv[])
char *file, pathname[sizeof(PATH_TEMPLATE)];
int tmp_file = 0, failed = 0;
- PLAIN_REQUIRE_KERNEL_MODULE("aio", 0);
PLAIN_REQUIRE_UNSAFE_AIO(0);
max_queue_per_proc_size = sizeof(max_queue_per_proc);
diff --git a/tests/sys/aio/lio_test.c b/tests/sys/aio/lio_test.c
index a59f9bd518bc..546cd6c5b790 100644
--- a/tests/sys/aio/lio_test.c
+++ b/tests/sys/aio/lio_test.c
@@ -38,7 +38,6 @@
#include <atf-c.h>
#include "local.h"
-#include "freebsd_test_suite/macros.h"
static sem_t completions;
@@ -71,7 +70,6 @@ ATF_TC_BODY(lio_listio_eagain_kevent, tc)
const char *path="tempfile";
void *udata[2];
- ATF_REQUIRE_KERNEL_MODULE("aio");
ATF_REQUIRE_UNSAFE_AIO();
max_queue_per_proc_size = sizeof(max_queue_per_proc);
diff --git a/tests/sys/common/vnet.subr b/tests/sys/common/vnet.subr
index bd98b02da33f..0a32e6caf813 100644
--- a/tests/sys/common/vnet.subr
+++ b/tests/sys/common/vnet.subr
@@ -42,6 +42,11 @@ vnet_init()
vnet_mkepair()
{
ifname=$(ifconfig epair create)
+ # When transmit checksum offloading is enabled, if_epair does not
+ # compute checksums, it just marks packets that this computation still
+ # needs to be done. However, some test cases verify the checksum.
+ # Therefore disable this for IPv4 and IPv6.
+ ifconfig ${ifname} -txcsum -txcsum6
list_interface $ifname
list_interface ${ifname%a}b
echo ${ifname%a}
diff --git a/tests/sys/file/Makefile b/tests/sys/file/Makefile
index f80d1b271b85..beb4452359b7 100644
--- a/tests/sys/file/Makefile
+++ b/tests/sys/file/Makefile
@@ -5,7 +5,7 @@ BINDIR= ${TESTSDIR}
ATF_TESTS_C+= path_test
TAP_TESTS_C+= closefrom_test
TAP_TESTS_C+= dup_test
-TAP_TESTS_C+= fcntlflags_test
+ATF_TESTS_C+= fcntlflags_test
TAP_TESTS_SH+= flock_test
PLAIN_TESTS_C+= ftruncate_test
PLAIN_TESTS_C+= newfileops_on_fork_test
diff --git a/tests/sys/file/fcntlflags_test.c b/tests/sys/file/fcntlflags_test.c
index c5026e38c48b..15a18c113c4a 100644
--- a/tests/sys/file/fcntlflags_test.c
+++ b/tests/sys/file/fcntlflags_test.c
@@ -24,85 +24,110 @@
* SUCH DAMAGE.
*/
-#include <sys/cdefs.h>
-
+#include <sys/filio.h>
+#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
+#include <atf-c.h>
+
/*
* O_ACCMODE is currently defined incorrectly. This is what it should be.
* Various code depends on the incorrect value.
*/
#define CORRECT_O_ACCMODE (O_ACCMODE | O_EXEC)
-static int testnum;
-
static void
-subtests(const char *path, int omode, const char *omodetext)
+basic_tests(const char *path, int omode, const char *omodetext)
{
int fd, flags1, flags2, flags3;
fd = open(path, omode);
- if (fd == -1)
- printf("not ok %d - open(\"%s\", %s) failed\n",
- testnum++, path, omodetext);
- else
- printf("ok %d - open(\"%s\", %s) succeeded\n",
- testnum++, path, omodetext);
+ ATF_REQUIRE_MSG(fd != -1, "open(\"%s\", %s) failed: %s", path,
+ omodetext, strerror(errno));
+
flags1 = fcntl(fd, F_GETFL);
- if (flags1 == -1)
- printf("not ok %d - fcntl(F_GETFL) failed\n", testnum++);
- else if ((flags1 & CORRECT_O_ACCMODE) == omode)
- printf("ok %d - fcntl(F_GETFL) gave correct result\n",
- testnum++);
- else
- printf("not ok %d - fcntl(F_GETFL) gave incorrect result "
- "(%#x & %#x != %#x)\n",
- testnum++, flags1, CORRECT_O_ACCMODE, omode);
- if (fcntl(fd, F_SETFL, flags1) == -1)
- printf("not ok %d - fcntl(F_SETFL) same flags failed\n",
- testnum++);
- else
- printf("ok %d - fcntl(F_SETFL) same flags succeeded\n",
- testnum++);
+ ATF_REQUIRE_MSG(flags1 != -1, "fcntl(F_GETFL) (1) failed: %s",
+ strerror(errno));
+ ATF_REQUIRE_INTEQ(omode, flags1 & CORRECT_O_ACCMODE);
+ ATF_REQUIRE((flags1 & O_NONBLOCK) == 0);
+
+ ATF_REQUIRE_MSG(fcntl(fd, F_SETFL, flags1) != -1,
+ "fcntl(F_SETFL) same flags failed: %s", strerror(errno));
+
flags2 = fcntl(fd, F_GETFL);
- if (flags2 == -1)
- printf("not ok %d - fcntl(F_GETFL) failed\n", testnum++);
- else if (flags2 == flags1)
- printf("ok %d - fcntl(F_GETFL) gave same result\n",
- testnum++);
- else
- printf("not ok %d - fcntl(F_SETFL) caused fcntl(F_GETFL) to "
- "change from %#x to %#x\n",
- testnum++, flags1, flags2);
- if (fcntl(fd, F_SETFL, flags2 | O_NONBLOCK) == -1)
- printf("not ok %d - fcntl(F_SETFL) O_NONBLOCK failed\n",
- testnum++);
- else
- printf("ok %d - fcntl(F_SETFL) O_NONBLOCK succeeded\n",
- testnum++);
+ ATF_REQUIRE_MSG(flags2 != -1, "fcntl(F_GETFL) (2) failed: %s",
+ strerror(errno));
+ ATF_REQUIRE_INTEQ(flags1, flags2);
+
+ ATF_REQUIRE_MSG(fcntl(fd, F_SETFL, flags2 | O_NONBLOCK) != -1,
+ "fcntl(F_SETFL) O_NONBLOCK failed: %s", strerror(errno));
+
flags3 = fcntl(fd, F_GETFL);
- if (flags3 == -1)
- printf("not ok %d - fcntl(F_GETFL) failed\n", testnum++);
- else if (flags3 == (flags2 | O_NONBLOCK))
- printf("ok %d - fcntl(F_GETFL) gave expected result\n",
- testnum++);
- else
- printf("not ok %d - fcntl(F_SETFL) gave unexpected result "
- "(%#x != %#x)\n",
- testnum++, flags3, flags2 | O_NONBLOCK);
+ ATF_REQUIRE_MSG(flags3 != -1, "fcntl(F_GETFL) (3) failed: %s",
+ strerror(errno));
+ ATF_REQUIRE_INTEQ(flags2 | O_NONBLOCK, flags3);
+
(void)close(fd);
}
-int
-main(int argc __unused, char **argv __unused)
+ATF_TC_WITHOUT_HEAD(read_only_null);
+ATF_TC_BODY(read_only_null, tc)
{
- printf("1..24\n");
- testnum = 1;
- subtests("/dev/null", O_RDONLY, "O_RDONLY");
- subtests("/dev/null", O_WRONLY, "O_WRONLY");
- subtests("/dev/null", O_RDWR, "O_RDWR");
- subtests("/bin/sh", O_EXEC, "O_EXEC");
- return (0);
+ basic_tests("/dev/null", O_RDONLY, "O_RDONLY");
+}
+
+ATF_TC_WITHOUT_HEAD(write_only_null);
+ATF_TC_BODY(write_only_null, tc)
+{
+ basic_tests("/dev/null", O_WRONLY, "O_WRONLY");
+}
+
+ATF_TC_WITHOUT_HEAD(read_write_null);
+ATF_TC_BODY(read_write_null, tc)
+{
+ basic_tests("/dev/null", O_RDWR, "O_RDWR");
+}
+
+ATF_TC_WITHOUT_HEAD(exec_only_sh);
+ATF_TC_BODY(exec_only_sh, tc)
+{
+ basic_tests("/bin/sh", O_EXEC, "O_EXEC");
+}
+
+ATF_TC_WITHOUT_HEAD(fioasync_dev_null);
+ATF_TC_BODY(fioasync_dev_null, tc)
+{
+ int fd, flags1, flags2, val;
+
+ fd = open("/dev/null", O_RDONLY);
+ ATF_REQUIRE_MSG(fd != -1, "open(\"/dev/null\") failed: %s",
+ strerror(errno));
+
+ flags1 = fcntl(fd, F_GETFL);
+ ATF_REQUIRE_MSG(flags1 != -1, "fcntl(F_GETFL) (1) failed: %s",
+ strerror(errno));
+ ATF_REQUIRE((flags1 & O_ASYNC) == 0);
+
+ val = 1;
+ ATF_REQUIRE_ERRNO(EINVAL, ioctl(fd, FIOASYNC, &val) == -1);
+
+ flags2 = fcntl(fd, F_GETFL);
+ ATF_REQUIRE_MSG(flags2 != -1, "fcntl(F_GETFL) (2) failed: %s",
+ strerror(errno));
+ ATF_REQUIRE_INTEQ(flags1, flags2);
+
+ (void)close(fd);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, read_only_null);
+ ATF_TP_ADD_TC(tp, write_only_null);
+ ATF_TP_ADD_TC(tp, read_write_null);
+ ATF_TP_ADD_TC(tp, exec_only_sh);
+ ATF_TP_ADD_TC(tp, fioasync_dev_null);
+
+ return (atf_no_error());
}
diff --git a/tests/sys/fs/fusefs/last_local_modify.cc b/tests/sys/fs/fusefs/last_local_modify.cc
index 5fcd3c36c892..6b8c19f1efc7 100644
--- a/tests/sys/fs/fusefs/last_local_modify.cc
+++ b/tests/sys/fs/fusefs/last_local_modify.cc
@@ -174,7 +174,15 @@ static void* write_th(void* arg) {
if (sem)
sem_wait(sem);
- fd = open("mountpoint/some_file.txt", O_RDWR);
+ /*
+ * Open the file in direct mode.
+ * The race condition affects both direct and non-direct writes, and
+ * they have separate code paths. However, in the non-direct case, the
+ * kernel updates last_local_modify _before_ sending FUSE_WRITE to the
+ * server. So the technique that this test program uses to invoke the
+ * race cannot work. Therefore, test with O_DIRECT only.
+ */
+ fd = open("mountpoint/some_file.txt", O_RDWR | O_DIRECT);
if (fd < 0)
return (void*)(intptr_t)errno;
@@ -332,7 +340,7 @@ TEST_P(LastLocalModify, lookup)
/* Wait for FUSE_SETATTR to be sent */
sem_wait(&sem);
- /* Lookup again, which will race with setattr */
+ /* Lookup again, which will race with the mutator */
ASSERT_EQ(0, stat(FULLPATH, &sb)) << strerror(errno);
ASSERT_EQ((off_t)newsize, sb.st_size);
diff --git a/tests/sys/fs/fusefs/mockfs.cc b/tests/sys/fs/fusefs/mockfs.cc
index 65cdc3919652..e8081dea9604 100644
--- a/tests/sys/fs/fusefs/mockfs.cc
+++ b/tests/sys/fs/fusefs/mockfs.cc
@@ -472,7 +472,7 @@ MockFS::MockFS(int max_read, int max_readahead, bool allow_other,
sprintf(fdstr, "%d", m_fuse_fd);
build_iovec(&iov, &iovlen, "fd", fdstr, -1);
if (m_maxread > 0) {
- char val[10];
+ char val[12];
snprintf(val, sizeof(val), "%d", m_maxread);
build_iovec(&iov, &iovlen, "max_read=", &val, -1);
diff --git a/tests/sys/fs/fusefs/release.cc b/tests/sys/fs/fusefs/release.cc
index b664ba512b64..9df236bfbaf7 100644
--- a/tests/sys/fs/fusefs/release.cc
+++ b/tests/sys/fs/fusefs/release.cc
@@ -29,6 +29,9 @@
*/
extern "C" {
+#include <sys/socket.h>
+#include <sys/un.h>
+
#include <fcntl.h>
#include <unistd.h>
}
@@ -188,6 +191,57 @@ TEST_F(Release, ok)
ASSERT_EQ(0, close(fd)) << strerror(errno);
}
+/*
+ * Nothing bad should happen when closing a Unix-domain named socket that
+ * contains a fusefs file descriptor within its receive buffer.
+ * Regression test for
+ * https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=289686
+ */
+TEST_F(Release, scm_rights)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ struct msghdr msg;
+ struct iovec iov;
+ char message[CMSG_SPACE(sizeof(int))];
+ uint64_t ino = 42;
+ int fd;
+ int s[2];
+ union {
+ char buf[CMSG_SPACE(sizeof(fd))];
+ struct cmsghdr align;
+ } u;
+
+ expect_lookup(RELPATH, ino, 1);
+ expect_open(ino, 0, 1);
+ expect_flush(ino, 1, ReturnErrno(0));
+ expect_release(ino, getpid(), O_RDONLY, 0);
+
+ ASSERT_EQ(0, socketpair(AF_UNIX, SOCK_STREAM, 0, s)) << strerror(errno);
+
+ fd = open(FULLPATH, O_RDONLY);
+ ASSERT_LE(0, fd) << strerror(errno);
+
+ memset(&message, 0, sizeof(message));
+ memset(&msg, 0, sizeof(msg));
+ iov.iov_base = NULL;
+ iov.iov_len = 0;
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ msg.msg_control = u.buf,
+ msg.msg_controllen = sizeof(u.buf);
+ struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_RIGHTS;
+ cmsg->cmsg_len = CMSG_LEN(sizeof(fd));
+ memcpy(CMSG_DATA(cmsg), &fd, sizeof(fd));
+ ASSERT_GE(sendmsg(s[0], &msg, 0), 0) << strerror(errno);
+
+ close(fd); // Close fd within our process
+ close(s[0]);
+ close(s[1]); // The last copy of fd is within this socket's rcvbuf
+}
+
/* When closing a file with a POSIX file lock, release should release the lock*/
TEST_F(ReleaseWithLocks, unlock_on_close)
{
diff --git a/tests/sys/fs/fusefs/write.cc b/tests/sys/fs/fusefs/write.cc
index 1fe2e3cc522d..f5573a865a04 100644
--- a/tests/sys/fs/fusefs/write.cc
+++ b/tests/sys/fs/fusefs/write.cc
@@ -32,9 +32,11 @@ extern "C" {
#include <sys/param.h>
#include <sys/mman.h>
#include <sys/resource.h>
+#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/uio.h>
+#include <sys/un.h>
#include <aio.h>
#include <fcntl.h>
@@ -1398,6 +1400,77 @@ TEST_F(WriteBackAsync, eof)
leak(fd);
}
+/*
+ * Nothing bad should happen if a file with a dirty writeback cache is closed
+ * while the last copy lies in some socket's socket buffer. Inspired by bug
+ * 289686 .
+ */
+TEST_F(WriteBackAsync, scm_rights)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ const char *CONTENTS = "abcdefgh";
+ uint64_t ino = 42;
+ int fd;
+ ssize_t bufsize = strlen(CONTENTS);
+ int s[2];
+ struct msghdr msg;
+ struct iovec iov;
+ char message[CMSG_SPACE(sizeof(int))];
+ union {
+ char buf[CMSG_SPACE(sizeof(fd))];
+ struct cmsghdr align;
+ } u;
+
+ expect_lookup(RELPATH, ino, 0);
+ expect_open(ino, 0, 1);
+ /* VOP_SETATTR will try to set timestamps during flush */
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_SETATTR &&
+ in.header.nodeid == ino);
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, attr);
+ out.body.attr.attr.ino = ino;
+ out.body.attr.attr.mode = S_IFREG | 0644;
+ out.body.attr.attr.size = bufsize;
+ })));
+
+ expect_write(ino, 0, bufsize, bufsize, CONTENTS);
+ expect_flush(ino, 1, ReturnErrno(0));
+ expect_release(ino, ReturnErrno(0));
+
+ /* Open a file on the fusefs file system */
+ fd = open(FULLPATH, O_RDWR);
+ ASSERT_LE(0, fd) << strerror(errno);
+
+ /* Write to the file to dirty its writeback cache */
+ ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
+
+ /* Send the file into a socket */
+ ASSERT_EQ(0, socketpair(AF_UNIX, SOCK_STREAM, 0, s)) << strerror(errno);
+ memset(&message, 0, sizeof(message));
+ memset(&msg, 0, sizeof(msg));
+ iov.iov_base = NULL;
+ iov.iov_len = 0;
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ msg.msg_control = u.buf,
+ msg.msg_controllen = sizeof(u.buf);
+ struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_RIGHTS;
+ cmsg->cmsg_len = CMSG_LEN(sizeof(fd));
+ memcpy(CMSG_DATA(cmsg), &fd, sizeof(fd));
+ ASSERT_GE(sendmsg(s[0], &msg, 0), 0) << strerror(errno);
+
+ close(fd); // Close fd within our process
+ close(s[0]);
+ close(s[1]); // The last copy of fd is within this socket's rcvbuf
+}
+
/*
* When a file has dirty writes that haven't been flushed, the server's notion
* of its mtime and ctime will be wrong. The kernel should ignore those if it
diff --git a/tests/sys/kern/socket_accf.c b/tests/sys/kern/socket_accf.c
index ae6522397cf7..939ca9495689 100644
--- a/tests/sys/kern/socket_accf.c
+++ b/tests/sys/kern/socket_accf.c
@@ -69,14 +69,8 @@ clientsock(struct sockaddr_in *sin)
static void
accfon(int l, struct accept_filter_arg *af)
{
-
if (setsockopt(l, SOL_SOCKET, SO_ACCEPTFILTER, af, sizeof(*af)) != 0) {
- if (errno == ENOENT)
- atf_tc_skip("Accept filter %s not loaded in kernel",
- af->af_name);
- else
- atf_tc_fail("setsockopt(SO_ACCEPTFILTER): %s",
- strerror(errno));
+ atf_tc_fail("setsockopt(SO_ACCEPTFILTER): %s", strerror(errno));
}
}
@@ -95,7 +89,11 @@ usend(int s, const void *msg, size_t len)
return (rv);
}
-ATF_TC_WITHOUT_HEAD(data);
+ATF_TC(data);
+ATF_TC_HEAD(data, tc)
+{
+ atf_tc_set_md_var(tc, "require.kmods", "accf_data");
+}
ATF_TC_BODY(data, tc)
{
struct accept_filter_arg afa = {
@@ -113,7 +111,11 @@ ATF_TC_BODY(data, tc)
ATF_REQUIRE((a = accept(l, NULL, 0)) > 0);
}
-ATF_TC_WITHOUT_HEAD(http);
+ATF_TC(http);
+ATF_TC_HEAD(http, tc)
+{
+ atf_tc_set_md_var(tc, "require.kmods", "accf_http");
+}
ATF_TC_BODY(http, tc)
{
struct accept_filter_arg afa = {
@@ -152,7 +154,11 @@ ATF_TC_BODY(http, tc)
ATF_REQUIRE((a = accept(l, NULL, 0)) > 0);
}
-ATF_TC_WITHOUT_HEAD(tls);
+ATF_TC(tls);
+ATF_TC_HEAD(tls, tc)
+{
+ atf_tc_set_md_var(tc, "require.kmods", "accf_tls");
+}
ATF_TC_BODY(tls, tc)
{
struct accept_filter_arg afa = {
@@ -210,7 +216,11 @@ ATF_TC_BODY(tls, tc)
}
/* Check changing to a different filter. */
-ATF_TC_WITHOUT_HEAD(change);
+ATF_TC(change);
+ATF_TC_HEAD(change, tc)
+{
+ atf_tc_set_md_var(tc, "require.kmods", "accf_data accf_http");
+}
ATF_TC_BODY(change, tc)
{
struct accept_filter_arg dfa = {
diff --git a/tests/sys/kern/unix_stream.c b/tests/sys/kern/unix_stream.c
index bb811f78f620..49d621dc5b0a 100644
--- a/tests/sys/kern/unix_stream.c
+++ b/tests/sys/kern/unix_stream.c
@@ -467,6 +467,29 @@ ATF_TC_BODY(peershutdown_wakeup_kevent, tc)
});
}
+ATF_TC_WITHOUT_HEAD(ourshutdown_kevent);
+ATF_TC_BODY(ourshutdown_kevent, tc)
+{
+ struct kevent kev;
+ int sv[2], kq;
+
+ do_socketpair(sv);
+ ATF_REQUIRE(kq = kqueue());
+
+ EV_SET(&kev, sv[1], EVFILT_WRITE, EV_ADD, 0, 0, NULL);
+ ATF_REQUIRE(kevent(kq, &kev, 1, NULL, 0, NULL) == 0);
+
+ ATF_REQUIRE(shutdown(sv[1], SHUT_WR) == 0);
+
+ ATF_REQUIRE(kevent(kq, NULL, 0, &kev, 1, NULL) == 1);
+ ATF_REQUIRE(kev.ident == (uintptr_t)sv[1] &&
+ kev.filter == EVFILT_WRITE &&
+ kev.flags == EV_EOF);
+
+ close(sv[0]);
+ close(sv[1]);
+}
+
ATF_TP_ADD_TCS(tp)
{
ATF_TP_ADD_TC(tp, getpeereid);
@@ -482,6 +505,7 @@ ATF_TP_ADD_TCS(tp)
ATF_TP_ADD_TC(tp, peershutdown_wakeup_select);
ATF_TP_ADD_TC(tp, peershutdown_wakeup_poll);
ATF_TP_ADD_TC(tp, peershutdown_wakeup_kevent);
+ ATF_TP_ADD_TC(tp, ourshutdown_kevent);
return atf_no_error();
}
diff --git a/tests/sys/mqueue/mqtest1.c b/tests/sys/mqueue/mqtest1.c
index 78acde1122ce..7f4ee74f9e8d 100644
--- a/tests/sys/mqueue/mqtest1.c
+++ b/tests/sys/mqueue/mqtest1.c
@@ -6,8 +6,6 @@
#include <signal.h>
#include <stdio.h>
-#include "freebsd_test_suite/macros.h"
-
#define MQNAME "/mytstqueue1"
int
@@ -18,8 +16,6 @@ main(void)
mqd_t mq;
int status;
- PLAIN_REQUIRE_KERNEL_MODULE("mqueuefs", 0);
-
attr.mq_maxmsg = 2;
attr.mq_msgsize = 100;
mq = mq_open(MQNAME, O_CREAT | O_RDWR | O_EXCL, 0666, &attr);
diff --git a/tests/sys/mqueue/mqtest2.c b/tests/sys/mqueue/mqtest2.c
index 08d4d9a8003a..efdca42eb37e 100644
--- a/tests/sys/mqueue/mqtest2.c
+++ b/tests/sys/mqueue/mqtest2.c
@@ -9,8 +9,6 @@
#include <stdlib.h>
#include <unistd.h>
-#include "freebsd_test_suite/macros.h"
-
#define MQNAME "/mytstqueue2"
#define LOOPS 1000
#define PRIO 10
@@ -29,9 +27,7 @@ main(void)
mqd_t mq;
int status;
pid_t pid;
-
- PLAIN_REQUIRE_KERNEL_MODULE("mqueuefs", 0);
-
+
mq_unlink(MQNAME);
attr.mq_maxmsg = 5;
diff --git a/tests/sys/mqueue/mqtest3.c b/tests/sys/mqueue/mqtest3.c
index 65b8f4fcc2b9..b2c9155c37ba 100644
--- a/tests/sys/mqueue/mqtest3.c
+++ b/tests/sys/mqueue/mqtest3.c
@@ -10,8 +10,6 @@
#include <stdlib.h>
#include <unistd.h>
-#include "freebsd_test_suite/macros.h"
-
#define MQNAME "/mytstqueue3"
#define LOOPS 1000
#define PRIO 10
@@ -32,8 +30,6 @@ main(void)
mqd_t mq;
pid_t pid;
- PLAIN_REQUIRE_KERNEL_MODULE("mqueuefs", 0);
-
mq_unlink(MQNAME);
attr.mq_maxmsg = 5;
diff --git a/tests/sys/mqueue/mqtest4.c b/tests/sys/mqueue/mqtest4.c
index 99841c670b5c..68648b01a9e4 100644
--- a/tests/sys/mqueue/mqtest4.c
+++ b/tests/sys/mqueue/mqtest4.c
@@ -11,8 +11,6 @@
#include <stdlib.h>
#include <unistd.h>
-#include "freebsd_test_suite/macros.h"
-
#define MQNAME "/mytstqueue4"
#define LOOPS 1000
#define PRIO 10
@@ -33,8 +31,6 @@ main(void)
int kq, status;
pid_t pid;
- PLAIN_REQUIRE_KERNEL_MODULE("mqueuefs", 0);
-
mq_unlink(MQNAME);
attr.mq_maxmsg = 5;
diff --git a/tests/sys/mqueue/mqtest5.c b/tests/sys/mqueue/mqtest5.c
index f48ef1121289..6671839829bf 100644
--- a/tests/sys/mqueue/mqtest5.c
+++ b/tests/sys/mqueue/mqtest5.c
@@ -11,8 +11,6 @@
#include <stdlib.h>
#include <unistd.h>
-#include "freebsd_test_suite/macros.h"
-
#define MQNAME "/mytstqueue5"
#define LOOPS 1000
#define PRIO 10
@@ -35,8 +33,6 @@ main(void)
mqd_t mq;
pid_t pid;
- PLAIN_REQUIRE_KERNEL_MODULE("mqueuefs", 0);
-
mq_unlink(MQNAME);
sigemptyset(&set);
diff --git a/tests/sys/mqueue/mqueue_test.sh b/tests/sys/mqueue/mqueue_test.sh
index 8b3f45159ad9..4f947dc260ed 100644
--- a/tests/sys/mqueue/mqueue_test.sh
+++ b/tests/sys/mqueue/mqueue_test.sh
@@ -27,7 +27,7 @@
mqtest1_head()
{
- :
+ atf_set require.kmods mqueuefs
}
mqtest1_body()
{
@@ -36,7 +36,7 @@ mqtest1_body()
mqtest2_head()
{
- :
+ atf_set require.kmods mqueuefs
}
mqtest2_body()
{
@@ -45,7 +45,7 @@ mqtest2_body()
mqtest3_head()
{
- :
+ atf_set require.kmods mqueuefs
}
mqtest3_body()
{
@@ -54,7 +54,7 @@ mqtest3_body()
mqtest4_head()
{
- :
+ atf_set require.kmods mqueuefs
}
mqtest4_body()
{
@@ -63,7 +63,7 @@ mqtest4_body()
mqtest5_head()
{
- :
+ atf_set require.kmods mqueuefs
}
mqtest5_body()
{
@@ -74,7 +74,7 @@ atf_init_test_cases()
{
atf_add_test_case mqtest1
atf_add_test_case mqtest2
- #atf_add_test_case mqtest3
- #atf_add_test_case mqtest4
+ atf_add_test_case mqtest3
+ atf_add_test_case mqtest4
atf_add_test_case mqtest5
}
diff --git a/tests/sys/net/Makefile b/tests/sys/net/Makefile
index 65cc99a3e932..e390c6e8059d 100644
--- a/tests/sys/net/Makefile
+++ b/tests/sys/net/Makefile
@@ -40,6 +40,7 @@ ${PACKAGE}FILESMODE_stp.py= 0555
MAN=
PROGS+= randsleep
+PROGS+= transient_tuntap
CFLAGS+= -I${.CURDIR:H:H}
diff --git a/tests/sys/net/if_bridge_test.sh b/tests/sys/net/if_bridge_test.sh
index 0c19903714b1..b3405fd978c8 100755
--- a/tests/sys/net/if_bridge_test.sh
+++ b/tests/sys/net/if_bridge_test.sh
@@ -586,6 +586,25 @@ gif_body()
jexec one ping -c 1 -s 1200 198.51.100.2
atf_check -s exit:0 -o ignore \
jexec one ping -c 1 -s 2000 198.51.100.2
+
+ # Assigning IP addresses on the gif tunneling interfaces
+ jexec one sysctl net.link.bridge.member_ifaddrs=1
+ atf_check -s exit:0 -o ignore \
+ jexec one ifconfig ${gif_one} 192.168.0.224/24 192.168.169.254
+ atf_check -s exit:0 -o ignore \
+ jexec one ifconfig ${gif_one} inet6 no_dad 2001:db8::1/64
+ jexec one ifconfig ${bridge_one} deletem ${gif_one}
+ atf_check -s exit:0 -o ignore \
+ jexec one ifconfig ${bridge_one} addm ${gif_one}
+
+ jexec two sysctl net.link.bridge.member_ifaddrs=0
+ atf_check -s exit:0 -o ignore \
+ jexec two ifconfig ${gif_two} 192.168.169.254/24 192.168.0.224
+ atf_check -s exit:0 -o ignore \
+ jexec two ifconfig ${gif_two} inet6 no_dad 2001:db8::2/64
+ jexec two ifconfig ${bridge_two} deletem ${gif_two}
+ atf_check -s exit:0 -o ignore \
+ jexec two ifconfig ${bridge_two} addm ${gif_two}
}
gif_cleanup()
diff --git a/tests/sys/net/if_epair.c b/tests/sys/net/if_epair.c
index 0817b298d427..5ee4a48aea86 100644
--- a/tests/sys/net/if_epair.c
+++ b/tests/sys/net/if_epair.c
@@ -44,6 +44,7 @@ ATF_TC(params);
ATF_TC_HEAD(params, tc)
{
atf_tc_set_md_var(tc, "require.user", "root");
+ atf_tc_set_md_var(tc, "require.kmods", "if_epair");
}
ATF_TC_BODY(params, tc)
@@ -51,9 +52,6 @@ ATF_TC_BODY(params, tc)
struct ifreq ifr;
int s;
- kldload("if_epair");
- ATF_REQUIRE_KERNEL_MODULE("if_epair");
-
s = socket(AF_INET, SOCK_DGRAM, 0);
if (s < 0)
atf_tc_fail("Failed to create socket");
diff --git a/tests/sys/net/if_ovpn/if_ovpn.sh b/tests/sys/net/if_ovpn/if_ovpn.sh
index 0281e7fc273d..9dafce2242d8 100644
--- a/tests/sys/net/if_ovpn/if_ovpn.sh
+++ b/tests/sys/net/if_ovpn/if_ovpn.sh
@@ -510,6 +510,7 @@ linklocal_head()
linklocal_body()
{
ovpn_init
+ ovpn_check_version 2.7.0
l=$(vnet_mkepair)
@@ -1399,6 +1400,7 @@ float_head()
float_body()
{
ovpn_init
+ ovpn_check_version 2.7.0
l=$(vnet_mkepair)
diff --git a/tests/sys/net/if_ovpn/if_ovpn_c.c b/tests/sys/net/if_ovpn/if_ovpn_c.c
index fa8a9a07fa35..7b558f1975dd 100644
--- a/tests/sys/net/if_ovpn/if_ovpn_c.c
+++ b/tests/sys/net/if_ovpn/if_ovpn_c.c
@@ -78,6 +78,7 @@ ATF_TC_WITH_CLEANUP(tcp);
ATF_TC_HEAD(tcp, tc)
{
atf_tc_set_md_var(tc, "require.user", "root");
+ atf_tc_set_md_var(tc, "require.kmods", "if_ovpn");
}
ATF_TC_BODY(tcp, tc)
@@ -87,10 +88,6 @@ ATF_TC_BODY(tcp, tc)
int ret;
nvlist_t *nvl;
- /* Ensure the module is loaded. */
- if (kldfind("if_ovpn") == -1 && errno == ENOENT)
- atf_tc_skip("if_ovpn not loaded");
-
ovpn_fd = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0);
/* Kick off a connect so there's a local address set, which we need for
diff --git a/tests/sys/net/if_ovpn/utils.subr b/tests/sys/net/if_ovpn/utils.subr
index 0da35119b2bf..fbe7dc98630a 100644
--- a/tests/sys/net/if_ovpn/utils.subr
+++ b/tests/sys/net/if_ovpn/utils.subr
@@ -40,6 +40,25 @@ ovpn_init()
fi
}
+ovpn_check_version()
+{
+ expected=$1
+
+ expected_minor=$(echo $expected |
+ awk '{ split($1, ver, "\."); print(ver[2]); }')
+ actual_minor=$(openvpn --version 2>&1 |
+ awk 'NR == 1 \
+ { \
+ split($2, ver, "\."); \
+ split(ver[2], minor, "_"); \
+ print(minor[1]); \
+ }')
+
+ if [ ${actual_minor} -lt ${expected_minor} ]; then
+ atf_skip "OpenVPN version < ${expected}"
+ fi
+}
+
ovpn_cleanup()
{
for jail in `cat ovpn_jails.lst | sort -u`
diff --git a/tests/sys/net/if_tun_test.sh b/tests/sys/net/if_tun_test.sh
index a4ffe66e04ce..f4ce7800272e 100755
--- a/tests/sys/net/if_tun_test.sh
+++ b/tests/sys/net/if_tun_test.sh
@@ -56,8 +56,30 @@ basic_cleanup()
vnet_cleanup
}
+atf_test_case "transient" "cleanup"
+transient_head()
+{
+ atf_set descr "Test transient tunnel support"
+ atf_set require.user root
+}
+transient_body()
+{
+ vnet_init
+ vnet_mkjail one
+
+ tun=$(jexec one ifconfig tun create)
+ atf_check -s exit:0 -o not-empty jexec one ifconfig ${tun}
+ jexec one $(atf_get_srcdir)/transient_tuntap /dev/${tun}
+ atf_check -s not-exit:0 -e not-empty jexec one ifconfig ${tun}
+}
+transient_cleanup()
+{
+ vnet_cleanup
+}
+
atf_init_test_cases()
{
atf_add_test_case "235704"
atf_add_test_case "basic"
+ atf_add_test_case "transient"
}
diff --git a/tests/sys/net/transient_tuntap.c b/tests/sys/net/transient_tuntap.c
new file mode 100644
index 000000000000..b0cf43064317
--- /dev/null
+++ b/tests/sys/net/transient_tuntap.c
@@ -0,0 +1,54 @@
+/*-
+ * Copyright (c) 2024 Kyle Evans <kevans@FreeBSD.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+/*
+ * This test simply configures the tunnel as transient and exits. By the time
+ * we return, the tunnel should be gone because the last reference disappears.
+ */
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <net/if_tun.h>
+#include <net/if_tap.h>
+
+#include <assert.h>
+#include <err.h>
+#include <fcntl.h>
+#include <string.h>
+#include <unistd.h>
+
+int
+main(int argc, char *argv[])
+{
+ unsigned long tunreq;
+ const char *tundev;
+ int one = 1, tunfd;
+
+ assert(argc > 1);
+ tundev = argv[1];
+
+ tunfd = open(tundev, O_RDWR);
+ assert(tunfd >= 0);
+
+ /*
+ * These are technically the same request, but we'll use the technically
+ * correct one just in case.
+ */
+ if (strstr(tundev, "tun") != NULL) {
+ tunreq = TUNSTRANSIENT;
+ } else {
+ assert(strstr(tundev, "tap") != NULL);
+ tunreq = TAPSTRANSIENT;
+ }
+
+ if (ioctl(tunfd, tunreq, &one) == -1)
+ err(1, "ioctl");
+
+ /* Final close should destroy the tunnel automagically. */
+ close(tunfd);
+
+ return (0);
+}
diff --git a/tests/sys/netinet/Makefile b/tests/sys/netinet/Makefile
index cc525bf24480..b742342beecb 100644
--- a/tests/sys/netinet/Makefile
+++ b/tests/sys/netinet/Makefile
@@ -48,7 +48,7 @@ TEST_METADATA.forward+= required_programs="python" \
TEST_METADATA.output+= required_programs="python"
TEST_METADATA.redirect+= required_programs="python"
-PROGS= udp_dontroute tcp_user_cookie sendto-IP_MULTICAST_IF
+PROGS= udp_dontroute tcp_user_cookie multicast-send multicast-receive
${PACKAGE}FILES+= redirect.py
diff --git a/tests/sys/netinet/divert.sh b/tests/sys/netinet/divert.sh
index d50620d94a09..f521038ba687 100755
--- a/tests/sys/netinet/divert.sh
+++ b/tests/sys/netinet/divert.sh
@@ -29,19 +29,13 @@
. $(atf_get_srcdir)/../common/vnet.subr
-load_divert_module() {
- kldstat -q -m ipdivert
- if [ $? -ne 0 ]; then
- atf_skip "ipdivert module is not loaded"
- fi
-}
-
atf_test_case "ipdivert_ip_output_remote_success" "cleanup"
ipdivert_ip_output_remote_success_head() {
atf_set descr 'Test diverting IPv4 packet to remote destination'
atf_set require.user root
atf_set require.progs python3 scapy
+ atf_set require.kmods ipdivert
}
ipdivert_ip_output_remote_success_body() {
@@ -62,7 +56,6 @@ ipdivert_ip_output_remote_success_body() {
fi
vnet_init
- load_divert_module
ip4a="192.0.2.5"
ip4b="192.0.2.6"
@@ -97,6 +90,7 @@ ipdivert_ip_input_local_success_head() {
atf_set descr 'Test diverting IPv4 packet to remote destination'
atf_set require.user root
atf_set require.progs python3 scapy
+ atf_set require.kmods ipdivert
}
ipdivert_ip_input_local_success_body() {
@@ -117,7 +111,6 @@ ipdivert_ip_input_local_success_body() {
fi
vnet_init
- load_divert_module
ip4a="192.0.2.5"
ip4b="192.0.2.6"
diff --git a/tests/sys/netinet/multicast-receive.c b/tests/sys/netinet/multicast-receive.c
new file mode 100644
index 000000000000..81d0f10f5cfe
--- /dev/null
+++ b/tests/sys/netinet/multicast-receive.c
@@ -0,0 +1,130 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2025 Gleb Smirnoff <glebius@FreeBSD.org>
+ *
+ * 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/socket.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <arpa/inet.h>
+#include <net/if.h>
+#include <assert.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+#include <err.h>
+
+static in_port_t
+atop(const char *c)
+{
+ unsigned long ul;
+
+ errno = 0;
+ if ((ul = strtol(c, NULL, 10)) < 1 || ul > IPPORT_MAX || errno != 0)
+ err(1, "can't parse %s", c);
+
+ return ((in_port_t)ul);
+}
+
+int
+main(int argc, char *argv[])
+{
+ char buf[IP_MAXPACKET + 1];
+ struct sockaddr_in sin = {
+ .sin_family = AF_INET,
+ .sin_len = sizeof(struct sockaddr_in),
+ };
+ socklen_t slen = sizeof(struct sockaddr_in);
+ struct in_addr maddr, ifaddr;
+ ssize_t len;
+ int s, ifindex;
+ bool index;
+
+ if (argc < 4)
+usage:
+ errx(1, "Usage: %s (ip_mreq|ip_mreqn|group_req) "
+ "IPv4-group port interface", argv[0]);
+
+ if (inet_pton(AF_INET, argv[2], &maddr) != 1)
+ err(1, "inet_pton(%s) failed", argv[2]);
+ sin.sin_port = htons(atop(argv[3]));
+ if (inet_pton(AF_INET, argv[4], &ifaddr) == 1)
+ index = false;
+ else if ((ifindex = if_nametoindex(argv[4])) > 0)
+ index = true;
+ else if (strcmp(argv[4], "0") == 0) {
+ ifindex = 0;
+ index = true;
+ } else
+ err(1, "if_nametoindex(%s) failed", argv[4]);
+
+ assert((s = socket(PF_INET, SOCK_DGRAM, 0)) > 0);
+ assert(bind(s, (struct sockaddr *)&sin, sizeof(sin)) == 0);
+
+ if (strcmp(argv[1], "ip_mreq") == 0) {
+ if (index)
+ errx(1, "ip_mreq doesn't accept index");
+ struct ip_mreq mreq = {
+ .imr_multiaddr = maddr,
+ .imr_interface = ifaddr,
+ };
+ assert(setsockopt(s, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq,
+ sizeof(mreq)) == 0);
+ } else if (strcmp(argv[1], "ip_mreqn") == 0) {
+ /*
+ * ip_mreqn shall be used with index, but for testing
+ * purposes accept address too.
+ */
+ struct ip_mreqn mreqn = {
+ .imr_multiaddr = maddr,
+ .imr_address = index ? (struct in_addr){ 0 } : ifaddr,
+ .imr_ifindex = index ? ifindex : 0,
+ };
+ assert(setsockopt(s, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreqn,
+ sizeof(mreqn)) == 0);
+ } else if (strcmp(argv[1], "group_req") == 0) {
+ if (!index)
+ errx(1, "group_req expects index");
+ struct group_req greq = { .gr_interface = ifindex };
+ struct sockaddr_in *gsa = (struct sockaddr_in *)&greq.gr_group;
+
+ gsa->sin_family = AF_INET;
+ gsa->sin_len = sizeof(struct sockaddr_in);
+ gsa->sin_addr = maddr;
+ assert(setsockopt(s, IPPROTO_IP, MCAST_JOIN_GROUP, &greq,
+ sizeof(greq)) == 0);
+ } else
+ goto usage;
+
+ assert((len = recvfrom(s, buf, sizeof(buf) - 1, 0,
+ (struct sockaddr *)&sin, &slen)) > 0);
+ buf[len] = '\0';
+ printf("%s:%u %s\n", inet_ntoa(sin.sin_addr), ntohs(sin.sin_port), buf);
+
+ return (0);
+}
diff --git a/tests/sys/netinet/sendto-IP_MULTICAST_IF.c b/tests/sys/netinet/multicast-send.c
index d478e4da0b3b..f10b2b6338dd 100644
--- a/tests/sys/netinet/sendto-IP_MULTICAST_IF.c
+++ b/tests/sys/netinet/multicast-send.c
@@ -28,35 +28,69 @@
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
+#include <net/if.h>
#include <assert.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
#include <err.h>
+static in_port_t
+atop(const char *c)
+{
+ unsigned long ul;
+
+ errno = 0;
+ if ((ul = strtol(c, NULL, 10)) < 1 || ul > IPPORT_MAX || errno != 0)
+ err(1, "can't parse %s", c);
+
+ return ((in_port_t)ul);
+}
+
int
main(int argc, char *argv[])
{
- struct sockaddr_in sin = {
+ struct sockaddr_in src = {
+ .sin_family = AF_INET,
+ .sin_len = sizeof(struct sockaddr_in),
+ }, dst = {
.sin_family = AF_INET,
.sin_len = sizeof(struct sockaddr_in),
};
+ struct ip_mreqn mreqn;
struct in_addr in;
- int s, rv;
+ int s;
+ bool index;
- if (argc < 2)
- errx(1, "Usage: %s IPv4-address", argv[0]);
+ if (argc < 7)
+ errx(1, "Usage: %s src-IPv4 src-port dst-IPv4 dst-port "
+ "interface payload", argv[0]);
- if (inet_pton(AF_INET, argv[1], &in) != 1)
+ if (inet_pton(AF_INET, argv[1], &src.sin_addr) != 1)
err(1, "inet_pton(%s) failed", argv[1]);
+ src.sin_port = htons(atop(argv[2]));
+ if (inet_pton(AF_INET, argv[3], &dst.sin_addr) != 1)
+ err(1, "inet_pton(%s) failed", argv[3]);
+ dst.sin_port = htons(atop(argv[4]));
+ if (inet_pton(AF_INET, argv[5], &in) == 1)
+ index = false;
+ else if ((mreqn.imr_ifindex = if_nametoindex(argv[5])) > 0)
+ index = true;
+ else
+ err(1, "if_nametoindex(%s) failed", argv[5]);
assert((s = socket(PF_INET, SOCK_DGRAM, 0)) > 0);
- assert(bind(s, (struct sockaddr *)&sin, sizeof(sin)) == 0);
- assert(setsockopt(s, IPPROTO_IP, IP_MULTICAST_IF, &in, sizeof(in))
- == 0);
- /* RFC 6676 */
- assert(inet_pton(AF_INET, "233.252.0.1", &sin.sin_addr) == 1);
- sin.sin_port = htons(6676);
- rv = sendto(s, &sin, sizeof(sin), 0,
- (struct sockaddr *)&sin, sizeof(sin));
- if (rv != sizeof(sin))
+ assert(bind(s, (struct sockaddr *)&src, sizeof(src)) == 0);
+ if (index)
+ assert(setsockopt(s, IPPROTO_IP, IP_MULTICAST_IF, &mreqn,
+ sizeof(mreqn)) == 0);
+ else
+ assert(setsockopt(s, IPPROTO_IP, IP_MULTICAST_IF, &in,
+ sizeof(in)) == 0);
+ if (sendto(s, argv[6], strlen(argv[6]) + 1, 0, (struct sockaddr *)&dst,
+ sizeof(dst)) != (ssize_t)strlen(argv[6]) + 1)
err(1, "sendto failed");
return (0);
diff --git a/tests/sys/netinet/multicast.sh b/tests/sys/netinet/multicast.sh
index eb2b962dac70..a3854fd2fd20 100644..100755
--- a/tests/sys/netinet/multicast.sh
+++ b/tests/sys/netinet/multicast.sh
@@ -26,36 +26,130 @@
. $(atf_get_srcdir)/../common/vnet.subr
-# See regression fixed in baad45c9c12028964acd0b58096f3aaa0fb22859
-atf_test_case "IP_MULTICAST_IF" "cleanup"
-IP_MULTICAST_IF_head()
+# Set up two jails, mjail1 and mjail2, connected with two interface pairs
+multicast_vnet_init()
{
- atf_set descr \
- 'sendto() for IP_MULTICAST_IF socket does not do routing lookup'
+
+ vnet_init
+ epair1=$(vnet_mkepair)
+ epair2=$(vnet_mkepair)
+ vnet_mkjail mjail1 ${epair1}a ${epair2}a
+ jexec mjail1 ifconfig ${epair1}a up
+ jexec mjail1 ifconfig ${epair1}a 192.0.2.1/24
+ jexec mjail1 ifconfig ${epair2}a up
+ jexec mjail1 ifconfig ${epair2}a 192.0.3.1/24
+ vnet_mkjail mjail2 ${epair1}b ${epair2}b
+ jexec mjail2 ifconfig ${epair1}b up
+ jexec mjail2 ifconfig ${epair1}b 192.0.2.2/24
+ jexec mjail2 ifconfig ${epair2}b up
+ jexec mjail2 ifconfig ${epair2}b 192.0.3.2/24
+}
+
+atf_test_case "IP_ADD_MEMBERSHIP_ip_mreq" "cleanup"
+IP_ADD_MEMBERSHIP_ip_mreq_head()
+{
+ atf_set descr 'IP_ADD_MEMBERSHIP / IP_MULTICAST_IF with ip_mreq'
atf_set require.user root
+}
+IP_ADD_MEMBERSHIP_ip_mreq_body()
+{
+ multicast_vnet_init
+
+ # join group on interface with IP address 192.0.2.2
+ jexec mjail2 $(atf_get_srcdir)/multicast-receive \
+ ip_mreq 233.252.0.1 6676 192.0.2.2 > out & pid=$!
+ atf_check -s exit:0 -o empty \
+ jexec mjail1 $(atf_get_srcdir)/multicast-send \
+ 0.0.0.0 6676 233.252.0.1 6676 192.0.2.1 hello
+ atf_check -s exit:0 sh -c "wait $pid; exit $?"
+ atf_check -s exit:0 -o inline:"192.0.2.1:6676 hello\n" cat out
+ # join group on interface with IP address 192.0.3.2
+ jexec mjail2 $(atf_get_srcdir)/multicast-receive \
+ ip_mreq 233.252.0.1 6676 192.0.3.2 > out & pid=$!
+ atf_check -s exit:0 -o empty \
+ jexec mjail1 $(atf_get_srcdir)/multicast-send \
+ 0.0.0.0 6676 233.252.0.1 6676 192.0.3.1 hello
+ atf_check -s exit:0 sh -c "wait $pid; exit $?"
+ atf_check -s exit:0 -o inline:"192.0.3.1:6676 hello\n" cat out
+}
+IP_ADD_MEMBERSHIP_ip_mreq_cleanup()
+{
+ rm out
+ vnet_cleanup
}
-IP_MULTICAST_IF_body()
+atf_test_case "IP_ADD_MEMBERSHIP_ip_mreqn" "cleanup"
+IP_ADD_MEMBERSHIP_ip_mreqn_head()
+{
+ atf_set descr 'IP_ADD_MEMBERSHIP / IP_MULTICAST_IF with ip_mreqn'
+ atf_set require.user root
+}
+IP_ADD_MEMBERSHIP_ip_mreqn_body()
{
- local epair mjail
+ multicast_vnet_init
- vnet_init
- # The test doesn't use our half of epair
- epair=$(vnet_mkepair)
- vnet_mkjail mjail ${epair}a
- jexec mjail ifconfig ${epair}a up
- jexec mjail ifconfig ${epair}a 192.0.2.1/24
+ # join group on interface epair2
+ jexec mjail2 $(atf_get_srcdir)/multicast-receive \
+ ip_mreqn 233.252.0.1 6676 ${epair1}b > out & pid=$!
+ atf_check -s exit:0 -o empty \
+ jexec mjail1 $(atf_get_srcdir)/multicast-send \
+ 0.0.0.0 6676 233.252.0.1 6676 ${epair1}a hello
+ atf_check -s exit:0 sh -c "wait $pid; exit $?"
+ atf_check -s exit:0 -o inline:"192.0.2.1:6676 hello\n" cat out
+
+ # join group on interface epair2
+ jexec mjail2 $(atf_get_srcdir)/multicast-receive \
+ ip_mreqn 233.252.0.1 6676 ${epair2}b > out & pid=$!
atf_check -s exit:0 -o empty \
- jexec mjail $(atf_get_srcdir)/sendto-IP_MULTICAST_IF 192.0.2.1
+ jexec mjail1 $(atf_get_srcdir)/multicast-send \
+ 0.0.0.0 6676 233.252.0.1 6676 ${epair2}a hello
+ atf_check -s exit:0 sh -c "wait $pid; exit $?"
+ atf_check -s exit:0 -o inline:"192.0.3.1:6676 hello\n" cat out
+}
+IP_ADD_MEMBERSHIP_ip_mreqn_cleanup()
+{
+ rm out
+ vnet_cleanup
}
-IP_MULTICAST_IF_cleanup()
+atf_test_case "MCAST_JOIN_GROUP" "cleanup"
+MCAST_JOIN_GROUP_head()
+{
+ atf_set descr 'MCAST_JOIN_GROUP'
+ atf_set require.user root
+}
+MCAST_JOIN_GROUP_body()
+{
+ multicast_vnet_init
+
+ # join group on interface epair2
+ jexec mjail2 $(atf_get_srcdir)/multicast-receive \
+ group_req 233.252.0.1 6676 ${epair1}b > out & pid=$!
+ atf_check -s exit:0 -o empty \
+ jexec mjail1 $(atf_get_srcdir)/multicast-send \
+ 0.0.0.0 6676 233.252.0.1 6676 ${epair1}a hello
+ atf_check -s exit:0 sh -c "wait $pid; exit $?"
+ atf_check -s exit:0 -o inline:"192.0.2.1:6676 hello\n" cat out
+
+ # join group on interface epair2
+ jexec mjail2 $(atf_get_srcdir)/multicast-receive \
+ group_req 233.252.0.1 6676 ${epair2}b > out & pid=$!
+ atf_check -s exit:0 -o empty \
+ jexec mjail1 $(atf_get_srcdir)/multicast-send \
+ 0.0.0.0 6676 233.252.0.1 6676 ${epair2}a hello
+ atf_check -s exit:0 sh -c "wait $pid; exit $?"
+ atf_check -s exit:0 -o inline:"192.0.3.1:6676 hello\n" cat out
+}
+MCAST_JOIN_GROUP_cleanup()
{
+ rm out
vnet_cleanup
}
atf_init_test_cases()
{
- atf_add_test_case "IP_MULTICAST_IF"
+ atf_add_test_case "IP_ADD_MEMBERSHIP_ip_mreq"
+ atf_add_test_case "IP_ADD_MEMBERSHIP_ip_mreqn"
+ atf_add_test_case "MCAST_JOIN_GROUP"
}
diff --git a/tests/sys/netinet/tcp_md5_getsockopt.c b/tests/sys/netinet/tcp_md5_getsockopt.c
index deaa4170caea..e23cfa67185a 100644
--- a/tests/sys/netinet/tcp_md5_getsockopt.c
+++ b/tests/sys/netinet/tcp_md5_getsockopt.c
@@ -45,9 +45,6 @@ void test_tcp_md5_getsockopt(int);
void
test_tcp_md5_getsockopt(int v6)
{
- if (kldfind("tcpmd5.ko") == -1)
- atf_tc_skip("Test requires the tcpmd5 kernel module to be loaded");
-
struct sockaddr_in *s;
struct sockaddr_in6 s6 = { 0 };
struct sockaddr_in s4 = { 0 };
@@ -108,6 +105,7 @@ ATF_TC(tcp_md5_getsockopt_v4);
ATF_TC_HEAD(tcp_md5_getsockopt_v4, tc)
{
atf_tc_set_md_var(tc, "descr", "Test getsockopt for TCP MD5 SIG (IPv4)");
+ atf_tc_set_md_var(tc, "require.kmods", "tcpmd5");
}
ATF_TC_BODY(tcp_md5_getsockopt_v4, tc)
@@ -119,6 +117,7 @@ ATF_TC(tcp_md5_getsockopt_v6);
ATF_TC_HEAD(tcp_md5_getsockopt_v6, tc)
{
atf_tc_set_md_var(tc, "descr", "Test getsockopt for TCP MD5 SIG (IPv6)");
+ atf_tc_set_md_var(tc, "require.kmods", "tcpmd5");
}
ATF_TC_BODY(tcp_md5_getsockopt_v6, tc)
diff --git a/tests/sys/netinet6/ndp.sh b/tests/sys/netinet6/ndp.sh
index bac9764ee3c9..21a50cda02ba 100755
--- a/tests/sys/netinet6/ndp.sh
+++ b/tests/sys/netinet6/ndp.sh
@@ -188,9 +188,48 @@ ndp_slaac_default_route_cleanup() {
vnet_cleanup
}
+atf_test_case "ndp_prefix_len_mismatch" "cleanup"
+ndp_prefix_len_mismatch_head() {
+ atf_set descr 'Test RAs on an interface without a /64 lladdr'
+ atf_set require.user root
+ atf_set require.progs python3 scapy
+}
+
+ndp_prefix_len_mismatch_body() {
+ vnet_init
+
+ epair=$(vnet_mkepair)
+
+ vnet_mkjail alcatraz ${epair}a
+
+ jexec alcatraz ifconfig ${epair}a inet6 -auto_linklocal
+ jexec alcatraz ifconfig ${epair}a inet6 -ifdisabled
+ jexec alcatraz ifconfig ${epair}a inet6 accept_rtadv
+ jexec alcatraz ifconfig ${epair}a inet6 fe80::5a9c:fcff:fe10:5d07/127
+ jexec alcatraz ifconfig ${epair}a up
+
+ ifconfig ${epair}b inet6 -ifdisabled
+ ifconfig ${epair}b up
+
+ atf_check -e ignore python3 $(atf_get_srcdir)/ra.py \
+ --sendif ${epair}b \
+ --dst $(ndp_if_lladdr ${epair}a alcatraz) \
+ --src $(ndp_if_lladdr ${epair}b) \
+ --prefix "2001:db8:ffff:1000::" --prefixlen 64
+
+ atf_check \
+ -o match:"inet6 2001:db8:ffff:1000:.* prefixlen 64.*autoconf.*" \
+ jexec alcatraz ifconfig ${epair}a
+}
+
+ndp_prefix_len_mismatch_cleanup() {
+ vnet_cleanup
+}
+
atf_init_test_cases()
{
atf_add_test_case "ndp_add_gu_success"
atf_add_test_case "ndp_del_gu_success"
atf_add_test_case "ndp_slaac_default_route"
+ atf_add_test_case "ndp_prefix_len_mismatch"
}
diff --git a/tests/sys/netlink/netlink_socket.c b/tests/sys/netlink/netlink_socket.c
index 6dcc894b6695..cee864bb9bab 100644
--- a/tests/sys/netlink/netlink_socket.c
+++ b/tests/sys/netlink/netlink_socket.c
@@ -116,7 +116,11 @@ fullsocket(void)
return (fd);
}
-ATF_TC_WITHOUT_HEAD(overflow);
+ATF_TC(overflow);
+ATF_TC_HEAD(overflow, tc)
+{
+ atf_tc_set_md_var(tc, "require.kmods", "netlink");
+}
ATF_TC_BODY(overflow, tc)
{
char buf[BUFLEN];
@@ -143,7 +147,11 @@ ATF_TC_BODY(overflow, tc)
ATF_REQUIRE(timer_done == 0);
}
-ATF_TC_WITHOUT_HEAD(peek);
+ATF_TC(peek);
+ATF_TC_HEAD(peek, tc)
+{
+ atf_tc_set_md_var(tc, "require.kmods", "netlink");
+}
ATF_TC_BODY(peek, tc)
{
char *buf;
@@ -185,7 +193,11 @@ cmsg_check(struct msghdr *msg)
ATF_REQUIRE((msg->msg_flags & MSG_CTRUNC) == 0);
}
-ATF_TC_WITHOUT_HEAD(sizes);
+ATF_TC(sizes);
+ATF_TC_HEAD(sizes, tc)
+{
+ atf_tc_set_md_var(tc, "require.kmods", "netlink");
+}
ATF_TC_BODY(sizes, tc)
{
#define NLMSG_LARGE 2048 /* XXX: match kernel nl_buf */
@@ -199,9 +211,22 @@ ATF_TC_BODY(sizes, tc)
.msg_controllen = sizeof(cbuf),
};
ssize_t ss;
- int fd, size, rsize;
+ int fd, size, msize, rsize;
- fd = fullsocket();
+ /*
+ * Create a socket with NMSGS messages in the receive buffer.
+ */
+#define NMSGS 5
+ ATF_REQUIRE((fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) != -1);
+ ATF_REQUIRE(send(fd, &hdr, sizeof(hdr), 0) == sizeof(hdr));
+ ATF_REQUIRE(recv(fd, buf, sizeof(hdr), MSG_WAITALL | MSG_PEEK) ==
+ sizeof(hdr));
+ ATF_REQUIRE(ioctl(fd, FIONREAD, &msize) != -1);
+ for (u_int i = 0; i < NMSGS; i++)
+ ATF_REQUIRE(send(fd, &hdr, sizeof(hdr), 0) == sizeof(hdr));
+ do {
+ ATF_REQUIRE(ioctl(fd, FIONREAD, &rsize) != -1);
+ } while (rsize < msize * (NMSGS + 1));
/*
* Set NETLINK_MSG_INFO, so that later cmsg_check will check that any
@@ -252,6 +277,7 @@ ATF_TC_BODY(sizes, tc)
.iov_len = sizeof(buf) < rsize ? sizeof(buf) : rsize,
};
ss = recvmsg(fd, &msg, 0);
+ ATF_REQUIRE(ss > hdr.nlmsg_len);
ATF_REQUIRE(ss > NLMSG_LARGE * 9 || ss == rsize);
cmsg_check(&msg);
}
@@ -273,7 +299,11 @@ nla_RTA_DST(struct nlattr *start, ssize_t len)
* Check that NETLINK_ADD_MEMBERSHIP subscribes us. Add & delete a temporary
* route and check if announcements came in.
*/
-ATF_TC_WITHOUT_HEAD(membership);
+ATF_TC(membership);
+ATF_TC_HEAD(membership, tc)
+{
+ atf_tc_set_md_var(tc, "require.kmods", "netlink");
+}
ATF_TC_BODY(membership, tc)
{
struct {
@@ -329,9 +359,6 @@ ATF_TC_BODY(membership, tc)
ATF_TP_ADD_TCS(tp)
{
- if (modfind("netlink") == -1)
- atf_tc_skip("netlink module not loaded");
-
ATF_TP_ADD_TC(tp, overflow);
ATF_TP_ADD_TC(tp, peek);
ATF_TP_ADD_TC(tp, sizes);
diff --git a/tests/sys/netlink/test_snl.c b/tests/sys/netlink/test_snl.c
index 040414a96e2c..3990aa0b075d 100644
--- a/tests/sys/netlink/test_snl.c
+++ b/tests/sys/netlink/test_snl.c
@@ -25,13 +25,6 @@ static const struct snl_hdr_parser *snl_all_route_parsers[] = {
&_addr_fbsd_parser, &snl_rtm_addr_parser, &_nh_fbsd_parser, &snl_nhmsg_parser,
};
-static void
-require_netlink(void)
-{
- if (modfind("netlink") == -1)
- atf_tc_skip("netlink module not loaded");
-}
-
ATF_TC(snl_verify_core_parsers);
ATF_TC_HEAD(snl_verify_core_parsers, tc)
{
@@ -60,6 +53,7 @@ ATF_TC(snl_parse_errmsg_capped);
ATF_TC_HEAD(snl_parse_errmsg_capped, tc)
{
atf_tc_set_md_var(tc, "descr", "Tests snl(3) correctly parsing capped errors");
+ atf_tc_set_md_var(tc, "require.kmods", "netlink");
}
ATF_TC_BODY(snl_parse_errmsg_capped, tc)
@@ -67,8 +61,6 @@ ATF_TC_BODY(snl_parse_errmsg_capped, tc)
struct snl_state ss;
struct snl_writer nw;
- require_netlink();
-
if (!snl_init(&ss, NETLINK_ROUTE))
atf_tc_fail("snl_init() failed");
@@ -102,6 +94,7 @@ ATF_TC(snl_parse_errmsg_capped_extack);
ATF_TC_HEAD(snl_parse_errmsg_capped_extack, tc)
{
atf_tc_set_md_var(tc, "descr", "Tests snl(3) correctly parsing capped errors with extack");
+ atf_tc_set_md_var(tc, "require.kmods", "netlink");
}
ATF_TC_BODY(snl_parse_errmsg_capped_extack, tc)
@@ -109,8 +102,6 @@ ATF_TC_BODY(snl_parse_errmsg_capped_extack, tc)
struct snl_state ss;
struct snl_writer nw;
- require_netlink();
-
if (!snl_init(&ss, NETLINK_ROUTE))
atf_tc_fail("snl_init() failed");
@@ -145,6 +136,7 @@ ATF_TC(snl_parse_errmsg_uncapped_extack);
ATF_TC_HEAD(snl_parse_errmsg_uncapped_extack, tc)
{
atf_tc_set_md_var(tc, "descr", "Tests snl(3) correctly parsing errors with extack");
+ atf_tc_set_md_var(tc, "require.kmods", "netlink");
}
ATF_TC_BODY(snl_parse_errmsg_uncapped_extack, tc)
@@ -152,8 +144,6 @@ ATF_TC_BODY(snl_parse_errmsg_uncapped_extack, tc)
struct snl_state ss;
struct snl_writer nw;
- require_netlink();
-
ATF_CHECK(snl_init(&ss, NETLINK_ROUTE));
int optval = 1;
@@ -185,6 +175,7 @@ ATF_TC(snl_list_ifaces);
ATF_TC_HEAD(snl_list_ifaces, tc)
{
atf_tc_set_md_var(tc, "descr", "Tests snl(3) listing interfaces");
+ atf_tc_set_md_var(tc, "require.kmods", "netlink");
}
struct nl_parsed_link {
@@ -212,8 +203,6 @@ ATF_TC_BODY(snl_list_ifaces, tc)
struct snl_state ss;
struct snl_writer nw;
- require_netlink();
-
if (!snl_init(&ss, NETLINK_ROUTE))
atf_tc_fail("snl_init() failed");
diff --git a/tests/sys/netlink/test_snl_generic.c b/tests/sys/netlink/test_snl_generic.c
index c63b1380f2ad..8613bf04a45c 100644
--- a/tests/sys/netlink/test_snl_generic.c
+++ b/tests/sys/netlink/test_snl_generic.c
@@ -15,13 +15,6 @@ static const struct snl_hdr_parser *snl_all_genl_parsers[] = {
&_genl_ctrl_getfam_parser, &_genl_ctrl_mc_parser,
};
-static void
-require_netlink(void)
-{
- if (modfind("netlink") == -1)
- atf_tc_skip("netlink module not loaded");
-}
-
ATF_TC(snl_verify_genl_parsers);
ATF_TC_HEAD(snl_verify_genl_parsers, tc)
{
@@ -38,14 +31,13 @@ ATF_TC(test_snl_get_genl_family_success);
ATF_TC_HEAD(test_snl_get_genl_family_success, tc)
{
atf_tc_set_md_var(tc, "descr", "Tests successfull resolution of the 'nlctrl' family");
+ atf_tc_set_md_var(tc, "require.kmods", "netlink");
}
ATF_TC_BODY(test_snl_get_genl_family_success, tc)
{
struct snl_state ss;
- require_netlink();
-
if (!snl_init(&ss, NETLINK_GENERIC))
atf_tc_fail("snl_init() failed");
@@ -56,14 +48,13 @@ ATF_TC(test_snl_get_genl_family_failure);
ATF_TC_HEAD(test_snl_get_genl_family_failure, tc)
{
atf_tc_set_md_var(tc, "descr", "Tests unsuccessfull resolution of 'no-such-family' family");
+ atf_tc_set_md_var(tc, "require.kmods", "netlink");
}
ATF_TC_BODY(test_snl_get_genl_family_failure, tc)
{
struct snl_state ss;
- require_netlink();
-
if (!snl_init(&ss, NETLINK_GENERIC))
atf_tc_fail("snl_init() failed");
@@ -74,6 +65,7 @@ ATF_TC(test_snl_get_genl_family_groups);
ATF_TC_HEAD(test_snl_get_genl_family_groups, tc)
{
atf_tc_set_md_var(tc, "descr", "Tests getting 'nlctrl' groups");
+ atf_tc_set_md_var(tc, "require.kmods", "netlink");
}
ATF_TC_BODY(test_snl_get_genl_family_groups, tc)
@@ -82,8 +74,6 @@ ATF_TC_BODY(test_snl_get_genl_family_groups, tc)
struct snl_writer nw;
struct nlmsghdr *hdr;
- require_netlink();
-
if (!snl_init(&ss, NETLINK_GENERIC))
atf_tc_fail("snl_init() failed");
diff --git a/tests/sys/netmap/Makefile b/tests/sys/netmap/Makefile
index ee00d0421620..bd713ff795bc 100644
--- a/tests/sys/netmap/Makefile
+++ b/tests/sys/netmap/Makefile
@@ -3,6 +3,7 @@ PACKAGE= tests
TESTSDIR= ${TESTSBASE}/sys/netmap
TEST_METADATA+= required_user="root"
TEST_METADATA+= is_exclusive=true
+TEST_METADATA+= required_kmods="if_tap netmap"
PLAIN_TESTS_C+= ctrl-api-test
diff --git a/tests/sys/netmap/ctrl-api-test.c b/tests/sys/netmap/ctrl-api-test.c
index 6b45dbb1cfea..36c131980360 100644
--- a/tests/sys/netmap/ctrl-api-test.c
+++ b/tests/sys/netmap/ctrl-api-test.c
@@ -59,8 +59,6 @@
#include <stddef.h>
#ifdef __FreeBSD__
-#include "freebsd_test_suite/macros.h"
-
static int
eventfd(int x __unused, int y __unused)
{
@@ -2199,11 +2197,6 @@ main(int argc, char **argv)
int opt;
int i;
-#ifdef __FreeBSD__
- PLAIN_REQUIRE_KERNEL_MODULE("if_tap", 0);
- PLAIN_REQUIRE_KERNEL_MODULE("netmap", 0);
-#endif
-
memset(&ctx_, 0, sizeof(ctx_));
{
diff --git a/tests/sys/netpfil/pf/Makefile b/tests/sys/netpfil/pf/Makefile
index 616ffe560b3a..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 \
@@ -30,6 +31,7 @@ ATF_TESTS_SH+= altq \
names \
nat \
nat64 \
+ once \
pass_block \
pflog \
pflow \
@@ -61,6 +63,7 @@ ATF_TESTS_PYTEST+= header.py
ATF_TESTS_PYTEST+= icmp.py
ATF_TESTS_PYTEST+= igmp.py
ATF_TESTS_PYTEST+= mld.py
+ATF_TESTS_PYTEST+= nat44.py
ATF_TESTS_PYTEST+= nat64.py
ATF_TESTS_PYTEST+= nat66.py
ATF_TESTS_PYTEST+= return.py
@@ -69,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/fragmentation_pass.sh b/tests/sys/netpfil/pf/fragmentation_pass.sh
index 5deaba18301d..c749aac793ee 100644
--- a/tests/sys/netpfil/pf/fragmentation_pass.sh
+++ b/tests/sys/netpfil/pf/fragmentation_pass.sh
@@ -648,6 +648,72 @@ dummynet_fragmented_cleanup()
pft_cleanup
}
+atf_test_case "counters" "cleanup"
+counters_head()
+{
+ atf_set descr 'Test fragment counters'
+ atf_set require.user root
+}
+
+counters_body()
+{
+ pft_init
+
+ epair=$(vnet_mkepair)
+ vnet_mkjail alcatraz ${epair}a
+
+ ifconfig ${epair}b inet 192.0.2.1/24 up
+ jexec alcatraz ifconfig ${epair}a 192.0.2.2/24 up
+
+ jexec alcatraz pfctl -e
+ pft_set_rules alcatraz \
+ "set reassemble yes" \
+ "pass keep state"
+
+ # All fragment counters are zero
+ counters=$(jexec alcatraz pfctl -si -v | grep -A 4 '^Fragments')
+ atf_check -s exit:0 -o match:"current entries[[:space:]]+0" \
+ echo $counters
+ atf_check -s exit:0 -o match:"searches[[:space:]]+0" \
+ echo $counters
+ atf_check -s exit:0 -o match:"inserts[[:space:]]+0" \
+ echo $counters
+ atf_check -s exit:0 -o match:"removals[[:space:]]+0" \
+ echo $counters
+
+ # They remain zero after we've seen non-fragmented traffic
+ atf_check -s exit:0 -o ignore \
+ ping -c 1 192.0.2.2
+ counters=$(jexec alcatraz pfctl -si -v | grep -A 4 '^Fragments')
+ atf_check -s exit:0 -o match:"current entries[[:space:]]+0" \
+ echo $counters
+ atf_check -s exit:0 -o match:"searches[[:space:]]+0" \
+ echo $counters
+ atf_check -s exit:0 -o match:"inserts[[:space:]]+0" \
+ echo $counters
+ atf_check -s exit:0 -o match:"removals[[:space:]]+0" \
+ echo $counters
+
+ # But once we've reassembled they're no longer zero
+ # (Count is 2, because in + out)
+ atf_check -s exit:0 -o ignore \
+ ping -c 1 -s 2000 192.0.2.2
+ counters=$(jexec alcatraz pfctl -si -v | grep -A 4 '^Fragments')
+ atf_check -s exit:0 -o match:"current entries[[:space:]]+0" \
+ echo $counters
+ atf_check -s exit:0 -o match:"searches[[:space:]]+2" \
+ echo $counters
+ atf_check -s exit:0 -o match:"inserts[[:space:]]+2" \
+ echo $counters
+ atf_check -s exit:0 -o match:"removals[[:space:]]+2" \
+ echo $counters
+}
+
+counters_cleanup()
+{
+ pft_cleanup
+}
+
atf_init_test_cases()
{
atf_add_test_case "too_many_fragments"
@@ -665,4 +731,5 @@ atf_init_test_cases()
atf_add_test_case "dummynet"
atf_add_test_case "dummynet_nat"
atf_add_test_case "dummynet_fragmented"
+ atf_add_test_case "counters"
}
diff --git a/tests/sys/netpfil/pf/igmp.py b/tests/sys/netpfil/pf/igmp.py
index b339a2825082..5d72a1c093a7 100644
--- a/tests/sys/netpfil/pf/igmp.py
+++ b/tests/sys/netpfil/pf/igmp.py
@@ -93,3 +93,9 @@ class TestIGMP(VnetTestTemplate):
options=[sp.IPOption_Router_Alert()]) \
/ sc.igmp.IGMP(type=0x11, mrcode=1)
assert not self.find_igmp_reply(pkt, ifname)
+
+ # Or with the wrong destination address
+ pkt = sp.IP(dst="224.0.0.2%%%s" % ifname, ttl=2,
+ options=[sp.IPOption_Router_Alert()]) \
+ / sc.igmp.IGMP(type=0x11, mrcode=1)
+ assert not self.find_igmp_reply(pkt, ifname)
diff --git a/tests/sys/netpfil/pf/ioctl/validation.c b/tests/sys/netpfil/pf/ioctl/validation.c
index 18fafe11c6ab..3e03163cc752 100644
--- a/tests/sys/netpfil/pf/ioctl/validation.c
+++ b/tests/sys/netpfil/pf/ioctl/validation.c
@@ -41,8 +41,6 @@
static int dev;
#define COMMON_HEAD() \
- if (modfind("pf") == -1) \
- atf_tc_skip("pf not loaded"); \
dev = open("/dev/pf", O_RDWR); \
if (dev == -1) \
atf_tc_skip("Failed to open /dev/pf");
@@ -64,6 +62,7 @@ ATF_TC_WITH_CLEANUP(addtables);
ATF_TC_HEAD(addtables, tc)
{
atf_tc_set_md_var(tc, "require.user", "root");
+ atf_tc_set_md_var(tc, "require.kmods", "pf");
}
ATF_TC_BODY(addtables, tc)
@@ -116,6 +115,7 @@ ATF_TC_WITH_CLEANUP(deltables);
ATF_TC_HEAD(deltables, tc)
{
atf_tc_set_md_var(tc, "require.user", "root");
+ atf_tc_set_md_var(tc, "require.kmods", "pf");
}
ATF_TC_BODY(deltables, tc)
@@ -159,6 +159,7 @@ ATF_TC_WITH_CLEANUP(gettables);
ATF_TC_HEAD(gettables, tc)
{
atf_tc_set_md_var(tc, "require.user", "root");
+ atf_tc_set_md_var(tc, "require.kmods", "pf");
}
ATF_TC_BODY(gettables, tc)
@@ -197,6 +198,7 @@ ATF_TC_WITH_CLEANUP(gettstats);
ATF_TC_HEAD(gettstats, tc)
{
atf_tc_set_md_var(tc, "require.user", "root");
+ atf_tc_set_md_var(tc, "require.kmods", "pf");
}
ATF_TC_BODY(gettstats, tc)
@@ -235,6 +237,7 @@ ATF_TC_WITH_CLEANUP(clrtstats);
ATF_TC_HEAD(clrtstats, tc)
{
atf_tc_set_md_var(tc, "require.user", "root");
+ atf_tc_set_md_var(tc, "require.kmods", "pf");
}
ATF_TC_BODY(clrtstats, tc)
@@ -280,6 +283,7 @@ ATF_TC_WITH_CLEANUP(settflags);
ATF_TC_HEAD(settflags, tc)
{
atf_tc_set_md_var(tc, "require.user", "root");
+ atf_tc_set_md_var(tc, "require.kmods", "pf");
}
ATF_TC_BODY(settflags, tc)
@@ -325,6 +329,7 @@ ATF_TC_WITH_CLEANUP(addaddrs);
ATF_TC_HEAD(addaddrs, tc)
{
atf_tc_set_md_var(tc, "require.user", "root");
+ atf_tc_set_md_var(tc, "require.kmods", "pf");
}
ATF_TC_BODY(addaddrs, tc)
@@ -360,6 +365,7 @@ ATF_TC_WITH_CLEANUP(deladdrs);
ATF_TC_HEAD(deladdrs, tc)
{
atf_tc_set_md_var(tc, "require.user", "root");
+ atf_tc_set_md_var(tc, "require.kmods", "pf");
}
ATF_TC_BODY(deladdrs, tc)
@@ -395,6 +401,7 @@ ATF_TC_WITH_CLEANUP(setaddrs);
ATF_TC_HEAD(setaddrs, tc)
{
atf_tc_set_md_var(tc, "require.user", "root");
+ atf_tc_set_md_var(tc, "require.kmods", "pf");
}
ATF_TC_BODY(setaddrs, tc)
@@ -430,6 +437,7 @@ ATF_TC_WITH_CLEANUP(getaddrs);
ATF_TC_HEAD(getaddrs, tc)
{
atf_tc_set_md_var(tc, "require.user", "root");
+ atf_tc_set_md_var(tc, "require.kmods", "pf");
}
ATF_TC_BODY(getaddrs, tc)
@@ -467,6 +475,7 @@ ATF_TC_WITH_CLEANUP(getastats);
ATF_TC_HEAD(getastats, tc)
{
atf_tc_set_md_var(tc, "require.user", "root");
+ atf_tc_set_md_var(tc, "require.kmods", "pf");
}
ATF_TC_BODY(getastats, tc)
@@ -504,6 +513,7 @@ ATF_TC_WITH_CLEANUP(clrastats);
ATF_TC_HEAD(clrastats, tc)
{
atf_tc_set_md_var(tc, "require.user", "root");
+ atf_tc_set_md_var(tc, "require.kmods", "pf");
}
ATF_TC_BODY(clrastats, tc)
@@ -541,6 +551,7 @@ ATF_TC_WITH_CLEANUP(tstaddrs);
ATF_TC_HEAD(tstaddrs, tc)
{
atf_tc_set_md_var(tc, "require.user", "root");
+ atf_tc_set_md_var(tc, "require.kmods", "pf");
}
ATF_TC_BODY(tstaddrs, tc)
@@ -578,6 +589,7 @@ ATF_TC_WITH_CLEANUP(inadefine);
ATF_TC_HEAD(inadefine, tc)
{
atf_tc_set_md_var(tc, "require.user", "root");
+ atf_tc_set_md_var(tc, "require.kmods", "pf");
}
ATF_TC_BODY(inadefine, tc)
@@ -615,6 +627,7 @@ ATF_TC_WITH_CLEANUP(igetifaces);
ATF_TC_HEAD(igetifaces, tc)
{
atf_tc_set_md_var(tc, "require.user", "root");
+ atf_tc_set_md_var(tc, "require.kmods", "pf");
}
ATF_TC_BODY(igetifaces, tc)
@@ -649,6 +662,7 @@ ATF_TC_WITH_CLEANUP(cxbegin);
ATF_TC_HEAD(cxbegin, tc)
{
atf_tc_set_md_var(tc, "require.user", "root");
+ atf_tc_set_md_var(tc, "require.kmods", "pf");
}
ATF_TC_BODY(cxbegin, tc)
@@ -688,6 +702,7 @@ ATF_TC_WITH_CLEANUP(cxrollback);
ATF_TC_HEAD(cxrollback, tc)
{
atf_tc_set_md_var(tc, "require.user", "root");
+ atf_tc_set_md_var(tc, "require.kmods", "pf");
}
ATF_TC_BODY(cxrollback, tc)
@@ -727,6 +742,7 @@ ATF_TC_WITH_CLEANUP(commit);
ATF_TC_HEAD(commit, tc)
{
atf_tc_set_md_var(tc, "require.user", "root");
+ atf_tc_set_md_var(tc, "require.kmods", "pf");
}
ATF_TC_BODY(commit, tc)
@@ -766,6 +782,7 @@ ATF_TC_WITH_CLEANUP(getsrcnodes);
ATF_TC_HEAD(getsrcnodes, tc)
{
atf_tc_set_md_var(tc, "require.user", "root");
+ atf_tc_set_md_var(tc, "require.kmods", "pf");
}
ATF_TC_BODY(getsrcnodes, tc)
@@ -798,6 +815,7 @@ ATF_TC_WITH_CLEANUP(tag);
ATF_TC_HEAD(tag, tc)
{
atf_tc_set_md_var(tc, "require.user", "root");
+ atf_tc_set_md_var(tc, "require.kmods", "pf");
}
ATF_TC_BODY(tag, tc)
@@ -835,6 +853,7 @@ ATF_TC_WITH_CLEANUP(rpool_mtx);
ATF_TC_HEAD(rpool_mtx, tc)
{
atf_tc_set_md_var(tc, "require.user", "root");
+ atf_tc_set_md_var(tc, "require.kmods", "pf");
}
ATF_TC_BODY(rpool_mtx, tc)
@@ -872,6 +891,7 @@ ATF_TC_WITH_CLEANUP(rpool_mtx2);
ATF_TC_HEAD(rpool_mtx2, tc)
{
atf_tc_set_md_var(tc, "require.user", "root");
+ atf_tc_set_md_var(tc, "require.kmods", "pf");
}
ATF_TC_BODY(rpool_mtx2, tc)
@@ -898,6 +918,7 @@ ATF_TC_WITH_CLEANUP(natlook);
ATF_TC_HEAD(natlook, tc)
{
atf_tc_set_md_var(tc, "require.user", "root");
+ atf_tc_set_md_var(tc, "require.kmods", "pf");
}
ATF_TC_BODY(natlook, tc)
diff --git a/tests/sys/netpfil/pf/killstate.sh b/tests/sys/netpfil/pf/killstate.sh
index 0d98db822535..ffb01df57908 100644
--- a/tests/sys/netpfil/pf/killstate.sh
+++ b/tests/sys/netpfil/pf/killstate.sh
@@ -105,6 +105,68 @@ v4_cleanup()
pft_cleanup
}
+atf_test_case "src_dst" "cleanup"
+src_dst_head()
+{
+ atf_set descr 'Test killing a state with source and destination specified'
+ atf_set require.user root
+}
+
+src_dst_body()
+{
+ pft_init
+
+ epair=$(vnet_mkepair)
+ ifconfig ${epair}a 192.0.2.1/24 up
+
+ vnet_mkjail alcatraz ${epair}b
+ jexec alcatraz ifconfig ${epair}b 192.0.2.2/24 up
+ jexec alcatraz pfctl -e
+
+ pft_set_rules alcatraz "block all" \
+ "pass in proto icmp" \
+ "set skip on lo"
+
+ # Sanity check & establish state
+ atf_check -s exit:0 -o ignore ${common_dir}/pft_ping.py \
+ --sendif ${epair}a \
+ --to 192.0.2.2 \
+ --replyif ${epair}a
+
+ # Change rules to now deny the ICMP traffic
+ pft_set_rules noflush alcatraz "block all"
+ if ! find_state;
+ then
+ atf_fail "Setting new rules removed the state."
+ fi
+
+ # Killing with the wrong source IP doesn't affect our state
+ jexec alcatraz pfctl -k 192.0.2.3 -k 192.0.2.2
+ if ! find_state;
+ then
+ atf_fail "Killing with the wrong source IP removed our state."
+ fi
+
+ # Killing with the wrong destination IP doesn't affect our state
+ jexec alcatraz pfctl -k 192.0.2.1 -k 192.0.2.3
+ if ! find_state;
+ then
+ atf_fail "Killing with the wrong destination IP removed our state."
+ fi
+
+ # But it does with the correct one
+ jexec alcatraz pfctl -k 192.0.2.1 -k 192.0.2.2
+ if find_state;
+ then
+ atf_fail "Killing with the correct IPs did not remove our state."
+ fi
+}
+
+src_dst_cleanup()
+{
+ pft_cleanup
+}
+
atf_test_case "v6" "cleanup"
v6_head()
{
@@ -698,6 +760,7 @@ nat_cleanup()
atf_init_test_cases()
{
atf_add_test_case "v4"
+ atf_add_test_case "src_dst"
atf_add_test_case "v6"
atf_add_test_case "label"
atf_add_test_case "multilabel"
diff --git a/tests/sys/netpfil/pf/limits.sh b/tests/sys/netpfil/pf/limits.sh
index 69f0b6af2ccf..a0d6b891ee19 100644
--- a/tests/sys/netpfil/pf/limits.sh
+++ b/tests/sys/netpfil/pf/limits.sh
@@ -112,8 +112,43 @@ zero_cleanup()
pft_cleanup
}
+atf_test_case "anchors" "cleanup"
+anchors_head()
+{
+ atf_set descr 'Test increasing maximum number of anchors'
+ atf_set require.user root
+}
+
+anchors_body()
+{
+ pft_init
+
+ vnet_mkjail alcatraz
+
+ jexec alcatraz pfctl -e
+
+ pft_set_rules alcatraz \
+ "set limit anchors 1"
+
+ pft_set_rules alcatraz \
+ "set limit anchors 2" \
+ "pass" \
+ "anchor \"foo\" {\n
+ pass in\n
+ }" \
+ "anchor \"bar\" {\n
+ pass out\n
+ }"
+}
+
+anchors_cleanup()
+{
+ pft_cleanup
+}
+
atf_init_test_cases()
{
atf_add_test_case "basic"
atf_add_test_case "zero"
+ atf_add_test_case "anchors"
}
diff --git a/tests/sys/netpfil/pf/mbuf.sh b/tests/sys/netpfil/pf/mbuf.sh
index e3f138bb73b9..3abae65203cd 100644
--- a/tests/sys/netpfil/pf/mbuf.sh
+++ b/tests/sys/netpfil/pf/mbuf.sh
@@ -69,22 +69,22 @@ inet_in_mbuf_len_body()
# Should still work for m_len=0
jexec alcatraz pfilctl link -i dummymbuf:inet inet
jexec alcatraz sysctl net.dummymbuf.rules="inet in ${epair}b pull-head 0;"
- atf_check_equal "0" "$(jexec alcatraz sysctl -n net.dummymbuf.hits)"
+ atf_check_equal '0' '$(jexec alcatraz sysctl -n net.dummymbuf.hits)'
atf_check -s exit:0 -o ignore ping -c1 192.0.2.2
- atf_check_equal "1" "$(jexec alcatraz sysctl -n net.dummymbuf.hits)"
+ atf_check_equal '1' '$(jexec alcatraz sysctl -n net.dummymbuf.hits)'
# m_len=1
jexec alcatraz sysctl net.dummymbuf.rules="inet in ${epair}b pull-head 1;"
jexec alcatraz sysctl net.dummymbuf.hits=0
atf_check -s exit:0 -o ignore ping -c1 192.0.2.2
- atf_check_equal "1" "$(jexec alcatraz sysctl -n net.dummymbuf.hits)"
+ atf_check_equal '1' '$(jexec alcatraz sysctl -n net.dummymbuf.hits)'
# m_len=19
# provided IPv4 basic header is 20 bytes long, it should impact the dst addr
jexec alcatraz sysctl net.dummymbuf.rules="inet in ${epair}b pull-head 19;"
jexec alcatraz sysctl net.dummymbuf.hits=0
atf_check -s exit:0 -o ignore ping -c1 192.0.2.2
- atf_check_equal "1" "$(jexec alcatraz sysctl -n net.dummymbuf.hits)"
+ atf_check_equal '1' '$(jexec alcatraz sysctl -n net.dummymbuf.hits)'
}
inet_in_mbuf_len_cleanup()
{
@@ -140,22 +140,22 @@ inet6_in_mbuf_len_body()
# Should still work for m_len=0
jexec alcatraz pfilctl link -i dummymbuf:inet6 inet6
jexec alcatraz sysctl net.dummymbuf.rules="inet6 in ${epair}b pull-head 0;"
- atf_check_equal "0" "$(jexec alcatraz sysctl -n net.dummymbuf.hits)"
+ atf_check_equal '0' '$(jexec alcatraz sysctl -n net.dummymbuf.hits)'
atf_check -s exit:0 -o ignore ping -c1 2001:db8::2
- atf_check_equal "1" "$(jexec alcatraz sysctl -n net.dummymbuf.hits)"
+ atf_check_equal '1' '$(jexec alcatraz sysctl -n net.dummymbuf.hits)'
# m_len=1
jexec alcatraz sysctl net.dummymbuf.rules="inet6 in ${epair}b pull-head 1;"
jexec alcatraz sysctl net.dummymbuf.hits=0
atf_check -s exit:0 -o ignore ping -c1 2001:db8::2
- atf_check_equal "1" "$(jexec alcatraz sysctl -n net.dummymbuf.hits)"
+ atf_check_equal '1' '$(jexec alcatraz sysctl -n net.dummymbuf.hits)'
# m_len=39
# provided IPv6 basic header is 40 bytes long, it should impact the dst addr
jexec alcatraz sysctl net.dummymbuf.rules="inet6 in ${epair}b pull-head 39;"
jexec alcatraz sysctl net.dummymbuf.hits=0
atf_check -s exit:0 -o ignore ping -c1 2001:db8::2
- atf_check_equal "1" "$(jexec alcatraz sysctl -n net.dummymbuf.hits)"
+ atf_check_equal '1' '$(jexec alcatraz sysctl -n net.dummymbuf.hits)'
}
inet6_in_mbuf_len_cleanup()
{
@@ -205,29 +205,29 @@ ethernet_in_mbuf_len_body()
# Should still work for m_len=0
jexec alcatraz pfilctl link -i dummymbuf:ethernet ethernet
jexec alcatraz sysctl net.dummymbuf.rules="ethernet in ${epair}b pull-head 0;"
- atf_check_equal "0" "$(jexec alcatraz sysctl -n net.dummymbuf.hits)"
+ atf_check_equal '0' '$(jexec alcatraz sysctl -n net.dummymbuf.hits)'
atf_check -s exit:0 -o ignore ping -c1 192.0.2.2
- atf_check_equal "1" "$(jexec alcatraz sysctl -n net.dummymbuf.hits)"
+ atf_check_equal '1' '$(jexec alcatraz sysctl -n net.dummymbuf.hits)'
# m_len=1
jexec alcatraz sysctl net.dummymbuf.rules="ethernet in ${epair}b pull-head 1;"
jexec alcatraz sysctl net.dummymbuf.hits=0
atf_check -s exit:0 -o ignore ping -c1 192.0.2.2
- atf_check_equal "1" "$(jexec alcatraz sysctl -n net.dummymbuf.hits)"
+ atf_check_equal '1' '$(jexec alcatraz sysctl -n net.dummymbuf.hits)'
# m_len=11
# for the simplest L2 Ethernet frame it should impact src field
jexec alcatraz sysctl net.dummymbuf.rules="ethernet in ${epair}b pull-head 11;"
jexec alcatraz sysctl net.dummymbuf.hits=0
atf_check -s exit:0 -o ignore ping -c1 192.0.2.2
- atf_check_equal "1" "$(jexec alcatraz sysctl -n net.dummymbuf.hits)"
+ atf_check_equal '1' '$(jexec alcatraz sysctl -n net.dummymbuf.hits)'
# m_len=13
# provided L2 Ethernet simplest header is 14 bytes long, it should impact ethertype field
jexec alcatraz sysctl net.dummymbuf.rules="ethernet in ${epair}b pull-head 13;"
jexec alcatraz sysctl net.dummymbuf.hits=0
atf_check -s exit:0 -o ignore ping -c1 192.0.2.2
- atf_check_equal "1" "$(jexec alcatraz sysctl -n net.dummymbuf.hits)"
+ atf_check_equal '1' '$(jexec alcatraz sysctl -n net.dummymbuf.hits)'
}
ethernet_in_mbuf_len_cleanup()
{
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 5ea1dd6d8b2f..e55f46418221 100644
--- a/tests/sys/netpfil/pf/nat.sh
+++ b/tests/sys/netpfil/pf/nat.sh
@@ -810,6 +810,50 @@ empty_pool_cleanup()
pft_cleanup
}
+atf_test_case "dummynet_mask" "cleanup"
+dummynet_mask_head()
+{
+ atf_set descr 'Verify that dummynet uses the pre-nat address for masking'
+ atf_set require.user root
+}
+
+dummynet_mask_body()
+{
+ dummynet_init
+
+ epair_srv=$(vnet_mkepair)
+ epair_cl=$(vnet_mkepair)
+
+ ifconfig ${epair_cl}b 192.0.2.2/24 up
+ route add default 192.0.2.1
+
+ vnet_mkjail srv ${epair_srv}a
+ jexec srv ifconfig ${epair_srv}a 198.51.100.2/24 up
+
+ vnet_mkjail gw ${epair_srv}b ${epair_cl}a
+ jexec gw ifconfig ${epair_srv}b 198.51.100.1/24 up
+ jexec gw ifconfig ${epair_cl}a 192.0.2.1/24 up
+ jexec gw sysctl net.inet.ip.forwarding=1
+
+ jexec gw dnctl pipe 1 config delay 100 mask src-ip 0xffffff00
+ jexec gw pfctl -e
+ pft_set_rules gw \
+ "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 \
+ ping -c 3 198.51.100.2
+
+ # Now check that dummynet looked at the correct address
+ atf_check -s exit:0 -o match:"ip.*192.0.2.0/0" \
+ jexec gw dnctl pipe show
+}
+
+dummynet_mask_cleanup()
+{
+ pft_cleanup
+}
+
atf_init_test_cases()
{
atf_add_test_case "exhaust"
@@ -828,4 +872,5 @@ atf_init_test_cases()
atf_add_test_case "binat_compat"
atf_add_test_case "binat_match"
atf_add_test_case "empty_pool"
+ atf_add_test_case "dummynet_mask"
}
diff --git a/tests/sys/netpfil/pf/nat44.py b/tests/sys/netpfil/pf/nat44.py
new file mode 100644
index 000000000000..d69e794a62c3
--- /dev/null
+++ b/tests/sys/netpfil/pf/nat44.py
@@ -0,0 +1,76 @@
+#
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2025 Rubicon Communications, LLC (Netgate)
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+import pytest
+from atf_python.sys.net.tools import ToolsHelper
+from atf_python.sys.net.vnet import VnetTestTemplate
+
+class TestNAT44(VnetTestTemplate):
+ REQUIRED_MODULES = [ "pf" ]
+ TOPOLOGY = {
+ "vnet1": {"ifaces": ["if1"]},
+ "vnet2": {"ifaces": ["if1", "if2"]},
+ "vnet3": {"ifaces": ["if2"]},
+ "if1": {"prefixes4": [("192.0.2.2/24", "192.0.2.1/24")]},
+ "if2": {"prefixes4": [("198.51.100.1/24", "198.51.100.2")]},
+ }
+
+ def vnet2_handler(self, vnet):
+ outifname = vnet.iface_alias_map["if2"].name
+ ToolsHelper.print_output("/sbin/sysctl net.inet.ip.forwarding=1")
+
+ ToolsHelper.print_output("/sbin/pfctl -e")
+ ToolsHelper.print_output("/sbin/pfctl -x loud")
+ ToolsHelper.pf_rules([
+ "set reassemble yes",
+ "nat on {} inet from 192.0.2.0/24 -> ({})".format(outifname, outifname),
+ "pass"])
+
+ def vnet3_handler(self, vnet):
+ pass
+
+ @pytest.mark.require_user("root")
+ @pytest.mark.require_progs(["scapy"])
+ def test_nat_igmp(self):
+ "Verify that NAT translation of !(TCP|UDP|SCTP|ICMP) doesn't panic"
+ ToolsHelper.print_output("/sbin/route add default 192.0.2.1")
+ ToolsHelper.print_output("ping -c 3 198.51.100.2")
+
+ # Import in the correct vnet, so at to not confuse Scapy
+ import scapy.all as sp
+ import scapy.contrib as sc
+ import scapy.contrib.igmp
+
+ pkt = sp.IP(dst="198.51.100.2", ttl=64) \
+ / sc.igmp.IGMP(type=0x11, mrcode=1)
+ sp.send(pkt)
+
+ # This time we'll hit an existing state
+ pkt = sp.IP(dst="198.51.100.2", ttl=64) \
+ / sc.igmp.IGMP(type=0x11, mrcode=1)
+ reply = sp.sr1(pkt, timeout=3)
+ if reply:
+ reply.show()
diff --git a/tests/sys/netpfil/pf/nat64.sh b/tests/sys/netpfil/pf/nat64.sh
index 4438ad6abb85..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,19 +1040,23 @@ 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)"
atf_check -s exit:0 -o ignore \
+ -o match:'3 packets transmitted, 3 packets received, 0.0% packet loss' \
ping6 -c 3 64:ff9b::192.0.2.2
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}b ipv6-icmp 192.0.2.1:.* \(2001:db8::2\[[0-9]+\]\) -> 192.0.2.2:8 \(64:ff9b::c000:202\[[0-9]+\]\).*4:2 pkts.*route-to: 192.0.2.2@${epair_link}a" \
+ "${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
@@ -1094,6 +1105,7 @@ reply_to_body()
"pass in on ${epair}b reply-to (${epair}b 2001:db8::2) inet6 from any to 64:ff9b::/96 af-to inet from 192.0.2.1"
atf_check -s exit:0 -o ignore \
+ -o match:'3 packets transmitted, 3 packets received, 0.0% packet loss' \
ping6 -c 3 64:ff9b::192.0.2.2
}
@@ -1155,8 +1167,10 @@ v6_gateway_body()
"pass in on ${epair_lan}b inet6 from any to 64:ff9b::/96 af-to inet from (${epair_wan_one}a)"
atf_check -s exit:0 -o ignore \
+ -o match:'3 packets transmitted, 3 packets received, 0.0% packet loss' \
ping6 -c 3 64:ff9b::192.0.2.2
atf_check -s exit:0 -o ignore \
+ -o match:'3 packets transmitted, 3 packets received, 0.0% packet loss' \
ping6 -c 3 64:ff9b::198.51.100.1
}
diff --git a/tests/sys/netpfil/pf/once.sh b/tests/sys/netpfil/pf/once.sh
new file mode 100644
index 000000000000..54dbb030dbe6
--- /dev/null
+++ b/tests/sys/netpfil/pf/once.sh
@@ -0,0 +1,132 @@
+#
+# 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.
+
+. $(atf_get_srcdir)/utils.subr
+
+atf_test_case "basic" "cleanup"
+basic_head()
+{
+ atf_set descr 'Basic one shot rule test'
+ atf_set require.user root
+}
+
+basic_body()
+{
+ pft_init
+
+ epair=$(vnet_mkepair)
+
+ vnet_mkjail alcatraz ${epair}a
+ jexec alcatraz ifconfig ${epair}a 192.0.2.1/24 up
+
+ ifconfig ${epair}b 192.0.2.2/24 up
+
+ # Sanity checks
+ atf_check -s exit:0 -o ignore \
+ ping -c 1 192.0.2.1
+
+ jexec alcatraz pfctl -e
+ pft_set_rules alcatraz \
+ "block" \
+ "pass in from 192.0.2.2 once"
+
+ # First once succeeds
+ atf_check -s exit:0 -o ignore \
+ ping -c 3 192.0.2.1
+
+ # Check for '# expired'
+ atf_check -s exit:0 -e ignore \
+ -o match:'pass in inet from 192.0.2.2 to any flags S/SA keep state once # expired' \
+ jexec alcatraz pfctl -sr -vv
+
+ # The second one does not
+ atf_check -s exit:2 -o ignore \
+ ping -c 3 192.0.2.1
+
+ # Flush states, still shouldn't work
+ jexec alcatraz pfctl -Fs
+ atf_check -s exit:2 -o ignore \
+ ping -c 3 192.0.2.1
+}
+
+basic_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "anchor" "cleanup"
+anchor_head()
+{
+ atf_set descr 'Test one shot rule in anchors'
+ atf_set require.user root
+}
+
+anchor_body()
+{
+ pft_init
+ epair=$(vnet_mkepair)
+
+ vnet_mkjail alcatraz ${epair}a
+ jexec alcatraz ifconfig ${epair}a 192.0.2.1/24 up
+
+ ifconfig ${epair}b 192.0.2.2/24 up
+
+ # Sanity checks
+ atf_check -s exit:0 -o ignore \
+ ping -c 1 192.0.2.1
+
+ jexec alcatraz pfctl -e
+ pft_set_rules alcatraz \
+ "block" \
+ "anchor \"once\" {\n
+ pass in from 192.0.2.2 once\n
+ }"
+
+ # First once succeeds
+ atf_check -s exit:0 -o ignore \
+ ping -c 3 192.0.2.1
+
+ # Check for '# expired'
+ jexec alcatraz pfctl -sr -vv -a "*"
+ atf_check -s exit:0 -e ignore \
+ -o match:'pass in inet from 192.0.2.2 to any flags S/SA keep state once # expired' \
+ jexec alcatraz pfctl -sr -vv -a "*"
+
+ # The second one does not
+ atf_check -s exit:2 -o ignore \
+ ping -c 3 192.0.2.1
+}
+
+anchor_cleanup()
+{
+ pft_cleanup
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case "basic"
+ atf_add_test_case "anchor"
+}
diff --git a/tests/sys/netpfil/pf/pflog.sh b/tests/sys/netpfil/pf/pflog.sh
index a34ec893a75c..550548a59c11 100644
--- a/tests/sys/netpfil/pf/pflog.sh
+++ b/tests/sys/netpfil/pf/pflog.sh
@@ -394,6 +394,64 @@ rdr_action_cleanup()
pft_cleanup
}
+atf_test_case "rule_number" "cleanup"
+rule_number_head()
+{
+ atf_set descr 'Test rule numbers with anchors'
+ atf_set require.user root
+}
+
+rule_number_body()
+{
+ pflog_init
+
+ epair=$(vnet_mkepair)
+
+ vnet_mkjail alcatraz ${epair}b
+ jexec alcatraz ifconfig ${epair}b 192.0.2.1/24 up
+
+ ifconfig ${epair}a 192.0.2.2/24 up
+ ifconfig ${epair}a inet alias 192.0.2.3/24 up
+ ifconfig ${epair}a inet alias 192.0.2.4/24 up
+
+ jexec alcatraz pfctl -e
+ jexec alcatraz ifconfig pflog0 up
+ pft_set_rules alcatraz \
+ "pass log from 192.0.2.2" \
+ "anchor \"foo\" {\n \
+ pass log from 192.0.2.3\n \
+ }" \
+ "pass log from 192.0.2.4"
+
+ jexec alcatraz tcpdump -n -e -ttt --immediate-mode -l -U -i pflog0 >> pflog.txt &
+ sleep 1 # Wait for tcpdump to start
+
+ atf_check -s exit:0 -o ignore \
+ ping -c 1 -S 192.0.2.2 192.0.2.1
+ atf_check -s exit:0 -o ignore \
+ ping -c 1 -S 192.0.2.3 192.0.2.1
+ atf_check -s exit:0 -o ignore \
+ ping -c 1 -S 192.0.2.4 192.0.2.1
+
+ jexec alcatraz pfctl -sr -a '*' -vv
+
+ # Give tcpdump a little time to finish writing to the file
+ sleep 1
+ cat pflog.txt
+
+ atf_check -o match:"rule 0/0\(match\): pass in.*: 192.0.2.2.*ICMP echo request" \
+ cat pflog.txt
+ atf_check -o match:"rule 1.foo.0/0\(match\): pass in.*: 192.0.2.3.*: ICMP echo request" \
+ cat pflog.txt
+ atf_check -o match:"rule 2/0\(match\): pass in.*: 192.0.2.4.*: ICMP echo request" \
+ cat pflog.txt
+}
+
+rule_number_cleanup()
+{
+ pft_cleanup
+}
+
atf_init_test_cases()
{
atf_add_test_case "malformed"
@@ -403,4 +461,5 @@ atf_init_test_cases()
atf_add_test_case "unspecified_v4"
atf_add_test_case "unspecified_v6"
atf_add_test_case "rdr_action"
+ atf_add_test_case "rule_number"
}
diff --git a/tests/sys/netpfil/pf/pfsync.sh b/tests/sys/netpfil/pf/pfsync.sh
index 3be4a3024393..9843c470c06f 100644
--- a/tests/sys/netpfil/pf/pfsync.sh
+++ b/tests/sys/netpfil/pf/pfsync.sh
@@ -921,6 +921,8 @@ rtable_cleanup()
route_to_common_head()
{
+ # TODO: Extend setup_router_server_nat64 to create a 2nd router
+
pfsync_version=$1
shift
@@ -937,11 +939,16 @@ route_to_common_head()
# 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 ${epair_one}a 198.51.100.1/28 up
+ jexec one ifconfig ${epair_one}a inet6 2001:db8:4211::1/64 no_dad
+ jexec one ifconfig ${epair_one}a name inif
jexec one ifconfig ${epair_out_one}a 203.0.113.1/24 up
+ jexec one ifconfig ${epair_out_one}a inet6 2001:db8:4200::1/64 no_dad
jexec one ifconfig ${epair_out_one}a name outif
jexec one sysctl net.inet.ip.forwarding=1
- jexec one arp -s 203.0.113.254 00:01:02:03:04:05
+ jexec one sysctl net.inet6.ip6.forwarding=1
+ jexec one arp -s 203.0.113.254 00:01:02:00:00:04
+ jexec one ndp -s 2001:db8:4200::fe 00:01:02:00:00:06
jexec one ifconfig pfsync0 \
syncdev ${epair_sync}a \
maxupd 1 \
@@ -949,20 +956,30 @@ route_to_common_head()
up
jexec two ifconfig ${epair_sync}b 192.0.2.2/24 up
- jexec two ifconfig ${epair_two}a 198.51.100.2/24 up
- jexec two ifconfig ${epair_out_two}a 203.0.113.2/24 up
+ jexec two ifconfig ${epair_two}a 198.51.100.17/28 up
+ jexec two ifconfig ${epair_two}a inet6 2001:db8:4212::1/64 no_dad
+ jexec two ifconfig ${epair_two}a name inif
+ jexec two ifconfig ${epair_out_two}a 203.0.113.1/24 up
+ jexec two ifconfig ${epair_out_two}a inet6 2001:db8:4200::2/64 no_dad
jexec two ifconfig ${epair_out_two}a name outif
jexec two sysctl net.inet.ip.forwarding=1
- jexec two arp -s 203.0.113.254 00:01:02:03:04:05
+ jexec two sysctl net.inet6.ip6.forwarding=1
+ jexec two arp -s 203.0.113.254 00:01:02:00:00:04
+ jexec two ndp -s 2001:db8:4200::fe 00:01:02:00:00:06
jexec two ifconfig pfsync0 \
syncdev ${epair_sync}b \
maxupd 1 \
version $pfsync_version \
up
- ifconfig ${epair_one}b 198.51.100.254/24 up
- ifconfig ${epair_two}b 198.51.100.253/24 up
+ ifconfig ${epair_one}b 198.51.100.2/28 up
+ ifconfig ${epair_one}b inet6 2001:db8:4211::2/64 no_dad
+ ifconfig ${epair_two}b 198.51.100.18/28 up
+ ifconfig ${epair_two}b inet6 2001:db8:4212::2/64 no_dad
+ # Target is behind router "one"
route add -net 203.0.113.0/24 198.51.100.1
+ route add -inet6 -net 64:ff9b::/96 2001:db8:4211::1
+
ifconfig ${epair_two}b up
ifconfig ${epair_out_one}b up
ifconfig ${epair_out_two}b up
@@ -1206,6 +1223,435 @@ route_to_1400_bad_ifname_cleanup()
pfsynct_cleanup
}
+atf_test_case "af_to_in_floating" "cleanup"
+af_to_in_floating_head()
+{
+ atf_set descr 'Test syncing of states created by inbound af-to rules with floating states'
+ atf_set require.user root
+ atf_set require.progs python3 scapy
+}
+
+af_to_in_floating_body()
+{
+ route_to_common_head 1500
+
+ jexec one pfctl -e
+ pft_set_rules one \
+ "set state-policy floating" \
+ "set skip on ${epair_sync}a" \
+ "block" \
+ "pass inet6 proto icmp6 icmp6-type { neighbrsol, neighbradv } keep state (no-sync)" \
+ "pass in on inif to 64:ff9b::/96 af-to inet from (outif) keep state"
+
+ jexec two pfctl -e
+ pft_set_rules two \
+ "set skip on ${epair_sync}b" \
+ "block" \
+ "pass inet6 proto icmp6 icmp6-type { neighbrsol, neighbradv } keep state (no-sync)"
+
+ # ptf_ping can't deal with nat64, this test will fail but generate states
+ atf_check -s exit:1 env PYTHONPATH=${common_dir} \
+ ${common_dir}/pft_ping.py \
+ --sendif ${epair_one}b \
+ --fromaddr 2001:db8:4201::fe \
+ --to 64:ff9b::203.0.113.254 \
+ --recvif ${epair_out_one}b
+
+ # Allow time for sync
+ sleep 2
+
+ states_one=$(mktemp)
+ states_two=$(mktemp)
+ jexec one pfctl -qvvss | normalize_pfctl_s > $states_one
+ jexec two pfctl -qvvss | normalize_pfctl_s > $states_two
+
+ # Sanity check
+ grep -qE 'all ipv6-icmp 203.0.113.1 \(2001:db8:4201::fe\) -> 203.0.113.254:8 \(64:ff9b::cb00:71fe) .* rule 3 .* origif: inif' $states_one ||
+ atf_fail "State missing on router one"
+
+ grep -qE 'all ipv6-icmp 203.0.113.1 \(2001:db8:4201::fe\) -> 203.0.113.254:8 \(64:ff9b::cb00:71fe) .* origif: inif' $states_two ||
+ atf_fail "State missing on router two"
+}
+
+af_to_in_floating_cleanup()
+{
+ pfsynct_cleanup
+}
+
+atf_test_case "af_to_in_if_bound" "cleanup"
+af_to_in_if_bound_head()
+{
+ atf_set descr 'Test syncing of states created by inbound af-to rules with if-bound states'
+ atf_set require.user root
+ atf_set require.progs python3 scapy
+}
+
+af_to_in_if_bound_body()
+{
+ route_to_common_head 1500
+
+ jexec one pfctl -e
+ pft_set_rules one \
+ "set state-policy if-bound" \
+ "set skip on ${epair_sync}a" \
+ "block" \
+ "pass inet6 proto icmp6 icmp6-type { neighbrsol, neighbradv } keep state (no-sync)" \
+ "pass in on inif to 64:ff9b::/96 af-to inet from (outif) keep state"
+
+ jexec two pfctl -e
+ pft_set_rules two \
+ "set skip on ${epair_sync}b" \
+ "block" \
+ "pass inet6 proto icmp6 icmp6-type { neighbrsol, neighbradv } keep state (no-sync)"
+
+ # ptf_ping can't deal with nat64, this test will fail but generate states
+ atf_check -s exit:1 env PYTHONPATH=${common_dir} \
+ ${common_dir}/pft_ping.py \
+ --sendif ${epair_one}b \
+ --fromaddr 2001:db8:4201::fe \
+ --to 64:ff9b::203.0.113.254 \
+ --recvif ${epair_out_one}b
+
+ # Allow time for sync
+ sleep 2
+
+ states_one=$(mktemp)
+ states_two=$(mktemp)
+ jexec one pfctl -qvvss | normalize_pfctl_s > $states_one
+ jexec two pfctl -qvvss | normalize_pfctl_s > $states_two
+
+ # Sanity check
+ grep -qE 'outif ipv6-icmp 203.0.113.1 \(2001:db8:4201::fe\) -> 203.0.113.254:8 \(64:ff9b::cb00:71fe) .* rule 3 .* origif: inif' $states_one ||
+ atf_fail "State missing on router one"
+
+ grep -qE 'outif ipv6-icmp 203.0.113.1 \(2001:db8:4201::fe\) -> 203.0.113.254:8 \(64:ff9b::cb00:71fe) .* origif: inif' $states_two ||
+ atf_fail "State missing on router two"
+}
+
+af_to_in_if_bound_cleanup()
+{
+ pfsynct_cleanup
+}
+
+atf_test_case "af_to_out_if_bound" "cleanup"
+af_to_out_if_bound_head()
+{
+ atf_set descr 'Test syncing of states created by outbound af-to rules with if-bound states'
+ atf_set require.user root
+ atf_set require.progs python3 scapy
+}
+
+af_to_out_if_bound_body()
+{
+ route_to_common_head 1500
+
+ jexec one route add -inet6 -net 64:ff9b::/96 -iface outif
+ jexec one sysctl net.inet6.ip6.forwarding=1
+
+ jexec one pfctl -e
+ pft_set_rules one \
+ "set state-policy if-bound" \
+ "set skip on ${epair_sync}a" \
+ "block" \
+ "pass inet6 proto icmp6 icmp6-type { neighbrsol, neighbradv } keep state (no-sync)" \
+ "pass in on inif to 64:ff9b::/96 keep state" \
+ "pass out on outif to 64:ff9b::/96 af-to inet from (outif) keep state"
+
+ jexec two pfctl -e
+ pft_set_rules two \
+ "set skip on ${epair_sync}b" \
+ "block" \
+ "pass inet6 proto icmp6 icmp6-type { neighbrsol, neighbradv } keep state (no-sync)"
+
+ # ptf_ping can't deal with nat64, this test will fail but generate states
+ atf_check -s exit:1 env PYTHONPATH=${common_dir} \
+ ${common_dir}/pft_ping.py \
+ --sendif ${epair_one}b \
+ --fromaddr 2001:db8:4201::fe \
+ --to 64:ff9b::203.0.113.254 \
+ --recvif ${epair_out_one}b
+
+ # Allow time for sync
+ sleep 2
+
+ states_one=$(mktemp)
+ states_two=$(mktemp)
+ jexec one pfctl -qvvss | normalize_pfctl_s > $states_one
+ jexec two pfctl -qvvss | normalize_pfctl_s > $states_two
+
+ # Sanity check
+ # st->orig_kif is the same as st->kif, so st->orig_kif is not printed.
+ for state_regexp in \
+ "inif ipv6-icmp 64:ff9b::cb00:71fe\[128\] <- 2001:db8:4201::fe .* rule 3 .* creatorid: [0-9a-f]+" \
+ "outif icmp 203.0.113.1 \(64:ff9b::cb00:71fe\[8\]\) -> 203.0.113.254:8 \(2001:db8:4201::fe\) .* rule 4 .* creatorid: [0-9a-f]+" \
+ ; do
+ grep -qE "${state_regexp}" $states_one || atf_fail "State not found for '${state_regexp}'"
+ done
+
+ for state_regexp in \
+ "inif ipv6-icmp 64:ff9b::cb00:71fe\[128\] <- 2001:db8:4201::fe .* creatorid: [0-9a-f]+" \
+ "outif icmp 203.0.113.1 \(64:ff9b::cb00:71fe\[8\]\) -> 203.0.113.254:8 \(2001:db8:4201::fe\) .* creatorid: [0-9a-f]+" \
+ ; do
+ grep -qE "${state_regexp}" $states_two || atf_fail "State not found for '${state_regexp}'"
+ done
+}
+
+af_to_out_if_bound_cleanup()
+{
+ pfsynct_cleanup
+}
+
+atf_test_case "tag" "cleanup"
+tag_head()
+{
+ atf_set descr 'Test if the pf tag is synced'
+ atf_set require.user root
+ atf_set require.progs python3 scapy
+}
+
+tag_body()
+{
+ route_to_common_head 1500
+
+ jexec one pfctl -e
+ pft_set_rules one \
+ "set skip on ${epair_sync}a" \
+ "block" \
+ "pass inet6 proto icmp6 icmp6-type { neighbrsol, neighbradv } keep state (no-sync)" \
+ "pass in on inif inet proto udp tag sometag keep state" \
+ "pass out on outif tagged sometag keep state (no-sync)"
+
+ jexec two pfctl -e
+ pft_set_rules two \
+ "set debug loud" \
+ "set skip on ${epair_sync}b" \
+ "block" \
+ "pass inet6 proto icmp6 icmp6-type { neighbrsol, neighbradv } keep state (no-sync)" \
+ "block tagged othertag" \
+ "pass out on outif tagged sometag keep state (no-sync)"
+
+ atf_check -s exit:0 env PYTHONPATH=${common_dir} \
+ ${common_dir}/pft_ping.py \
+ --ping-type=udp \
+ --sendif ${epair_one}b \
+ --fromaddr 198.51.100.254 \
+ --to 203.0.113.254 \
+ --recvif ${epair_out_one}b
+
+ # Allow time for sync
+ sleep 2
+
+ # Force the next request to go through the 2nd router
+ route change -net 203.0.113.0/24 198.51.100.17
+
+ atf_check -s exit:0 env PYTHONPATH=${common_dir} \
+ ${common_dir}/pft_ping.py \
+ --ping-type=udp \
+ --sendif ${epair_two}b \
+ --fromaddr 198.51.100.254 \
+ --to 203.0.113.254 \
+ --recvif ${epair_out_two}b
+}
+
+tag_cleanup()
+{
+ pfsynct_cleanup
+}
+
+atf_test_case "altq_queues" "cleanup"
+altq_queues_head()
+{
+ atf_set descr 'Test if the altq queues are synced'
+ atf_set require.user root
+ atf_set require.progs python3 scapy
+}
+
+altq_queues_body()
+{
+ route_to_common_head 1500
+ altq_init
+ is_altq_supported hfsc
+
+ jexec one pfctl -e
+ pft_set_rules one \
+ "set skip on ${epair_sync}a" \
+ "altq on outif bandwidth 30000b hfsc queue { default other1 other2 }" \
+ "queue default hfsc(linkshare 10000b default)" \
+ "queue other1 hfsc(linkshare 10000b)" \
+ "queue other2 hfsc(linkshare 10000b)" \
+ "block" \
+ "pass inet6 proto icmp6 icmp6-type { neighbrsol, neighbradv } keep state (no-sync)" \
+ "pass in on inif inet proto udp queue other1 keep state" \
+ "pass out on outif inet proto udp keep state"
+
+ jexec two pfctl -e
+ pft_set_rules two \
+ "set debug loud" \
+ "set skip on ${epair_sync}b" \
+ "altq on outif bandwidth 30000b hfsc queue { default other2 other1 }" \
+ "queue default hfsc(linkshare 10000b default)" \
+ "queue other2 hfsc(linkshare 10000b)" \
+ "queue other1 hfsc(linkshare 10000b)" \
+ "block" \
+ "pass inet6 proto icmp6 icmp6-type { neighbrsol, neighbradv } keep state (no-sync)" \
+ "pass out on outif inet proto udp keep state"
+
+ atf_check -s exit:0 env PYTHONPATH=${common_dir} \
+ ${common_dir}/pft_ping.py \
+ --ping-type=udp \
+ --sendif ${epair_one}b \
+ --fromaddr 198.51.100.254 \
+ --to 203.0.113.254 \
+ --recvif ${epair_out_one}b
+
+ queues_one=$(mktemp)
+ jexec one pfctl -qvsq | normalize_pfctl_s > $queues_one
+ echo " === queues one === "
+ cat $queues_one
+ grep -qE 'queue other1 on outif .* pkts: 1 ' $queues_one || atf_fail 'Packets not sent through queue "other1"'
+
+ # Allow time for sync
+ sleep 2
+
+ # Force the next request to go through the 2nd router
+ route change -net 203.0.113.0/24 198.51.100.17
+
+ # Send a packet through router "two". It lacks the inbound rule
+ # but the inbound state should have been pfsynced from router "one"
+ # including altq queuing information. However the queues are created
+ # on router "two" in different order and we only sync queue index,
+ # so the packet ends up in a different queue. One must have identical
+ # queue set on both routers!
+ atf_check -s exit:0 env PYTHONPATH=${common_dir} \
+ ${common_dir}/pft_ping.py \
+ --ping-type=udp \
+ --sendif ${epair_two}b \
+ --fromaddr 198.51.100.254 \
+ --to 203.0.113.254 \
+ --recvif ${epair_out_two}b
+
+ queues_two=$(mktemp)
+ jexec two pfctl -qvsq | normalize_pfctl_s > $queues_two
+ echo " === queues two === "
+ cat $queues_two
+ grep -qE 'queue other2 on outif .* pkts: 1 ' $queues_two || atf_fail 'Packets not sent through queue "other2"'
+}
+
+altq_queues_cleanup()
+{
+ # Interface detaching seems badly broken in altq. If interfaces are
+ # destroyed when shutting down the vnet and then pf is unloaded, it will
+ # cause a kernel crash. Work around the issue by first flushing the
+ # pf rulesets
+ jexec one pfctl -F all
+ jexec two pfctl -F all
+ pfsynct_cleanup
+}
+
+atf_test_case "rt_af" "cleanup"
+rt_af_head()
+{
+ atf_set descr 'Test if the rt_af is synced'
+ atf_set require.user root
+ atf_set require.progs python3 scapy
+}
+
+rt_af_body()
+{
+ route_to_common_head 1500
+
+ jexec one pfctl -e
+ pft_set_rules one \
+ "set skip on ${epair_sync}a" \
+ "block" \
+ "pass inet6 proto icmp6 icmp6-type { neighbrsol, neighbradv } keep state (no-sync)" \
+ "pass in on inif \
+ route-to (outif 203.0.113.254) prefer-ipv6-nexthop \
+ inet proto udp \
+ to 203.0.113.241 \
+ keep state" \
+ "pass in on inif \
+ route-to (outif 2001:db8:4200::fe) prefer-ipv6-nexthop \
+ inet proto udp \
+ to 203.0.113.242 \
+ keep state" \
+ "pass in on inif \
+ route-to (outif 2001:db8:4200::fe) prefer-ipv6-nexthop \
+ inet6 proto udp \
+ to 2001:db8:4200::f3 \
+ keep state" \
+ "pass out on outif inet proto udp keep state (no-sync)" \
+ "pass out on outif inet6 proto udp keep state (no-sync)"
+
+ jexec two pfctl -e
+ pft_set_rules two \
+ "set debug loud" \
+ "set skip on ${epair_sync}b" \
+ "block" \
+ "pass inet6 proto icmp6 icmp6-type { neighbrsol, neighbradv } keep state (no-sync)" \
+
+ # IPv4 packet over IPv4 gateway
+ atf_check -s exit:0 env PYTHONPATH=${common_dir} \
+ ${common_dir}/pft_ping.py \
+ --ping-type=udp \
+ --sendif ${epair_one}b \
+ --fromaddr 198.51.100.254 \
+ --to 203.0.113.241 \
+ --recvif ${epair_out_one}b
+
+ # FIXME: Routing IPv4 packets over IPv6 gateways with gateway added
+ # with `ndp -s` causes the static NDP entry to become expired.
+ # Pfsync tests don't use "servers" which can reply to ARP and NDP,
+ # but such static entry for gateway and only check if a stateless
+ # ICMP or UDP packet is forward through.
+ #
+ # IPv4 packert over IPv6 gateway
+ #atf_check -s exit:0 env PYTHONPATH=${common_dir} \
+ # ${common_dir}/pft_ping.py \
+ # --ping-type=udp \
+ # --sendif ${epair_one}b \
+ # --fromaddr 198.51.100.254 \
+ # --to 203.0.113.242 \
+ # --recvif ${epair_out_one}b
+
+ # IPv6 packet over IPv6 gateway
+ atf_check -s exit:0 env PYTHONPATH=${common_dir} \
+ ${common_dir}/pft_ping.py \
+ --ping-type=udp \
+ --sendif ${epair_one}b \
+ --fromaddr 2001:db8:4211::fe \
+ --to 2001:db8:4200::f3 \
+ --recvif ${epair_out_one}b
+
+ sleep 5 # Wait for pfsync
+
+ states_one=$(mktemp)
+ states_two=$(mktemp)
+ jexec one pfctl -qvvss | normalize_pfctl_s > $states_one
+ jexec two pfctl -qvvss | normalize_pfctl_s > $states_two
+
+ echo " === states one === "
+ cat $states_one
+ echo " === states two === "
+ cat $states_two
+
+ for state_regexp in \
+ "all udp 203.0.113.241:9 <- 198.51.100.254 .* route-to: 203.0.113.254@outif origif: inif" \
+ "all udp 2001:db8:4200::f3\[9\] <- 2001:db8:4211::fe .* route-to: 2001:db8:4200::fe@outif origif: inif" \
+ ; do
+ grep -qE "${state_regexp}" $states_two || atf_fail "State not found for '${state_regexp}' on router two"
+ done
+}
+
+rt_af_cleanup()
+{
+ jexec one pfctl -qvvsr
+ jexec one pfctl -qvvss
+ jexec one arp -an
+ jexec one ndp -an
+ pfsynct_cleanup
+}
+
atf_init_test_cases()
{
atf_add_test_case "basic"
@@ -1224,4 +1670,10 @@ atf_init_test_cases()
atf_add_test_case "route_to_1301_bad_rpool"
atf_add_test_case "route_to_1400_bad_ruleset"
atf_add_test_case "route_to_1400_bad_ifname"
+ atf_add_test_case "af_to_in_floating"
+ atf_add_test_case "af_to_in_if_bound"
+ atf_add_test_case "af_to_out_if_bound"
+ atf_add_test_case "tag"
+ atf_add_test_case "altq_queues"
+ atf_add_test_case "rt_af"
}
diff --git a/tests/sys/netpfil/pf/rdr.sh b/tests/sys/netpfil/pf/rdr.sh
index f7c920bbfa8f..24b95b2047f4 100644
--- a/tests/sys/netpfil/pf/rdr.sh
+++ b/tests/sys/netpfil/pf/rdr.sh
@@ -281,8 +281,66 @@ 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_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"
diff --git a/tests/sys/netpfil/pf/route_to.sh b/tests/sys/netpfil/pf/route_to.sh
index 765403dcb79c..13b60c8f80bc 100644
--- a/tests/sys/netpfil/pf/route_to.sh
+++ b/tests/sys/netpfil/pf/route_to.sh
@@ -28,6 +28,75 @@
common_dir=$(atf_get_srcdir)/../common
+# We need to somehow test if the random algorithm of pf_map_addr() is working.
+# The table or prefix contains multiple IP next-hop addresses, for each one try
+# to establish up to 10 connections. Fail the test if with this many attempts
+# the "good" target has not been chosen. However this choice is random,
+# the check might still ocasionally fail.
+check_random() {
+ if [ "$1" = "IPv4" ]; then
+ ping_from="${net_clients_4}.1"
+ ping_to="${host_server_4}"
+ else
+ ping_from="${net_clients_6}::1"
+ ping_to="${host_server_6}"
+ fi
+ good_targets="$2"
+ bad_targets="$3"
+
+ port=42000
+ states=$(mktemp) || exit 1
+ for good_target in $good_targets; do
+ found="no"
+ for attempt in $(seq 1 10); do
+ port=$(( port + 1 ))
+ jexec router pfctl -Fs
+ atf_check -s exit:0 ${common_dir}/pft_ping.py \
+ --sendif ${epair_tester}a --replyif ${epair_tester}a \
+ --fromaddr ${ping_from} --to ${ping_to} \
+ --ping-type=tcp3way --send-sport=${port}
+ jexec router pfctl -qvvss | normalize_pfctl_s > $states
+ cat $states
+ if [ -n "${bad_targets}" ]; then
+ for bad_target in $bad_targets; do
+ if grep -qE "route-to: ${bad_target}@" $states; then
+ atf_fail "Bad target ${bad_target} selected!"
+ fi
+ done
+ fi;
+ if grep -qE "route-to: ${good_target}@" $states; then
+ found=yes
+ break
+ fi
+ done
+ if [ "${found}" = "no" ]; then
+ atf_fail "Target ${good_target} not selected after ${attempt} attempts!"
+ fi
+ done
+}
+
+pf_map_addr_common()
+{
+ setup_router_server_nat64
+
+ # Clients will connect from another network behind the router.
+ # This allows for using multiple source addresses.
+ jexec router route add -6 ${net_clients_6}::/${net_clients_6_mask} ${net_tester_6_host_tester}
+ jexec router route add ${net_clients_4}.0/${net_clients_4_mask} ${net_tester_4_host_tester}
+
+ # The servers are reachable over additional IP addresses for
+ # testing of tables and subnets. The addresses are noncontinougnus
+ # for pf_map_addr() counter tests.
+ for i in 0 1 4 5; do
+ a1=$((24 + i))
+ jexec server1 ifconfig ${epair_server1}b inet ${net_server1_4}.${a1}/32 alias
+ jexec server1 ifconfig ${epair_server1}b inet6 ${net_server1_6}::42:${i}/128 alias
+ a2=$((40 + i))
+ jexec server2 ifconfig ${epair_server2}b inet ${net_server2_4}.${a2}/32 alias
+ jexec server2 ifconfig ${epair_server2}b inet6 ${net_server2_6}::42:${i}/128 alias
+ done
+}
+
atf_test_case "v4" "cleanup"
v4_head()
{
@@ -893,7 +962,6 @@ empty_pool_cleanup()
pft_cleanup
}
-
atf_test_case "table_loop" "cleanup"
table_loop_head()
@@ -905,24 +973,7 @@ table_loop_head()
table_loop_body()
{
- setup_router_server_nat64
-
- # Clients will connect from another network behind the router.
- # This allows for using multiple source addresses.
- jexec router route add -6 ${net_clients_6}::/${net_clients_6_mask} ${net_tester_6_host_tester}
- jexec router route add ${net_clients_4}.0/${net_clients_4_mask} ${net_tester_4_host_tester}
-
- # The servers are reachable over additional IP addresses for
- # testing of tables and subnets. The addresses are noncontinougnus
- # for pf_map_addr() counter tests.
- for i in 0 1 4 5; do
- a1=$((24 + i))
- jexec server1 ifconfig ${epair_server1}b inet ${net_server1_4}.${a1}/32 alias
- jexec server1 ifconfig ${epair_server1}b inet6 ${net_server1_6}::42:${i}/128 alias
- a2=$((40 + i))
- jexec server2 ifconfig ${epair_server2}b inet ${net_server2_4}.${a2}/32 alias
- jexec server2 ifconfig ${epair_server2}b inet6 ${net_server2_6}::42:${i}/128 alias
- done
+ pf_map_addr_common
jexec router pfctl -e
pft_set_rules router \
@@ -976,6 +1027,626 @@ table_loop_cleanup()
}
+atf_test_case "roundrobin" "cleanup"
+
+roundrobin_head()
+{
+ atf_set descr 'multiple gateways of mixed AF, including prefixes and tables, for IPv6 packets'
+ atf_set require.user root
+ atf_set require.progs python3 scapy
+}
+
+roundrobin_body()
+{
+ pf_map_addr_common
+
+ # The rule is defined as "inet6 proto tcp" so directly given IPv4 hosts
+ # will be removed from the pool by pfctl. Tables will still be loaded
+ # and pf_map_addr() will only use IPv6 addresses from them. It will
+ # iterate over members of the pool and inside of tables and prefixes.
+
+ jexec router pfctl -e
+ pft_set_rules router \
+ "set debug loud" \
+ "set reassemble yes" \
+ "set state-policy if-bound" \
+ "table <rt_targets> { ${net_server2_4}.40/31 ${net_server2_4}.44 ${net_server2_6}::42:0/127 ${net_server2_6}::42:4 }" \
+ "pass in on ${epair_tester}b \
+ route-to { \
+ (${epair_server1}a ${net_server1_4_host_server}) \
+ (${epair_server2}a <rt_targets_empty>) \
+ (${epair_server1}a ${net_server1_6}::42:0/127) \
+ (${epair_server2}a <rt_targets_empty>) \
+ (${epair_server2}a <rt_targets>) \
+ } \
+ inet6 proto tcp \
+ keep state"
+
+ for port in $(seq 1 6); do
+ port=$((4200 + port))
+ atf_check -s exit:0 ${common_dir}/pft_ping.py \
+ --sendif ${epair_tester}a --replyif ${epair_tester}a \
+ --fromaddr ${net_clients_6}::1 --to ${host_server_6} \
+ --ping-type=tcp3way --send-sport=${port}
+ done
+
+ states=$(mktemp) || exit 1
+ jexec router pfctl -qvvss | normalize_pfctl_s > $states
+
+ for state_regexp in \
+ "${epair_tester}b tcp ${host_server_6}\[9\] <- ${net_clients_6}::1\[4201\] .* route-to: ${net_server1_6}::42:0@${epair_server1}a" \
+ "${epair_tester}b tcp ${host_server_6}\[9\] <- ${net_clients_6}::1\[4202\] .* route-to: ${net_server1_6}::42:1@${epair_server1}a" \
+ "${epair_tester}b tcp ${host_server_6}\[9\] <- ${net_clients_6}::1\[4203\] .* route-to: ${net_server2_6}::42:0@${epair_server2}a" \
+ "${epair_tester}b tcp ${host_server_6}\[9\] <- ${net_clients_6}::1\[4204\] .* route-to: ${net_server2_6}::42:1@${epair_server2}a" \
+ "${epair_tester}b tcp ${host_server_6}\[9\] <- ${net_clients_6}::1\[4205\] .* route-to: ${net_server2_6}::42:4@${epair_server2}a" \
+ "${epair_tester}b tcp ${host_server_6}\[9\] <- ${net_clients_6}::1\[4206\] .* route-to: ${net_server1_6}::42:0@${epair_server1}a" \
+ ; do
+ grep -qE "${state_regexp}" $states || atf_fail "State not found for '${state_regexp}'"
+ done
+}
+
+roundrobin_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "random_table" "cleanup"
+
+random_table_head()
+{
+ atf_set descr 'Pool with random flag and a table for IPv6'
+ atf_set require.user root
+ atf_set require.progs python3 scapy
+}
+
+random_table_body()
+{
+ pf_map_addr_common
+
+ # The "random" flag will pick random hosts from the table but will
+ # not dive into prefixes, always choosing the 0th address.
+ # Proper address family will be choosen.
+
+ jexec router pfctl -e
+ pft_set_rules router \
+ "set debug loud" \
+ "set reassemble yes" \
+ "set state-policy if-bound" \
+ "table <rt_targets> { ${net_server2_4}.40/31 ${net_server2_4}.44 ${net_server2_6}::42:0/127 ${net_server2_6}::42:4 }" \
+ "pass in on ${epair_tester}b \
+ route-to { (${epair_server2}a <rt_targets>) } random \
+ inet6 proto tcp \
+ keep state"
+
+ good_targets="${net_server2_6}::42:0 ${net_server2_6}::42:4"
+ bad_targets="${net_server2_6}::42:1"
+ check_random IPv6 "${good_targets}" "${bad_targets}"
+}
+
+random_table_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "random_prefix" "cleanup"
+
+random_prefix_head()
+{
+ atf_set descr 'Pool with random flag and a table for IPv4'
+ atf_set require.user root
+ atf_set require.progs python3 scapy
+}
+
+random_prefix_body()
+{
+ pf_map_addr_common
+
+ # The "random" flag will pick random hosts from given prefix.
+ # The choice being random makes testing it non-trivial. We do 10
+ # attempts to have each target chosen. Hopefully this is enough to have
+ # this test pass often enough.
+
+ jexec router pfctl -e
+ pft_set_rules router \
+ "set debug loud" \
+ "set reassemble yes" \
+ "set state-policy if-bound" \
+ "pass in on ${epair_tester}b \
+ route-to { (${epair_server2}a ${net_server2_6}::42:0/127) } random \
+ inet6 proto tcp \
+ keep state"
+
+ good_targets="${net_server2_6}::42:0 ${net_server2_6}::42:1"
+ check_random IPv6 "${good_targets}"
+}
+
+random_prefix_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "prefer_ipv6_nexthop_single_ipv4" "cleanup"
+
+prefer_ipv6_nexthop_single_ipv4_head()
+{
+ atf_set descr 'prefer-ipv6-nexthop option for a single IPv4 gateway'
+ atf_set require.user root
+ atf_set require.progs python3 scapy
+}
+
+prefer_ipv6_nexthop_single_ipv4_body()
+{
+ pf_map_addr_common
+
+ # Basic forwarding test for prefer-ipv6-nexthop pool option.
+ # A single IPv4 gateway will work only for IPv4 traffic.
+
+ jexec router pfctl -e
+ pft_set_rules router \
+ "set reassemble yes" \
+ "set state-policy if-bound" \
+ "pass in on ${epair_tester}b \
+ route-to (${epair_server1}a ${net_server1_4_host_server}) prefer-ipv6-nexthop \
+ proto tcp \
+ keep state"
+
+ atf_check -s exit:0 ${common_dir}/pft_ping.py \
+ --sendif ${epair_tester}a --replyif ${epair_tester}a \
+ --fromaddr ${net_clients_4}.1 --to ${host_server_4} \
+ --ping-type=tcp3way --send-sport=4201 \
+
+ atf_check -s exit:1 ${common_dir}/pft_ping.py \
+ --sendif ${epair_tester}a --replyif ${epair_tester}a \
+ --fromaddr ${net_clients_6}::1 --to ${host_server_6} \
+ --ping-type=tcp3way --send-sport=4202
+
+ states=$(mktemp) || exit 1
+ jexec router pfctl -qvvss | normalize_pfctl_s > $states
+
+ for state_regexp in \
+ "${epair_tester}b tcp ${host_server_4}:9 <- ${net_clients_4}.1:4201 .* route-to: ${net_server1_4_host_server}@${epair_server1}a" \
+ ; do
+ grep -qE "${state_regexp}" $states || atf_fail "State not found for '${state_regexp}'"
+ done
+
+ # The IPv6 packet could not have been routed over IPv4 gateway.
+ atf_check -o "match:map-failed +1 +" -x "jexec router pfctl -qvvsi 2> /dev/null"
+}
+
+prefer_ipv6_nexthop_single_ipv4_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "prefer_ipv6_nexthop_single_ipv6" "cleanup"
+
+prefer_ipv6_nexthop_single_ipv6_head()
+{
+ atf_set descr 'prefer-ipv6-nexthop option for a single IPv6 gateway'
+ atf_set require.user root
+ atf_set require.progs python3 scapy
+}
+
+prefer_ipv6_nexthop_single_ipv6_body()
+{
+ pf_map_addr_common
+
+ # Basic forwarding test for prefer-ipv6-nexthop pool option.
+ # A single IPve gateway will work both for IPv4 and IPv6 traffic.
+
+ jexec router pfctl -e
+ pft_set_rules router \
+ "set reassemble yes" \
+ "set state-policy if-bound" \
+ "pass in on ${epair_tester}b \
+ route-to (${epair_server1}a ${net_server1_6_host_server}) prefer-ipv6-nexthop \
+ proto tcp \
+ keep state"
+
+ atf_check -s exit:0 ${common_dir}/pft_ping.py \
+ --sendif ${epair_tester}a --replyif ${epair_tester}a \
+ --fromaddr ${net_clients_4}.1 --to ${host_server_4} \
+ --ping-type=tcp3way --send-sport=4201 \
+
+ atf_check -s exit:0 ${common_dir}/pft_ping.py \
+ --sendif ${epair_tester}a --replyif ${epair_tester}a \
+ --fromaddr ${net_clients_6}::1 --to ${host_server_6} \
+ --ping-type=tcp3way --send-sport=4202
+
+ states=$(mktemp) || exit 1
+ jexec router pfctl -qvvss | normalize_pfctl_s > $states
+
+ for state_regexp in \
+ "${epair_tester}b tcp ${host_server_4}:9 <- ${net_clients_4}.1:4201 .* route-to: ${net_server1_6_host_server}@${epair_server1}a" \
+ "${epair_tester}b tcp ${host_server_6}\[9\] <- ${net_clients_6}::1\[4202\] .* route-to: ${net_server1_6_host_server}@${epair_server1}a" \
+ ; do
+ grep -qE "${state_regexp}" $states || atf_fail "State not found for '${state_regexp}'"
+ done
+}
+
+prefer_ipv6_nexthop_single_ipv6_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "prefer_ipv6_nexthop_mixed_af_roundrobin_ipv4" "cleanup"
+
+prefer_ipv6_nexthop_mixed_af_roundrobin_ipv4_head()
+{
+ atf_set descr 'prefer-ipv6-nexthop option for multiple gateways of mixed AF with prefixes and tables, round robin selection, for IPv4 packets'
+ atf_set require.user root
+ atf_set require.progs python3 scapy
+}
+
+prefer_ipv6_nexthop_mixed_af_roundrobin_ipv4_body()
+{
+ pf_map_addr_common
+
+ # pf_map_addr() starts iterating over hosts of the pool from the 2nd
+ # host. This behaviour was here before adding prefer-ipv6-nexthop
+ # support, we test for it. Some targets are hosts and some are tables.
+ # Those are iterated too. Finally the iteration loops back
+ # to the beginning of the pool. Send out IPv4 probes. They will use all
+ # IPv4 and IPv6 addresses from the pool.
+
+ jexec router pfctl -e
+ pft_set_rules router \
+ "set reassemble yes" \
+ "set state-policy if-bound" \
+ "table <rt_targets> { ${net_server2_4}.40/31 ${net_server2_4}.44 ${net_server2_6}::42:4/127 }" \
+ "pass in on ${epair_tester}b \
+ route-to { \
+ (${epair_server1}a ${net_server1_6_host_server}) \
+ (${epair_server1}a ${net_server1_6}::42:0/127) \
+ (${epair_server2}a ${net_server2_4_host_server}) \
+ (${epair_server2}a <rt_targets_empty>) \
+ (${epair_server1}a ${net_server1_4}.24/31) \
+ (${epair_server2}a <rt_targets>) \
+ } prefer-ipv6-nexthop \
+ proto tcp \
+ keep state"
+
+ for port in $(seq 1 12); do
+ port=$((4200 + port))
+ atf_check -s exit:0 ${common_dir}/pft_ping.py \
+ --sendif ${epair_tester}a --replyif ${epair_tester}a \
+ --fromaddr ${net_clients_4}.1 --to ${host_server_4} \
+ --ping-type=tcp3way --send-sport=${port}
+ done
+
+ states=$(mktemp) || exit 1
+ jexec router pfctl -qvvss | normalize_pfctl_s > $states
+
+ for state_regexp in \
+ "${epair_tester}b tcp ${host_server_4}:9 <- ${net_clients_4}.1:4201 .* route-to: ${net_server1_6}::42:0@${epair_server1}a" \
+ "${epair_tester}b tcp ${host_server_4}:9 <- ${net_clients_4}.1:4202 .* route-to: ${net_server1_6}::42:1@${epair_server1}a" \
+ "${epair_tester}b tcp ${host_server_4}:9 <- ${net_clients_4}.1:4203 .* route-to: ${net_server2_4_host_server}@${epair_server2}a" \
+ "${epair_tester}b tcp ${host_server_4}:9 <- ${net_clients_4}.1:4204 .* route-to: ${net_server1_4}.24@${epair_server1}a" \
+ "${epair_tester}b tcp ${host_server_4}:9 <- ${net_clients_4}.1:4205 .* route-to: ${net_server1_4}.25@${epair_server1}a" \
+ "${epair_tester}b tcp ${host_server_4}:9 <- ${net_clients_4}.1:4206 .* route-to: ${net_server2_6}::42:4@${epair_server2}a" \
+ "${epair_tester}b tcp ${host_server_4}:9 <- ${net_clients_4}.1:4207 .* route-to: ${net_server2_6}::42:5@${epair_server2}a" \
+ "${epair_tester}b tcp ${host_server_4}:9 <- ${net_clients_4}.1:4208 .* route-to: ${net_server2_4}.40@${epair_server2}a" \
+ "${epair_tester}b tcp ${host_server_4}:9 <- ${net_clients_4}.1:4209 .* route-to: ${net_server2_4}.41@${epair_server2}a" \
+ "${epair_tester}b tcp ${host_server_4}:9 <- ${net_clients_4}.1:4210 .* route-to: ${net_server2_4}.44@${epair_server2}a" \
+ "${epair_tester}b tcp ${host_server_4}:9 <- ${net_clients_4}.1:4211 .* route-to: ${net_server1_6_host_server}@${epair_server1}a" \
+ "${epair_tester}b tcp ${host_server_4}:9 <- ${net_clients_4}.1:4212 .* route-to: ${net_server1_6}::42:0@${epair_server1}a" \
+ ; do
+ grep -qE "${state_regexp}" $states || atf_fail "State not found for '${state_regexp}'"
+ done
+}
+
+prefer_ipv6_nexthop_mixed_af_roundrobin_ipv4_cleanup()
+{
+ pft_cleanup
+}
+
+prefer_ipv6_nexthop_mixed_af_roundrobin_ipv6_head()
+{
+ atf_set descr 'prefer-ipv6-nexthop option for multiple gateways of mixed AF with prefixes and tables, round-robin selection, for IPv6 packets'
+ atf_set require.user root
+ atf_set require.progs python3 scapy
+}
+
+prefer_ipv6_nexthop_mixed_af_roundrobin_ipv6_body()
+{
+ pf_map_addr_common
+
+ # The "random" flag will pick random hosts from the table but will
+ # not dive into prefixes, always choosing the 0th address.
+ # Proper address family will be choosen. The choice being random makes
+ # testing it non-trivial. We do 10 attempts to have each target chosen.
+ # Hopefully this is enough to have this test pass often enough.
+
+ # pf_map_addr() starts iterating over hosts of the pool from the 2nd
+ # host. This behaviour was here before adding prefer-ipv6-nexthop
+ # support, we test for it. Some targets are hosts and some are tables.
+ # Those are iterated too. Finally the iteration loops back
+ # to the beginning of the pool. Send out IPv6 probes. They will use only
+ # IPv6 addresses from the pool.
+
+ jexec router pfctl -e
+ pft_set_rules router \
+ "set reassemble yes" \
+ "set state-policy if-bound" \
+ "table <rt_targets> { ${net_server2_4}.40/31 ${net_server2_4}.44 ${net_server2_6}::42:4/127 }" \
+ "pass in on ${epair_tester}b \
+ route-to { \
+ (${epair_server1}a ${net_server1_6_host_server}) \
+ (${epair_server1}a ${net_server1_6}::42:0/127) \
+ (${epair_server2}a ${net_server2_4_host_server}) \
+ (${epair_server2}a <rt_targets_empty>) \
+ (${epair_server1}a ${net_server1_4}.24/31) \
+ (${epair_server2}a <rt_targets>) \
+ } prefer-ipv6-nexthop \
+ proto tcp \
+ keep state"
+
+ for port in $(seq 1 6); do
+ port=$((4200 + port))
+ atf_check -s exit:0 ${common_dir}/pft_ping.py \
+ --sendif ${epair_tester}a --replyif ${epair_tester}a \
+ --fromaddr ${net_clients_6}::1 --to ${host_server_6} \
+ --ping-type=tcp3way --send-sport=${port}
+ done
+
+ states=$(mktemp) || exit 1
+ jexec router pfctl -qvvss | normalize_pfctl_s > $states
+
+ for state_regexp in \
+ "${epair_tester}b tcp ${host_server_6}\[9\] <- ${net_clients_6}::1\[4201\] .* route-to: ${net_server1_6}::42:0@${epair_server1}a" \
+ "${epair_tester}b tcp ${host_server_6}\[9\] <- ${net_clients_6}::1\[4202\] .* route-to: ${net_server1_6}::42:1@${epair_server1}a" \
+ "${epair_tester}b tcp ${host_server_6}\[9\] <- ${net_clients_6}::1\[4203\] .* route-to: ${net_server2_6}::42:4@${epair_server2}a" \
+ "${epair_tester}b tcp ${host_server_6}\[9\] <- ${net_clients_6}::1\[4204\] .* route-to: ${net_server2_6}::42:5@${epair_server2}a" \
+ "${epair_tester}b tcp ${host_server_6}\[9\] <- ${net_clients_6}::1\[4205\] .* route-to: ${net_server1_6_host_server}@${epair_server1}a" \
+ "${epair_tester}b tcp ${host_server_6}\[9\] <- ${net_clients_6}::1\[4206\] .* route-to: ${net_server1_6}::42:0@${epair_server1}a" \
+ ; do
+ grep -qE "${state_regexp}" $states || atf_fail "State not found for '${state_regexp}'"
+ done
+}
+
+prefer_ipv6_nexthop_mixed_af_roundrobin_ipv6_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "prefer_ipv6_nexthop_mixed_af_random_ipv4" "cleanup"
+
+prefer_ipv6_nexthop_mixed_af_random_table_ipv4_head()
+{
+ atf_set descr 'prefer-ipv6-nexthop option for a mixed-af table with random selection for IPv4 packets'
+ atf_set require.user root
+ atf_set require.progs python3 scapy
+}
+
+prefer_ipv6_nexthop_mixed_af_random_table_ipv4_body()
+{
+ pf_map_addr_common
+
+ # Similarly to the test "random_table" the algorithm will choose
+ # IP addresses from the table not diving into prefixes.
+ # *prefer*-ipv6-nexthop means that IPv6 nexthops are preferred,
+ # so IPv4 ones will not be chosen as long as there are IPv6 ones
+ # available. With this tested there is no need for a test for IPv6-only
+ # next-hops table.
+
+ jexec router pfctl -e
+ pft_set_rules router \
+ "set reassemble yes" \
+ "set state-policy if-bound" \
+ "table <rt_targets> { ${net_server2_4}.40/31 ${net_server2_4}.44 ${net_server2_6}::42:0/127 ${net_server2_6}::42:4 }" \
+ "pass in on ${epair_tester}b \
+ route-to { (${epair_server2}a <rt_targets>) } random prefer-ipv6-nexthop \
+ proto tcp \
+ keep state"
+
+ good_targets="${net_server2_6}::42:0 ${net_server2_6}::42:4"
+ bad_targets="${net_server2_4}.40 ${net_server2_4}.41 ${net_server2_4}.44 ${net_server2_6}::42:1"
+ check_random IPv4 "${good_targets}" "${bad_targets}"
+}
+
+prefer_ipv6_nexthop_mixed_af_random_table_ipv4_cleanup()
+{
+ pft_cleanup
+}
+
+prefer_ipv6_nexthop_ipv4_random_table_ipv4_head()
+{
+ atf_set descr 'prefer-ipv6-nexthop option for an IPv4-only table with random selection for IPv4 packets'
+ atf_set require.user root
+ atf_set require.progs python3 scapy
+}
+
+prefer_ipv6_nexthop_ipv4_random_table_ipv4_body()
+{
+ pf_map_addr_common
+
+ # Similarly to the test pf_map_addr:random_table the algorithm will
+ # choose IP addresses from the table not diving into prefixes.
+ # There are no IPv6 nexthops in the table, so the algorithm will
+ # fall back to IPv4.
+
+ jexec router pfctl -e
+ pft_set_rules router \
+ "set reassemble yes" \
+ "set state-policy if-bound" \
+ "table <rt_targets> { ${net_server2_4}.40/31 ${net_server2_4}.44 }" \
+ "pass in on ${epair_tester}b \
+ route-to { (${epair_server2}a <rt_targets>) } random prefer-ipv6-nexthop \
+ proto tcp \
+ keep state"
+
+ good_targets="${net_server2_4}.40 ${net_server2_4}.44"
+ bad_targets="${net_server2_4}.41"
+ check_random IPv4 "${good_targets}" "${bad_targets}"
+}
+
+prefer_ipv6_nexthop_ipv4_random_table_ipv4_cleanup()
+{
+ pft_cleanup
+}
+
+prefer_ipv6_nexthop_ipv4_random_table_ipv6_head()
+{
+ atf_set descr 'prefer-ipv6-nexthop option for an IPv4-only table with random selection for IPv6 packets'
+ atf_set require.user root
+ atf_set require.progs python3 scapy
+}
+
+prefer_ipv6_nexthop_ipv4_random_table_ipv6_body()
+{
+ pf_map_addr_common
+
+ # IPv6 packets can't be forwarded over IPv4 next-hops.
+ # The failure happens in pf_map_addr() and increases the respective
+ # error counter.
+
+ jexec router pfctl -e
+ pft_set_rules router \
+ "set reassemble yes" \
+ "set state-policy if-bound" \
+ "table <rt_targets> { ${net_server2_4}.40/31 ${net_server2_4}.44 }" \
+ "pass in on ${epair_tester}b \
+ route-to { (${epair_server2}a <rt_targets>) } random prefer-ipv6-nexthop \
+ proto tcp \
+ keep state"
+
+ atf_check -s exit:1 ${common_dir}/pft_ping.py \
+ --sendif ${epair_tester}a --replyif ${epair_tester}a \
+ --fromaddr ${net_clients_6}::1 --to ${host_server_6} \
+ --ping-type=tcp3way --send-sport=4201
+
+ atf_check -o "match:map-failed +1 +" -x "jexec router pfctl -qvvsi 2> /dev/null"
+}
+
+prefer_ipv6_nexthop_ipv4_random_table_ipv6_cleanup()
+{
+ pft_cleanup
+}
+
+prefer_ipv6_nexthop_ipv6_random_prefix_ipv4_head()
+{
+ atf_set descr 'prefer-ipv6-nexthop option for an IPv6 prefix with random selection for IPv4 packets'
+ atf_set require.user root
+ atf_set require.progs python3 scapy
+}
+
+prefer_ipv6_nexthop_ipv6_random_prefix_ipv4_body()
+{
+ pf_map_addr_common
+
+ jexec router pfctl -e
+ pft_set_rules router \
+ "set reassemble yes" \
+ "set state-policy if-bound" \
+ "pass in on ${epair_tester}b \
+ route-to { (${epair_server2}a ${net_server2_6}::42:0/127) } random prefer-ipv6-nexthop \
+ proto tcp \
+ keep state"
+
+ good_targets="${net_server2_6}::42:0 ${net_server2_6}::42:1"
+ check_random IPv4 "${good_targets}"
+}
+
+prefer_ipv6_nexthop_ipv6_random_prefix_ipv4_cleanup()
+{
+ pft_cleanup
+}
+
+prefer_ipv6_nexthop_ipv6_random_prefix_ipv6_head()
+{
+ atf_set descr 'prefer-ipv6-nexthop option for an IPv6 prefix with random selection for IPv6 packets'
+ atf_set require.user root
+ atf_set require.progs python3 scapy
+}
+
+prefer_ipv6_nexthop_ipv6_random_prefix_ipv6_body()
+{
+ pf_map_addr_common
+
+ jexec router pfctl -e
+ pft_set_rules router \
+ "set reassemble yes" \
+ "set state-policy if-bound" \
+ "pass in on ${epair_tester}b \
+ route-to { (${epair_server2}a ${net_server2_6}::42:0/127) } random prefer-ipv6-nexthop \
+ proto tcp \
+ keep state"
+
+ good_targets="${net_server2_6}::42:0 ${net_server2_6}::42:1"
+ check_random IPv6 "${good_targets}"
+}
+
+prefer_ipv6_nexthop_ipv6_random_prefix_ipv6_cleanup()
+{
+ pft_cleanup
+}
+
+prefer_ipv6_nexthop_ipv4_random_prefix_ipv4_head()
+{
+ atf_set descr 'prefer-ipv6-nexthop option for an IPv4 prefix with random selection for IPv4 packets'
+ atf_set require.user root
+ atf_set require.progs python3 scapy
+}
+
+prefer_ipv6_nexthop_ipv4_random_prefix_ipv4_body()
+{
+ pf_map_addr_common
+
+ jexec router pfctl -e
+ pft_set_rules router \
+ "set reassemble yes" \
+ "set state-policy if-bound" \
+ "pass in on ${epair_tester}b \
+ route-to { (${epair_server2}a ${net_server2_4}.40/31) } random prefer-ipv6-nexthop \
+ proto tcp \
+ keep state"
+
+ good_targets="${net_server2_4}.40 ${net_server2_4}.41"
+ check_random IPv4 "${good_targets}"
+}
+
+prefer_ipv6_nexthop_ipv4_random_prefix_ipv4_cleanup()
+{
+ pft_cleanup
+}
+
+prefer_ipv6_nexthop_ipv4_random_prefix_ipv6_head()
+{
+ atf_set descr 'prefer-ipv6-nexthop option for an IPv4 prefix with random selection for IPv6 packets'
+ atf_set require.user root
+ atf_set require.progs python3 scapy
+}
+
+prefer_ipv6_nexthop_ipv4_random_prefix_ipv6_body()
+{
+ pf_map_addr_common
+
+ # IPv6 packets can't be forwarded over IPv4 next-hops.
+ # The failure happens in pf_map_addr() and increases the respective
+ # error counter.
+
+ jexec router pfctl -e
+ pft_set_rules router \
+ "set reassemble yes" \
+ "set state-policy if-bound" \
+ "pass in on ${epair_tester}b \
+ route-to { (${epair_server2}a ${net_server2_4}.40/31) } random prefer-ipv6-nexthop \
+ proto tcp \
+ keep state"
+
+ atf_check -s exit:1 ${common_dir}/pft_ping.py \
+ --sendif ${epair_tester}a --replyif ${epair_tester}a \
+ --fromaddr ${net_clients_6}::1 --to ${host_server_6} \
+ --ping-type=tcp3way --send-sport=4201
+
+ atf_check -o "match:map-failed +1 +" -x "jexec router pfctl -qvvsi 2> /dev/null"
+}
+
+prefer_ipv6_nexthop_ipv4_random_prefix_ipv6_cleanup()
+{
+ pft_cleanup
+}
+
atf_init_test_cases()
{
atf_add_test_case "v4"
@@ -995,5 +1666,25 @@ atf_init_test_cases()
atf_add_test_case "sticky"
atf_add_test_case "ttl"
atf_add_test_case "empty_pool"
+ # Tests for pf_map_addr() without prefer-ipv6-nexthop
atf_add_test_case "table_loop"
+ atf_add_test_case "roundrobin"
+ atf_add_test_case "random_table"
+ atf_add_test_case "random_prefix"
+ # Tests for pf_map_addr() without prefer-ipv6-nexthop
+ # Next hop is a Single IP address
+ atf_add_test_case "prefer_ipv6_nexthop_single_ipv4"
+ atf_add_test_case "prefer_ipv6_nexthop_single_ipv6"
+ # Next hop is tables and prefixes, accessed by the round-robin algorithm
+ atf_add_test_case "prefer_ipv6_nexthop_mixed_af_roundrobin_ipv4"
+ atf_add_test_case "prefer_ipv6_nexthop_mixed_af_roundrobin_ipv6"
+ # Next hop is a table, accessed by the random algorithm
+ atf_add_test_case "prefer_ipv6_nexthop_mixed_af_random_table_ipv4"
+ atf_add_test_case "prefer_ipv6_nexthop_ipv4_random_table_ipv4"
+ atf_add_test_case "prefer_ipv6_nexthop_ipv4_random_table_ipv6"
+ # Next hop is a prefix, accessed by the random algorithm
+ atf_add_test_case "prefer_ipv6_nexthop_ipv6_random_prefix_ipv4"
+ atf_add_test_case "prefer_ipv6_nexthop_ipv6_random_prefix_ipv6"
+ atf_add_test_case "prefer_ipv6_nexthop_ipv4_random_prefix_ipv4"
+ atf_add_test_case "prefer_ipv6_nexthop_ipv4_random_prefix_ipv6"
}
diff --git a/tests/sys/netpfil/pf/sctp.sh b/tests/sys/netpfil/pf/sctp.sh
index 57dcdad1d866..78055f5a9dd2 100644
--- a/tests/sys/netpfil/pf/sctp.sh
+++ b/tests/sys/netpfil/pf/sctp.sh
@@ -673,6 +673,9 @@ pfsync_body()
atf_fail "Initial SCTP connection failed"
fi
+ # Give pfsync some time to do its thing
+ sleep 1
+
# Verify that two has the connection too
state=$(jexec ${j}two pfctl -ss | grep sctp)
if [ -z "${state}" ];
diff --git a/tests/sys/netpfil/pf/src_track.sh b/tests/sys/netpfil/pf/src_track.sh
index ae60a5df809b..d86b4ce6c466 100755
--- a/tests/sys/netpfil/pf/src_track.sh
+++ b/tests/sys/netpfil/pf/src_track.sh
@@ -526,10 +526,12 @@ mixed_af_body()
"block" \
"pass inet6 proto icmp6 icmp6-type { neighbrsol, neighbradv }" \
"pass in on ${epair_tester}b \
- route-to { (${epair_server1}a ${net_server1_4_host_server}) \
- } sticky-address \
- inet6 proto tcp from any to 64:ff9b::/96 \
- af-to inet from ${net_clients_4}.0/${net_clients_4_mask} round-robin sticky-address"
+ route-to { \
+ (${epair_server1}a ${net_server1_4_host_server}) \
+ (${epair_server2}a ${net_server2_6_host_server}) \
+ } prefer-ipv6-nexthop sticky-address \
+ inet6 proto tcp from any to 64:ff9b::/96 \
+ af-to inet from ${net_clients_4}.0/${net_clients_4_mask} round-robin sticky-address"
atf_check -s exit:0 ${common_dir}/pft_ping.py \
--sendif ${epair_tester}a \
@@ -538,6 +540,20 @@ mixed_af_body()
--to 64:ff9b::192.0.2.100 \
--ping-type=tcp3way \
--send-sport=4201
+ atf_check -s exit:0 ${common_dir}/pft_ping.py \
+ --sendif ${epair_tester}a \
+ --replyif ${epair_tester}a \
+ --fromaddr 2001:db8:44::1 \
+ --to 64:ff9b::192.0.2.100 \
+ --ping-type=tcp3way \
+ --send-sport=4202
+ atf_check -s exit:0 ${common_dir}/pft_ping.py \
+ --sendif ${epair_tester}a \
+ --replyif ${epair_tester}a \
+ --fromaddr 2001:db8:44::2 \
+ --to 64:ff9b::192.0.2.100 \
+ --ping-type=tcp3way \
+ --send-sport=4203
states=$(mktemp) || exit 1
jexec router pfctl -qvvss | normalize_pfctl_s > $states
@@ -546,16 +562,22 @@ mixed_af_body()
# States are checked for proper route-to information.
# The route-to gateway is IPv4.
+ # FIXME: Sticky-address is broken for af-to pools!
+ # The SN is created but apparently not used, as seen in states.
for state_regexp in \
- "${epair_tester}b tcp 203.0.113.0:4201 \(2001:db8:44::1\[4201\]\) -> 192.0.2.100:9 \(64:ff9b::c000:264\[9\]\) .* route-to: 198.51.100.18@${epair_server1}a" \
+ "${epair_server2}a tcp 203.0.113.0:4201 \(2001:db8:44::1\[4201\]\) -> 192.0.2.100:9 \(64:ff9b::c000:264\[9\]\) .* route-to: 2001:db8:4202::2@${epair_server2}a origif: ${epair_tester}b" \
+ "${epair_server2}a tcp 203.0.113.1:4202 \(2001:db8:44::1\[4202\]\) -> 192.0.2.100:9 \(64:ff9b::c000:264\[9\]\) .* route-to: 2001:db8:4202::2@${epair_server2}a origif: ${epair_tester}b" \
+ "${epair_server1}a tcp 203.0.113.2:4203 \(2001:db8:44::2\[4203\]\) -> 192.0.2.100:9 \(64:ff9b::c000:264\[9\]\) .* route-to: 198.51.100.18@${epair_server1}a origif: ${epair_tester}b" \
; do
grep -qE "${state_regexp}" $states || atf_fail "State not found for '${state_regexp}'"
done
# Source nodes map IPv6 source address onto IPv4 gateway and IPv4 SNAT address.
for node_regexp in \
- '2001:db8:44::1 -> 203.0.113.0 .* states 1, .* NAT/RDR sticky-address' \
- '2001:db8:44::1 -> 198.51.100.18 .* states 1, .* route sticky-address' \
+ '2001:db8:44::2 -> 203.0.113.2 .* states 1, .* NAT/RDR sticky-address' \
+ '2001:db8:44::2 -> 198.51.100.18 .* states 1, .* route sticky-address' \
+ '2001:db8:44::1 -> 203.0.113.0 .* states 2, .* NAT/RDR sticky-address' \
+ '2001:db8:44::1 -> 2001:db8:4202::2 .* states 2, .* route sticky-address' \
; do
grep -qE "${node_regexp}" $nodes || atf_fail "Source node not found for '${node_regexp}'"
done
diff --git a/tests/sys/netpfil/pf/table.sh b/tests/sys/netpfil/pf/table.sh
index c773518e95e4..69fe12fc9804 100644
--- a/tests/sys/netpfil/pf/table.sh
+++ b/tests/sys/netpfil/pf/table.sh
@@ -641,9 +641,31 @@ large_body()
-e match:"${expected}/${expected} addresses added." \
jexec alcatraz pfctl -t foo -T add -f ${pwd}/foo.lst
actual=$(jexec alcatraz pfctl -t foo -T show | wc -l | awk '{ print $1; }')
- if [[ $actual -ne $expected ]]; then
+ if [ $actual -ne $expected ]; then
atf_fail "Unexpected number of table entries $expected $acual"
fi
+
+ # The second pass should work too, but confirm we've inserted everything
+ atf_check -s exit:0 \
+ -e match:"0/${expected} addresses added." \
+ jexec alcatraz pfctl -t foo -T add -f ${pwd}/foo.lst
+
+ echo '42.42.42.42' >> ${pwd}/foo.lst
+ expected=$((${expected} + 1))
+
+ # And we can also insert one additional address
+ atf_check -s exit:0 \
+ -e match:"1/${expected} addresses added." \
+ jexec alcatraz pfctl -t foo -T add -f ${pwd}/foo.lst
+
+ # Try to delete one address
+ atf_check -s exit:0 \
+ -e match:"1/1 addresses deleted." \
+ jexec alcatraz pfctl -t foo -T delete 42.42.42.42
+ # And again, for the same address
+ atf_check -s exit:0 \
+ -e match:"0/1 addresses deleted." \
+ jexec alcatraz pfctl -t foo -T delete 42.42.42.42
}
large_cleanup()
@@ -651,6 +673,80 @@ large_cleanup()
pft_cleanup
}
+atf_test_case "show_recursive" "cleanup"
+show_recursive_head()
+{
+ atf_set descr 'Test displaying tables in every anchor'
+ atf_set require.user root
+}
+
+show_recursive_body()
+{
+ pft_init
+
+ vnet_mkjail alcatraz
+
+ pft_set_rules alcatraz \
+
+ (echo "table <bar> persist"
+ echo "block in quick from <bar> to any"
+ ) | jexec alcatraz pfctl -a anchorage -f -
+
+ pft_set_rules noflush alcatraz \
+ "table <foo> counters { 192.0.2.1 }" \
+ "pass in from <foo>" \
+ "anchor anchorage"
+
+ jexec alcatraz pfctl -sr -a "*"
+
+ atf_check -s exit:0 -e ignore -o match:'-pa-r-- bar@anchorage' \
+ jexec alcatraz pfctl -v -a "*" -sT
+ atf_check -s exit:0 -e ignore -o match:'--a-r-C foo' \
+ jexec alcatraz pfctl -v -a "*" -sT
+}
+
+show_recursive_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "in_anchor" "cleanup"
+in_anchor_head()
+{
+ atf_set descr 'Test declaring tables in anchors'
+ atf_set require.user root
+}
+
+in_anchor_body()
+{
+ pft_init
+
+ epair_send=$(vnet_mkepair)
+ ifconfig ${epair_send}a 192.0.2.1/24 up
+
+ vnet_mkjail alcatraz ${epair_send}b
+ jexec alcatraz ifconfig ${epair_send}b 192.0.2.2/24 up
+
+ jexec alcatraz pfctl -e
+
+ pft_set_rules alcatraz \
+ "block all" \
+ "anchor \"foo\" {\n
+ table <bar> counters { 192.0.2.1 }\n
+ pass in from <bar>\n
+ }\n"
+
+ atf_check -s exit:0 -o ignore ping -c 3 192.0.2.2
+
+ jexec alcatraz pfctl -sr -a "*" -vv
+ jexec alcatraz pfctl -sT -a "*" -vv
+}
+
+in_anchor_cleanup()
+{
+ pft_cleanup
+}
+
atf_init_test_cases()
{
atf_add_test_case "v4_counters"
@@ -667,4 +763,6 @@ atf_init_test_cases()
atf_add_test_case "anchor"
atf_add_test_case "flush"
atf_add_test_case "large"
+ atf_add_test_case "show_recursive"
+ atf_add_test_case "in_anchor"
}
diff --git a/tests/sys/netpfil/pf/utils.subr b/tests/sys/netpfil/pf/utils.subr
index 3f8d437920f9..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,11 +272,12 @@ 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
}
-# Create a router and 2 server jails for nat64 and rfc5549 test cases.
-# The router is connected to servers, both are dual-stack, and to the
+# Create a router and 2 server jails for nat64 and prefer-ipv6-nexthop test
+# cases. The router is connected to servers, both are dual-stack, and to the
# tester jail. All links are dual stack.
setup_router_server_nat64()
{
@@ -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
diff --git a/tests/sys/opencrypto/blake2_test.c b/tests/sys/opencrypto/blake2_test.c
index b397f8a8ec4c..0e6943d150cf 100644
--- a/tests/sys/opencrypto/blake2_test.c
+++ b/tests/sys/opencrypto/blake2_test.c
@@ -126,15 +126,12 @@ do_cryptop(int fd, int ses, size_t inlen, void *out)
}
static void
-test_blake2b_vectors(const char *devname, const char *modname)
+test_blake2b_vectors(const char *devname)
{
uint8_t hash[BLAKE2B_OUTBYTES];
int crid, fd, ses;
size_t i;
- ATF_REQUIRE_KERNEL_MODULE(modname);
- ATF_REQUIRE_KERNEL_MODULE("cryptodev");
-
initialize_constant_buffers();
fd = get_handle_fd();
crid = lookup_crid(fd, devname);
@@ -150,15 +147,12 @@ test_blake2b_vectors(const char *devname, const char *modname)
}
static void
-test_blake2s_vectors(const char *devname, const char *modname)
+test_blake2s_vectors(const char *devname)
{
uint8_t hash[BLAKE2S_OUTBYTES];
int crid, fd, ses;
size_t i;
- ATF_REQUIRE_KERNEL_MODULE(modname);
- ATF_REQUIRE_KERNEL_MODULE("cryptodev");
-
initialize_constant_buffers();
fd = get_handle_fd();
crid = lookup_crid(fd, devname);
@@ -173,33 +167,49 @@ test_blake2s_vectors(const char *devname, const char *modname)
}
}
-ATF_TC_WITHOUT_HEAD(blake2b_vectors);
+ATF_TC(blake2b_vectors);
+ATF_TC_HEAD(blake2b_vectors, tc)
+{
+ atf_tc_set_md_var(tc, "require.kmods", "nexus/cryptosoft cryptodev");
+}
ATF_TC_BODY(blake2b_vectors, tc)
{
ATF_REQUIRE_SYSCTL_INT("kern.crypto.allow_soft", 1);
- test_blake2b_vectors("cryptosoft0", "nexus/cryptosoft");
+ test_blake2b_vectors("cryptosoft0");
}
-ATF_TC_WITHOUT_HEAD(blake2s_vectors);
+ATF_TC(blake2s_vectors);
+ATF_TC_HEAD(blake2s_vectors, tc)
+{
+ atf_tc_set_md_var(tc, "require.kmods", "nexus/cryptosoft cryptodev");
+}
ATF_TC_BODY(blake2s_vectors, tc)
{
ATF_REQUIRE_SYSCTL_INT("kern.crypto.allow_soft", 1);
- test_blake2s_vectors("cryptosoft0", "nexus/cryptosoft");
+ test_blake2s_vectors("cryptosoft0");
}
#if defined(__i386__) || defined(__amd64__)
-ATF_TC_WITHOUT_HEAD(blake2b_vectors_x86);
+ATF_TC(blake2b_vectors_x86);
+ATF_TC_HEAD(blake2b_vectors_x86, tc)
+{
+ atf_tc_set_md_var(tc, "require.kmods", "nexus/blake2 cryptodev");
+}
ATF_TC_BODY(blake2b_vectors_x86, tc)
{
ATF_REQUIRE_SYSCTL_INT("kern.crypto.allow_soft", 1);
- test_blake2b_vectors("blaketwo0", "nexus/blake2");
+ test_blake2b_vectors("blaketwo0");
}
-ATF_TC_WITHOUT_HEAD(blake2s_vectors_x86);
+ATF_TC(blake2s_vectors_x86);
+ATF_TC_HEAD(blake2s_vectors_x86, tc)
+{
+ atf_tc_set_md_var(tc, "require.kmods", "nexus/blake2 cryptodev");
+}
ATF_TC_BODY(blake2s_vectors_x86, tc)
{
ATF_REQUIRE_SYSCTL_INT("kern.crypto.allow_soft", 1);
- test_blake2s_vectors("blaketwo0", "nexus/blake2");
+ test_blake2s_vectors("blaketwo0");
}
#endif
diff --git a/tests/sys/opencrypto/poly1305_test.c b/tests/sys/opencrypto/poly1305_test.c
index ab455784efba..c51ffacfd1cc 100644
--- a/tests/sys/opencrypto/poly1305_test.c
+++ b/tests/sys/opencrypto/poly1305_test.c
@@ -350,16 +350,13 @@ do_cryptop(int fd, int ses, const void *inp, size_t inlen, void *out)
}
static void
-test_rfc7539_poly1305_vectors(int crid, const char *modname)
+test_rfc7539_poly1305_vectors(int crid)
{
uint8_t comptag[POLY1305_HASH_LEN], exptag[POLY1305_HASH_LEN],
key[POLY1305_KEY_LEN], msg[512];
int fd, ses;
size_t i;
- ATF_REQUIRE_KERNEL_MODULE(modname);
- ATF_REQUIRE_KERNEL_MODULE("cryptodev");
-
fd = get_handle_fd();
for (i = 0; i < nitems(rfc7539_kats); i++) {
@@ -378,11 +375,15 @@ test_rfc7539_poly1305_vectors(int crid, const char *modname)
}
}
-ATF_TC_WITHOUT_HEAD(poly1305_vectors);
+ATF_TC(poly1305_vectors);
+ATF_TC_HEAD(poly1305_vectors, tc)
+{
+ atf_tc_set_md_var(tc, "require.kmods", "nexus/cryptosoft cryptodev");
+}
ATF_TC_BODY(poly1305_vectors, tc)
{
ATF_REQUIRE_SYSCTL_INT("kern.crypto.allow_soft", 1);
- test_rfc7539_poly1305_vectors(CRYPTO_FLAG_SOFTWARE, "nexus/cryptosoft");
+ test_rfc7539_poly1305_vectors(CRYPTO_FLAG_SOFTWARE);
}
ATF_TP_ADD_TCS(tp)
diff --git a/tests/sys/sound/sndstat.c b/tests/sys/sound/sndstat.c
index ed292b570429..bbf18aca2824 100644
--- a/tests/sys/sound/sndstat.c
+++ b/tests/sys/sound/sndstat.c
@@ -40,17 +40,11 @@
#include <stdlib.h>
#include <unistd.h>
-static void
-load_dummy(void)
-{
- if (kldload("snd_dummy.ko") < 0 && errno != EEXIST)
- atf_tc_skip("snd_dummy.ko not found");
-}
-
ATF_TC(sndstat_nv);
ATF_TC_HEAD(sndstat_nv, tc)
{
atf_tc_set_md_var(tc, "descr", "/dev/sndstat nvlist test");
+ atf_tc_set_md_var(tc, "require.kmods", "snd_dummy");
}
ATF_TC_BODY(sndstat_nv, tc)
@@ -62,8 +56,6 @@ ATF_TC_BODY(sndstat_nv, tc)
size_t nitems, nchans, i, j;
int fd, rc, pchan, rchan;
- load_dummy();
-
if ((fd = open("/dev/sndstat", O_RDONLY)) < 0)
atf_tc_skip("/dev/sndstat not found, load sound(4)");
@@ -223,6 +215,7 @@ ATF_TC(sndstat_udev);
ATF_TC_HEAD(sndstat_udev, tc)
{
atf_tc_set_md_var(tc, "descr", "/dev/sndstat userdev interface test");
+ atf_tc_set_md_var(tc, "require.kmods", "snd_dummy");
}
ATF_TC_BODY(sndstat_udev, tc)
@@ -234,8 +227,6 @@ ATF_TC_BODY(sndstat_udev, tc)
size_t nitems, i;
int fd, rc, pchan, rchan, n;
- load_dummy();
-
if ((fd = open("/dev/sndstat", O_RDWR)) < 0)
atf_tc_skip("/dev/sndstat not found, load sound(4)");
diff --git a/tests/sys/sys/bitstring_test.c b/tests/sys/sys/bitstring_test.c
index a48042a4a063..bf436040c00f 100644
--- a/tests/sys/sys/bitstring_test.c
+++ b/tests/sys/sys/bitstring_test.c
@@ -559,14 +559,13 @@ BITSTRING_TC_DEFINE(bit_nclear)
bit_nclear(bitstr, i, j);
bit_ffc(bitstr, nbits, &found_clear_bit);
- ATF_REQUIRE_MSG(
- found_clear_bit == i,
+ ATF_REQUIRE_INTEQ_MSG(i, found_clear_bit,
"bit_nclear_%d_%d_%d%s: Failed with result %d",
nbits, i, j, memloc, found_clear_bit);
bit_ffs_at(bitstr, i, nbits, &found_set_bit);
- ATF_REQUIRE_MSG(
- (j + 1 < nbits) ? found_set_bit == j + 1 : -1,
+ ATF_REQUIRE_INTEQ_MSG((j + 1 < nbits) ? j + 1 : -1,
+ found_set_bit,
"bit_nset_%d_%d_%d%s: Failed with result %d",
nbits, i, j, memloc, found_set_bit);
}
@@ -586,14 +585,13 @@ BITSTRING_TC_DEFINE(bit_nset)
bit_nset(bitstr, i, j);
bit_ffs(bitstr, nbits, &found_set_bit);
- ATF_REQUIRE_MSG(
- found_set_bit == i,
+ ATF_REQUIRE_INTEQ_MSG(i, found_set_bit,
"bit_nset_%d_%d_%d%s: Failed with result %d",
nbits, i, j, memloc, found_set_bit);
bit_ffc_at(bitstr, i, nbits, &found_clear_bit);
- ATF_REQUIRE_MSG(
- (j + 1 < nbits) ? found_clear_bit == j + 1 : -1,
+ ATF_REQUIRE_INTEQ_MSG((j + 1 < nbits) ? j + 1 : -1,
+ found_clear_bit,
"bit_nset_%d_%d_%d%s: Failed with result %d",
nbits, i, j, memloc, found_clear_bit);
}
diff --git a/tests/sys/vmm/vmm_cred_jail.sh b/tests/sys/vmm/vmm_cred_jail.sh
index 5b02b5dc0b42..dd7907b15352 100644
--- a/tests/sys/vmm/vmm_cred_jail.sh
+++ b/tests/sys/vmm/vmm_cred_jail.sh
@@ -35,12 +35,10 @@ vmm_cred_jail_host_head()
{
atf_set "descr" "Tests deleting the host's VM from within a jail"
atf_set "require.user" "root"
+ atf_set "require.kmods" "vmm"
}
vmm_cred_jail_host_body()
{
- if ! -c /dev/vmmctl; then
- atf_skip "vmm is not loaded"
- fi
bhyvectl --vm=testvm --create
vmm_mkjail myjail
atf_check -s exit:1 -e ignore jexec myjail bhyvectl --vm=testvm --destroy
@@ -56,12 +54,10 @@ vmm_cred_jail_other_head()
{
atf_set "descr" "Tests deleting a jail's VM from within another jail"
atf_set "require.user" "root"
+ atf_set "require.kmods" "vmm"
}
vmm_cred_jail_other_body()
{
- if ! -c /dev/vmmctl; then
- atf_skip "vmm is not loaded"
- fi
vmm_mkjail myjail1
vmm_mkjail myjail2
atf_check -s exit:0 jexec myjail1 bhyvectl --vm=testvm --create