aboutsummaryrefslogtreecommitdiff
path: root/tests/sys
diff options
context:
space:
mode:
Diffstat (limited to 'tests/sys')
-rw-r--r--tests/sys/kern/Makefile1
-rw-r--r--tests/sys/kern/getdirentries_test.c172
-rw-r--r--tests/sys/netpfil/pf/anchor.sh61
-rw-r--r--tests/sys/netpfil/pf/header.py23
-rw-r--r--tests/sys/netpfil/pf/icmp.py10
-rw-r--r--tests/sys/netpfil/pf/ioctl/validation.c35
-rw-r--r--tests/sys/netpfil/pf/pfsync.sh85
7 files changed, 368 insertions, 19 deletions
diff --git a/tests/sys/kern/Makefile b/tests/sys/kern/Makefile
index 26c0013696c7..f2c24ad9dec9 100644
--- a/tests/sys/kern/Makefile
+++ b/tests/sys/kern/Makefile
@@ -18,6 +18,7 @@ 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+= 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/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/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/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..59f2e8190b30 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)
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/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"