diff options
Diffstat (limited to 'tests')
-rw-r--r-- | tests/ci/Makefile | 17 | ||||
-rw-r--r-- | tests/ci/tools/ci.conf | 72 | ||||
-rwxr-xr-x | tests/ci/tools/freebsdci | 5 | ||||
-rw-r--r-- | tests/sys/fs/fusefs/mockfs.cc | 2 | ||||
-rw-r--r-- | tests/sys/kern/Makefile | 2 | ||||
-rw-r--r-- | tests/sys/kern/copy_file_range.c | 231 | ||||
-rw-r--r-- | tests/sys/net/Makefile | 1 | ||||
-rwxr-xr-x | tests/sys/net/if_tun_test.sh | 22 | ||||
-rw-r--r-- | tests/sys/net/transient_tuntap.c | 54 | ||||
-rw-r--r-- | tests/sys/netpfil/pf/mbuf.sh | 26 | ||||
-rw-r--r-- | tests/sys/netpfil/pf/route_to.sh | 716 | ||||
-rwxr-xr-x | tests/sys/netpfil/pf/src_track.sh | 36 | ||||
-rw-r--r-- | tests/sys/netpfil/pf/table.sh | 24 | ||||
-rw-r--r-- | tests/sys/netpfil/pf/utils.subr | 4 |
14 files changed, 1141 insertions, 71 deletions
diff --git a/tests/ci/Makefile b/tests/ci/Makefile index 48e638fdb79c..30ca34a810be 100644 --- a/tests/ci/Makefile +++ b/tests/ci/Makefile @@ -41,6 +41,11 @@ TARGET_ARCH= ${TARGET} .endif IMAKE= ${MAKE} TARGET=${TARGET} TARGET_ARCH=${TARGET_ARCH} +.if !defined(CIENV) || empty(CIENV) +CIENV= local +LOG_TARGET= > ${.CURDIR}/_.${TARGET_ARCH}.${.TARGET} 2>&1 || (echo "${.TARGET} failed, check _.${TARGET_ARCH}.${.TARGET} for details" ; false) +.endif + .if defined(CROSS_TOOLCHAIN) || !empty(CROSS_TOOLCHAIN) CROSS_TOOLCHAIN_PARAM= "CROSS_TOOLCHAIN=${CROSS_TOOLCHAIN}" .endif @@ -73,6 +78,7 @@ CIIMAGE= ci-${OSRELEASE}-${GITREV}-${KERNCONF}.${FORMAT} CIDISK?= ${.OBJDIR}/${CIIMAGE} VMSIZE?= 6g CITYPE?= full +KYUA_TEST_FILTERS?= META_TAR!=mktemp /tmp/meta.XXXXXX META_DIR!=mktemp -d /tmp/meta.XXXXXX META_DIROUT!=mktemp -d /tmp/meta.XXXXXX @@ -169,17 +175,14 @@ ci-buildworld: .PHONY @echo "Building world for ${TARGET_ARCH}" ${IMAKE} -j${PARALLEL_JOBS} -C ${WORLDDIR} ${METAMODE} \ ${CROSS_TOOLCHAIN_PARAM} __MAKE_CONF=${MAKECONF} SRCCONF=${SRCCONF} \ - ${EXTRA_MAKE_FLAGS} buildworld > ${.CURDIR}/_.${TARGET_ARCH}.${.TARGET} 2>&1 || \ - (echo "${.TARGET} failed, check _.${TARGET_ARCH}.${.TARGET} for details" ; false) - + ${EXTRA_MAKE_FLAGS} buildworld ${LOG_TARGET} ci-buildkernel: ci-buildworld-${TARGET_ARCH:tl} .PHONY @echo "Building kernel for ${TARGET_ARCH}" ${IMAKE} -j${PARALLEL_JOBS} -C ${WORLDDIR} ${METAMODE} \ ${CROSS_TOOLCHAIN_PARAM} __MAKE_CONF=${MAKECONF} SRCCONF=${SRCCONF} \ ${EXTRA_MAKE_FLAGS} KERNCONF=${KERNCONF} \ - buildkernel > ${.CURDIR}/_.${TARGET_ARCH}.${.TARGET} 2>&1 || \ - (echo "${.TARGET} failed, check _.${TARGET_ARCH}.${.TARGET} for details" ; false) + buildkernel ${LOG_TARGET} ci-buildimage: ${QEMUTGT} ci-buildkernel-${TARGET_ARCH:tl} .PHONY @echo "Building ci image for ${TARGET_ARCH}" @@ -189,9 +192,7 @@ ci-buildimage: ${QEMUTGT} ci-buildkernel-${TARGET_ARCH:tl} .PHONY ${RELEASEDIR}/scripts/mk-vmimage.sh \ -C ${RELEASEDIR}/tools/vmimage.subr -d ${.OBJDIR}/${.TARGET} -F ${VMFS} \ -i ${.OBJDIR}/ci.img -s ${VMSIZE} -f ${FORMAT} \ - -S ${WORLDDIR} -o ${.OBJDIR}/${CIIMAGE} -c ${CICONF} \ - > ${.CURDIR}/_.${TARGET_ARCH}.${.TARGET} 2>&1 || \ - (echo "${.TARGET} failed, check _.${TARGET_ARCH}.${.TARGET} for details" ; false) + -S ${WORLDDIR} -o ${.OBJDIR}/${CIIMAGE} -c ${CICONF} ${LOG_TARGET} touch ${.TARGET} ci-set-smoke-var: .PHONY diff --git a/tests/ci/tools/ci.conf b/tests/ci/tools/ci.conf index a9998a3e5373..1d2921ab75f3 100644 --- a/tests/ci/tools/ci.conf +++ b/tests/ci/tools/ci.conf @@ -11,10 +11,32 @@ export VM_RC_LIST="auditd freebsdci" if [ "${CITYPE}" != "smoke" ]; then -export VM_EXTRA_PACKAGES="coreutils devel/py-pytest gdb jq ksh93 net/py-dpkt net/scapy nist-kat nmap perl5 python python3 sudo sysutils/porch tcptestsuite" +export VM_EXTRA_PACKAGES=" +archivers/gtar +devel/git +devel/gdb +devel/py-pytest +perl5 +lang/python +lang/python3 +net/isc-dhcp44-server +net/ndisc6 +net/py-dpkt +net/scapy +net/tcptestsuite +security/nist-kat +security/nmap +security/openvpn +security/sudo +shells/ksh93 +sysutils/coreutils +sysutils/porch +sysutils/sg3_utils +textproc/jq +" if [ "${TARGET}" = "amd64" ]; then - export VM_EXTRA_PACKAGES="${VM_EXTRA_PACKAGES} linux-c7-ltp" + export VM_EXTRA_PACKAGES="${VM_EXTRA_PACKAGES} devel/linux-ltp" fi fi @@ -39,22 +61,35 @@ test_suites.FreeBSD.disks = '/dev/vtbd2 /dev/vtbd3 /dev/vtbd4 /dev/vtbd5 /dev/vt EOF cat << EOF >> ${DESTDIR}/etc/rc.conf kld_list="" # Load modules needed by tests -kld_list="${kld_list} blake2" # sys/opencrypto -kld_list="${kld_list} cryptodev" # sys/opencrypto -kld_list="${kld_list} dummymbuf" # sys/netpfil -kld_list="${kld_list} fusefs" # sys/fs/fusefs -kld_list="${kld_list} ipsec" # sys/netipsec -kld_list="${kld_list} mac_portacl" # sys/mac/portacl -kld_list="${kld_list} mqueuefs" # sys/kern/mqueue_test -kld_list="${kld_list} pfsync" # sys/netpfil/pf (loads pf) -kld_list="${kld_list} pflog" # sys/netpfil/pf -kld_list="${kld_list} ipl" # sys/sbin/ipf (loads ipfilter) -kld_list="${kld_list} ipfw" # sys/netpfil/ipfw (loads ipfw) -kld_list="${kld_list} ipfw_nat" # sys/netpfil/ipfw (loads ipfw_nat) -kld_list="${kld_list} ipdivert" # sys/netinet (loads ipdivert) -kld_list="${kld_list} dummynet" # sys/netpfil/common -kld_list="${kld_list} carp" # sys/netinet/carp -kld_list="${kld_list} if_stf" # sys/net/if_stf +kld_list="\${kld_list} accf_data" # sys/kern/socket_accf +kld_list="\${kld_list} accf_dns" # sys/kern/socket_accf +kld_list="\${kld_list} accf_http" # sys/kern/socket_accf +kld_list="\${kld_list} accf_tls" # sys/kern/socket_accf +kld_list="\${kld_list} blake2" # sys/opencrypto +kld_list="\${kld_list} carp" # sys/netinet/carp +kld_list="\${kld_list} cryptodev" # sys/opencrypto +kld_list="\${kld_list} dummymbuf" # sys/netpfil +kld_list="\${kld_list} dummynet" # sys/netpfil/common +kld_list="\${kld_list} fusefs" # sys/fs/fusefs +kld_list="\${kld_list} if_bridge" # sys/net/if_bridge_test +kld_list="\${kld_list} if_enc" # sys/netpfil/pf +kld_list="\${kld_list} if_epair" # sys/net/if_epair_test +kld_list="\${kld_list} if_ovpn" # sys/net/if_ovpn +kld_list="\${kld_list} if_stf" # sys/net/if_stf +kld_list="\${kld_list} ipdivert" # sys/netinet (loads ipdivert) +kld_list="\${kld_list} ipfw" # sys/netpfil/ipfw (loads ipfw) +kld_list="\${kld_list} ipfw_nat" # sys/netpfil/ipfw (loads ipfw_nat) +kld_list="\${kld_list} ipl" # sys/sbin/ipf (loads ipfilter) +kld_list="\${kld_list} ipsec" # sys/netipsec +kld_list="\${kld_list} mac_portacl" # sys/mac/portacl +kld_list="\${kld_list} mqueuefs" # sys/kern/mqueue_test +kld_list="\${kld_list} pf" # sys/netpfil/pf +kld_list="\${kld_list} pflog" # sys/netpfil/pf +kld_list="\${kld_list} pflow" # sys/netpfil/pf +kld_list="\${kld_list} pfsync" # sys/netpfil/pf (loads pf) +kld_list="\${kld_list} sctp" # sys/netpfil/pf +kld_list="\${kld_list} tarfs" # sys/fs/tarfs +kld_list="\${kld_list} tcpmd5" # sys/netinet background_fsck="NO" sendmail_enable="NONE" cron_enable="NO" @@ -68,6 +103,7 @@ EOF elif [ "${CITYPE}" = "full" ]; then cat << EOF >> ${DESTDIR}/etc/rc.conf freebsdci_type="full" +freebsdci_test_filters="${KYUA_TEST_FILTERS}" EOF fi cat << EOF >> ${DESTDIR}/etc/sysctl.conf diff --git a/tests/ci/tools/freebsdci b/tests/ci/tools/freebsdci index 51bd19e2967d..42c565a45055 100755 --- a/tests/ci/tools/freebsdci +++ b/tests/ci/tools/freebsdci @@ -39,6 +39,7 @@ istar=$(file -s ${tardev} | grep "POSIX tar archive" | wc -l) load_rc_config $name : ${freebsdci_enable:="NO"} : ${freebsdci_type:="full"} +: ${freebsdci_test_filters:=""} PATH="${PATH}:/usr/local/sbin:/usr/local/bin" auto_shutdown() @@ -77,7 +78,9 @@ full_tests() tar xvf ${tardev} -C ${metadir} cd /usr/tests set +e - kyua -v parallelism=${parallelism} test + kyua \ + -v parallelism=${parallelism} \ + test ${freebsdci_test_filters} rc=$? set -e if [ ${rc} -ne 0 ] && [ ${rc} -ne 1 ]; then 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/kern/Makefile b/tests/sys/kern/Makefile index 336e73f29835..9044b1e7e4f2 100644 --- a/tests/sys/kern/Makefile +++ b/tests/sys/kern/Makefile @@ -8,6 +8,7 @@ TESTSRC= ${SRCTOP}/contrib/netbsd-tests/kernel TESTSDIR= ${TESTSBASE}/sys/kern ATF_TESTS_C+= basic_signal +ATF_TESTS_C+= copy_file_range .if ${MACHINE_ARCH} != "i386" && ${MACHINE_ARCH} != "powerpc" && \ ${MACHINE_ARCH} != "powerpcspe" # No support for atomic_load_64 on i386 or (32-bit) powerpc @@ -81,6 +82,7 @@ PROGS+= coredump_phnum_helper PROGS+= pdeathsig_helper PROGS+= sendfile_helper +LIBADD.copy_file_range+= md LIBADD.jail_lookup_root+= jail util CFLAGS.sys_getrandom+= -I${SRCTOP}/sys/contrib/zstd/lib LIBADD.sys_getrandom+= zstd diff --git a/tests/sys/kern/copy_file_range.c b/tests/sys/kern/copy_file_range.c new file mode 100644 index 000000000000..ca52eaf668e3 --- /dev/null +++ b/tests/sys/kern/copy_file_range.c @@ -0,0 +1,231 @@ +/* + * Copyright (c) 2025 Mark Johnston <markj@FreeBSD.org> + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include <sys/mman.h> +#include <sys/stat.h> + +#include <errno.h> +#include <fcntl.h> +#include <limits.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +#include <atf-c.h> +#include <sha256.h> + +/* + * Create a file with random data and size between 1B and 32MB. Return a file + * descriptor for the file. + */ +static int +genfile(void) +{ + char buf[256], file[NAME_MAX]; + size_t sz; + int fd; + + sz = (random() % (32 * 1024 * 1024ul)) + 1; + + snprintf(file, sizeof(file), "testfile.XXXXXX"); + fd = mkstemp(file); + ATF_REQUIRE(fd != -1); + + while (sz > 0) { + ssize_t n; + int error; + + error = getentropy(buf, sizeof(buf)); + ATF_REQUIRE(error == 0); + n = write(fd, buf, sizeof(buf) < sz ? sizeof(buf) : sz); + ATF_REQUIRE(n > 0); + + sz -= n; + } + + ATF_REQUIRE(lseek(fd, 0, SEEK_SET) == 0); + return (fd); +} + +/* + * Return true if the file data in the two file descriptors is the same, + * false otherwise. + */ +static bool +cmpfile(int fd1, int fd2) +{ + struct stat st1, st2; + void *addr1, *addr2; + size_t sz; + int res; + + ATF_REQUIRE(fstat(fd1, &st1) == 0); + ATF_REQUIRE(fstat(fd2, &st2) == 0); + if (st1.st_size != st2.st_size) + return (false); + + sz = st1.st_size; + addr1 = mmap(NULL, sz, PROT_READ, MAP_PRIVATE, fd1, 0); + ATF_REQUIRE(addr1 != MAP_FAILED); + addr2 = mmap(NULL, sz, PROT_READ, MAP_PRIVATE, fd2, 0); + ATF_REQUIRE(addr2 != MAP_FAILED); + + res = memcmp(addr1, addr2, sz); + + ATF_REQUIRE(munmap(addr1, sz) == 0); + ATF_REQUIRE(munmap(addr2, sz) == 0); + + return (res == 0); +} + +/* + * Exercise a few error paths in the copy_file_range() syscall. + */ +ATF_TC_WITHOUT_HEAD(copy_file_range_invalid); +ATF_TC_BODY(copy_file_range_invalid, tc) +{ + off_t off1, off2; + int fd1, fd2; + + fd1 = genfile(); + fd2 = genfile(); + + /* Can't copy a file to itself without explicit offsets. */ + ATF_REQUIRE_ERRNO(EINVAL, + copy_file_range(fd1, NULL, fd1, NULL, SSIZE_MAX, 0) == -1); + + /* When copying a file to itself, ranges cannot overlap. */ + off1 = off2 = 0; + ATF_REQUIRE_ERRNO(EINVAL, + copy_file_range(fd1, &off1, fd1, &off2, 1, 0) == -1); + + /* Negative offsets are not allowed. */ + off1 = -1; + off2 = 0; + ATF_REQUIRE_ERRNO(EINVAL, + copy_file_range(fd1, &off1, fd2, &off2, 42, 0) == -1); + ATF_REQUIRE_ERRNO(EINVAL, + copy_file_range(fd2, &off2, fd1, &off1, 42, 0) == -1); +} + +/* + * Make sure that copy_file_range() updates the file offsets passed to it. + */ +ATF_TC_WITHOUT_HEAD(copy_file_range_offset); +ATF_TC_BODY(copy_file_range_offset, tc) +{ + struct stat sb; + off_t off1, off2; + ssize_t n; + int fd1, fd2; + + off1 = off2 = 0; + + fd1 = genfile(); + fd2 = open("copy", O_RDWR | O_CREAT, 0644); + ATF_REQUIRE(fd2 != -1); + + ATF_REQUIRE(fstat(fd1, &sb) == 0); + + ATF_REQUIRE(lseek(fd1, 0, SEEK_CUR) == 0); + ATF_REQUIRE(lseek(fd2, 0, SEEK_CUR) == 0); + + do { + off_t ooff1, ooff2; + + ooff1 = off1; + ooff2 = off2; + n = copy_file_range(fd1, &off1, fd2, &off2, sb.st_size, 0); + ATF_REQUIRE(n >= 0); + ATF_REQUIRE_EQ(off1, ooff1 + n); + ATF_REQUIRE_EQ(off2, ooff2 + n); + } while (n != 0); + + /* Offsets should have been adjusted by copy_file_range(). */ + ATF_REQUIRE_EQ(off1, sb.st_size); + ATF_REQUIRE_EQ(off2, sb.st_size); + /* Seek offsets should have been left alone. */ + ATF_REQUIRE(lseek(fd1, 0, SEEK_CUR) == 0); + ATF_REQUIRE(lseek(fd2, 0, SEEK_CUR) == 0); + /* Make sure the file contents are the same. */ + ATF_REQUIRE_MSG(cmpfile(fd1, fd2), "file contents differ"); + + ATF_REQUIRE(close(fd1) == 0); + ATF_REQUIRE(close(fd2) == 0); +} + +/* + * Make sure that copying to a larger file doesn't cause it to be truncated. + */ +ATF_TC_WITHOUT_HEAD(copy_file_range_truncate); +ATF_TC_BODY(copy_file_range_truncate, tc) +{ + struct stat sb, sb1, sb2; + char digest1[65], digest2[65]; + off_t off; + ssize_t n; + int fd1, fd2; + + fd1 = genfile(); + fd2 = genfile(); + + ATF_REQUIRE(fstat(fd1, &sb1) == 0); + ATF_REQUIRE(fstat(fd2, &sb2) == 0); + + /* fd1 refers to the smaller file. */ + if (sb1.st_size > sb2.st_size) { + int tmp; + + tmp = fd1; + fd1 = fd2; + fd2 = tmp; + ATF_REQUIRE(fstat(fd1, &sb1) == 0); + ATF_REQUIRE(fstat(fd2, &sb2) == 0); + } + + /* + * Compute a hash of the bytes in the larger file which lie beyond the + * length of the smaller file. + */ + SHA256_FdChunk(fd2, digest1, sb1.st_size, sb2.st_size - sb1.st_size); + ATF_REQUIRE(lseek(fd2, 0, SEEK_SET) == 0); + + do { + n = copy_file_range(fd1, NULL, fd2, NULL, SSIZE_MAX, 0); + ATF_REQUIRE(n >= 0); + } while (n != 0); + + /* Validate file offsets after the copy. */ + off = lseek(fd1, 0, SEEK_CUR); + ATF_REQUIRE(off == sb1.st_size); + off = lseek(fd2, 0, SEEK_CUR); + ATF_REQUIRE(off == sb1.st_size); + + /* The larger file's size should remain the same. */ + ATF_REQUIRE(fstat(fd2, &sb) == 0); + ATF_REQUIRE(sb.st_size == sb2.st_size); + + /* The bytes beyond the end of the copy should be unchanged. */ + SHA256_FdChunk(fd2, digest2, sb1.st_size, sb2.st_size - sb1.st_size); + ATF_REQUIRE_MSG(strcmp(digest1, digest2) == 0, + "trailing file contents differ after copy_file_range()"); + + /* + * Verify that the copy actually replicated bytes from the smaller file. + */ + ATF_REQUIRE(ftruncate(fd2, sb1.st_size) == 0); + ATF_REQUIRE(cmpfile(fd1, fd2)); +} + +ATF_TP_ADD_TCS(tp) +{ + ATF_TP_ADD_TC(tp, copy_file_range_invalid); + ATF_TP_ADD_TC(tp, copy_file_range_offset); + ATF_TP_ADD_TC(tp, copy_file_range_truncate); + + return (atf_no_error()); +} 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_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/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/route_to.sh b/tests/sys/netpfil/pf/route_to.sh index 765403dcb79c..872de0dcbb91 100644 --- a/tests/sys/netpfil/pf/route_to.sh +++ b/tests/sys/netpfil/pf/route_to.sh @@ -28,6 +28,75 @@ common_dir=$(atf_get_srcdir)/../common +# We need to somehow test if the random algorithm of pf_map_addr() is working. +# The table or prefix contains multiple IP next-hop addresses, for each one try +# to establish up to 10 connections. Fail the test if with this many attempts +# the "good" target has not been chosen. However this choice is random, +# the check might still ocasionally fail. +check_random() { + if [ "$1" = "IPv4" ]; then + ping_from="${net_clients_4}.1" + ping_to="${host_server_4}" + else + ping_from="${net_clients_6}::1" + ping_to="${host_server_6}" + fi + good_targets="$2" + bad_targets="$3" + + port=42000 + states=$(mktemp) || exit 1 + for good_target in $good_targets; do + found="no" + for attempt in $(seq 1 10); do + port=$(( port + 1 )) + jexec router pfctl -Fs + atf_check -s exit:0 ${common_dir}/pft_ping.py \ + --sendif ${epair_tester}a --replyif ${epair_tester}a \ + --fromaddr ${ping_from} --to ${ping_to} \ + --ping-type=tcp3way --send-sport=${port} + jexec router pfctl -qvvss | normalize_pfctl_s > $states + cat $states + if [ -n "${bad_targets}" ]; then + for bad_target in $bad_targets; do + if grep -qE "route-to: ${bad_target}@" $states; then + atf_fail "Bad target ${bad_target} selected!" + fi + done + fi; + if grep -qE "route-to: ${good_target}@" $states; then + found=yes + break + fi + done + if [ "${found}" = "no" ]; then + atf_fail "Target ${good_target} not selected after ${attempt} attempts!" + fi + done +} + +pf_map_addr_common() +{ + setup_router_server_nat64 + + # Clients will connect from another network behind the router. + # This allows for using multiple source addresses. + jexec router route add -6 ${net_clients_6}::/${net_clients_6_mask} ${net_tester_6_host_tester} + jexec router route add ${net_clients_4}.0/${net_clients_4_mask} ${net_tester_4_host_tester} + + # The servers are reachable over additional IP addresses for + # testing of tables and subnets. The addresses are noncontinougnus + # for pf_map_addr() counter tests. + for i in 0 1 4 5; do + a1=$((24 + i)) + jexec server1 ifconfig ${epair_server1}b inet ${net_server1_4}.${a1}/32 alias + jexec server1 ifconfig ${epair_server1}b inet6 ${net_server1_6}::42:${i}/128 alias + a2=$((40 + i)) + jexec server2 ifconfig ${epair_server2}b inet ${net_server2_4}.${a2}/32 alias + jexec server2 ifconfig ${epair_server2}b inet6 ${net_server2_6}::42:${i}/128 alias + done +} + atf_test_case "v4" "cleanup" v4_head() { @@ -893,36 +962,17 @@ empty_pool_cleanup() pft_cleanup } - atf_test_case "table_loop" "cleanup" table_loop_head() { atf_set descr 'Check that iterating over tables poperly loops' atf_set require.user root - atf_set require.progs python3 scapy } table_loop_body() { - setup_router_server_nat64 - - # Clients will connect from another network behind the router. - # This allows for using multiple source addresses. - jexec router route add -6 ${net_clients_6}::/${net_clients_6_mask} ${net_tester_6_host_tester} - jexec router route add ${net_clients_4}.0/${net_clients_4_mask} ${net_tester_4_host_tester} - - # The servers are reachable over additional IP addresses for - # testing of tables and subnets. The addresses are noncontinougnus - # for pf_map_addr() counter tests. - for i in 0 1 4 5; do - a1=$((24 + i)) - jexec server1 ifconfig ${epair_server1}b inet ${net_server1_4}.${a1}/32 alias - jexec server1 ifconfig ${epair_server1}b inet6 ${net_server1_6}::42:${i}/128 alias - a2=$((40 + i)) - jexec server2 ifconfig ${epair_server2}b inet ${net_server2_4}.${a2}/32 alias - jexec server2 ifconfig ${epair_server2}b inet6 ${net_server2_6}::42:${i}/128 alias - done + pf_map_addr_common jexec router pfctl -e pft_set_rules router \ @@ -976,6 +1026,612 @@ table_loop_cleanup() } +atf_test_case "roundrobin" "cleanup" + +roundrobin_head() +{ + atf_set descr 'multiple gateways of mixed AF, including prefixes and tables, for IPv6 packets' + atf_set require.user root +} + +roundrobin_body() +{ + pf_map_addr_common + + # The rule is defined as "inet6 proto tcp" so directly given IPv4 hosts + # will be removed from the pool by pfctl. Tables will still be loaded + # and pf_map_addr() will only use IPv6 addresses from them. It will + # iterate over members of the pool and inside of tables and prefixes. + + jexec router pfctl -e + pft_set_rules router \ + "set debug loud" \ + "set reassemble yes" \ + "set state-policy if-bound" \ + "table <rt_targets> { ${net_server2_4}.40/31 ${net_server2_4}.44 ${net_server2_6}::42:0/127 ${net_server2_6}::42:4 }" \ + "pass in on ${epair_tester}b \ + route-to { \ + (${epair_server1}a ${net_server1_4_host_server}) \ + (${epair_server2}a <rt_targets_empty>) \ + (${epair_server1}a ${net_server1_6}::42:0/127) \ + (${epair_server2}a <rt_targets_empty>) \ + (${epair_server2}a <rt_targets>) \ + } \ + inet6 proto tcp \ + keep state" + + for port in $(seq 1 6); do + port=$((4200 + port)) + atf_check -s exit:0 ${common_dir}/pft_ping.py \ + --sendif ${epair_tester}a --replyif ${epair_tester}a \ + --fromaddr ${net_clients_6}::1 --to ${host_server_6} \ + --ping-type=tcp3way --send-sport=${port} + done + + states=$(mktemp) || exit 1 + jexec router pfctl -qvvss | normalize_pfctl_s > $states + + for state_regexp in \ + "${epair_tester}b tcp ${host_server_6}\[9\] <- ${net_clients_6}::1\[4201\] .* route-to: ${net_server1_6}::42:0@${epair_server1}a" \ + "${epair_tester}b tcp ${host_server_6}\[9\] <- ${net_clients_6}::1\[4202\] .* route-to: ${net_server1_6}::42:1@${epair_server1}a" \ + "${epair_tester}b tcp ${host_server_6}\[9\] <- ${net_clients_6}::1\[4203\] .* route-to: ${net_server2_6}::42:0@${epair_server2}a" \ + "${epair_tester}b tcp ${host_server_6}\[9\] <- ${net_clients_6}::1\[4204\] .* route-to: ${net_server2_6}::42:1@${epair_server2}a" \ + "${epair_tester}b tcp ${host_server_6}\[9\] <- ${net_clients_6}::1\[4205\] .* route-to: ${net_server2_6}::42:4@${epair_server2}a" \ + "${epair_tester}b tcp ${host_server_6}\[9\] <- ${net_clients_6}::1\[4206\] .* route-to: ${net_server1_6}::42:0@${epair_server1}a" \ + ; do + grep -qE "${state_regexp}" $states || atf_fail "State not found for '${state_regexp}'" + done +} + +roundrobin_cleanup() +{ + pft_cleanup +} + +atf_test_case "random_table" "cleanup" + +random_table_head() +{ + atf_set descr 'Pool with random flag and a table for IPv6' + atf_set require.user root +} + +random_table_body() +{ + pf_map_addr_common + + # The "random" flag will pick random hosts from the table but will + # not dive into prefixes, always choosing the 0th address. + # Proper address family will be choosen. + + jexec router pfctl -e + pft_set_rules router \ + "set debug loud" \ + "set reassemble yes" \ + "set state-policy if-bound" \ + "table <rt_targets> { ${net_server2_4}.40/31 ${net_server2_4}.44 ${net_server2_6}::42:0/127 ${net_server2_6}::42:4 }" \ + "pass in on ${epair_tester}b \ + route-to { (${epair_server2}a <rt_targets>) } random \ + inet6 proto tcp \ + keep state" + + good_targets="${net_server2_6}::42:0 ${net_server2_6}::42:4" + bad_targets="${net_server2_6}::42:1" + check_random IPv6 "${good_targets}" "${bad_targets}" +} + +random_table_cleanup() +{ + pft_cleanup +} + +atf_test_case "random_prefix" "cleanup" + +random_prefix_head() +{ + atf_set descr 'Pool with random flag and a table for IPv4' + atf_set require.user root +} + +random_prefix_body() +{ + pf_map_addr_common + + # The "random" flag will pick random hosts from given prefix. + # The choice being random makes testing it non-trivial. We do 10 + # attempts to have each target chosen. Hopefully this is enough to have + # this test pass often enough. + + jexec router pfctl -e + pft_set_rules router \ + "set debug loud" \ + "set reassemble yes" \ + "set state-policy if-bound" \ + "pass in on ${epair_tester}b \ + route-to { (${epair_server2}a ${net_server2_6}::42:0/127) } random \ + inet6 proto tcp \ + keep state" + + good_targets="${net_server2_6}::42:0 ${net_server2_6}::42:1" + check_random IPv6 "${good_targets}" +} + +random_prefix_cleanup() +{ + pft_cleanup +} + +atf_test_case "prefer_ipv6_nexthop_single_ipv4" "cleanup" + +prefer_ipv6_nexthop_single_ipv4_head() +{ + atf_set descr 'prefer-ipv6-nexthop option for a single IPv4 gateway' + atf_set require.user root +} + +prefer_ipv6_nexthop_single_ipv4_body() +{ + pf_map_addr_common + + # Basic forwarding test for prefer-ipv6-nexthop pool option. + # A single IPv4 gateway will work only for IPv4 traffic. + + jexec router pfctl -e + pft_set_rules router \ + "set reassemble yes" \ + "set state-policy if-bound" \ + "pass in on ${epair_tester}b \ + route-to (${epair_server1}a ${net_server1_4_host_server}) prefer-ipv6-nexthop \ + proto tcp \ + keep state" + + atf_check -s exit:0 ${common_dir}/pft_ping.py \ + --sendif ${epair_tester}a --replyif ${epair_tester}a \ + --fromaddr ${net_clients_4}.1 --to ${host_server_4} \ + --ping-type=tcp3way --send-sport=4201 \ + + atf_check -s exit:1 ${common_dir}/pft_ping.py \ + --sendif ${epair_tester}a --replyif ${epair_tester}a \ + --fromaddr ${net_clients_6}::1 --to ${host_server_6} \ + --ping-type=tcp3way --send-sport=4202 + + states=$(mktemp) || exit 1 + jexec router pfctl -qvvss | normalize_pfctl_s > $states + + for state_regexp in \ + "${epair_tester}b tcp ${host_server_4}:9 <- ${net_clients_4}.1:4201 .* route-to: ${net_server1_4_host_server}@${epair_server1}a" \ + ; do + grep -qE "${state_regexp}" $states || atf_fail "State not found for '${state_regexp}'" + done + + # The IPv6 packet could not have been routed over IPv4 gateway. + atf_check -o "match:map-failed +1 +" -x "jexec router pfctl -qvvsi 2> /dev/null" +} + +prefer_ipv6_nexthop_single_ipv4_cleanup() +{ + pft_cleanup +} + +atf_test_case "prefer_ipv6_nexthop_single_ipv6" "cleanup" + +prefer_ipv6_nexthop_single_ipv6_head() +{ + atf_set descr 'prefer-ipv6-nexthop option for a single IPv6 gateway' + atf_set require.user root +} + +prefer_ipv6_nexthop_single_ipv6_body() +{ + pf_map_addr_common + + # Basic forwarding test for prefer-ipv6-nexthop pool option. + # A single IPve gateway will work both for IPv4 and IPv6 traffic. + + jexec router pfctl -e + pft_set_rules router \ + "set reassemble yes" \ + "set state-policy if-bound" \ + "pass in on ${epair_tester}b \ + route-to (${epair_server1}a ${net_server1_6_host_server}) prefer-ipv6-nexthop \ + proto tcp \ + keep state" + + atf_check -s exit:0 ${common_dir}/pft_ping.py \ + --sendif ${epair_tester}a --replyif ${epair_tester}a \ + --fromaddr ${net_clients_4}.1 --to ${host_server_4} \ + --ping-type=tcp3way --send-sport=4201 \ + + atf_check -s exit:0 ${common_dir}/pft_ping.py \ + --sendif ${epair_tester}a --replyif ${epair_tester}a \ + --fromaddr ${net_clients_6}::1 --to ${host_server_6} \ + --ping-type=tcp3way --send-sport=4202 + + states=$(mktemp) || exit 1 + jexec router pfctl -qvvss | normalize_pfctl_s > $states + + for state_regexp in \ + "${epair_tester}b tcp ${host_server_4}:9 <- ${net_clients_4}.1:4201 .* route-to: ${net_server1_6_host_server}@${epair_server1}a" \ + "${epair_tester}b tcp ${host_server_6}\[9\] <- ${net_clients_6}::1\[4202\] .* route-to: ${net_server1_6_host_server}@${epair_server1}a" \ + ; do + grep -qE "${state_regexp}" $states || atf_fail "State not found for '${state_regexp}'" + done +} + +prefer_ipv6_nexthop_single_ipv6_cleanup() +{ + pft_cleanup +} + +atf_test_case "prefer_ipv6_nexthop_mixed_af_roundrobin_ipv4" "cleanup" + +prefer_ipv6_nexthop_mixed_af_roundrobin_ipv4_head() +{ + atf_set descr 'prefer-ipv6-nexthop option for multiple gateways of mixed AF with prefixes and tables, round robin selection, for IPv4 packets' + atf_set require.user root +} + +prefer_ipv6_nexthop_mixed_af_roundrobin_ipv4_body() +{ + pf_map_addr_common + + # pf_map_addr() starts iterating over hosts of the pool from the 2nd + # host. This behaviour was here before adding prefer-ipv6-nexthop + # support, we test for it. Some targets are hosts and some are tables. + # Those are iterated too. Finally the iteration loops back + # to the beginning of the pool. Send out IPv4 probes. They will use all + # IPv4 and IPv6 addresses from the pool. + + jexec router pfctl -e + pft_set_rules router \ + "set reassemble yes" \ + "set state-policy if-bound" \ + "table <rt_targets> { ${net_server2_4}.40/31 ${net_server2_4}.44 ${net_server2_6}::42:4/127 }" \ + "pass in on ${epair_tester}b \ + route-to { \ + (${epair_server1}a ${net_server1_6_host_server}) \ + (${epair_server1}a ${net_server1_6}::42:0/127) \ + (${epair_server2}a ${net_server2_4_host_server}) \ + (${epair_server2}a <rt_targets_empty>) \ + (${epair_server1}a ${net_server1_4}.24/31) \ + (${epair_server2}a <rt_targets>) \ + } prefer-ipv6-nexthop \ + proto tcp \ + keep state" + + for port in $(seq 1 12); do + port=$((4200 + port)) + atf_check -s exit:0 ${common_dir}/pft_ping.py \ + --sendif ${epair_tester}a --replyif ${epair_tester}a \ + --fromaddr ${net_clients_4}.1 --to ${host_server_4} \ + --ping-type=tcp3way --send-sport=${port} + done + + states=$(mktemp) || exit 1 + jexec router pfctl -qvvss | normalize_pfctl_s > $states + + for state_regexp in \ + "${epair_tester}b tcp ${host_server_4}:9 <- ${net_clients_4}.1:4201 .* route-to: ${net_server1_6}::42:0@${epair_server1}a" \ + "${epair_tester}b tcp ${host_server_4}:9 <- ${net_clients_4}.1:4202 .* route-to: ${net_server1_6}::42:1@${epair_server1}a" \ + "${epair_tester}b tcp ${host_server_4}:9 <- ${net_clients_4}.1:4203 .* route-to: ${net_server2_4_host_server}@${epair_server2}a" \ + "${epair_tester}b tcp ${host_server_4}:9 <- ${net_clients_4}.1:4204 .* route-to: ${net_server1_4}.24@${epair_server1}a" \ + "${epair_tester}b tcp ${host_server_4}:9 <- ${net_clients_4}.1:4205 .* route-to: ${net_server1_4}.25@${epair_server1}a" \ + "${epair_tester}b tcp ${host_server_4}:9 <- ${net_clients_4}.1:4206 .* route-to: ${net_server2_6}::42:4@${epair_server2}a" \ + "${epair_tester}b tcp ${host_server_4}:9 <- ${net_clients_4}.1:4207 .* route-to: ${net_server2_6}::42:5@${epair_server2}a" \ + "${epair_tester}b tcp ${host_server_4}:9 <- ${net_clients_4}.1:4208 .* route-to: ${net_server2_4}.40@${epair_server2}a" \ + "${epair_tester}b tcp ${host_server_4}:9 <- ${net_clients_4}.1:4209 .* route-to: ${net_server2_4}.41@${epair_server2}a" \ + "${epair_tester}b tcp ${host_server_4}:9 <- ${net_clients_4}.1:4210 .* route-to: ${net_server2_4}.44@${epair_server2}a" \ + "${epair_tester}b tcp ${host_server_4}:9 <- ${net_clients_4}.1:4211 .* route-to: ${net_server1_6_host_server}@${epair_server1}a" \ + "${epair_tester}b tcp ${host_server_4}:9 <- ${net_clients_4}.1:4212 .* route-to: ${net_server1_6}::42:0@${epair_server1}a" \ + ; do + grep -qE "${state_regexp}" $states || atf_fail "State not found for '${state_regexp}'" + done +} + +prefer_ipv6_nexthop_mixed_af_roundrobin_ipv4_cleanup() +{ + pft_cleanup +} + +prefer_ipv6_nexthop_mixed_af_roundrobin_ipv6_head() +{ + atf_set descr 'prefer-ipv6-nexthop option for multiple gateways of mixed AF with prefixes and tables, round-robin selection, for IPv6 packets' + atf_set require.user root +} + +prefer_ipv6_nexthop_mixed_af_roundrobin_ipv6_body() +{ + pf_map_addr_common + + # The "random" flag will pick random hosts from the table but will + # not dive into prefixes, always choosing the 0th address. + # Proper address family will be choosen. The choice being random makes + # testing it non-trivial. We do 10 attempts to have each target chosen. + # Hopefully this is enough to have this test pass often enough. + + # pf_map_addr() starts iterating over hosts of the pool from the 2nd + # host. This behaviour was here before adding prefer-ipv6-nexthop + # support, we test for it. Some targets are hosts and some are tables. + # Those are iterated too. Finally the iteration loops back + # to the beginning of the pool. Send out IPv6 probes. They will use only + # IPv6 addresses from the pool. + + jexec router pfctl -e + pft_set_rules router \ + "set reassemble yes" \ + "set state-policy if-bound" \ + "table <rt_targets> { ${net_server2_4}.40/31 ${net_server2_4}.44 ${net_server2_6}::42:4/127 }" \ + "pass in on ${epair_tester}b \ + route-to { \ + (${epair_server1}a ${net_server1_6_host_server}) \ + (${epair_server1}a ${net_server1_6}::42:0/127) \ + (${epair_server2}a ${net_server2_4_host_server}) \ + (${epair_server2}a <rt_targets_empty>) \ + (${epair_server1}a ${net_server1_4}.24/31) \ + (${epair_server2}a <rt_targets>) \ + } prefer-ipv6-nexthop \ + proto tcp \ + keep state" + + for port in $(seq 1 6); do + port=$((4200 + port)) + atf_check -s exit:0 ${common_dir}/pft_ping.py \ + --sendif ${epair_tester}a --replyif ${epair_tester}a \ + --fromaddr ${net_clients_6}::1 --to ${host_server_6} \ + --ping-type=tcp3way --send-sport=${port} + done + + states=$(mktemp) || exit 1 + jexec router pfctl -qvvss | normalize_pfctl_s > $states + + for state_regexp in \ + "${epair_tester}b tcp ${host_server_6}\[9\] <- ${net_clients_6}::1\[4201\] .* route-to: ${net_server1_6}::42:0@${epair_server1}a" \ + "${epair_tester}b tcp ${host_server_6}\[9\] <- ${net_clients_6}::1\[4202\] .* route-to: ${net_server1_6}::42:1@${epair_server1}a" \ + "${epair_tester}b tcp ${host_server_6}\[9\] <- ${net_clients_6}::1\[4203\] .* route-to: ${net_server2_6}::42:4@${epair_server2}a" \ + "${epair_tester}b tcp ${host_server_6}\[9\] <- ${net_clients_6}::1\[4204\] .* route-to: ${net_server2_6}::42:5@${epair_server2}a" \ + "${epair_tester}b tcp ${host_server_6}\[9\] <- ${net_clients_6}::1\[4205\] .* route-to: ${net_server1_6_host_server}@${epair_server1}a" \ + "${epair_tester}b tcp ${host_server_6}\[9\] <- ${net_clients_6}::1\[4206\] .* route-to: ${net_server1_6}::42:0@${epair_server1}a" \ + ; do + grep -qE "${state_regexp}" $states || atf_fail "State not found for '${state_regexp}'" + done +} + +prefer_ipv6_nexthop_mixed_af_roundrobin_ipv6_cleanup() +{ + pft_cleanup +} + +atf_test_case "prefer_ipv6_nexthop_mixed_af_random_ipv4" "cleanup" + +prefer_ipv6_nexthop_mixed_af_random_table_ipv4_head() +{ + atf_set descr 'prefer-ipv6-nexthop option for a mixed-af table with random selection for IPv4 packets' + atf_set require.user root +} + +prefer_ipv6_nexthop_mixed_af_random_table_ipv4_body() +{ + pf_map_addr_common + + # Similarly to the test "random_table" the algorithm will choose + # IP addresses from the table not diving into prefixes. + # *prefer*-ipv6-nexthop means that IPv6 nexthops are preferred, + # so IPv4 ones will not be chosen as long as there are IPv6 ones + # available. With this tested there is no need for a test for IPv6-only + # next-hops table. + + jexec router pfctl -e + pft_set_rules router \ + "set reassemble yes" \ + "set state-policy if-bound" \ + "table <rt_targets> { ${net_server2_4}.40/31 ${net_server2_4}.44 ${net_server2_6}::42:0/127 ${net_server2_6}::42:4 }" \ + "pass in on ${epair_tester}b \ + route-to { (${epair_server2}a <rt_targets>) } random prefer-ipv6-nexthop \ + proto tcp \ + keep state" + + good_targets="${net_server2_6}::42:0 ${net_server2_6}::42:4" + bad_targets="${net_server2_4}.40 ${net_server2_4}.41 ${net_server2_4}.44 ${net_server2_6}::42:1" + check_random IPv4 "${good_targets}" "${bad_targets}" +} + +prefer_ipv6_nexthop_mixed_af_random_table_ipv4_cleanup() +{ + pft_cleanup +} + +prefer_ipv6_nexthop_ipv4_random_table_ipv4_head() +{ + atf_set descr 'prefer-ipv6-nexthop option for an IPv4-only table with random selection for IPv4 packets' + atf_set require.user root +} + +prefer_ipv6_nexthop_ipv4_random_table_ipv4_body() +{ + pf_map_addr_common + + # Similarly to the test pf_map_addr:random_table the algorithm will + # choose IP addresses from the table not diving into prefixes. + # There are no IPv6 nexthops in the table, so the algorithm will + # fall back to IPv4. + + jexec router pfctl -e + pft_set_rules router \ + "set reassemble yes" \ + "set state-policy if-bound" \ + "table <rt_targets> { ${net_server2_4}.40/31 ${net_server2_4}.44 }" \ + "pass in on ${epair_tester}b \ + route-to { (${epair_server2}a <rt_targets>) } random prefer-ipv6-nexthop \ + proto tcp \ + keep state" + + good_targets="${net_server2_4}.40 ${net_server2_4}.44" + bad_targets="${net_server2_4}.41" + check_random IPv4 "${good_targets}" "${bad_targets}" +} + +prefer_ipv6_nexthop_ipv4_random_table_ipv4_cleanup() +{ + pft_cleanup +} + +prefer_ipv6_nexthop_ipv4_random_table_ipv6_head() +{ + atf_set descr 'prefer-ipv6-nexthop option for an IPv4-only table with random selection for IPv6 packets' + atf_set require.user root +} + +prefer_ipv6_nexthop_ipv4_random_table_ipv6_body() +{ + pf_map_addr_common + + # IPv6 packets can't be forwarded over IPv4 next-hops. + # The failure happens in pf_map_addr() and increases the respective + # error counter. + + jexec router pfctl -e + pft_set_rules router \ + "set reassemble yes" \ + "set state-policy if-bound" \ + "table <rt_targets> { ${net_server2_4}.40/31 ${net_server2_4}.44 }" \ + "pass in on ${epair_tester}b \ + route-to { (${epair_server2}a <rt_targets>) } random prefer-ipv6-nexthop \ + proto tcp \ + keep state" + + atf_check -s exit:1 ${common_dir}/pft_ping.py \ + --sendif ${epair_tester}a --replyif ${epair_tester}a \ + --fromaddr ${net_clients_6}::1 --to ${host_server_6} \ + --ping-type=tcp3way --send-sport=4201 + + atf_check -o "match:map-failed +1 +" -x "jexec router pfctl -qvvsi 2> /dev/null" +} + +prefer_ipv6_nexthop_ipv4_random_table_ipv6_cleanup() +{ + pft_cleanup +} + +prefer_ipv6_nexthop_ipv6_random_prefix_ipv4_head() +{ + atf_set descr 'prefer-ipv6-nexthop option for an IPv6 prefix with random selection for IPv4 packets' + atf_set require.user root +} + +prefer_ipv6_nexthop_ipv6_random_prefix_ipv4_body() +{ + pf_map_addr_common + + jexec router pfctl -e + pft_set_rules router \ + "set reassemble yes" \ + "set state-policy if-bound" \ + "pass in on ${epair_tester}b \ + route-to { (${epair_server2}a ${net_server2_6}::42:0/127) } random prefer-ipv6-nexthop \ + proto tcp \ + keep state" + + good_targets="${net_server2_6}::42:0 ${net_server2_6}::42:1" + check_random IPv4 "${good_targets}" +} + +prefer_ipv6_nexthop_ipv6_random_prefix_ipv4_cleanup() +{ + pft_cleanup +} + +prefer_ipv6_nexthop_ipv6_random_prefix_ipv6_head() +{ + atf_set descr 'prefer-ipv6-nexthop option for an IPv6 prefix with random selection for IPv6 packets' + atf_set require.user root +} + +prefer_ipv6_nexthop_ipv6_random_prefix_ipv6_body() +{ + pf_map_addr_common + + jexec router pfctl -e + pft_set_rules router \ + "set reassemble yes" \ + "set state-policy if-bound" \ + "pass in on ${epair_tester}b \ + route-to { (${epair_server2}a ${net_server2_6}::42:0/127) } random prefer-ipv6-nexthop \ + proto tcp \ + keep state" + + good_targets="${net_server2_6}::42:0 ${net_server2_6}::42:1" + check_random IPv6 "${good_targets}" +} + +prefer_ipv6_nexthop_ipv6_random_prefix_ipv6_cleanup() +{ + pft_cleanup +} + +prefer_ipv6_nexthop_ipv4_random_prefix_ipv4_head() +{ + atf_set descr 'prefer-ipv6-nexthop option for an IPv4 prefix with random selection for IPv4 packets' + atf_set require.user root +} + +prefer_ipv6_nexthop_ipv4_random_prefix_ipv4_body() +{ + pf_map_addr_common + + jexec router pfctl -e + pft_set_rules router \ + "set reassemble yes" \ + "set state-policy if-bound" \ + "pass in on ${epair_tester}b \ + route-to { (${epair_server2}a ${net_server2_4}.40/31) } random prefer-ipv6-nexthop \ + proto tcp \ + keep state" + + good_targets="${net_server2_4}.40 ${net_server2_4}.41" + check_random IPv4 "${good_targets}" +} + +prefer_ipv6_nexthop_ipv4_random_prefix_ipv4_cleanup() +{ + pft_cleanup +} + +prefer_ipv6_nexthop_ipv4_random_prefix_ipv6_head() +{ + atf_set descr 'prefer-ipv6-nexthop option for an IPv4 prefix with random selection for IPv6 packets' + atf_set require.user root +} + +prefer_ipv6_nexthop_ipv4_random_prefix_ipv6_body() +{ + pf_map_addr_common + + # IPv6 packets can't be forwarded over IPv4 next-hops. + # The failure happens in pf_map_addr() and increases the respective + # error counter. + + jexec router pfctl -e + pft_set_rules router \ + "set reassemble yes" \ + "set state-policy if-bound" \ + "pass in on ${epair_tester}b \ + route-to { (${epair_server2}a ${net_server2_4}.40/31) } random prefer-ipv6-nexthop \ + proto tcp \ + keep state" + + atf_check -s exit:1 ${common_dir}/pft_ping.py \ + --sendif ${epair_tester}a --replyif ${epair_tester}a \ + --fromaddr ${net_clients_6}::1 --to ${host_server_6} \ + --ping-type=tcp3way --send-sport=4201 + + atf_check -o "match:map-failed +1 +" -x "jexec router pfctl -qvvsi 2> /dev/null" +} + +prefer_ipv6_nexthop_ipv4_random_prefix_ipv6_cleanup() +{ + pft_cleanup +} + atf_init_test_cases() { atf_add_test_case "v4" @@ -995,5 +1651,25 @@ atf_init_test_cases() atf_add_test_case "sticky" atf_add_test_case "ttl" atf_add_test_case "empty_pool" + # Tests for pf_map_addr() without prefer-ipv6-nexthop atf_add_test_case "table_loop" + atf_add_test_case "roundrobin" + atf_add_test_case "random_table" + atf_add_test_case "random_prefix" + # Tests for pf_map_addr() without prefer-ipv6-nexthop + # Next hop is a Single IP address + atf_add_test_case "prefer_ipv6_nexthop_single_ipv4" + atf_add_test_case "prefer_ipv6_nexthop_single_ipv6" + # Next hop is tables and prefixes, accessed by the round-robin algorithm + atf_add_test_case "prefer_ipv6_nexthop_mixed_af_roundrobin_ipv4" + atf_add_test_case "prefer_ipv6_nexthop_mixed_af_roundrobin_ipv6" + # Next hop is a table, accessed by the random algorithm + atf_add_test_case "prefer_ipv6_nexthop_mixed_af_random_table_ipv4" + atf_add_test_case "prefer_ipv6_nexthop_ipv4_random_table_ipv4" + atf_add_test_case "prefer_ipv6_nexthop_ipv4_random_table_ipv6" + # Next hop is a prefix, accessed by the random algorithm + atf_add_test_case "prefer_ipv6_nexthop_ipv6_random_prefix_ipv4" + atf_add_test_case "prefer_ipv6_nexthop_ipv6_random_prefix_ipv6" + atf_add_test_case "prefer_ipv6_nexthop_ipv4_random_prefix_ipv4" + atf_add_test_case "prefer_ipv6_nexthop_ipv4_random_prefix_ipv6" } diff --git a/tests/sys/netpfil/pf/src_track.sh b/tests/sys/netpfil/pf/src_track.sh index ae60a5df809b..e12d0464ee8c 100755 --- a/tests/sys/netpfil/pf/src_track.sh +++ b/tests/sys/netpfil/pf/src_track.sh @@ -526,10 +526,12 @@ mixed_af_body() "block" \ "pass inet6 proto icmp6 icmp6-type { neighbrsol, neighbradv }" \ "pass in on ${epair_tester}b \ - route-to { (${epair_server1}a ${net_server1_4_host_server}) \ - } sticky-address \ - inet6 proto tcp from any to 64:ff9b::/96 \ - af-to inet from ${net_clients_4}.0/${net_clients_4_mask} round-robin sticky-address" + route-to { \ + (${epair_server1}a ${net_server1_4_host_server}) \ + (${epair_server2}a ${net_server2_6_host_server}) \ + } prefer-ipv6-nexthop sticky-address \ + inet6 proto tcp from any to 64:ff9b::/96 \ + af-to inet from ${net_clients_4}.0/${net_clients_4_mask} round-robin sticky-address" atf_check -s exit:0 ${common_dir}/pft_ping.py \ --sendif ${epair_tester}a \ @@ -538,6 +540,20 @@ mixed_af_body() --to 64:ff9b::192.0.2.100 \ --ping-type=tcp3way \ --send-sport=4201 + atf_check -s exit:0 ${common_dir}/pft_ping.py \ + --sendif ${epair_tester}a \ + --replyif ${epair_tester}a \ + --fromaddr 2001:db8:44::1 \ + --to 64:ff9b::192.0.2.100 \ + --ping-type=tcp3way \ + --send-sport=4202 + atf_check -s exit:0 ${common_dir}/pft_ping.py \ + --sendif ${epair_tester}a \ + --replyif ${epair_tester}a \ + --fromaddr 2001:db8:44::2 \ + --to 64:ff9b::192.0.2.100 \ + --ping-type=tcp3way \ + --send-sport=4203 states=$(mktemp) || exit 1 jexec router pfctl -qvvss | normalize_pfctl_s > $states @@ -546,16 +562,22 @@ mixed_af_body() # States are checked for proper route-to information. # The route-to gateway is IPv4. + # FIXME: Sticky-address is broken for af-to pools! + # The SN is created but apparently not used, as seen in states. for state_regexp in \ - "${epair_tester}b tcp 203.0.113.0:4201 \(2001:db8:44::1\[4201\]\) -> 192.0.2.100:9 \(64:ff9b::c000:264\[9\]\) .* route-to: 198.51.100.18@${epair_server1}a" \ + "${epair_tester}b tcp 203.0.113.0:4201 \(2001:db8:44::1\[4201\]\) -> 192.0.2.100:9 \(64:ff9b::c000:264\[9\]\) .* route-to: 2001:db8:4202::2@${epair_server2}a" \ + "${epair_tester}b tcp 203.0.113.1:4202 \(2001:db8:44::1\[4202\]\) -> 192.0.2.100:9 \(64:ff9b::c000:264\[9\]\) .* route-to: 2001:db8:4202::2@${epair_server2}a" \ + "${epair_tester}b tcp 203.0.113.2:4203 \(2001:db8:44::2\[4203\]\) -> 192.0.2.100:9 \(64:ff9b::c000:264\[9\]\) .* route-to: 198.51.100.18@${epair_server1}a" \ ; do grep -qE "${state_regexp}" $states || atf_fail "State not found for '${state_regexp}'" done # Source nodes map IPv6 source address onto IPv4 gateway and IPv4 SNAT address. for node_regexp in \ - '2001:db8:44::1 -> 203.0.113.0 .* states 1, .* NAT/RDR sticky-address' \ - '2001:db8:44::1 -> 198.51.100.18 .* states 1, .* route sticky-address' \ + '2001:db8:44::2 -> 203.0.113.2 .* states 1, .* NAT/RDR sticky-address' \ + '2001:db8:44::2 -> 198.51.100.18 .* states 1, .* route sticky-address' \ + '2001:db8:44::1 -> 203.0.113.0 .* states 2, .* NAT/RDR sticky-address' \ + '2001:db8:44::1 -> 2001:db8:4202::2 .* states 2, .* route sticky-address' \ ; do grep -qE "${node_regexp}" $nodes || atf_fail "Source node not found for '${node_regexp}'" done diff --git a/tests/sys/netpfil/pf/table.sh b/tests/sys/netpfil/pf/table.sh index c773518e95e4..65492545a13b 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() diff --git a/tests/sys/netpfil/pf/utils.subr b/tests/sys/netpfil/pf/utils.subr index 3f8d437920f9..a48f26653f8c 100644 --- a/tests/sys/netpfil/pf/utils.subr +++ b/tests/sys/netpfil/pf/utils.subr @@ -274,8 +274,8 @@ setup_router_server_ipv6() jexec server inetd -p ${PWD}/inetd.pid $inetd_conf } -# Create a router and 2 server jails for nat64 and rfc5549 test cases. -# The router is connected to servers, both are dual-stack, and to the +# Create a router and 2 server jails for nat64 and prefer-ipv6-nexthop test +# cases. The router is connected to servers, both are dual-stack, and to the # tester jail. All links are dual stack. setup_router_server_nat64() { |