diff options
Diffstat (limited to 'tests')
46 files changed, 1638 insertions, 92 deletions
diff --git a/tests/Makefile b/tests/Makefile index e8dd7793f169..451d55498a26 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -1,3 +1,5 @@ +.include <src.opts.mk> + PACKAGE= tests TESTSDIR= ${TESTSBASE} @@ -11,6 +13,9 @@ SUBDIR+= examples SUBDIR+= include SUBDIR+= sys SUBDIR+= atf_python +.if ${MK_CDDL} != "no" +SUBDIR+= oclo +.endif SUBDIR_PARALLEL= diff --git a/tests/atf_python/sys/net/vnet.py b/tests/atf_python/sys/net/vnet.py index 68c8ce4e0cba..7afb5c721bf3 100644 --- a/tests/atf_python/sys/net/vnet.py +++ b/tests/atf_python/sys/net/vnet.py @@ -61,6 +61,7 @@ class VnetInterface(object): self.iftype = self.IFT_LOOP else: self.iftype = self.IFT_ETHER + self.ether = ToolsHelper.get_output("/sbin/ifconfig %s ether | awk '/ether/ { print $2; }'" % iface_name).rstrip() @property def ifindex(self): @@ -99,9 +100,12 @@ class VnetInterface(object): name = run_cmd("/sbin/ifconfig {} create".format(iface_name)).rstrip() if not name: raise Exception("Unable to create iface {}".format(iface_name)) - ret = [cls(alias_name, name)] + if1 = cls(alias_name, name) + ret = [if1] if name.startswith("epair"): - ret.append(cls(alias_name, name[:-1] + "b")) + if2 = cls(alias_name, name[:-1] + "b") + if1.epairb = if2 + ret.append(if2); return ret def setup_addr(self, _addr: str): diff --git a/tests/ci/Makefile b/tests/ci/Makefile index b57de345f852..48e638fdb79c 100644 --- a/tests/ci/Makefile +++ b/tests/ci/Makefile @@ -33,11 +33,11 @@ EXTRA_MAKE_FLAGS?= TARGET= ${MACHINE} .endif .if !defined(TARGET_ARCH) || empty(TARGET_ARCH) -.if ${TARGET} == ${MACHINE} +. if ${TARGET} == ${MACHINE} TARGET_ARCH= ${MACHINE_ARCH} -.else +. else TARGET_ARCH= ${TARGET} -.endif +. endif .endif IMAKE= ${MAKE} TARGET=${TARGET} TARGET_ARCH=${TARGET_ARCH} @@ -47,20 +47,20 @@ CROSS_TOOLCHAIN_PARAM= "CROSS_TOOLCHAIN=${CROSS_TOOLCHAIN}" # Define OSRELEASE by using newvers.sh .if !defined(OSRELEASE) || empty(OSRELEASE) -.for _V in TYPE BRANCH REVISION -. if !defined(${_V}) || empty(${_V}) +. for _V in TYPE BRANCH REVISION +. if !defined(${_V}) || empty(${_V}) ${_V}!= eval $$(awk '/^${_V}=/{print}' ${.CURDIR}/../../sys/conf/newvers.sh); echo $$${_V} -. endif -.endfor -.for _V in ${TARGET_ARCH} -.if !empty(TARGET:M${_V}) +. endif +. endfor +. for _V in ${TARGET_ARCH} +. if !empty(TARGET:M${_V}) OSRELEASE= ${TYPE}-${REVISION}-${BRANCH}-${TARGET} VOLUME_LABEL= ${REVISION:C/[.-]/_/g}_${BRANCH:C/[.-]/_/g}_${TARGET} -.else +. else OSRELEASE= ${TYPE}-${REVISION}-${BRANCH}-${TARGET}-${TARGET_ARCH} VOLUME_LABEL= ${REVISION:C/[.-]/_/g}_${BRANCH:C/[.-]/_/g}_${TARGET_ARCH} -.endif -.endfor +. endif +. endfor .endif .if exists(${.CURDIR}/tools/ci.conf) && !defined(CICONF) @@ -104,13 +104,13 @@ TIMEOUT_VM=$$((${TIMEOUT_EXPECT} - 120)) . include "${.CURDIR}/Makefile.${TARGET_ARCH}" .endif .if ${TARGET_ARCH} != ${MACHINE_ARCH} -.if ( ${TARGET_ARCH} != "i386" ) || ( ${MACHINE_ARCH} != "amd64" ) +. if ( ${TARGET_ARCH} != "i386" ) || ( ${MACHINE_ARCH} != "amd64" ) QEMUSTATIC=/usr/local/bin/qemu-${QEMU_ARCH}-static QEMUTGT=portinstall-qemu -.endif +. endif .endif QEMUTGT?= -QEMU_DEVICES?=-device virtio-blk,drive=hd0 +QEMU_DEVICES?= QEMU_EXTRA_PARAM?= QEMU_MACHINE?=virt QEMUBIN=/usr/local/bin/qemu-system-${QEMU_ARCH} @@ -134,7 +134,8 @@ METAMODE?=-DWITH_META_MODE .endif CLEANFILES+= ${.OBJDIR}/${CIIMAGE} ${.OBJDIR}/ci.img ${META_TAR} -CLEANDIRS+= ${.OBJDIR}/ci-buildimage +IMAGEDIR= ${.OBJDIR}/ci-buildimage +CLEANDIRS+= ${IMAGEDIR} portinstall: portinstall-pkg portinstall-qemu portinstall-expect portinstall-${TARGET_ARCH:tl} .PHONY @@ -144,10 +145,10 @@ portinstall-pkg: .PHONY .endif portinstall-qemu: portinstall-pkg .PHONY -.if !exists(/usr/local/bin/qemu-${TARGET_ARCH}-static) +.if !exists(/usr/local/bin/qemu-${QEMU_ARCH}-static) env ASSUME_ALWAYS_YES=yes pkg install emulators/qemu-user-static .endif -.if !exists(/usr/local/bin/qemu-system-${QEMU_ARCH}) +.if !exists(${QEMUBIN}) env ASSUME_ALWAYS_YES=yes pkg install emulators/qemu@nox11 .endif @@ -157,7 +158,7 @@ portinstall-expect: portinstall-pkg .PHONY .endif beforeclean: .PHONY - chflags -R noschg ${.OBJDIR}/${.TARGET} + chflags -R noschg ${IMAGEDIR} .include <bsd.obj.mk> clean: beforeclean .PHONY @@ -205,12 +206,17 @@ ci-create-meta: .PHONY ci-extract-meta: .PHONY tar xfv ${META_TAR} -C ${META_DIROUT} + rm -rf ${META_TAR} ${META_DIR} @echo "Extracted kyua reports to ${META_DIROUT}" ci-runtest: ci-buildimage-${TARGET_ARCH:tl} portinstall .PHONY .if ${MACHINE} == "amd64" && ( ${TARGET_ARCH} == "amd64" || ${TARGET_ARCH} == "i386" ) && ( !defined(USE_QEMU) || empty(USE_QEMU) ) /usr/sbin/bhyvectl --vm=${TEST_VM_NAME} --destroy || true - /usr/sbin/bhyveload -c stdio -m ${VM_MEM_SIZE} -d ${CIDISK} ${TEST_VM_NAME} + expect -c "set timeout ${TIMEOUT_EXPECT}; \ + spawn /usr/bin/timeout -k 5s 30s /usr/sbin/bhyveload \ + -c stdio -m ${VM_MEM_SIZE} -d ${CIDISK} ${TEST_VM_NAME}; \ + expect { eof }; \ + exit [lindex [wait] 3]" expect -c "set timeout ${TIMEOUT_EXPECT}; \ spawn /usr/bin/timeout -k 60 ${TIMEOUT_VM} /usr/sbin/bhyve \ -c ${PARALLEL_JOBS} -m ${VM_MEM_SIZE} -A -H -P \ @@ -231,8 +237,10 @@ ci-runtest: ci-buildimage-${TARGET_ARCH:tl} portinstall .PHONY -nographic \ -no-reboot \ ${QEMU_EXTRA_PARAM} \ - -drive if=none,file=${CIDISK},format=raw,id=hd0 \ - -drive if=none,file=${META_TAR},format=raw,id=hd1 \ + -device virtio-blk,drive=hd0 \ + -device virtio-blk,drive=hd1 \ + -blockdev driver=raw,node-name=hd0,file.driver=file,file.filename=${CIDISK} \ + -blockdev driver=raw,node-name=hd1,file.driver=file,file.filename=${META_TAR} \ ${QEMU_DEVICES} .endif @@ -250,7 +258,7 @@ ci-checktarget: .PHONY ci-smoke: ci-set-smoke-var ci-create-meta ci-checktarget .WAIT ci-runtest-${TARGET_ARCH:tl} .PHONY -ci-full: ci-set-full-var ci-create-meta ci-checktarget .WAIT ci-runtest-${TARGET_ARCH:tl} ci-extract-meta .PHONY +ci-full: ci-set-full-var ci-create-meta ci-checktarget .WAIT ci-runtest-${TARGET_ARCH:tl} .WAIT ci-extract-meta .PHONY ci: ci-${CITYPE:tl} .PHONY diff --git a/tests/ci/Makefile.aarch64 b/tests/ci/Makefile.aarch64 index 9cbec6010a36..5a62e73d8eaa 100644 --- a/tests/ci/Makefile.aarch64 +++ b/tests/ci/Makefile.aarch64 @@ -8,7 +8,7 @@ # CI Makefile for aarch64. # QEMU_ARCH=aarch64 -QEMU_DEVICES=-device virtio-blk,drive=hd0 -device ahci,id=ahci +QEMU_DEVICES=-device ahci,id=ahci QEMU_EXTRA_PARAM=-bios /usr/local/share/u-boot/u-boot-qemu-arm64/u-boot.bin -cpu cortex-a57 QEMU_MAX_CPU_COUNT=64 QEMU_MAX_MEM_SIZE=64 diff --git a/tests/ci/Makefile.armv7 b/tests/ci/Makefile.armv7 index 21ee6b387b05..3b0d180fa352 100644 --- a/tests/ci/Makefile.armv7 +++ b/tests/ci/Makefile.armv7 @@ -8,7 +8,7 @@ # CI Makefile for armv7. # QEMU_ARCH=arm -QEMU_DEVICES=-device virtio-blk,drive=hd0 -device ahci,id=ahci +QEMU_DEVICES=-device ahci,id=ahci QEMU_EXTRA_PARAM=-bios /usr/local/share/u-boot/u-boot-qemu-arm/u-boot.bin QEMU_MAX_CPU_COUNT=1 QEMU_MAX_MEM_SIZE=3 diff --git a/tests/ci/Makefile.powerpc64 b/tests/ci/Makefile.powerpc64 index 26712b45f30b..d4e8e2cdc778 100644 --- a/tests/ci/Makefile.powerpc64 +++ b/tests/ci/Makefile.powerpc64 @@ -8,7 +8,6 @@ # CI Makefile for powerpc64. # QEMU_ARCH=ppc64 -QEMU_DEVICES=-device virtio-blk,drive=hd0 QEMU_EXTRA_PARAM=-vga none -accel tcg,thread=multi QEMU_MACHINE=pseries,cap-hpt-max-page-size=16M QEMU_MAX_CPU_COUNT=1 diff --git a/tests/ci/Makefile.powerpc64le b/tests/ci/Makefile.powerpc64le index 974ab04b8eed..60c255f569fa 100644 --- a/tests/ci/Makefile.powerpc64le +++ b/tests/ci/Makefile.powerpc64le @@ -8,7 +8,6 @@ # CI Makefile for powerpc64le. # QEMU_ARCH=ppc64 -QEMU_DEVICES=-device virtio-blk,drive=hd0 QEMU_EXTRA_PARAM=-vga none -accel tcg,thread=multi QEMU_MACHINE=pseries,cap-hpt-max-page-size=16M QEMU_MAX_CPU_COUNT=1 diff --git a/tests/ci/Makefile.riscv64 b/tests/ci/Makefile.riscv64 index 749df3f0b369..d494fc4f43f5 100644 --- a/tests/ci/Makefile.riscv64 +++ b/tests/ci/Makefile.riscv64 @@ -8,7 +8,6 @@ # CI Makefile for riscv64. # QEMU_ARCH=riscv64 -QEMU_DEVICES=-device virtio-blk-device,drive=hd0 QEMU_EXTRA_PARAM=-bios /usr/local/share/opensbi/lp64/generic/firmware/fw_jump.elf -kernel /usr/local/share/u-boot/u-boot-qemu-riscv64/u-boot.bin QEMU_MAX_CPU_COUNT=16 QEMU_MAX_MEM_SIZE=64 diff --git a/tests/ci/tools/freebsdci b/tests/ci/tools/freebsdci index 7b4ce9669ab2..51bd19e2967d 100755 --- a/tests/ci/tools/freebsdci +++ b/tests/ci/tools/freebsdci @@ -25,9 +25,6 @@ . /etc/rc.subr -: ${freebsdci_enable:="NO"} -: ${freebsdci_type:="full"} - name="freebsdci" desc="Run FreeBSD CI" rcvar=freebsdci_enable @@ -39,6 +36,11 @@ tardev=/dev/vtbd1 metadir=/meta istar=$(file -s ${tardev} | grep "POSIX tar archive" | wc -l) +load_rc_config $name +: ${freebsdci_enable:="NO"} +: ${freebsdci_type:="full"} +PATH="${PATH}:/usr/local/sbin:/usr/local/bin" + auto_shutdown() { # NOTE: Currently RISC-V kernels lack the ability to @@ -105,5 +107,4 @@ firstboot_ci_run() auto_shutdown } -load_rc_config $name run_rc_command "$1" diff --git a/tests/oclo/Makefile b/tests/oclo/Makefile new file mode 100644 index 000000000000..350c9f857c85 --- /dev/null +++ b/tests/oclo/Makefile @@ -0,0 +1,11 @@ +.PATH: ${SRCTOP}/cddl/contrib/opensolaris/tests/os-tests/tests/oclo + +TESTSDIR= ${TESTSBASE}/cddl/oclo + +PLAIN_TESTS_C= oclo oclo_errors ocloexec_verify + +SRCS.oclo= oclo.c +LIBADD.oclo+= openbsd +LIBADD.ocloexec_verify+= util + +.include <bsd.test.mk> diff --git a/tests/sys/cam/ctl/ctl.subr b/tests/sys/cam/ctl/ctl.subr index 5da441b806f0..6cc02d774bdb 100644 --- a/tests/sys/cam/ctl/ctl.subr +++ b/tests/sys/cam/ctl/ctl.subr @@ -25,15 +25,6 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -load_modules() { - if ! kldstat -q -m ctl; then - kldload ctl || atf_skip "could not load ctl kernel mod" - fi - if ! ctladm port -o on -p 0; then - atf_skip "could not enable the camsim frontend" - fi -} - find_device() { LUN=$1 diff --git a/tests/sys/file/closefrom_test.c b/tests/sys/file/closefrom_test.c index e30c5eb3d591..7dccf858c772 100644 --- a/tests/sys/file/closefrom_test.c +++ b/tests/sys/file/closefrom_test.c @@ -144,7 +144,7 @@ main(void) pid_t pid; int fd, flags, i, start; - printf("1..21\n"); + printf("1..22\n"); /* We'd better start up with fd's 0, 1, and 2 open. */ start = devnull(); @@ -356,5 +356,38 @@ main(void) fail_err("close_range"); ok("close_range(..., CLOSE_RANGE_CLOEXEC)"); + /* test CLOSE_RANGE_CLOFORK */ + for (i = 0; i < 8; i++) + (void)devnull(); + fd = highest_fd(); + start = fd - 8; + if (close_range(start + 1, start + 4, CLOSE_RANGE_CLOFORK) < 0) + fail_err("close_range(..., CLOSE_RANGE_CLOFORK)"); + flags = fcntl(start, F_GETFD); + if (flags < 0) + fail_err("fcntl(.., F_GETFD)"); + if ((flags & FD_CLOFORK) != 0) + fail("close_range", "CLOSE_RANGE_CLOFORK set close-on-exec " + "when it should not have on fd %d", start); + for (i = start + 1; i <= start + 4; i++) { + flags = fcntl(i, F_GETFD); + if (flags < 0) + fail_err("fcntl(.., F_GETFD)"); + if ((flags & FD_CLOFORK) == 0) + fail("close_range", "CLOSE_RANGE_CLOFORK did not set " + "close-on-exec on fd %d", i); + } + for (; i < start + 8; i++) { + flags = fcntl(i, F_GETFD); + if (flags < 0) + fail_err("fcntl(.., F_GETFD)"); + if ((flags & FD_CLOFORK) != 0) + fail("close_range", "CLOSE_RANGE_CLOFORK set close-on-exec " + "when it should not have on fd %d", i); + } + if (close_range(start, start + 8, 0) < 0) + fail_err("close_range"); + ok("close_range(..., CLOSE_RANGE_CLOFORK)"); + return (0); } diff --git a/tests/sys/file/dup_test.c b/tests/sys/file/dup_test.c index b024e72d0d1a..455115eda8c8 100644 --- a/tests/sys/file/dup_test.c +++ b/tests/sys/file/dup_test.c @@ -46,6 +46,8 @@ * Test #31: check if dup3(0) fails if oldfd == newfd. * Test #32: check if dup3(O_CLOEXEC) to a fd > current maximum number of * open files limit work. + * Tests #33-43 : Same as #18-26, 30 & 32 with O_CLOFORK instead of O_CLOEXEC, + * except F_DUP2FD_CLOEXEC. */ #include <sys/types.h> @@ -82,7 +84,7 @@ main(int __unused argc, char __unused *argv[]) orgfd = getafile(); - printf("1..32\n"); + printf("1..43\n"); /* If dup(2) ever work? */ if ((fd1 = dup(orgfd)) < 0) @@ -380,5 +382,99 @@ main(int __unused argc, char __unused *argv[]) printf("ok %d - dup3(O_CLOEXEC) didn't bypass NOFILE limit\n", test); + /* Does fcntl(F_DUPFD_CLOFORK) work? */ + if ((fd2 = fcntl(fd1, F_DUPFD_CLOFORK, 10)) < 0) + err(1, "fcntl(F_DUPFD_CLOFORK)"); + if (fd2 < 10) + printf("not ok %d - fcntl(F_DUPFD_CLOFORK) returned wrong fd %d\n", + ++test, fd2); + else + printf("ok %d - fcntl(F_DUPFD_CLOFORK) works\n", ++test); + + /* Was close-on-fork cleared? */ + ++test; + if (fcntl(fd2, F_GETFD) != FD_CLOFORK) + printf( + "not ok %d - fcntl(F_DUPFD_CLOFORK) didn't set close-on-fork\n", + test); + else + printf("ok %d - fcntl(F_DUPFD_CLOFORK) set close-on-fork\n", + test); + + /* Does dup3(O_CLOFORK) ever work? */ + if ((fd2 = dup3(fd1, fd1 + 1, O_CLOFORK)) < 0) + err(1, "dup3(O_CLOFORK)"); + printf("ok %d - dup3(O_CLOFORK) works\n", ++test); + + /* Do we get the right fd? */ + ++test; + if (fd2 != fd1 + 1) + printf( + "no ok %d - dup3(O_CLOFORK) didn't give us the right fd\n", + test); + else + printf("ok %d - dup3(O_CLOFORK) returned a correct fd\n", + test); + + /* Was close-on-fork set? */ + ++test; + if (fcntl(fd2, F_GETFD) != FD_CLOFORK) + printf( + "not ok %d - dup3(O_CLOFORK) didn't set close-on-fork\n", + test); + else + printf("ok %d - dup3(O_CLOFORK) set close-on-fork\n", + test); + + /* Does dup3(0) ever work? */ + if ((fd2 = dup3(fd1, fd1 + 1, 0)) < 0) + err(1, "dup3(0)"); + printf("ok %d - dup3(0) works\n", ++test); + + /* Do we get the right fd? */ + ++test; + if (fd2 != fd1 + 1) + printf( + "no ok %d - dup3(0) didn't give us the right fd\n", + test); + else + printf("ok %d - dup3(0) returned a correct fd\n", + test); + + /* Was close-on-fork cleared? */ + ++test; + if (fcntl(fd2, F_GETFD) != 0) + printf( + "not ok %d - dup3(0) didn't clear close-on-fork\n", + test); + else + printf("ok %d - dup3(0) cleared close-on-fork\n", + test); + + /* dup3() does not allow duplicating to the same fd */ + ++test; + if (dup3(fd1, fd1, O_CLOFORK) != -1) + printf( + "not ok %d - dup3(fd1, fd1, O_CLOFORK) succeeded\n", test); + else + printf("ok %d - dup3(fd1, fd1, O_CLOFORK) failed\n", test); + + ++test; + if (dup3(fd1, fd1, 0) != -1) + printf( + "not ok %d - dup3(fd1, fd1, 0) succeeded\n", test); + else + printf("ok %d - dup3(fd1, fd1, 0) failed\n", test); + + ++test; + if (getrlimit(RLIMIT_NOFILE, &rlp) < 0) + err(1, "getrlimit"); + if ((fd2 = dup3(fd1, rlp.rlim_cur + 1, O_CLOFORK)) >= 0) + printf("not ok %d - dup3(O_CLOFORK) bypassed NOFILE limit\n", + test); + else + printf("ok %d - dup3(O_CLOFORK) didn't bypass NOFILE limit\n", + test); + return (0); } diff --git a/tests/sys/fs/fusefs/Makefile b/tests/sys/fs/fusefs/Makefile index b11f11bdfa98..a21512798597 100644 --- a/tests/sys/fs/fusefs/Makefile +++ b/tests/sys/fs/fusefs/Makefile @@ -70,7 +70,8 @@ TEST_METADATA.nfs+= required_user="root" TEST_METADATA.ctl+= is_exclusive="true" TEST_METADATA.ctl+= required_user="root" -TEST_METADATA+= timeout=10 +TEST_METADATA+= timeout=10 +TEST_METADATA+= required_kmods="fusefs" FUSEFS= ${SRCTOP}/sys/fs/fuse # Suppress warnings that GCC generates for the libc++ and gtest headers. diff --git a/tests/sys/kern/Makefile b/tests/sys/kern/Makefile index 26c0013696c7..336e73f29835 100644 --- a/tests/sys/kern/Makefile +++ b/tests/sys/kern/Makefile @@ -17,7 +17,9 @@ ATF_TESTS_C+= kern_copyin ATF_TESTS_C+= kern_descrip_test # One test modifies the maxfiles limit, which can cause spurious test failures. TEST_METADATA.kern_descrip_test+= is_exclusive="true" +ATF_TESTS_C+= exterr_test ATF_TESTS_C+= fdgrowtable_test +ATF_TESTS_C+= getdirentries_test ATF_TESTS_C+= jail_lookup_root ATF_TESTS_C+= inotify_test ATF_TESTS_C+= kill_zombie diff --git a/tests/sys/kern/exterr_test.c b/tests/sys/kern/exterr_test.c new file mode 100644 index 000000000000..17c84c1f8ed4 --- /dev/null +++ b/tests/sys/kern/exterr_test.c @@ -0,0 +1,108 @@ +/*- + * Copyright (C) 2025 ConnectWise, LLC. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/exterrvar.h> +#include <sys/mman.h> + +#include <atf-c.h> +#include <errno.h> +#include <exterr.h> +#include <stdio.h> + +ATF_TC(gettext_extended); +ATF_TC_HEAD(gettext_extended, tc) +{ + atf_tc_set_md_var(tc, "descr", "Retrieve an extended error message"); +} +ATF_TC_BODY(gettext_extended, tc) +{ + char exterr[UEXTERROR_MAXLEN]; + int r; + + /* + * Use an invalid call to mmap() because it supports extended error + * messages, requires no special resources, and does not need root. + */ + ATF_CHECK_ERRNO(ENOTSUP, + mmap(NULL, 0, PROT_MAX(PROT_READ) | PROT_WRITE, 0, -1, 0)); + r = uexterr_gettext(exterr, sizeof(exterr)); + ATF_CHECK_EQ(0, r); + printf("Extended error: %s\n", exterr); + /* Note: error string may need to be updated due to kernel changes */ + ATF_CHECK(strstr(exterr, "prot is not subset of max_prot") != 0); +} + +ATF_TC(gettext_noextended); +ATF_TC_HEAD(gettext_noextended, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Fail to retrieve an extended error message because none exists"); +} +ATF_TC_BODY(gettext_noextended, tc) +{ + char exterr[UEXTERROR_MAXLEN]; + int r; + + ATF_CHECK_ERRNO(EINVAL, exterrctl(EXTERRCTL_UD, 0, NULL)); + r = uexterr_gettext(exterr, sizeof(exterr)); + ATF_CHECK_EQ(0, r); + ATF_CHECK_STREQ(exterr, ""); +} + +ATF_TC(gettext_noextended_after_extended); +ATF_TC_HEAD(gettext_noextended_after_extended, tc) +{ + atf_tc_set_md_var(tc, "descr", + "uexterr_gettext should not return a stale extended error message"); +} +ATF_TC_BODY(gettext_noextended_after_extended, tc) +{ + char exterr[UEXTERROR_MAXLEN]; + int r; + + /* + * First do something that will create an extended error message, but + * ignore it. + */ + ATF_CHECK_ERRNO(ENOTSUP, + mmap(NULL, 0, PROT_MAX(PROT_READ) | PROT_WRITE, 0, -1, 0)); + + /* Then do something that won't create an extended error message */ + ATF_CHECK_ERRNO(EINVAL, exterrctl(EXTERRCTL_UD, 0, NULL)); + + /* Hopefully we won't see the stale extended error message */ + r = uexterr_gettext(exterr, sizeof(exterr)); + ATF_CHECK_EQ(0, r); + ATF_CHECK_STREQ(exterr, ""); +} + +ATF_TP_ADD_TCS(tp) +{ + ATF_TP_ADD_TC(tp, gettext_extended); + ATF_TP_ADD_TC(tp, gettext_noextended); + ATF_TP_ADD_TC(tp, gettext_noextended_after_extended); + + return (atf_no_error()); +} diff --git a/tests/sys/kern/getdirentries_test.c b/tests/sys/kern/getdirentries_test.c new file mode 100644 index 000000000000..e66872ffe5b6 --- /dev/null +++ b/tests/sys/kern/getdirentries_test.c @@ -0,0 +1,172 @@ +/*- + * Copyright (c) 2025 Klara, Inc. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include <sys/stat.h> +#include <sys/mount.h> + +#include <dirent.h> +#include <fcntl.h> +#include <errno.h> +#include <stdint.h> + +#include <atf-c.h> + +ATF_TC(getdirentries_ok); +ATF_TC_HEAD(getdirentries_ok, tc) +{ + atf_tc_set_md_var(tc, "descr", "Successfully read a directory."); +} +ATF_TC_BODY(getdirentries_ok, tc) +{ + char dbuf[4096]; + struct dirent *d; + off_t base; + ssize_t ret; + int dd, n; + + ATF_REQUIRE_EQ(0, mkdir("dir", 0755)); + ATF_REQUIRE((dd = open("dir", O_DIRECTORY | O_RDONLY)) >= 0); + ATF_REQUIRE((ret = getdirentries(dd, dbuf, sizeof(dbuf), &base)) > 0); + ATF_REQUIRE_EQ(0, getdirentries(dd, dbuf, sizeof(dbuf), &base)); + ATF_REQUIRE_EQ(base, lseek(dd, 0, SEEK_CUR)); + ATF_CHECK_EQ(0, close(dd)); + for (n = 0, d = (struct dirent *)dbuf; + d < (struct dirent *)(dbuf + ret); + d = (struct dirent *)((char *)d + d->d_reclen), n++) + /* nothing */ ; + ATF_CHECK_EQ((struct dirent *)(dbuf + ret), d); + ATF_CHECK_EQ(2, n); +} + +ATF_TC(getdirentries_ebadf); +ATF_TC_HEAD(getdirentries_ebadf, tc) +{ + atf_tc_set_md_var(tc, "descr", "Attempt to read a directory " + "from an invalid descriptor."); +} +ATF_TC_BODY(getdirentries_ebadf, tc) +{ + char dbuf[4096]; + off_t base; + int fd; + + ATF_REQUIRE((fd = open("file", O_CREAT | O_WRONLY, 0644)) >= 0); + ATF_REQUIRE_EQ(-1, getdirentries(fd, dbuf, sizeof(dbuf), &base)); + ATF_CHECK_EQ(EBADF, errno); + ATF_REQUIRE_EQ(0, close(fd)); + ATF_REQUIRE_EQ(-1, getdirentries(fd, dbuf, sizeof(dbuf), &base)); + ATF_CHECK_EQ(EBADF, errno); +} + +ATF_TC(getdirentries_efault); +ATF_TC_HEAD(getdirentries_efault, tc) +{ + atf_tc_set_md_var(tc, "descr", "Attempt to read a directory " + "to an invalid buffer."); +} +ATF_TC_BODY(getdirentries_efault, tc) +{ + char dbuf[4096]; + off_t base, *basep; + int dd; + + ATF_REQUIRE_EQ(0, mkdir("dir", 0755)); + ATF_REQUIRE((dd = open("dir", O_DIRECTORY | O_RDONLY)) >= 0); + ATF_REQUIRE_EQ(-1, getdirentries(dd, NULL, sizeof(dbuf), &base)); + ATF_CHECK_EQ(EFAULT, errno); + basep = NULL; + basep++; + ATF_REQUIRE_EQ(-1, getdirentries(dd, dbuf, sizeof(dbuf), basep)); + ATF_CHECK_EQ(EFAULT, errno); + ATF_CHECK_EQ(0, close(dd)); +} + +ATF_TC(getdirentries_einval); +ATF_TC_HEAD(getdirentries_einval, tc) +{ + atf_tc_set_md_var(tc, "descr", "Attempt to read a directory " + "with various invalid parameters."); +} +ATF_TC_BODY(getdirentries_einval, tc) +{ + struct statfs fsb; + char dbuf[4096]; + off_t base; + ssize_t ret; + int dd; + + ATF_REQUIRE_EQ(0, mkdir("dir", 0755)); + ATF_REQUIRE((dd = open("dir", O_DIRECTORY | O_RDONLY)) >= 0); + ATF_REQUIRE_EQ(0, fstatfs(dd, &fsb)); + /* nbytes too small */ + ATF_REQUIRE_EQ(-1, getdirentries(dd, dbuf, 8, &base)); + ATF_CHECK_EQ(EINVAL, errno); + /* nbytes too big */ + ATF_REQUIRE_EQ(-1, getdirentries(dd, dbuf, SIZE_MAX, &base)); + ATF_CHECK_EQ(EINVAL, errno); + /* invalid position */ + ATF_REQUIRE((ret = getdirentries(dd, dbuf, sizeof(dbuf), &base)) > 0); + ATF_REQUIRE_EQ(0, getdirentries(dd, dbuf, sizeof(dbuf), &base)); + ATF_REQUIRE(base > 0); + ATF_REQUIRE_EQ(base + 3, lseek(dd, 3, SEEK_CUR)); + /* known to fail on ufs (FFS2) and zfs, and work on tmpfs */ + if (strcmp(fsb.f_fstypename, "ufs") == 0 || + strcmp(fsb.f_fstypename, "zfs") == 0) { + atf_tc_expect_fail("incorrectly returns 0 instead of EINVAL " + "on %s", fsb.f_fstypename); + } + ATF_REQUIRE_EQ(-1, getdirentries(dd, dbuf, sizeof(dbuf), &base)); + ATF_CHECK_EQ(EINVAL, errno); + ATF_CHECK_EQ(0, close(dd)); +} + +ATF_TC(getdirentries_enoent); +ATF_TC_HEAD(getdirentries_enoent, tc) +{ + atf_tc_set_md_var(tc, "descr", "Attempt to read a directory " + "after it is deleted."); +} +ATF_TC_BODY(getdirentries_enoent, tc) +{ + char dbuf[4096]; + off_t base; + int dd; + + ATF_REQUIRE_EQ(0, mkdir("dir", 0755)); + ATF_REQUIRE((dd = open("dir", O_DIRECTORY | O_RDONLY)) >= 0); + ATF_REQUIRE_EQ(0, rmdir("dir")); + ATF_REQUIRE_EQ(-1, getdirentries(dd, dbuf, sizeof(dbuf), &base)); + ATF_CHECK_EQ(ENOENT, errno); +} + +ATF_TC(getdirentries_enotdir); +ATF_TC_HEAD(getdirentries_enotdir, tc) +{ + atf_tc_set_md_var(tc, "descr", "Attempt to read a directory " + "from a descriptor not associated with a directory."); +} +ATF_TC_BODY(getdirentries_enotdir, tc) +{ + char dbuf[4096]; + off_t base; + int fd; + + ATF_REQUIRE((fd = open("file", O_CREAT | O_RDWR, 0644)) >= 0); + ATF_REQUIRE_EQ(-1, getdirentries(fd, dbuf, sizeof(dbuf), &base)); + ATF_CHECK_EQ(ENOTDIR, errno); + ATF_CHECK_EQ(0, close(fd)); +} + +ATF_TP_ADD_TCS(tp) +{ + ATF_TP_ADD_TC(tp, getdirentries_ok); + ATF_TP_ADD_TC(tp, getdirentries_ebadf); + ATF_TP_ADD_TC(tp, getdirentries_efault); + ATF_TP_ADD_TC(tp, getdirentries_einval); + ATF_TP_ADD_TC(tp, getdirentries_enoent); + ATF_TP_ADD_TC(tp, getdirentries_enotdir); + return (atf_no_error()); +} diff --git a/tests/sys/kern/inotify_test.c b/tests/sys/kern/inotify_test.c index ed7cef5d148c..713db55afc22 100644 --- a/tests/sys/kern/inotify_test.c +++ b/tests/sys/kern/inotify_test.c @@ -79,7 +79,8 @@ close_inotify(int fd) } static uint32_t -consume_event_cookie(int ifd, int wd, int event, int flags, const char *name) +consume_event_cookie(int ifd, int wd, unsigned int event, unsigned int flags, + const char *name) { struct inotify_event *ev; size_t evsz, namelen; @@ -118,7 +119,8 @@ consume_event_cookie(int ifd, int wd, int event, int flags, const char *name) * matches the expected values. */ static void -consume_event(int ifd, int wd, int event, int flags, const char *name) +consume_event(int ifd, int wd, unsigned int event, unsigned int flags, + const char *name) { (void)consume_event_cookie(ifd, wd, event, flags, name); } diff --git a/tests/sys/kern/unix_passfd_test.c b/tests/sys/kern/unix_passfd_test.c index 95271c04a16b..7dc4541ad402 100644 --- a/tests/sys/kern/unix_passfd_test.c +++ b/tests/sys/kern/unix_passfd_test.c @@ -380,6 +380,30 @@ ATF_TC_BODY(simple_send_fd_msg_cmsg_cloexec, tc) } /* + * Like simple_send_fd but also sets MSG_CMSG_CLOFORK and checks that the + * received file descriptor has the FD_CLOFORK flag set. + */ +ATF_TC_WITHOUT_HEAD(simple_send_fd_msg_cmsg_clofork); +ATF_TC_BODY(simple_send_fd_msg_cmsg_clofork, tc) +{ + struct stat getfd_stat, putfd_stat; + int fd[2], getfd, putfd; + + domainsocketpair(fd); + tempfile(&putfd); + dofstat(putfd, &putfd_stat); + sendfd(fd[0], putfd); + recvfd(fd[1], &getfd, MSG_CMSG_CLOFORK); + dofstat(getfd, &getfd_stat); + samefile(&putfd_stat, &getfd_stat); + ATF_REQUIRE_EQ_MSG(fcntl(getfd, F_GETFD) & FD_CLOFORK, FD_CLOFORK, + "FD_CLOFORK not set on the received file descriptor"); + close(putfd); + close(getfd); + closesocketpair(fd); +} + +/* * Same as simple_send_fd, only close the file reference after sending, so that * the only reference is the descriptor in the UNIX domain socket buffer. */ @@ -1170,6 +1194,7 @@ ATF_TP_ADD_TCS(tp) ATF_TP_ADD_TC(tp, simple_send_fd); ATF_TP_ADD_TC(tp, simple_send_fd_msg_cmsg_cloexec); + ATF_TP_ADD_TC(tp, simple_send_fd_msg_cmsg_clofork); ATF_TP_ADD_TC(tp, send_and_close); ATF_TP_ADD_TC(tp, send_and_cancel); ATF_TP_ADD_TC(tp, send_and_shutdown); diff --git a/tests/sys/mac/bsdextended/Makefile b/tests/sys/mac/bsdextended/Makefile index 69cd27c0e321..cc3a3f8ea534 100644 --- a/tests/sys/mac/bsdextended/Makefile +++ b/tests/sys/mac/bsdextended/Makefile @@ -9,5 +9,6 @@ TEST_METADATA.ugidfw_test+= required_user="root" # Each test case of matches_test reuses the same ruleset number, so they cannot # be run simultaneously TEST_METADATA.matches_test+= is_exclusive=true +TEST_METADATA+= required_kmods="mac_bsdextended" .include <bsd.test.mk> diff --git a/tests/sys/mac/bsdextended/matches_test.sh b/tests/sys/mac/bsdextended/matches_test.sh index 2a28be0f231b..41fa04f221e3 100644 --- a/tests/sys/mac/bsdextended/matches_test.sh +++ b/tests/sys/mac/bsdextended/matches_test.sh @@ -12,9 +12,6 @@ gidoutrange="daemon" # We expect $uidinrange in this group check_ko() { - if ! sysctl -N security.mac.bsdextended >/dev/null 2>&1; then - atf_skip "mac_bsdextended(4) support isn't available" - fi if [ $(sysctl -n security.mac.bsdextended.enabled) = "0" ]; then # The kernel module is loaded but disabled. Enable it for the # duration of the test. diff --git a/tests/sys/mac/portacl/Makefile b/tests/sys/mac/portacl/Makefile index c9fb6bbaae3e..856a85d331d5 100644 --- a/tests/sys/mac/portacl/Makefile +++ b/tests/sys/mac/portacl/Makefile @@ -10,6 +10,7 @@ TAP_TESTS_SH+= root_test .for t in ${TAP_TESTS_SH} TEST_METADATA.$t+= required_user="root" TEST_METADATA.$t+= timeout="450" +TEST_METADATA.$t+= is_exclusive="true" .endfor .include <bsd.test.mk> diff --git a/tests/sys/net/if_bridge_test.sh b/tests/sys/net/if_bridge_test.sh index cc0b212aebd2..c0c085f22273 100755 --- a/tests/sys/net/if_bridge_test.sh +++ b/tests/sys/net/if_bridge_test.sh @@ -537,7 +537,7 @@ get_mtu() { intf=$1 - ifconfig ${intf} ether | awk '$5 == "mtu" { print $6 }' + ifconfig ${intf} | awk '$5 == "mtu" { print $6 }' } check_mtu() @@ -546,7 +546,7 @@ check_mtu() expected=$2 mtu=$(get_mtu $intf) - if [ $mtu -ne $expected ]; + if [ "$mtu" -ne "$expected" ]; then atf_fail "Expected MTU of $expected on $intf but found $mtu" fi @@ -1221,6 +1221,29 @@ vlan_qinq_cleanup() vnet_cleanup } +# Adding a bridge SVI to a bridge should not be allowed. +atf_test_case "bridge_svi_in_bridge" "cleanup" +bridge_svi_in_bridge_head() +{ + atf_set descr 'adding a bridge SVI to a bridge is not allowed (1)' + atf_set require.user root +} + +bridge_svi_in_bridge_body() +{ + vnet_init + vnet_init_bridge + + bridge=$(vnet_mkbridge) + atf_check -s exit:0 ifconfig ${bridge}.1 create + atf_check -s exit:1 -e ignore ifconfig ${bridge} addm ${bridge}.1 +} + +bridge_svi_in_bridge_cleanup() +{ + vnet_cleanup +} + atf_init_test_cases() { atf_add_test_case "bridge_transmit_ipv4_unicast" @@ -1247,4 +1270,5 @@ atf_init_test_cases() atf_add_test_case "vlan_ifconfig_tagged" atf_add_test_case "vlan_svi" atf_add_test_case "vlan_qinq" + atf_add_test_case "bridge_svi_in_bridge" } diff --git a/tests/sys/net/if_ovpn/if_ovpn.sh b/tests/sys/net/if_ovpn/if_ovpn.sh index 2138e0f666ec..c42344da1a3b 100644 --- a/tests/sys/net/if_ovpn/if_ovpn.sh +++ b/tests/sys/net/if_ovpn/if_ovpn.sh @@ -1149,6 +1149,261 @@ destroy_unused_cleanup() ovpn_cleanup } +atf_test_case "multihome4" "cleanup" +multihome4_head() +{ + atf_set descr 'Test multihome IPv4 with OpenVPN' + atf_set require.user root + atf_set require.progs openvpn +} + +multihome4_body() +{ + pft_init + ovpn_init + + l=$(vnet_mkepair) + + vnet_mkjail a ${l}a + atf_check jexec a ifconfig ${l}a inet 192.0.2.1/24 + atf_check jexec a ifconfig ${l}a alias 192.0.2.2/24 + vnet_mkjail b ${l}b + atf_check jexec b ifconfig ${l}b inet 192.0.2.3/24 + + # Sanity check + atf_check -s exit:0 -o ignore jexec b ping -c 1 192.0.2.1 + atf_check -s exit:0 -o ignore jexec b ping -c 1 192.0.2.2 + + ovpn_start a " + dev ovpn0 + dev-type tun + proto udp4 + + cipher AES-256-GCM + auth SHA256 + + multihome + server 198.51.100.0 255.255.255.0 + ca $(atf_get_srcdir)/ca.crt + cert $(atf_get_srcdir)/server.crt + key $(atf_get_srcdir)/server.key + dh $(atf_get_srcdir)/dh.pem + + mode server + script-security 2 + auth-user-pass-verify /usr/bin/true via-env + topology subnet + + keepalive 100 600 + " + ovpn_start b " + dev tun0 + dev-type tun + + client + + remote 192.0.2.2 + auth-user-pass $(atf_get_srcdir)/user.pass + + ca $(atf_get_srcdir)/ca.crt + cert $(atf_get_srcdir)/client.crt + key $(atf_get_srcdir)/client.key + dh $(atf_get_srcdir)/dh.pem + + keepalive 100 600 + " + + # Block packets from the primary address, openvpn should only use the + # configured remote address. + jexec b pfctl -e + pft_set_rules b \ + "block in quick from 192.0.2.1 to any" \ + "pass all" + + # Give the tunnel time to come up + sleep 10 + + atf_check -s exit:0 -o ignore jexec b ping -c 3 198.51.100.1 +} + +multihome4_cleanup() +{ + ovpn_cleanup + pft_cleanup +} + +multihome6_head() +{ + atf_set descr 'Test multihome IPv6 with OpenVPN' + atf_set require.user root + atf_set require.progs openvpn +} + +multihome6_body() +{ + ovpn_init + + l=$(vnet_mkepair) + + vnet_mkjail a ${l}a + atf_check jexec a ifconfig ${l}a inet6 2001:db8::1/64 no_dad + atf_check jexec a ifconfig ${l}a inet6 alias 2001:db8::2/64 no_dad + vnet_mkjail b ${l}b + atf_check jexec b ifconfig ${l}b inet6 2001:db8::3/64 no_dad + + # Sanity check + atf_check -s exit:0 -o ignore jexec b ping6 -c 1 2001:db8::1 + atf_check -s exit:0 -o ignore jexec b ping6 -c 1 2001:db8::2 + + ovpn_start a " + dev ovpn0 + dev-type tun + proto udp6 + + cipher AES-256-GCM + auth SHA256 + + multihome + server-ipv6 2001:db8:1::/64 + + ca $(atf_get_srcdir)/ca.crt + cert $(atf_get_srcdir)/server.crt + key $(atf_get_srcdir)/server.key + dh $(atf_get_srcdir)/dh.pem + + mode server + script-security 2 + auth-user-pass-verify /usr/bin/true via-env + topology subnet + + keepalive 100 600 + " + ovpn_start b " + dev tun0 + dev-type tun + + client + + remote 2001:db8::2 + auth-user-pass $(atf_get_srcdir)/user.pass + + ca $(atf_get_srcdir)/ca.crt + cert $(atf_get_srcdir)/client.crt + key $(atf_get_srcdir)/client.key + dh $(atf_get_srcdir)/dh.pem + + keepalive 100 600 + " + + # Block packets from the primary address, openvpn should only use the + # configured remote address. + jexec b pfctl -e + pft_set_rules b \ + "block in quick from 2001:db8::1 to any" \ + "pass all" + + # Give the tunnel time to come up + sleep 10 + + atf_check -s exit:0 -o ignore jexec b ping6 -c 3 2001:db8:1::1 + atf_check -s exit:0 -o ignore jexec b ping6 -c 3 -z 16 2001:db8:1::1 +} + +multihome6_cleanup() +{ + ovpn_cleanup +} + +atf_test_case "float" "cleanup" +float_head() +{ + atf_set descr 'Test peer float notification' + atf_set require.user root +} + +float_body() +{ + ovpn_init + + l=$(vnet_mkepair) + + vnet_mkjail a ${l}a + jexec a ifconfig ${l}a 192.0.2.1/24 up + jexec a ifconfig lo0 127.0.0.1/8 up + vnet_mkjail b ${l}b + jexec b ifconfig ${l}b 192.0.2.2/24 up + + # Sanity check + atf_check -s exit:0 -o ignore jexec a ping -c 1 192.0.2.2 + + ovpn_start a " + dev ovpn0 + dev-type tun + proto udp4 + + cipher AES-256-GCM + auth SHA256 + + local 192.0.2.1 + server 198.51.100.0 255.255.255.0 + ca $(atf_get_srcdir)/ca.crt + cert $(atf_get_srcdir)/server.crt + key $(atf_get_srcdir)/server.key + dh $(atf_get_srcdir)/dh.pem + + mode server + script-security 2 + auth-user-pass-verify /usr/bin/true via-env + topology subnet + + keepalive 2 10 + + management 192.0.2.1 1234 + " + ovpn_start b " + dev tun0 + dev-type tun + + client + + remote 192.0.2.1 + auth-user-pass $(atf_get_srcdir)/user.pass + + ca $(atf_get_srcdir)/ca.crt + cert $(atf_get_srcdir)/client.crt + key $(atf_get_srcdir)/client.key + dh $(atf_get_srcdir)/dh.pem + + keepalive 2 10 + " + + # Give the tunnel time to come up + sleep 10 + + atf_check -s exit:0 -o ignore jexec b ping -c 3 198.51.100.1 + + # We expect the client on 192.0.2.2 + if ! echo "status" | jexec a nc -N 192.0.2.1 1234 | grep 192.0.2.2; then + atf_fail "Client not found in status list!" + fi + + # Now change the client IP + jexec b ifconfig ${l}b 192.0.2.3/24 up + + # And wait for keepalives to trigger the float notification + sleep 5 + + # So the client now has the new address in userspace + if ! echo "status" | jexec a nc -N 192.0.2.1 1234 | grep 192.0.2.3; then + atf_fail "Client not found in status list!" + fi +} + +float_cleanup() +{ + ovpn_cleanup +} + atf_init_test_cases() { atf_add_test_case "4in4" @@ -1165,4 +1420,7 @@ atf_init_test_cases() atf_add_test_case "chacha" atf_add_test_case "gcm_128" atf_add_test_case "destroy_unused" + atf_add_test_case "multihome4" + atf_add_test_case "multihome6" + atf_add_test_case "float" } diff --git a/tests/sys/net/if_vlan.sh b/tests/sys/net/if_vlan.sh index 424eac705b94..8122203337e2 100755 --- a/tests/sys/net/if_vlan.sh +++ b/tests/sys/net/if_vlan.sh @@ -333,6 +333,32 @@ conflict_id_cleanup() } +# If a vlan interface is in a bridge, changing the vlandev to refer to +# a bridge should not be allowed. +atf_test_case "bridge_vlandev" "cleanup" +bridge_vlandev_head() +{ + atf_set descr 'transforming a bridge member vlan into an SVI is not allowed' + atf_set require.user root +} + +bridge_vlandev_body() +{ + vnet_init + vnet_init_bridge + + bridge=$(vnet_mkbridge) + vlan=$(vnet_mkvlan) + + atf_check -s exit:0 ifconfig ${bridge} addm ${vlan} + atf_check -s exit:1 -e ignore ifconfig ${vlan} vlan 1 vlandev ${bridge} +} + +bridge_vlandev_cleanup() +{ + vnet_cleanup +} + atf_init_test_cases() { atf_add_test_case "basic" @@ -343,4 +369,5 @@ atf_init_test_cases() atf_add_test_case "qinq_setflags" atf_add_test_case "bpf_pcp" atf_add_test_case "conflict_id" + atf_add_test_case "bridge_vlandev" } diff --git a/tests/sys/netinet6/addr6.sh b/tests/sys/netinet6/addr6.sh index 38e4bb152240..6fd66d5aa0c7 100755 --- a/tests/sys/netinet6/addr6.sh +++ b/tests/sys/netinet6/addr6.sh @@ -39,7 +39,32 @@ addr6_invalid_addr_cleanup() vnet_cleanup } +atf_test_case "anycast_raw_addr" "cleanup" +anycast_raw_addr_head() +{ + atf_set descr "a raw socket can bind to an anycast address" + atf_set require.user root +} + +anycast_raw_addr_body() +{ + # lo0 needs to be up in the test jail for this test to work + ifconfig lo0 up + + netif=$(ifconfig lo create) + echo $netif >netif + atf_check -s exit:0 ifconfig $netif inet6 2001:db8::1/128 up + atf_check -s exit:0 ifconfig $netif inet6 2001:db8::2/128 anycast + atf_check -s exit:0 -o ignore ping -c1 -S 2001:db8::2 2001:db8::1 +} + +anycast_raw_addr_cleanup() +{ + ifconfig $(cat netif) destroy +} + atf_init_test_cases() { atf_add_test_case "addr6_invalid_addr" + atf_add_test_case "anycast_raw_addr" } diff --git a/tests/sys/netpfil/common/dummynet.sh b/tests/sys/netpfil/common/dummynet.sh index b77b2df84010..66736fbecdb7 100644 --- a/tests/sys/netpfil/common/dummynet.sh +++ b/tests/sys/netpfil/common/dummynet.sh @@ -265,10 +265,6 @@ queue_body() { fw=$1 - if [ $fw = "ipfw" ] && [ "$(atf_config_get ci false)" = "true" ]; then - atf_skip "https://bugs.freebsd.org/264805" - fi - firewall_init $fw dummynet_init $fw diff --git a/tests/sys/netpfil/pf/Makefile b/tests/sys/netpfil/pf/Makefile index 3adaef09ddbd..404d5adfb07a 100644 --- a/tests/sys/netpfil/pf/Makefile +++ b/tests/sys/netpfil/pf/Makefile @@ -58,6 +58,8 @@ ATF_TESTS_SH+= altq \ ATF_TESTS_PYTEST+= frag6.py ATF_TESTS_PYTEST+= header.py ATF_TESTS_PYTEST+= icmp.py +ATF_TESTS_PYTEST+= igmp.py +ATF_TESTS_PYTEST+= mld.py ATF_TESTS_PYTEST+= nat64.py ATF_TESTS_PYTEST+= nat66.py ATF_TESTS_PYTEST+= return.py diff --git a/tests/sys/netpfil/pf/anchor.sh b/tests/sys/netpfil/pf/anchor.sh index b4b52d7a24d6..64ca84b34c3d 100644 --- a/tests/sys/netpfil/pf/anchor.sh +++ b/tests/sys/netpfil/pf/anchor.sh @@ -350,9 +350,9 @@ nat_body() jexec alcatraz pfctl -sn -a "foo/bar" jexec alcatraz pfctl -sn -a "foo/baz" - atf_check -s exit:0 -o match:"nat log on epair0a inet from 192.0.2.0/24 to any port = domain -> 192.0.2.1" \ + atf_check -s exit:0 -o match:"nat log on ${epair}a inet from 192.0.2.0/24 to any port = domain -> 192.0.2.1" \ jexec alcatraz pfctl -sn -a "*" - atf_check -s exit:0 -o match:"rdr on epair0a inet proto tcp from any to any port = echo -> 127.0.0.1 port 7" \ + atf_check -s exit:0 -o match:"rdr on ${epair}a inet proto tcp from any to any port = echo -> 127.0.0.1 port 7" \ jexec alcatraz pfctl -sn -a "*" } @@ -437,6 +437,62 @@ quick_cleanup() pft_cleanup } +atf_test_case "recursive_flush" "cleanup" +recursive_flush_head() +{ + atf_set descr 'Test recursive flushing of rules' + atf_set require.user root +} + +recursive_flush_body() +{ + pft_init + + epair=$(vnet_mkepair) + vnet_mkjail alcatraz ${epair}a + + ifconfig ${epair}b 192.0.2.2/24 up + jexec alcatraz ifconfig ${epair}a 192.0.2.1/24 up + + # Sanity check + atf_check -s exit:0 -o ignore ping -c 1 192.0.2.1 + + jexec alcatraz pfctl -e + pft_set_rules alcatraz \ + "block" \ + "anchor \"foo\" {\n\ + pass\n\ + }" + + # We can ping thanks to the pass rule in foo + atf_check -s exit:0 -o ignore ping -c 1 192.0.2.1 + + # Only reset the main rules. I.e. not a recursive flush + pft_set_rules alcatraz \ + "block" \ + "anchor \"foo\"" + + # "foo" still has the pass rule, so this works + jexec alcatraz pfctl -a "*" -sr + atf_check -s exit:0 -o ignore ping -c 1 192.0.2.1 + + # Now do a recursive flush + atf_check -s exit:0 -e ignore -o ignore \ + jexec alcatraz pfctl -a "*" -Fr + pft_set_rules alcatraz \ + "block" \ + "anchor \"foo\"" + + # So this fails + jexec alcatraz pfctl -a "*" -sr + atf_check -s exit:2 -o ignore ping -c 1 192.0.2.1 +} + +recursive_flush_cleanup() +{ + pft_cleanup +} + atf_init_test_cases() { atf_add_test_case "pr183198" @@ -450,4 +506,5 @@ atf_init_test_cases() atf_add_test_case "nat" atf_add_test_case "include" atf_add_test_case "quick" + atf_add_test_case "recursive_flush" } diff --git a/tests/sys/netpfil/pf/debug.sh b/tests/sys/netpfil/pf/debug.sh index 18a7febfbb5b..404d37ab8932 100644 --- a/tests/sys/netpfil/pf/debug.sh +++ b/tests/sys/netpfil/pf/debug.sh @@ -50,7 +50,57 @@ basic_cleanup() pft_cleanup } +atf_test_case "reset" "cleanup" +reset_head() +{ + atf_set descr 'Test resetting debug level' + atf_set require.user root +} + +reset_body() +{ + pft_init + + vnet_mkjail debug + + # Default is Urgent + atf_check -s exit:0 -o match:'Debug: Urgent' \ + jexec debug pfctl -sa + state_limit=$(jexec debug pfctl -sa | grep 'states.*hard limit' | awk '{ print $4; }') + + # Change defaults + pft_set_rules debug \ + "set limit states 42" + atf_check -s exit:0 -e ignore \ + jexec debug pfctl -x loud + + atf_check -s exit:0 -o match:'Debug: Loud' \ + jexec debug pfctl -sa + new_state_limit=$(jexec debug pfctl -sa | grep 'states.*hard limit' | awk '{ print $4; }') + if [ $state_limit -eq $new_state_limit ]; then + jexec debug pfctl -sa + atf_fail "Failed to change state limit" + fi + + # Reset + atf_check -s exit:0 -o ignore -e ignore \ + jexec debug pfctl -FR + atf_check -s exit:0 -o match:'Debug: Urgent' \ + jexec debug pfctl -sa + new_state_limit=$(jexec debug pfctl -sa | grep 'states.*hard limit' | awk '{ print $4; }') + if [ $state_limit -ne $new_state_limit ]; then + jexec debug pfctl -sa + atf_fail "Failed to reset state limit" + fi +} + +reset_cleanup() +{ + pft_cleanup +} + atf_init_test_cases() { atf_add_test_case "basic" + atf_add_test_case "reset" } diff --git a/tests/sys/netpfil/pf/forward.sh b/tests/sys/netpfil/pf/forward.sh index 5d7d48a5dd9a..e9539bc9d278 100644 --- a/tests/sys/netpfil/pf/forward.sh +++ b/tests/sys/netpfil/pf/forward.sh @@ -101,10 +101,6 @@ v6_body() { pft_init - if [ "$(atf_config_get ci false)" = "true" ]; then - atf_skip "https://bugs.freebsd.org/260460" - fi - epair_send=$(vnet_mkepair) epair_recv=$(vnet_mkepair) diff --git a/tests/sys/netpfil/pf/header.py b/tests/sys/netpfil/pf/header.py index 6832cfe6d42b..a5e36bc85d14 100644 --- a/tests/sys/netpfil/pf/header.py +++ b/tests/sys/netpfil/pf/header.py @@ -53,10 +53,9 @@ class TestHeader(VnetTestTemplate): def test_too_many(self): "Verify that we drop packets with silly numbers of headers." - sendif = self.vnet.iface_alias_map["if1"].name + sendif = self.vnet.iface_alias_map["if1"] recvif = self.vnet.iface_alias_map["if2"].name - gw_mac = ToolsHelper.get_output("/sbin/ifconfig %s ether | awk '/ether/ { print $2; }'" % sendif) - gw_mac = re.sub("0a$", "0b", gw_mac) + gw_mac = sendif.epairb.ether ToolsHelper.print_output("/sbin/route add default 192.0.2.1") @@ -67,7 +66,7 @@ class TestHeader(VnetTestTemplate): pkt = sp.Ether(dst=gw_mac) \ / sp.IP(dst="198.51.100.3") \ / sp.ICMP(type='echo-request') - s = DelayedSend(pkt, sendif) + s = DelayedSend(pkt, sendif.name) reply = sp.sniff(iface=recvif, timeout=3) print(reply) @@ -89,7 +88,7 @@ class TestHeader(VnetTestTemplate): pkt = pkt / sp.AH(nh=51, payloadlen=1) pkt = pkt / sp.AH(nh=1, payloadlen=1) / sp.ICMP(type='echo-request') - s = DelayedSend(pkt, sendif) + s = DelayedSend(pkt, sendif.name) reply = sp.sniff(iface=recvif, timeout=3) print(reply) found = False @@ -109,7 +108,7 @@ class TestHeader(VnetTestTemplate): pkt = pkt / sp.AH(nh=51, payloadlen=1) pkt = pkt / sp.AH(nh=1, payloadlen=1) / sp.ICMP(type='echo-request') - s = DelayedSend(pkt, sendif) + s = DelayedSend(pkt, sendif.name) reply = sp.sniff(iface=recvif, timeout=3) print(reply) @@ -148,10 +147,10 @@ class TestHeader6(VnetTestTemplate): "Verify that we drop packets with silly numbers of headers." ToolsHelper.print_output("/sbin/ifconfig") - sendif = self.vnet.iface_alias_map["if1"].name + sendif = self.vnet.iface_alias_map["if1"] recvif = self.vnet.iface_alias_map["if2"].name - our_mac = ToolsHelper.get_output("/sbin/ifconfig %s ether | awk '/ether/ { print $2; }'" % sendif) - gw_mac = re.sub("0a$", "0b", our_mac) + our_mac = sendif.ether + gw_mac = sendif.epairb.ether ToolsHelper.print_output("/sbin/route -6 add default 2001:db8::1") @@ -162,7 +161,7 @@ class TestHeader6(VnetTestTemplate): pkt = sp.Ether(src=our_mac, dst=gw_mac) \ / sp.IPv6(src="2001:db8::2", dst="2001:db8:1::3") \ / sp.ICMPv6EchoRequest() - s = DelayedSend(pkt, sendif) + s = DelayedSend(pkt, sendif.name) reply = sp.sniff(iface=recvif, timeout=3) print(reply) @@ -182,7 +181,7 @@ class TestHeader6(VnetTestTemplate): for i in range(0, 18): pkt = pkt / sp.AH(nh=51, payloadlen=1) pkt = pkt / sp.AH(nh=58, payloadlen=1) / sp.ICMPv6EchoRequest() - s = DelayedSend(pkt, sendif) + s = DelayedSend(pkt, sendif.name) reply = sp.sniff(iface=recvif, timeout=3) print(reply) @@ -202,7 +201,7 @@ class TestHeader6(VnetTestTemplate): for i in range(0, 19): pkt = pkt / sp.AH(nh=51, payloadlen=1) pkt = pkt / sp.AH(nh=58, payloadlen=1) / sp.ICMPv6EchoRequest() - s = DelayedSend(pkt, sendif) + s = DelayedSend(pkt, sendif.name) reply = sp.sniff(iface=recvif, timeout=3) print(reply) diff --git a/tests/sys/netpfil/pf/icmp.py b/tests/sys/netpfil/pf/icmp.py index 83096886691e..c5e945d60e99 100644 --- a/tests/sys/netpfil/pf/icmp.py +++ b/tests/sys/netpfil/pf/icmp.py @@ -91,10 +91,10 @@ class TestICMP(VnetTestTemplate): def test_inner_match(self): vnet = self.vnet_map["vnet1"] dst_vnet = self.vnet_map["vnet3"] - sendif = vnet.iface_alias_map["if1"].name + sendif = vnet.iface_alias_map["if1"] - our_mac = ToolsHelper.get_output("/sbin/ifconfig %s ether | awk '/ether/ { print $2; }'" % sendif) - dst_mac = re.sub("0a$", "0b", our_mac) + our_mac = sendif.ether + dst_mac = sendif.epairb.ether # Import in the correct vnet, so at to not confuse Scapy import scapy.all as sp @@ -111,7 +111,7 @@ class TestICMP(VnetTestTemplate): / sp.IP(src="192.0.2.2", dst="198.51.100.2") \ / sp.ICMP(type='echo-request') \ / "PAYLOAD" - sp.sendp(pkt, sendif, verbose=False) + sp.sendp(pkt, sendif.name, verbose=False) # Now try to pass an ICMP error message piggy-backing on that state, but # use a different source address @@ -120,7 +120,7 @@ class TestICMP(VnetTestTemplate): / sp.ICMP(type='dest-unreach') \ / sp.IP(src="198.51.100.2", dst="192.0.2.2") \ / sp.ICMP(type='echo-reply') - sp.sendp(pkt, sendif, verbose=False) + sp.sendp(pkt, sendif.name, verbose=False) try: rcvd = self.wait_object(dst_vnet.pipe, timeout=1) @@ -136,8 +136,7 @@ class TestICMP(VnetTestTemplate): / sp.ICMP(type='echo-request') \ / sp.raw(bytes.fromhex('f0') * payload_size) - p = sp.sr1(packet, iface=self.vnet.iface_alias_map["if1"].name, - timeout=3) + p = sp.sr1(packet, timeout=3) p.show() ip = p.getlayer(sp.IP) @@ -176,6 +175,22 @@ class TestICMP(VnetTestTemplate): self.check_icmp_echo(sp, 1464) self.check_icmp_echo(sp, 1468) + @pytest.mark.require_user("root") + @pytest.mark.require_progs(["scapy"]) + def test_truncated_opts(self): + ToolsHelper.print_output("/sbin/route add default 192.0.2.1") + + # Import in the correct vnet, so at to not confuse Scapy + import scapy.all as sp + + packet = sp.IP(dst="198.51.100.2", flags="DF") \ + / sp.ICMP(type='dest-unreach', length=108) \ + / sp.IP(src="198.51.100.2", dst="192.0.2.2", len=1000, \ + ihl=(120 >> 2), options=[ \ + sp.IPOption_Security(length=100)]) + packet.show() + sp.sr1(packet, timeout=3) + class TestICMP_NAT(VnetTestTemplate): REQUIRED_MODULES = [ "pf" ] TOPOLOGY = { diff --git a/tests/sys/netpfil/pf/igmp.py b/tests/sys/netpfil/pf/igmp.py new file mode 100644 index 000000000000..b339a2825082 --- /dev/null +++ b/tests/sys/netpfil/pf/igmp.py @@ -0,0 +1,95 @@ +# +# SPDX-License-Identifier: BSD-2-Clause +# +# Copyright (c) 2025 Rubicon Communications, LLC (Netgate) +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. + +import pytest +from utils import DelayedSend +from atf_python.sys.net.tools import ToolsHelper +from atf_python.sys.net.vnet import VnetTestTemplate + +class TestIGMP(VnetTestTemplate): + REQUIRED_MODULES = [ "pf" ] + TOPOLOGY = { + "vnet1": {"ifaces": ["if1"]}, + "vnet2": {"ifaces": ["if1"]}, + "if1": {"prefixes4": [("192.0.2.2/24", "192.0.2.1/24")]}, + } + + def vnet2_handler(self, vnet): + ifname = vnet.iface_alias_map["if1"].name + ToolsHelper.print_output("/sbin/pfctl -e") + ToolsHelper.pf_rules([ + "pass", + ]) + ToolsHelper.print_output("/sbin/pfctl -x loud") + ToolsHelper.print_output("echo \"j 230.0.0.1 %s\ns 3600\nq\" | /usr/sbin/mtest" % ifname) + + def find_igmp_reply(self, pkt, ifname): + pkt.show() + s = DelayedSend(pkt) + + found = False + packets = self.sp.sniff(iface=ifname, timeout=5) + for r in packets: + r.show() + igmp = r.getlayer(self.sc.igmp.IGMP) + if not igmp: + continue + igmp.show() + if not igmp.gaddr == "230.0.0.1": + continue + found = True + return found + + @pytest.mark.require_user("root") + @pytest.mark.require_progs(["scapy"]) + def test_ip_opts(self): + """Verify that we allow IGMP packets with IP options""" + ifname = self.vnet.iface_alias_map["if1"].name + + # Import in the correct vnet, so at to not confuse Scapy + import scapy.all as sp + import scapy.contrib as sc + import scapy.contrib.igmp + self.sp = sp + self.sc = sc + + # We allow IGMP packets with the router alert option + pkt = sp.IP(dst="224.0.0.1%%%s" % ifname, ttl=1, + options=[sp.IPOption_Router_Alert()]) \ + / sc.igmp.IGMP(type=0x11, mrcode=1) + assert self.find_igmp_reply(pkt, ifname) + + # But not with other options + pkt = sp.IP(dst="224.0.0.1%%%s" % ifname, ttl=1, + options=[sp.IPOption_NOP()]) \ + / sc.igmp.IGMP(type=0x11, mrcode=1) + assert not self.find_igmp_reply(pkt, ifname) + + # Or with the wrong TTL + pkt = sp.IP(dst="224.0.0.1%%%s" % ifname, ttl=2, + options=[sp.IPOption_Router_Alert()]) \ + / sc.igmp.IGMP(type=0x11, mrcode=1) + assert not self.find_igmp_reply(pkt, ifname) diff --git a/tests/sys/netpfil/pf/ioctl/validation.c b/tests/sys/netpfil/pf/ioctl/validation.c index 1ce8999dcb91..18fafe11c6ab 100644 --- a/tests/sys/netpfil/pf/ioctl/validation.c +++ b/tests/sys/netpfil/pf/ioctl/validation.c @@ -32,6 +32,7 @@ #include <net/if.h> #include <net/pfvar.h> +#include <errno.h> #include <fcntl.h> #include <stdio.h> @@ -893,6 +894,39 @@ ATF_TC_CLEANUP(rpool_mtx2, tc) COMMON_CLEANUP(); } +ATF_TC_WITH_CLEANUP(natlook); +ATF_TC_HEAD(natlook, tc) +{ + atf_tc_set_md_var(tc, "require.user", "root"); +} + +ATF_TC_BODY(natlook, tc) +{ + struct pfioc_natlook nl = { 0 }; + + COMMON_HEAD(); + + nl.af = AF_INET; + nl.proto = IPPROTO_ICMP; + nl.saddr.v4.s_addr = 0x01020304; + nl.daddr.v4.s_addr = 0x05060708; + + /* Invalid direction */ + nl.direction = 42; + + ATF_CHECK_ERRNO(EINVAL, ioctl(dev, DIOCNATLOOK, &nl) == -1); + + /* Invalid af */ + nl.direction = PF_IN; + nl.af = 99; + + ATF_CHECK_ERRNO(EAFNOSUPPORT, ioctl(dev, DIOCNATLOOK, &nl) == -1); +} + +ATF_TC_CLEANUP(natlook, tc) +{ + COMMON_CLEANUP(); +} ATF_TP_ADD_TCS(tp) { @@ -918,6 +952,7 @@ ATF_TP_ADD_TCS(tp) ATF_TP_ADD_TC(tp, tag); ATF_TP_ADD_TC(tp, rpool_mtx); ATF_TP_ADD_TC(tp, rpool_mtx2); + ATF_TP_ADD_TC(tp, natlook); return (atf_no_error()); } diff --git a/tests/sys/netpfil/pf/killstate.sh b/tests/sys/netpfil/pf/killstate.sh index 447a4e388f11..0d98db822535 100644 --- a/tests/sys/netpfil/pf/killstate.sh +++ b/tests/sys/netpfil/pf/killstate.sh @@ -117,10 +117,6 @@ v6_body() { pft_init - if [ "$(atf_config_get ci false)" = "true" ]; then - atf_skip "https://bugs.freebsd.org/260458" - fi - epair=$(vnet_mkepair) ifconfig ${epair}a inet6 2001:db8::1/64 up no_dad diff --git a/tests/sys/netpfil/pf/mbuf.sh b/tests/sys/netpfil/pf/mbuf.sh index d845f793a969..e3f138bb73b9 100644 --- a/tests/sys/netpfil/pf/mbuf.sh +++ b/tests/sys/netpfil/pf/mbuf.sh @@ -105,6 +105,12 @@ inet6_in_mbuf_len_body() epair=$(vnet_mkepair) ifconfig ${epair}a inet6 2001:db8::1/64 up no_dad + # Ensure we don't unintentionally send MLD packets to alcatraz + pfctl -e + echo "block + pass out inet6 proto icmp6 icmp6-type { neighbrsol, neighbradv, echoreq, echorep } + " | pfctl -g -f - + # Set up a simple jail with one interface vnet_mkjail alcatraz ${epair}b jexec alcatraz ifconfig ${epair}b inet6 2001:db8::2/64 up no_dad diff --git a/tests/sys/netpfil/pf/mld.py b/tests/sys/netpfil/pf/mld.py new file mode 100644 index 000000000000..d118a34c8a7d --- /dev/null +++ b/tests/sys/netpfil/pf/mld.py @@ -0,0 +1,95 @@ +# +# SPDX-License-Identifier: BSD-2-Clause +# +# Copyright (c) 2025 Rubicon Communications, LLC (Netgate) +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. + +import pytest +from utils import DelayedSend +from atf_python.sys.net.tools import ToolsHelper +from atf_python.sys.net.vnet import VnetTestTemplate + +class TestMLD(VnetTestTemplate): + REQUIRED_MODULES = [ "pf" ] + TOPOLOGY = { + "vnet1": {"ifaces": ["if1"]}, + "vnet2": {"ifaces": ["if1"]}, + "if1": {"prefixes6": [("2001:db8::2/64", "2001:db8::1/64")]}, + } + + def vnet2_handler(self, vnet): + ifname = vnet.iface_alias_map["if1"].name + #ToolsHelper.print_output("/sbin/pfctl -e") + ToolsHelper.pf_rules([ + "pass", + ]) + ToolsHelper.print_output("/sbin/pfctl -x loud") + #ToolsHelper.print_output("echo \"j 230.0.0.1 %s\ns 3600\nq\" | /usr/sbin/mtest" % ifname) + + def find_mld_reply(self, pkt, ifname): + pkt.show() + s = DelayedSend(pkt) + + found = False + packets = self.sp.sniff(iface=ifname, timeout=5) + for r in packets: + r.show() + mld = r.getlayer(self.sp.ICMPv6MLReport2) + if not mld: + continue + mld.show() + found = True + return found + + @pytest.mark.require_user("root") + @pytest.mark.require_progs(["scapy"]) + def test_router_alert(self): + """Verify that we allow MLD packets with router alert extension header""" + ifname = self.vnet.iface_alias_map["if1"].name + #ToolsHelper.print_output("/sbin/ifconfig %s inet6 -ifdisable" % ifname) + ToolsHelper.print_output("/sbin/ifconfig") + + # Import in the correct vnet, so at to not confuse Scapy + import scapy.all as sp + import scapy.contrib as sc + import scapy.contrib.igmp + self.sp = sp + self.sc = sc + + # A correct MLD query gets a reply + pkt = sp.IPv6(src="fe80::1%%%s" % ifname, dst="ff02::1", hlim=1) \ + / sp.RouterAlert(value=0) \ + / sp.ICMPv6MLQuery2() + assert self.find_mld_reply(pkt, ifname) + + # The wrong extension header does not + pkt = sp.IPv6(src="fe80::1%%%s" % ifname, dst="ff02::1", hlim=1) \ + / sp.IPv6ExtHdrRouting() \ + / sp.ICMPv6MLQuery2() + assert not self.find_mld_reply(pkt, ifname) + + # Neither does an incorrect hop limit + pkt = sp.IPv6(src="fe80::1%%%s" % ifname, dst="ff02::1", hlim=2) \ + / sp.RouterAlert(value=0) \ + / sp.ICMPv6MLQuery2() + assert not self.find_mld_reply(pkt, ifname) diff --git a/tests/sys/netpfil/pf/nat.sh b/tests/sys/netpfil/pf/nat.sh index f1fdf6405d97..16c981f97399 100644 --- a/tests/sys/netpfil/pf/nat.sh +++ b/tests/sys/netpfil/pf/nat.sh @@ -777,6 +777,38 @@ binat_match_cleanup() kill $(cat ${PWD}/inetd_tester.pid) } +atf_test_case "empty_pool" "cleanup" +empty_pool_head() +{ + atf_set descr 'NAT with empty pool' + atf_set require.user root +} + +empty_pool_body() +{ + pft_init + setup_router_server_ipv6 + + + pft_set_rules router \ + "block" \ + "pass inet6 proto icmp6 icmp6-type { neighbrsol, neighbradv }" \ + "pass in on ${epair_tester}b" \ + "pass out on ${epair_server}a inet6 from any to ${net_server_host_server} nat-to <nonexistent>" \ + + # pf_map_addr_sn() won't be able to pick a target address, because + # the table used in redireciton pool is empty. Packet will not be + # forwarded, error counter will be increased. + ping_server_check_reply exit:1 + # Ignore warnings about not-loaded ALTQ + atf_check -o "match:map-failed +1 +" -x "jexec router pfctl -qvvsi 2> /dev/null" +} + +empty_pool_cleanup() +{ + pft_cleanup +} + atf_init_test_cases() { atf_add_test_case "exhaust" @@ -794,4 +826,5 @@ atf_init_test_cases() atf_add_test_case "nat_match" atf_add_test_case "binat_compat" atf_add_test_case "binat_match" + atf_add_test_case "empty_pool" } diff --git a/tests/sys/netpfil/pf/nat64.py b/tests/sys/netpfil/pf/nat64.py index adae2489ce5e..a5890fc4a161 100644 --- a/tests/sys/netpfil/pf/nat64.py +++ b/tests/sys/netpfil/pf/nat64.py @@ -33,7 +33,7 @@ from atf_python.sys.net.tools import ToolsHelper from atf_python.sys.net.vnet import VnetTestTemplate class TestNAT64(VnetTestTemplate): - REQUIRED_MODULES = [ "pf" ] + REQUIRED_MODULES = [ "pf", "pflog" ] TOPOLOGY = { "vnet1": {"ifaces": ["if1"]}, "vnet2": {"ifaces": ["if1", "if2"]}, @@ -92,12 +92,15 @@ class TestNAT64(VnetTestTemplate): def vnet2_handler(self, vnet): ifname = vnet.iface_alias_map["if1"].name + ToolsHelper.print_output("/sbin/sysctl net.inet6.ip6.forwarding=1") ToolsHelper.print_output("/sbin/route add default 192.0.2.2") ToolsHelper.print_output("/sbin/pfctl -e") ToolsHelper.pf_rules([ "pass inet6 proto icmp6", "pass in on %s inet6 af-to inet from 192.0.2.1" % ifname]) + vnet.pipe.send(socket.if_nametoindex("pflog0")) + @pytest.mark.require_user("root") @pytest.mark.require_progs(["scapy"]) def test_tcp_rst(self): @@ -272,3 +275,54 @@ class TestNAT64(VnetTestTemplate): reply = self.common_test_source_addr(packet) icmp = reply.getlayer(sp.ICMPv6EchoRequest) assert icmp + + @pytest.mark.require_user("root") + @pytest.mark.require_progs(["scapy"]) + def test_bad_len(self): + """ + PR 288224: we can panic if the IPv6 plen is longer than the packet length. + """ + ToolsHelper.print_output("/sbin/route -6 add default 2001:db8::1") + import scapy.all as sp + + packet = sp.IPv6(dst="64:ff9b::198.51.100.2", hlim=2, plen=512) \ + / sp.ICMPv6EchoRequest() / sp.Raw("foo") + reply = sp.sr1(packet, timeout=3) + # We don't expect a reply to a corrupted packet + assert not reply + + @pytest.mark.require_user("root") + @pytest.mark.require_progs(["scapy"]) + def test_noip6(self): + """ + PR 288263: link-local target address in icmp6 ADVERT can cause NULL deref + """ + ifname = self.vnet.iface_alias_map["if1"].name + gw_mac = self.vnet.iface_alias_map["if1"].epairb.ether + scopeid = self.wait_object(self.vnet_map["vnet2"].pipe) + ToolsHelper.print_output("/sbin/route -6 add default 2001:db8::1") + + import scapy.all as sp + + pkt = sp.Ether(dst=gw_mac) \ + / sp.IPv6(dst="64:ff9b::203.0.113.2") \ + / sp.ICMPv6ND_NA(tgt="FFA2:%x:2821:125F:1D27:B3B2:3F6F:C43C" % scopeid) + pkt.show() + sp.hexdump(pkt) + s = DelayedSend(pkt, sendif=ifname) + + packets = sp.sniff(iface=ifname, timeout=5) + for r in packets: + r.show() + + # Try scope id that likely doesn't have an interface at all + pkt = sp.Ether(dst=gw_mac) \ + / sp.IPv6(dst="64:ff9b::203.0.113.2") \ + / sp.ICMPv6ND_NA(tgt="FFA2:%x:2821:125F:1D27:B3B2:3F6F:C43C" % 255) + pkt.show() + sp.hexdump(pkt) + s = DelayedSend(pkt, sendif=ifname) + + packets = sp.sniff(iface=ifname, timeout=5) + for r in packets: + r.show() diff --git a/tests/sys/netpfil/pf/pfsync.sh b/tests/sys/netpfil/pf/pfsync.sh index 7f545b43a066..3be4a3024393 100644 --- a/tests/sys/netpfil/pf/pfsync.sh +++ b/tests/sys/netpfil/pf/pfsync.sh @@ -835,6 +835,90 @@ basic_ipv6_cleanup() pfsynct_cleanup } +atf_test_case "rtable" "cleanup" +rtable_head() +{ + atf_set descr 'Test handling of invalid rtableid' + atf_set require.user root +} + +rtable_body() +{ + pfsynct_init + + epair_sync=$(vnet_mkepair) + epair_one=$(vnet_mkepair) + epair_two=$(vnet_mkepair) + + vnet_mkjail one ${epair_one}a ${epair_sync}a + vnet_mkjail two ${epair_two}a ${epair_sync}b + + # pfsync interface + jexec one ifconfig ${epair_sync}a 192.0.2.1/24 up + jexec one ifconfig ${epair_one}a 198.51.100.1/24 up + jexec one ifconfig pfsync0 \ + syncdev ${epair_sync}a \ + maxupd 1 \ + up + jexec two ifconfig ${epair_two}a 198.51.100.1/24 up + jexec two ifconfig ${epair_sync}b 192.0.2.2/24 up + jexec two ifconfig pfsync0 \ + syncdev ${epair_sync}b \ + maxupd 1 \ + up + + # Make life easy, give ${epair_two}a the same mac addrss as ${epair_one}a + mac=$(jexec one ifconfig ${epair_one}a | awk '/ether/ { print($2); }') + jexec two ifconfig ${epair_two}a ether ${mac} + + # Enable pf! + jexec one /sbin/sysctl net.fibs=8 + jexec one pfctl -e + pft_set_rules one \ + "set skip on ${epair_sync}a" \ + "pass rtable 3 keep state" + # No extra fibs in two + jexec two pfctl -e + pft_set_rules two \ + "set skip on ${epair_sync}b" \ + "pass keep state" + + ifconfig ${epair_one}b 198.51.100.254/24 up + ifconfig ${epair_two}b 198.51.100.253/24 up + + # Create a new state + env PYTHONPATH=${common_dir} \ + ${common_dir}/pft_ping.py \ + --sendif ${epair_one}b \ + --fromaddr 198.51.100.254 \ + --to 198.51.100.1 \ + --recvif ${epair_one}b + + # Now + jexec one pfctl -ss -vv + sleep 2 + + # Now try to use that state on jail two + env PYTHONPATH=${common_dir} \ + ${common_dir}/pft_ping.py \ + --sendif ${epair_two}b \ + --fromaddr 198.51.100.254 \ + --to 198.51.100.1 \ + --recvif ${epair_two}b + + echo one + jexec one pfctl -ss -vv + jexec one pfctl -sr -vv + echo two + jexec two pfctl -ss -vv + jexec two pfctl -sr -vv +} + +rtable_cleanup() +{ + pfsynct_cleanup +} + route_to_common_head() { pfsync_version=$1 @@ -1134,6 +1218,7 @@ atf_init_test_cases() atf_add_test_case "timeout" atf_add_test_case "basic_ipv6_unicast" atf_add_test_case "basic_ipv6" + atf_add_test_case "rtable" atf_add_test_case "route_to_1301" atf_add_test_case "route_to_1301_bad_ruleset" atf_add_test_case "route_to_1301_bad_rpool" diff --git a/tests/sys/netpfil/pf/rdr.sh b/tests/sys/netpfil/pf/rdr.sh index 4c08b4973891..f7c920bbfa8f 100644 --- a/tests/sys/netpfil/pf/rdr.sh +++ b/tests/sys/netpfil/pf/rdr.sh @@ -142,7 +142,7 @@ tcp_v6_pass_body() { tcp_v6_setup # Sets ${epair_…} variables tcp_v6_common \ - "rdr on ${epair_one}a proto tcp from any to any port 80 -> 2001:db8:b::2 port 8000" + "pass in on ${epair_one}a proto tcp from any to any port 80 rdr-to 2001:db8:b::2 port 8000" } tcp_v6_pass_cleanup() diff --git a/tests/sys/netpfil/pf/route_to.sh b/tests/sys/netpfil/pf/route_to.sh index 5c0d355b8ea1..fd1653cce311 100644 --- a/tests/sys/netpfil/pf/route_to.sh +++ b/tests/sys/netpfil/pf/route_to.sh @@ -859,6 +859,121 @@ ttl_cleanup() pft_cleanup } + +atf_test_case "empty_pool" "cleanup" +empty_pool_head() +{ + atf_set descr 'Route-to with empty pool' + atf_set require.user root +} + +empty_pool_body() +{ + pft_init + setup_router_server_ipv6 + + + pft_set_rules router \ + "block" \ + "pass inet6 proto icmp6 icmp6-type { neighbrsol, neighbradv }" \ + "pass in on ${epair_tester}b route-to (${epair_server}a <nonexistent>) inet6 from any to ${net_server_host_server}" \ + "pass out on ${epair_server}a" + + # pf_map_addr_sn() won't be able to pick a target address, because + # the table used in redireciton pool is empty. Packet will not be + # forwarded, error counter will be increased. + ping_server_check_reply exit:1 + # Ignore warnings about not-loaded ALTQ + atf_check -o "match:map-failed +1 +" -x "jexec router pfctl -qvvsi 2> /dev/null" +} + +empty_pool_cleanup() +{ + pft_cleanup +} + + +atf_test_case "table_loop" "cleanup" + +table_loop_head() +{ + atf_set descr 'Check that iterating over tables poperly loops' + atf_set require.user root +} + +table_loop_body() +{ + setup_router_server_nat64 + + # Clients will connect from another network behind the router. + # This allows for using multiple source addresses. + jexec router route add -6 ${net_clients_6}::/${net_clients_6_mask} ${net_tester_6_host_tester} + jexec router route add ${net_clients_4}.0/${net_clients_4_mask} ${net_tester_4_host_tester} + + # The servers are reachable over additional IP addresses for + # testing of tables and subnets. The addresses are noncontinougnus + # for pf_map_addr() counter tests. + for i in 0 1 4 5; do + a1=$((24 + i)) + jexec server1 ifconfig ${epair_server1}b inet ${net_server1_4}.${a1}/32 alias + jexec server1 ifconfig ${epair_server1}b inet6 ${net_server1_6}::42:${i}/128 alias + a2=$((40 + i)) + jexec server2 ifconfig ${epair_server2}b inet ${net_server2_4}.${a2}/32 alias + jexec server2 ifconfig ${epair_server2}b inet6 ${net_server2_6}::42:${i}/128 alias + done + + jexec router pfctl -e + pft_set_rules router \ + "set debug loud" \ + "set reassemble yes" \ + "set state-policy if-bound" \ + "table <rt_targets_1> { ${net_server1_6}::42:4/127 ${net_server1_6}::42:0/127 }" \ + "table <rt_targets_2> { ${net_server2_6}::42:4/127 }" \ + "pass in on ${epair_tester}b \ + route-to { \ + (${epair_server1}a <rt_targets_1>) \ + (${epair_server2}a <rt_targets_2_empty>) \ + (${epair_server2}a <rt_targets_2>) \ + } \ + inet6 proto tcp \ + keep state" + + # Both hosts of the pool are tables. Each table gets iterated over once, + # then the pool iterates to the next host, which is also iterated, + # then the pool loops back to the 1st host. If an empty table is found, + # it is skipped. Unless that's the only table, that is tested by + # the "empty_pool" test. + for port in $(seq 1 7); do + port=$((4200 + port)) + atf_check -s exit:0 ${common_dir}/pft_ping.py \ + --sendif ${epair_tester}a --replyif ${epair_tester}a \ + --fromaddr ${net_clients_6}::1 --to ${host_server_6} \ + --ping-type=tcp3way --send-sport=${port} + done + + states=$(mktemp) || exit 1 + jexec router pfctl -qvvss | normalize_pfctl_s > $states + cat $states + + for state_regexp in \ + "${epair_tester}b tcp ${host_server_6}\[9\] <- ${net_clients_6}::1\[4201\] .* route-to: ${net_server1_6}::42:0@${epair_server1}a" \ + "${epair_tester}b tcp ${host_server_6}\[9\] <- ${net_clients_6}::1\[4202\] .* route-to: ${net_server1_6}::42:1@${epair_server1}a" \ + "${epair_tester}b tcp ${host_server_6}\[9\] <- ${net_clients_6}::1\[4203\] .* route-to: ${net_server1_6}::42:4@${epair_server1}a" \ + "${epair_tester}b tcp ${host_server_6}\[9\] <- ${net_clients_6}::1\[4204\] .* route-to: ${net_server1_6}::42:5@${epair_server1}a" \ + "${epair_tester}b tcp ${host_server_6}\[9\] <- ${net_clients_6}::1\[4205\] .* route-to: ${net_server2_6}::42:4@${epair_server2}a" \ + "${epair_tester}b tcp ${host_server_6}\[9\] <- ${net_clients_6}::1\[4206\] .* route-to: ${net_server2_6}::42:5@${epair_server2}a" \ + "${epair_tester}b tcp ${host_server_6}\[9\] <- ${net_clients_6}::1\[4207\] .* route-to: ${net_server1_6}::42:0@${epair_server1}a" \ + ; do + grep -qE "${state_regexp}" $states || atf_fail "State not found for '${state_regexp}'" + done +} + +table_loop_cleanup() +{ + pft_cleanup +} + + atf_init_test_cases() { atf_add_test_case "v4" @@ -877,4 +992,6 @@ atf_init_test_cases() atf_add_test_case "dummynet_double" atf_add_test_case "sticky" atf_add_test_case "ttl" + atf_add_test_case "empty_pool" + atf_add_test_case "table_loop" } diff --git a/tests/sys/netpfil/pf/set_tos.sh b/tests/sys/netpfil/pf/set_tos.sh index 75b96edbab6e..842377ee97c6 100644 --- a/tests/sys/netpfil/pf/set_tos.sh +++ b/tests/sys/netpfil/pf/set_tos.sh @@ -129,10 +129,6 @@ v6_body() { pft_init - if [ "$(atf_config_get ci false)" = "true" ]; then - atf_skip "https://bugs.freebsd.org/260459" - fi - epair=$(vnet_mkepair) ifconfig ${epair}a inet6 add 2001:db8:192::1 vnet_mkjail alcatraz ${epair}b diff --git a/tests/sys/netpfil/pf/table.sh b/tests/sys/netpfil/pf/table.sh index 78320375db7c..5e5fccdaca20 100644 --- a/tests/sys/netpfil/pf/table.sh +++ b/tests/sys/netpfil/pf/table.sh @@ -582,6 +582,34 @@ anchor_cleanup() pft_cleanup } +atf_test_case "flush" "cleanup" +flush_head() +{ + atf_set descr 'Test flushing addresses from tables' + atf_set require.user root +} + +flush_body() +{ + pft_init + + vnet_mkjail alcatraz + + atf_check -s exit:0 -e match:"1/1 addresses added." \ + jexec alcatraz pfctl -t foo -T add 1.2.3.4 + atf_check -s exit:0 -o match:" 1.2.3.4" \ + jexec alcatraz pfctl -t foo -T show + atf_check -s exit:0 -e match:"1 addresses deleted." \ + jexec alcatraz pfctl -t foo -T flush + atf_check -s exit:0 -o not-match:"1.2.3.4" \ + jexec alcatraz pfctl -t foo -T show +} + +flush_cleanup() +{ + pft_cleanup +} + atf_init_test_cases() { atf_add_test_case "v4_counters" @@ -596,4 +624,5 @@ atf_init_test_cases() atf_add_test_case "pr259689" atf_add_test_case "precreate" atf_add_test_case "anchor" + atf_add_test_case "flush" } diff --git a/tests/sys/netpfil/pf/utils.subr b/tests/sys/netpfil/pf/utils.subr index 6af10e80390d..3f8d437920f9 100644 --- a/tests/sys/netpfil/pf/utils.subr +++ b/tests/sys/netpfil/pf/utils.subr @@ -274,6 +274,107 @@ setup_router_server_ipv6() jexec server inetd -p ${PWD}/inetd.pid $inetd_conf } +# Create a router and 2 server jails for nat64 and rfc5549 test cases. +# The router is connected to servers, both are dual-stack, and to the +# tester jail. All links are dual stack. +setup_router_server_nat64() +{ + pft_init + + epair_tester=$(vnet_mkepair) + epair_server1=$(vnet_mkepair) + epair_server2=$(vnet_mkepair) + + # Funny how IPv4 address space is to small to even assign nice /24 + # prefixes on all needed networks. On IPv6 we have a separate /64 for + # each link, loopback server, and client/SNAT pool. On IPv4 we must + # use small /28 prefixes, so even though we define all networks + # as variables we can't easily use them in tests if additional addresses + # are needed. + + # IP addresses which can be used by the tester jail. + # Can be used as SNAT or as source with pft_ping.py. It is up to + # the test code to make them accessible from router. + net_clients_4=203.0.113 + net_clients_4_mask=24 + net_clients_6=2001:db8:44 + net_clients_6_mask=64 + + # IP addresses on loopback interfaces of both servers. They can be + # accessed using the route-to targtet. + host_server_4=192.0.2.100 + host_server_6=2001:db8:4203::100 + + net_tester_4=198.51.100 + net_tester_4_mask=28 + net_tester_4_host_router=198.51.100.1 + net_tester_4_host_tester=198.51.100.2 + + net_tester_6=2001:db8:4200 + net_tester_6_mask=64 + net_tester_6_host_router=2001:db8:4200::1 + net_tester_6_host_tester=2001:db8:4200::2 + + net_server1_4=198.51.100 + net_server1_4_mask=28 + net_server1_4_host_router=198.51.100.17 + net_server1_4_host_server=198.51.100.18 + + net_server1_6=2001:db8:4201 + net_server1_6_mask=64 + net_server1_6_host_router=2001:db8:4201::1 + net_server1_6_host_server=2001:db8:4201::2 + + net_server2_4=198.51.100 + net_server2_4_mask=28 + net_server2_4_host_router=198.51.100.33 + net_server2_4_host_server=198.51.100.34 + + net_server2_6=2001:db8:4202 + net_server2_6_mask=64 + net_server2_6_host_router=2001:db8:4202::1 + net_server2_6_host_server=2001:db8:4202::2 + + vnet_mkjail router ${epair_tester}b ${epair_server1}a ${epair_server2}a + jexec router ifconfig ${epair_tester}b inet ${net_tester_4_host_router}/${net_tester_4_mask} up + jexec router ifconfig ${epair_tester}b inet6 ${net_tester_6_host_router}/${net_tester_6_mask} up no_dad + jexec router ifconfig ${epair_server1}a inet ${net_server1_4_host_router}/${net_server1_4_mask} up + jexec router ifconfig ${epair_server1}a inet6 ${net_server1_6_host_router}/${net_server1_6_mask} up no_dad + jexec router ifconfig ${epair_server2}a inet ${net_server2_4_host_router}/${net_server2_4_mask} up + jexec router ifconfig ${epair_server2}a inet6 ${net_server2_6_host_router}/${net_server2_6_mask} up no_dad + jexec router sysctl net.inet.ip.forwarding=1 + jexec router sysctl net.inet6.ip6.forwarding=1 + jexec router pfctl -e + + ifconfig ${epair_tester}a inet ${net_tester_4_host_tester}/${net_tester_4_mask} up + ifconfig ${epair_tester}a inet6 ${net_tester_6_host_tester}/${net_tester_6_mask} up no_dad + route add 0.0.0.0/0 ${net_tester_4_host_router} + route add -6 ::/0 ${net_tester_6_host_router} + + inetd_conf=$(mktemp) + echo "discard stream tcp46 nowait root internal" >> $inetd_conf + + vnet_mkjail server1 ${epair_server1}b + jexec server1 /etc/rc.d/netif start lo0 + jexec server1 ifconfig ${epair_server1}b inet ${net_server1_4_host_server}/${net_server1_4_mask} up + jexec server1 ifconfig ${epair_server1}b inet6 ${net_server1_6_host_server}/${net_server1_6_mask} up no_dad + jexec server1 ifconfig lo0 ${host_server_4}/32 alias + jexec server1 ifconfig lo0 inet6 ${host_server_6}/128 alias + jexec server1 inetd -p ${PWD}/inetd_1.pid $inetd_conf + jexec server1 route add 0.0.0.0/0 ${net_server1_4_host_router} + + jexec server1 route add -6 ::/0 ${net_server1_6_host_router} + vnet_mkjail server2 ${epair_server2}b + jexec server2 /etc/rc.d/netif start lo0 + jexec server2 ifconfig ${epair_server2}b inet ${net_server2_4_host_server}/${net_server2_4_mask} up + jexec server2 ifconfig ${epair_server2}b inet6 ${net_server2_6_host_server}/${net_server2_6_mask} up no_dad + jexec server2 ifconfig lo0 ${host_server_4}/32 alias + jexec server2 ifconfig lo0 inet6 ${host_server_6}/128 alias + jexec server2 inetd -p ${PWD}/inetd_2.pid $inetd_conf + jexec server2 route add 0.0.0.0/0 ${net_server2_4_host_router} + jexec server2 route add -6 ::/0 ${net_server2_6_host_router} +} + # Ping the dummy static NDP target. # Check for pings being forwarded through the router towards the target. ping_dummy_check_request() |