diff options
Diffstat (limited to 'tests/sys')
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 |