aboutsummaryrefslogtreecommitdiff
path: root/tests/sys
diff options
context:
space:
mode:
Diffstat (limited to 'tests/sys')
-rw-r--r--tests/sys/file/Makefile2
-rw-r--r--tests/sys/file/fcntlflags_test.c143
-rw-r--r--tests/sys/netinet/Makefile2
-rw-r--r--tests/sys/netinet/multicast-receive.c130
-rw-r--r--tests/sys/netinet/multicast-send.c (renamed from tests/sys/netinet/sendto-IP_MULTICAST_IF.c)62
-rwxr-xr-x[-rw-r--r--]tests/sys/netinet/multicast.sh126
-rwxr-xr-xtests/sys/netinet6/ndp.sh39
-rw-r--r--tests/sys/netlink/netlink_socket.c18
-rw-r--r--tests/sys/netpfil/pf/Makefile3
-rw-r--r--tests/sys/netpfil/pf/counters.sh831
-rw-r--r--tests/sys/netpfil/pf/mld.py35
-rw-r--r--tests/sys/netpfil/pf/nat.sh2
-rw-r--r--tests/sys/netpfil/pf/nat64.sh30
-rw-r--r--tests/sys/netpfil/pf/rdr.sh58
-rw-r--r--tests/sys/netpfil/pf/utils.subr3
-rw-r--r--tests/sys/sys/bitstring_test.c14
16 files changed, 1365 insertions, 133 deletions
diff --git a/tests/sys/file/Makefile b/tests/sys/file/Makefile
index f80d1b271b85..beb4452359b7 100644
--- a/tests/sys/file/Makefile
+++ b/tests/sys/file/Makefile
@@ -5,7 +5,7 @@ BINDIR= ${TESTSDIR}
ATF_TESTS_C+= path_test
TAP_TESTS_C+= closefrom_test
TAP_TESTS_C+= dup_test
-TAP_TESTS_C+= fcntlflags_test
+ATF_TESTS_C+= fcntlflags_test
TAP_TESTS_SH+= flock_test
PLAIN_TESTS_C+= ftruncate_test
PLAIN_TESTS_C+= newfileops_on_fork_test
diff --git a/tests/sys/file/fcntlflags_test.c b/tests/sys/file/fcntlflags_test.c
index c5026e38c48b..15a18c113c4a 100644
--- a/tests/sys/file/fcntlflags_test.c
+++ b/tests/sys/file/fcntlflags_test.c
@@ -24,85 +24,110 @@
* SUCH DAMAGE.
*/
-#include <sys/cdefs.h>
-
+#include <sys/filio.h>
+#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
+#include <atf-c.h>
+
/*
* O_ACCMODE is currently defined incorrectly. This is what it should be.
* Various code depends on the incorrect value.
*/
#define CORRECT_O_ACCMODE (O_ACCMODE | O_EXEC)
-static int testnum;
-
static void
-subtests(const char *path, int omode, const char *omodetext)
+basic_tests(const char *path, int omode, const char *omodetext)
{
int fd, flags1, flags2, flags3;
fd = open(path, omode);
- if (fd == -1)
- printf("not ok %d - open(\"%s\", %s) failed\n",
- testnum++, path, omodetext);
- else
- printf("ok %d - open(\"%s\", %s) succeeded\n",
- testnum++, path, omodetext);
+ ATF_REQUIRE_MSG(fd != -1, "open(\"%s\", %s) failed: %s", path,
+ omodetext, strerror(errno));
+
flags1 = fcntl(fd, F_GETFL);
- if (flags1 == -1)
- printf("not ok %d - fcntl(F_GETFL) failed\n", testnum++);
- else if ((flags1 & CORRECT_O_ACCMODE) == omode)
- printf("ok %d - fcntl(F_GETFL) gave correct result\n",
- testnum++);
- else
- printf("not ok %d - fcntl(F_GETFL) gave incorrect result "
- "(%#x & %#x != %#x)\n",
- testnum++, flags1, CORRECT_O_ACCMODE, omode);
- if (fcntl(fd, F_SETFL, flags1) == -1)
- printf("not ok %d - fcntl(F_SETFL) same flags failed\n",
- testnum++);
- else
- printf("ok %d - fcntl(F_SETFL) same flags succeeded\n",
- testnum++);
+ ATF_REQUIRE_MSG(flags1 != -1, "fcntl(F_GETFL) (1) failed: %s",
+ strerror(errno));
+ ATF_REQUIRE_INTEQ(omode, flags1 & CORRECT_O_ACCMODE);
+ ATF_REQUIRE((flags1 & O_NONBLOCK) == 0);
+
+ ATF_REQUIRE_MSG(fcntl(fd, F_SETFL, flags1) != -1,
+ "fcntl(F_SETFL) same flags failed: %s", strerror(errno));
+
flags2 = fcntl(fd, F_GETFL);
- if (flags2 == -1)
- printf("not ok %d - fcntl(F_GETFL) failed\n", testnum++);
- else if (flags2 == flags1)
- printf("ok %d - fcntl(F_GETFL) gave same result\n",
- testnum++);
- else
- printf("not ok %d - fcntl(F_SETFL) caused fcntl(F_GETFL) to "
- "change from %#x to %#x\n",
- testnum++, flags1, flags2);
- if (fcntl(fd, F_SETFL, flags2 | O_NONBLOCK) == -1)
- printf("not ok %d - fcntl(F_SETFL) O_NONBLOCK failed\n",
- testnum++);
- else
- printf("ok %d - fcntl(F_SETFL) O_NONBLOCK succeeded\n",
- testnum++);
+ ATF_REQUIRE_MSG(flags2 != -1, "fcntl(F_GETFL) (2) failed: %s",
+ strerror(errno));
+ ATF_REQUIRE_INTEQ(flags1, flags2);
+
+ ATF_REQUIRE_MSG(fcntl(fd, F_SETFL, flags2 | O_NONBLOCK) != -1,
+ "fcntl(F_SETFL) O_NONBLOCK failed: %s", strerror(errno));
+
flags3 = fcntl(fd, F_GETFL);
- if (flags3 == -1)
- printf("not ok %d - fcntl(F_GETFL) failed\n", testnum++);
- else if (flags3 == (flags2 | O_NONBLOCK))
- printf("ok %d - fcntl(F_GETFL) gave expected result\n",
- testnum++);
- else
- printf("not ok %d - fcntl(F_SETFL) gave unexpected result "
- "(%#x != %#x)\n",
- testnum++, flags3, flags2 | O_NONBLOCK);
+ ATF_REQUIRE_MSG(flags3 != -1, "fcntl(F_GETFL) (3) failed: %s",
+ strerror(errno));
+ ATF_REQUIRE_INTEQ(flags2 | O_NONBLOCK, flags3);
+
(void)close(fd);
}
-int
-main(int argc __unused, char **argv __unused)
+ATF_TC_WITHOUT_HEAD(read_only_null);
+ATF_TC_BODY(read_only_null, tc)
{
- printf("1..24\n");
- testnum = 1;
- subtests("/dev/null", O_RDONLY, "O_RDONLY");
- subtests("/dev/null", O_WRONLY, "O_WRONLY");
- subtests("/dev/null", O_RDWR, "O_RDWR");
- subtests("/bin/sh", O_EXEC, "O_EXEC");
- return (0);
+ basic_tests("/dev/null", O_RDONLY, "O_RDONLY");
+}
+
+ATF_TC_WITHOUT_HEAD(write_only_null);
+ATF_TC_BODY(write_only_null, tc)
+{
+ basic_tests("/dev/null", O_WRONLY, "O_WRONLY");
+}
+
+ATF_TC_WITHOUT_HEAD(read_write_null);
+ATF_TC_BODY(read_write_null, tc)
+{
+ basic_tests("/dev/null", O_RDWR, "O_RDWR");
+}
+
+ATF_TC_WITHOUT_HEAD(exec_only_sh);
+ATF_TC_BODY(exec_only_sh, tc)
+{
+ basic_tests("/bin/sh", O_EXEC, "O_EXEC");
+}
+
+ATF_TC_WITHOUT_HEAD(fioasync_dev_null);
+ATF_TC_BODY(fioasync_dev_null, tc)
+{
+ int fd, flags1, flags2, val;
+
+ fd = open("/dev/null", O_RDONLY);
+ ATF_REQUIRE_MSG(fd != -1, "open(\"/dev/null\") failed: %s",
+ strerror(errno));
+
+ flags1 = fcntl(fd, F_GETFL);
+ ATF_REQUIRE_MSG(flags1 != -1, "fcntl(F_GETFL) (1) failed: %s",
+ strerror(errno));
+ ATF_REQUIRE((flags1 & O_ASYNC) == 0);
+
+ val = 1;
+ ATF_REQUIRE_ERRNO(EINVAL, ioctl(fd, FIOASYNC, &val) == -1);
+
+ flags2 = fcntl(fd, F_GETFL);
+ ATF_REQUIRE_MSG(flags2 != -1, "fcntl(F_GETFL) (2) failed: %s",
+ strerror(errno));
+ ATF_REQUIRE_INTEQ(flags1, flags2);
+
+ (void)close(fd);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, read_only_null);
+ ATF_TP_ADD_TC(tp, write_only_null);
+ ATF_TP_ADD_TC(tp, read_write_null);
+ ATF_TP_ADD_TC(tp, exec_only_sh);
+ ATF_TP_ADD_TC(tp, fioasync_dev_null);
+
+ return (atf_no_error());
}
diff --git a/tests/sys/netinet/Makefile b/tests/sys/netinet/Makefile
index cc525bf24480..b742342beecb 100644
--- a/tests/sys/netinet/Makefile
+++ b/tests/sys/netinet/Makefile
@@ -48,7 +48,7 @@ TEST_METADATA.forward+= required_programs="python" \
TEST_METADATA.output+= required_programs="python"
TEST_METADATA.redirect+= required_programs="python"
-PROGS= udp_dontroute tcp_user_cookie sendto-IP_MULTICAST_IF
+PROGS= udp_dontroute tcp_user_cookie multicast-send multicast-receive
${PACKAGE}FILES+= redirect.py
diff --git a/tests/sys/netinet/multicast-receive.c b/tests/sys/netinet/multicast-receive.c
new file mode 100644
index 000000000000..81d0f10f5cfe
--- /dev/null
+++ b/tests/sys/netinet/multicast-receive.c
@@ -0,0 +1,130 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2025 Gleb Smirnoff <glebius@FreeBSD.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <arpa/inet.h>
+#include <net/if.h>
+#include <assert.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+#include <err.h>
+
+static in_port_t
+atop(const char *c)
+{
+ unsigned long ul;
+
+ errno = 0;
+ if ((ul = strtol(c, NULL, 10)) < 1 || ul > IPPORT_MAX || errno != 0)
+ err(1, "can't parse %s", c);
+
+ return ((in_port_t)ul);
+}
+
+int
+main(int argc, char *argv[])
+{
+ char buf[IP_MAXPACKET + 1];
+ struct sockaddr_in sin = {
+ .sin_family = AF_INET,
+ .sin_len = sizeof(struct sockaddr_in),
+ };
+ socklen_t slen = sizeof(struct sockaddr_in);
+ struct in_addr maddr, ifaddr;
+ ssize_t len;
+ int s, ifindex;
+ bool index;
+
+ if (argc < 4)
+usage:
+ errx(1, "Usage: %s (ip_mreq|ip_mreqn|group_req) "
+ "IPv4-group port interface", argv[0]);
+
+ if (inet_pton(AF_INET, argv[2], &maddr) != 1)
+ err(1, "inet_pton(%s) failed", argv[2]);
+ sin.sin_port = htons(atop(argv[3]));
+ if (inet_pton(AF_INET, argv[4], &ifaddr) == 1)
+ index = false;
+ else if ((ifindex = if_nametoindex(argv[4])) > 0)
+ index = true;
+ else if (strcmp(argv[4], "0") == 0) {
+ ifindex = 0;
+ index = true;
+ } else
+ err(1, "if_nametoindex(%s) failed", argv[4]);
+
+ assert((s = socket(PF_INET, SOCK_DGRAM, 0)) > 0);
+ assert(bind(s, (struct sockaddr *)&sin, sizeof(sin)) == 0);
+
+ if (strcmp(argv[1], "ip_mreq") == 0) {
+ if (index)
+ errx(1, "ip_mreq doesn't accept index");
+ struct ip_mreq mreq = {
+ .imr_multiaddr = maddr,
+ .imr_interface = ifaddr,
+ };
+ assert(setsockopt(s, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq,
+ sizeof(mreq)) == 0);
+ } else if (strcmp(argv[1], "ip_mreqn") == 0) {
+ /*
+ * ip_mreqn shall be used with index, but for testing
+ * purposes accept address too.
+ */
+ struct ip_mreqn mreqn = {
+ .imr_multiaddr = maddr,
+ .imr_address = index ? (struct in_addr){ 0 } : ifaddr,
+ .imr_ifindex = index ? ifindex : 0,
+ };
+ assert(setsockopt(s, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreqn,
+ sizeof(mreqn)) == 0);
+ } else if (strcmp(argv[1], "group_req") == 0) {
+ if (!index)
+ errx(1, "group_req expects index");
+ struct group_req greq = { .gr_interface = ifindex };
+ struct sockaddr_in *gsa = (struct sockaddr_in *)&greq.gr_group;
+
+ gsa->sin_family = AF_INET;
+ gsa->sin_len = sizeof(struct sockaddr_in);
+ gsa->sin_addr = maddr;
+ assert(setsockopt(s, IPPROTO_IP, MCAST_JOIN_GROUP, &greq,
+ sizeof(greq)) == 0);
+ } else
+ goto usage;
+
+ assert((len = recvfrom(s, buf, sizeof(buf) - 1, 0,
+ (struct sockaddr *)&sin, &slen)) > 0);
+ buf[len] = '\0';
+ printf("%s:%u %s\n", inet_ntoa(sin.sin_addr), ntohs(sin.sin_port), buf);
+
+ return (0);
+}
diff --git a/tests/sys/netinet/sendto-IP_MULTICAST_IF.c b/tests/sys/netinet/multicast-send.c
index d478e4da0b3b..f10b2b6338dd 100644
--- a/tests/sys/netinet/sendto-IP_MULTICAST_IF.c
+++ b/tests/sys/netinet/multicast-send.c
@@ -28,35 +28,69 @@
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
+#include <net/if.h>
#include <assert.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
#include <err.h>
+static in_port_t
+atop(const char *c)
+{
+ unsigned long ul;
+
+ errno = 0;
+ if ((ul = strtol(c, NULL, 10)) < 1 || ul > IPPORT_MAX || errno != 0)
+ err(1, "can't parse %s", c);
+
+ return ((in_port_t)ul);
+}
+
int
main(int argc, char *argv[])
{
- struct sockaddr_in sin = {
+ struct sockaddr_in src = {
+ .sin_family = AF_INET,
+ .sin_len = sizeof(struct sockaddr_in),
+ }, dst = {
.sin_family = AF_INET,
.sin_len = sizeof(struct sockaddr_in),
};
+ struct ip_mreqn mreqn;
struct in_addr in;
- int s, rv;
+ int s;
+ bool index;
- if (argc < 2)
- errx(1, "Usage: %s IPv4-address", argv[0]);
+ if (argc < 7)
+ errx(1, "Usage: %s src-IPv4 src-port dst-IPv4 dst-port "
+ "interface payload", argv[0]);
- if (inet_pton(AF_INET, argv[1], &in) != 1)
+ if (inet_pton(AF_INET, argv[1], &src.sin_addr) != 1)
err(1, "inet_pton(%s) failed", argv[1]);
+ src.sin_port = htons(atop(argv[2]));
+ if (inet_pton(AF_INET, argv[3], &dst.sin_addr) != 1)
+ err(1, "inet_pton(%s) failed", argv[3]);
+ dst.sin_port = htons(atop(argv[4]));
+ if (inet_pton(AF_INET, argv[5], &in) == 1)
+ index = false;
+ else if ((mreqn.imr_ifindex = if_nametoindex(argv[5])) > 0)
+ index = true;
+ else
+ err(1, "if_nametoindex(%s) failed", argv[5]);
assert((s = socket(PF_INET, SOCK_DGRAM, 0)) > 0);
- assert(bind(s, (struct sockaddr *)&sin, sizeof(sin)) == 0);
- assert(setsockopt(s, IPPROTO_IP, IP_MULTICAST_IF, &in, sizeof(in))
- == 0);
- /* RFC 6676 */
- assert(inet_pton(AF_INET, "233.252.0.1", &sin.sin_addr) == 1);
- sin.sin_port = htons(6676);
- rv = sendto(s, &sin, sizeof(sin), 0,
- (struct sockaddr *)&sin, sizeof(sin));
- if (rv != sizeof(sin))
+ assert(bind(s, (struct sockaddr *)&src, sizeof(src)) == 0);
+ if (index)
+ assert(setsockopt(s, IPPROTO_IP, IP_MULTICAST_IF, &mreqn,
+ sizeof(mreqn)) == 0);
+ else
+ assert(setsockopt(s, IPPROTO_IP, IP_MULTICAST_IF, &in,
+ sizeof(in)) == 0);
+ if (sendto(s, argv[6], strlen(argv[6]) + 1, 0, (struct sockaddr *)&dst,
+ sizeof(dst)) != (ssize_t)strlen(argv[6]) + 1)
err(1, "sendto failed");
return (0);
diff --git a/tests/sys/netinet/multicast.sh b/tests/sys/netinet/multicast.sh
index eb2b962dac70..a3854fd2fd20 100644..100755
--- a/tests/sys/netinet/multicast.sh
+++ b/tests/sys/netinet/multicast.sh
@@ -26,36 +26,130 @@
. $(atf_get_srcdir)/../common/vnet.subr
-# See regression fixed in baad45c9c12028964acd0b58096f3aaa0fb22859
-atf_test_case "IP_MULTICAST_IF" "cleanup"
-IP_MULTICAST_IF_head()
+# Set up two jails, mjail1 and mjail2, connected with two interface pairs
+multicast_vnet_init()
{
- atf_set descr \
- 'sendto() for IP_MULTICAST_IF socket does not do routing lookup'
+
+ vnet_init
+ epair1=$(vnet_mkepair)
+ epair2=$(vnet_mkepair)
+ vnet_mkjail mjail1 ${epair1}a ${epair2}a
+ jexec mjail1 ifconfig ${epair1}a up
+ jexec mjail1 ifconfig ${epair1}a 192.0.2.1/24
+ jexec mjail1 ifconfig ${epair2}a up
+ jexec mjail1 ifconfig ${epair2}a 192.0.3.1/24
+ vnet_mkjail mjail2 ${epair1}b ${epair2}b
+ jexec mjail2 ifconfig ${epair1}b up
+ jexec mjail2 ifconfig ${epair1}b 192.0.2.2/24
+ jexec mjail2 ifconfig ${epair2}b up
+ jexec mjail2 ifconfig ${epair2}b 192.0.3.2/24
+}
+
+atf_test_case "IP_ADD_MEMBERSHIP_ip_mreq" "cleanup"
+IP_ADD_MEMBERSHIP_ip_mreq_head()
+{
+ atf_set descr 'IP_ADD_MEMBERSHIP / IP_MULTICAST_IF with ip_mreq'
atf_set require.user root
+}
+IP_ADD_MEMBERSHIP_ip_mreq_body()
+{
+ multicast_vnet_init
+
+ # join group on interface with IP address 192.0.2.2
+ jexec mjail2 $(atf_get_srcdir)/multicast-receive \
+ ip_mreq 233.252.0.1 6676 192.0.2.2 > out & pid=$!
+ atf_check -s exit:0 -o empty \
+ jexec mjail1 $(atf_get_srcdir)/multicast-send \
+ 0.0.0.0 6676 233.252.0.1 6676 192.0.2.1 hello
+ atf_check -s exit:0 sh -c "wait $pid; exit $?"
+ atf_check -s exit:0 -o inline:"192.0.2.1:6676 hello\n" cat out
+ # join group on interface with IP address 192.0.3.2
+ jexec mjail2 $(atf_get_srcdir)/multicast-receive \
+ ip_mreq 233.252.0.1 6676 192.0.3.2 > out & pid=$!
+ atf_check -s exit:0 -o empty \
+ jexec mjail1 $(atf_get_srcdir)/multicast-send \
+ 0.0.0.0 6676 233.252.0.1 6676 192.0.3.1 hello
+ atf_check -s exit:0 sh -c "wait $pid; exit $?"
+ atf_check -s exit:0 -o inline:"192.0.3.1:6676 hello\n" cat out
+}
+IP_ADD_MEMBERSHIP_ip_mreq_cleanup()
+{
+ rm out
+ vnet_cleanup
}
-IP_MULTICAST_IF_body()
+atf_test_case "IP_ADD_MEMBERSHIP_ip_mreqn" "cleanup"
+IP_ADD_MEMBERSHIP_ip_mreqn_head()
+{
+ atf_set descr 'IP_ADD_MEMBERSHIP / IP_MULTICAST_IF with ip_mreqn'
+ atf_set require.user root
+}
+IP_ADD_MEMBERSHIP_ip_mreqn_body()
{
- local epair mjail
+ multicast_vnet_init
- vnet_init
- # The test doesn't use our half of epair
- epair=$(vnet_mkepair)
- vnet_mkjail mjail ${epair}a
- jexec mjail ifconfig ${epair}a up
- jexec mjail ifconfig ${epair}a 192.0.2.1/24
+ # join group on interface epair2
+ jexec mjail2 $(atf_get_srcdir)/multicast-receive \
+ ip_mreqn 233.252.0.1 6676 ${epair1}b > out & pid=$!
+ atf_check -s exit:0 -o empty \
+ jexec mjail1 $(atf_get_srcdir)/multicast-send \
+ 0.0.0.0 6676 233.252.0.1 6676 ${epair1}a hello
+ atf_check -s exit:0 sh -c "wait $pid; exit $?"
+ atf_check -s exit:0 -o inline:"192.0.2.1:6676 hello\n" cat out
+
+ # join group on interface epair2
+ jexec mjail2 $(atf_get_srcdir)/multicast-receive \
+ ip_mreqn 233.252.0.1 6676 ${epair2}b > out & pid=$!
atf_check -s exit:0 -o empty \
- jexec mjail $(atf_get_srcdir)/sendto-IP_MULTICAST_IF 192.0.2.1
+ jexec mjail1 $(atf_get_srcdir)/multicast-send \
+ 0.0.0.0 6676 233.252.0.1 6676 ${epair2}a hello
+ atf_check -s exit:0 sh -c "wait $pid; exit $?"
+ atf_check -s exit:0 -o inline:"192.0.3.1:6676 hello\n" cat out
+}
+IP_ADD_MEMBERSHIP_ip_mreqn_cleanup()
+{
+ rm out
+ vnet_cleanup
}
-IP_MULTICAST_IF_cleanup()
+atf_test_case "MCAST_JOIN_GROUP" "cleanup"
+MCAST_JOIN_GROUP_head()
+{
+ atf_set descr 'MCAST_JOIN_GROUP'
+ atf_set require.user root
+}
+MCAST_JOIN_GROUP_body()
+{
+ multicast_vnet_init
+
+ # join group on interface epair2
+ jexec mjail2 $(atf_get_srcdir)/multicast-receive \
+ group_req 233.252.0.1 6676 ${epair1}b > out & pid=$!
+ atf_check -s exit:0 -o empty \
+ jexec mjail1 $(atf_get_srcdir)/multicast-send \
+ 0.0.0.0 6676 233.252.0.1 6676 ${epair1}a hello
+ atf_check -s exit:0 sh -c "wait $pid; exit $?"
+ atf_check -s exit:0 -o inline:"192.0.2.1:6676 hello\n" cat out
+
+ # join group on interface epair2
+ jexec mjail2 $(atf_get_srcdir)/multicast-receive \
+ group_req 233.252.0.1 6676 ${epair2}b > out & pid=$!
+ atf_check -s exit:0 -o empty \
+ jexec mjail1 $(atf_get_srcdir)/multicast-send \
+ 0.0.0.0 6676 233.252.0.1 6676 ${epair2}a hello
+ atf_check -s exit:0 sh -c "wait $pid; exit $?"
+ atf_check -s exit:0 -o inline:"192.0.3.1:6676 hello\n" cat out
+}
+MCAST_JOIN_GROUP_cleanup()
{
+ rm out
vnet_cleanup
}
atf_init_test_cases()
{
- atf_add_test_case "IP_MULTICAST_IF"
+ atf_add_test_case "IP_ADD_MEMBERSHIP_ip_mreq"
+ atf_add_test_case "IP_ADD_MEMBERSHIP_ip_mreqn"
+ atf_add_test_case "MCAST_JOIN_GROUP"
}
diff --git a/tests/sys/netinet6/ndp.sh b/tests/sys/netinet6/ndp.sh
index bac9764ee3c9..21a50cda02ba 100755
--- a/tests/sys/netinet6/ndp.sh
+++ b/tests/sys/netinet6/ndp.sh
@@ -188,9 +188,48 @@ ndp_slaac_default_route_cleanup() {
vnet_cleanup
}
+atf_test_case "ndp_prefix_len_mismatch" "cleanup"
+ndp_prefix_len_mismatch_head() {
+ atf_set descr 'Test RAs on an interface without a /64 lladdr'
+ atf_set require.user root
+ atf_set require.progs python3 scapy
+}
+
+ndp_prefix_len_mismatch_body() {
+ vnet_init
+
+ epair=$(vnet_mkepair)
+
+ vnet_mkjail alcatraz ${epair}a
+
+ jexec alcatraz ifconfig ${epair}a inet6 -auto_linklocal
+ jexec alcatraz ifconfig ${epair}a inet6 -ifdisabled
+ jexec alcatraz ifconfig ${epair}a inet6 accept_rtadv
+ jexec alcatraz ifconfig ${epair}a inet6 fe80::5a9c:fcff:fe10:5d07/127
+ jexec alcatraz ifconfig ${epair}a up
+
+ ifconfig ${epair}b inet6 -ifdisabled
+ ifconfig ${epair}b up
+
+ atf_check -e ignore python3 $(atf_get_srcdir)/ra.py \
+ --sendif ${epair}b \
+ --dst $(ndp_if_lladdr ${epair}a alcatraz) \
+ --src $(ndp_if_lladdr ${epair}b) \
+ --prefix "2001:db8:ffff:1000::" --prefixlen 64
+
+ atf_check \
+ -o match:"inet6 2001:db8:ffff:1000:.* prefixlen 64.*autoconf.*" \
+ jexec alcatraz ifconfig ${epair}a
+}
+
+ndp_prefix_len_mismatch_cleanup() {
+ vnet_cleanup
+}
+
atf_init_test_cases()
{
atf_add_test_case "ndp_add_gu_success"
atf_add_test_case "ndp_del_gu_success"
atf_add_test_case "ndp_slaac_default_route"
+ atf_add_test_case "ndp_prefix_len_mismatch"
}
diff --git a/tests/sys/netlink/netlink_socket.c b/tests/sys/netlink/netlink_socket.c
index 3c2c5f857591..cee864bb9bab 100644
--- a/tests/sys/netlink/netlink_socket.c
+++ b/tests/sys/netlink/netlink_socket.c
@@ -211,9 +211,22 @@ ATF_TC_BODY(sizes, tc)
.msg_controllen = sizeof(cbuf),
};
ssize_t ss;
- int fd, size, rsize;
+ int fd, size, msize, rsize;
- fd = fullsocket();
+ /*
+ * Create a socket with NMSGS messages in the receive buffer.
+ */
+#define NMSGS 5
+ ATF_REQUIRE((fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) != -1);
+ ATF_REQUIRE(send(fd, &hdr, sizeof(hdr), 0) == sizeof(hdr));
+ ATF_REQUIRE(recv(fd, buf, sizeof(hdr), MSG_WAITALL | MSG_PEEK) ==
+ sizeof(hdr));
+ ATF_REQUIRE(ioctl(fd, FIONREAD, &msize) != -1);
+ for (u_int i = 0; i < NMSGS; i++)
+ ATF_REQUIRE(send(fd, &hdr, sizeof(hdr), 0) == sizeof(hdr));
+ do {
+ ATF_REQUIRE(ioctl(fd, FIONREAD, &rsize) != -1);
+ } while (rsize < msize * (NMSGS + 1));
/*
* Set NETLINK_MSG_INFO, so that later cmsg_check will check that any
@@ -264,6 +277,7 @@ ATF_TC_BODY(sizes, tc)
.iov_len = sizeof(buf) < rsize ? sizeof(buf) : rsize,
};
ss = recvmsg(fd, &msg, 0);
+ ATF_REQUIRE(ss > hdr.nlmsg_len);
ATF_REQUIRE(ss > NLMSG_LARGE * 9 || ss == rsize);
cmsg_check(&msg);
}
diff --git a/tests/sys/netpfil/pf/Makefile b/tests/sys/netpfil/pf/Makefile
index 7ddeb5369f47..b363e0b17c76 100644
--- a/tests/sys/netpfil/pf/Makefile
+++ b/tests/sys/netpfil/pf/Makefile
@@ -5,6 +5,7 @@ TESTS_SUBDIRS+= ioctl
ATF_TESTS_SH+= altq \
anchor \
+ counters \
debug \
divert-to \
dup \
@@ -71,7 +72,7 @@ ATF_TESTS_PYTEST+= tcp.py
# Allow tests to run in parallel in their own jails
TEST_METADATA+= execenv="jail"
-TEST_METADATA+= execenv_jail_params="vnet allow.raw_sockets"
+TEST_METADATA+= execenv_jail_params="vnet allow.raw_sockets allow.read_msgbuf"
${PACKAGE}FILES+= \
bsnmpd.conf \
diff --git a/tests/sys/netpfil/pf/counters.sh b/tests/sys/netpfil/pf/counters.sh
new file mode 100644
index 000000000000..20d7dc3c6d89
--- /dev/null
+++ b/tests/sys/netpfil/pf/counters.sh
@@ -0,0 +1,831 @@
+#
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2025 Kajetan Staszkiewicz
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+. $(atf_get_srcdir)/utils.subr
+
+get_counters()
+{
+ echo " === rules ==="
+ rules=$(mktemp) || exit
+ (jexec router pfctl -qvvsn ; jexec router pfctl -qvvsr) | normalize_pfctl_s > $rules
+ cat $rules
+
+ echo " === tables ==="
+ tables=$(mktemp) || exit 1
+ jexec router pfctl -qvvsT > $tables
+ cat $tables
+
+ echo " === states ==="
+ states=$(mktemp) || exit 1
+ jexec router pfctl -qvvss | normalize_pfctl_s > $states
+ cat $states
+
+ echo " === nodes ==="
+ nodes=$(mktemp) || exit 1
+ jexec router pfctl -qvvsS | normalize_pfctl_s > $nodes
+ cat $nodes
+}
+
+atf_test_case "match_pass_state" "cleanup"
+match_pass_state_head()
+{
+ atf_set descr 'Counters on match and pass rules'
+ atf_set require.user root
+}
+
+match_pass_state_body()
+{
+ setup_router_server_ipv6
+
+ # Thest counters for a statefull firewall. Expose the behaviour of
+ # increasing table counters if a table is used multiple times.
+ # The table "tbl_in" is used both in match and pass rule. It's counters
+ # are incremented twice. The tables "tbl_out_match" and "tbl_out_pass"
+ # are used only once and have their countes increased only once.
+ # Test source node counters for this simple scenario too.
+ pft_set_rules router \
+ "set state-policy if-bound" \
+ "table <tbl_in> { ${net_tester_host_tester} }" \
+ "table <tbl_out_pass> { ${net_server_host_server} }" \
+ "table <tbl_out_match> { ${net_server_host_server} }" \
+ "block" \
+ "pass inet6 proto icmp6 icmp6-type { neighbrsol, neighbradv }" \
+ "match in on ${epair_tester}b inet6 proto tcp from <tbl_in> scrub (random-id)" \
+ "pass in on ${epair_tester}b inet6 proto tcp from <tbl_in> keep state (max-src-states 3 source-track rule)" \
+ "match out on ${epair_server}a inet6 proto tcp to <tbl_out_match> scrub (random-id)" \
+ "pass out on ${epair_server}a inet6 proto tcp to <tbl_out_pass> keep state"
+
+ # Use a real TCP connection so that it will be properly closed, guaranteeing the amount of packets.
+ atf_check -s exit:0 -o match:"This is a test" -x \
+ "echo 'This is a test' | nc -w3 ${net_server_host_server} echo"
+ # Let FINs pass through.
+ sleep 1
+ get_counters
+
+ for rule_regexp in \
+ "@3 match in on ${epair_tester}b .* Packets: 10 Bytes: 766 States: 1 " \
+ "@4 pass in on ${epair_tester}b .* Packets: 10 Bytes: 766 States: 1 " \
+ "@5 match out on ${epair_server}a .* Packets: 10 Bytes: 766 States: 1 " \
+ "@6 pass out on ${epair_server}a .* Packets: 10 Bytes: 766 States: 1 " \
+ ; do
+ grep -qE "${rule_regexp}" $rules || atf_fail "Rule regexp not found for '${rule_regexp}'"
+ done
+
+ table_counters_single="Evaluations: NoMatch: 0 Match: 1 In/Block: Packets: 0 Bytes: 0 In/Pass: Packets: 4 Bytes: 311 In/XPass: Packets: 0 Bytes: 0 Out/Block: Packets: 0 Bytes: 0 Out/Pass: Packets: 6 Bytes: 455 Out/XPass: Packets: 0 Bytes: 0"
+ table_counters_double="Evaluations: NoMatch: 0 Match: 2 In/Block: Packets: 0 Bytes: 0 In/Pass: Packets: 12 Bytes: 910 In/XPass: Packets: 0 Bytes: 0 Out/Block: Packets: 0 Bytes: 0 Out/Pass: Packets: 8 Bytes: 622 Out/XPass: Packets: 0 Bytes: 0"
+ for table_test in \
+ "tbl_in___${table_counters_double}" \
+ "tbl_out_match___${table_counters_single}" \
+ "tbl_out_pass___${table_counters_single}" \
+ ; do
+ table_name=${table_test%%___*}
+ table_regexp=${table_test##*___}
+ table=$(mktemp) || exit 1
+ cat $tables | grep -A10 $table_name | tr '\n' ' ' | awk '{gsub("[\\[\\]]", " ", $0); gsub("[[:blank:]]+"," ",$0); print $0}' > ${table}
+ grep -qE "${table_regexp}" ${table} || atf_fail "Bad counters for table ${table_name}"
+ done;
+
+ for state_regexp in \
+ "${epair_tester}b tcp ${net_server_host_server}.* <- ${net_tester_host_tester}.* 6:4 pkts, 455:311 bytes, rule 4," \
+ "${epair_server}a tcp ${net_server_host_tester}.* -> ${net_server_host_server}.* 6:4 pkts, 455:311 bytes, rule 6," \
+ ; do
+ grep -qE "${state_regexp}" $states || atf_fail "State not found for '${state_regexp}'"
+ done
+
+ for node_regexp in \
+ "${net_tester_host_tester} -> :: .* 10 pkts, 766 bytes, filter rule 4, limit source-track"\
+ ; do
+ grep -qE "${node_regexp}" $nodes || atf_fail "Source node not found for '${node_regexp}'"
+ done
+}
+
+match_pass_state_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "match_pass_no_state" "cleanup"
+match_pass_no_state_head()
+{
+ atf_set descr 'Counters on match and pass rules without keep state'
+ atf_set require.user root
+}
+
+match_pass_no_state_body()
+{
+ setup_router_server_ipv6
+
+ # Test counters for a stateless firewall.
+ # The table "tbl_in" is used both in match and pass rule in the inbound
+ # direction. The "In/Pass" counter is incremented twice. The table
+ # "tbl_inout" matches the same host on inbound and outbound direction.
+ # It will also be incremented twice. The tables "tbl_out_match" and
+ # "tbl_out_pass" will have their counters increased only once.
+ pft_set_rules router \
+ "table <tbl_in> { ${net_tester_host_tester} }" \
+ "table <tbl_inout> { ${net_tester_host_tester} }" \
+ "table <tbl_out_match> { ${net_server_host_server} }" \
+ "table <tbl_out_pass> { ${net_server_host_server} }" \
+ "block" \
+ "pass inet6 proto icmp6 icmp6-type { neighbrsol, neighbradv }" \
+ "match in on ${epair_tester}b inet6 proto tcp from <tbl_inout>" \
+ "match in on ${epair_tester}b inet6 proto tcp from <tbl_in>" \
+ "pass in on ${epair_tester}b inet6 proto tcp from <tbl_in> no state" \
+ "pass out on ${epair_tester}b inet6 proto tcp to <tbl_in> no state" \
+ "match in on ${epair_server}a inet6 proto tcp from <tbl_out_match>" \
+ "pass in on ${epair_server}a inet6 proto tcp from <tbl_out_pass> no state" \
+ "match out on ${epair_server}a inet6 proto tcp from <tbl_inout> no state" \
+ "pass out on ${epair_server}a inet6 proto tcp to <tbl_out_pass> no state"
+
+ # Use a real TCP connection so that it will be properly closed, guaranteeing the amount of packets.
+ atf_check -s exit:0 -o match:"This is a test" -x \
+ "echo 'This is a test' | nc -w3 ${net_server_host_server} echo"
+ sleep 1
+ get_counters
+
+ for rule_regexp in \
+ "@3 match in on ${epair_tester}b .* Packets: 6 Bytes: 455 " \
+ "@4 match in on ${epair_tester}b .* Packets: 6 Bytes: 455 " \
+ "@5 pass in on ${epair_tester}b .* Packets: 6 Bytes: 455 " \
+ "@6 pass out on ${epair_tester}b .* Packets: 4 Bytes: 311 " \
+ "@7 match in on ${epair_server}a .* Packets: 4 Bytes: 311 " \
+ "@8 pass in on ${epair_server}a .* Packets: 4 Bytes: 311 " \
+ "@10 pass out on ${epair_server}a .* Packets: 6 Bytes: 455 " \
+ ; do
+ grep -qE "${rule_regexp}" $rules || atf_fail "Rule regexp not found for '${rule_regexp}'"
+ done
+
+ for table_test in \
+ "tbl_in___Evaluations: NoMatch: 0 Match: 16 In/Block: Packets: 0 Bytes: 0 In/Pass: Packets: 12 Bytes: 910 In/XPass: Packets: 0 Bytes: 0 Out/Block: Packets: 0 Bytes: 0 Out/Pass: Packets: 4 Bytes: 311 Out/XPass: Packets: 0 Bytes: 0" \
+ "tbl_out_match___Evaluations: NoMatch: 0 Match: 4 In/Block: Packets: 0 Bytes: 0 In/Pass: Packets: 4 Bytes: 311 In/XPass: Packets: 0 Bytes: 0 Out/Block: Packets: 0 Bytes: 0 Out/Pass: Packets: 0 Bytes: 0 Out/XPass: Packets: 0 Bytes: 0" \
+ "tbl_out_pass___Evaluations: NoMatch: 0 Match: 10 In/Block: Packets: 0 Bytes: 0 In/Pass: Packets: 4 Bytes: 311 In/XPass: Packets: 0 Bytes: 0 Out/Block: Packets: 0 Bytes: 0 Out/Pass: Packets: 6 Bytes: 455 Out/XPass: Packets: 0 Bytes: 0" \
+ "tbl_inout___Evaluations: NoMatch: 0 Match: 12 In/Block: Packets: 0 Bytes: 0 In/Pass: Packets: 6 Bytes: 455 In/XPass: Packets: 0 Bytes: 0 Out/Block: Packets: 0 Bytes: 0 Out/Pass: Packets: 6 Bytes: 455 Out/XPass: Packets: 0 Bytes: 0" \
+ ; do
+ table_name=${table_test%%___*}
+ table_regexp=${table_test##*___}
+ table=$(mktemp) || exit 1
+ cat $tables | grep -A10 $table_name | tr '\n' ' ' | awk '{gsub("[\\[\\]]", " ", $0); gsub("[[:blank:]]+"," ",$0); print $0}' > ${table}
+ grep -qE "${table_regexp}" ${table} || atf_fail "Bad counters for table ${table_name}"
+ done;
+}
+
+match_pass_no_state_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "match_block" "cleanup"
+match_block_head()
+{
+ atf_set descr 'Counters on match and block rules'
+ atf_set require.user root
+}
+
+match_block_body()
+{
+ setup_router_server_ipv6
+
+ # Stateful firewall with a blocking rule. The rule will have its
+ # counters increased because it matches and applies correctly.
+ # The "match" rule before the "pass" rule will have its counters
+ # increased for blocked traffic too.
+ pft_set_rules router \
+ "set state-policy if-bound" \
+ "table <tbl_in_match> { ${net_server_host_server} }" \
+ "table <tbl_in_block> { ${net_server_host_server} }" \
+ "block" \
+ "pass inet6 proto icmp6 icmp6-type { neighbrsol, neighbradv }" \
+ "match in on ${epair_tester}b inet6 proto tcp to <tbl_in_match> scrub (random-id)" \
+ "block in on ${epair_tester}b inet6 proto tcp to <tbl_in_block>" \
+ "pass out on ${epair_server}a inet6 proto tcp keep state"
+
+ # Wait 3 seconds, that will cause 2 SYNs to be sent out.
+ echo 'This is a test' | nc -w3 ${net_server_host_server} echo
+ sleep 1
+ get_counters
+
+ for rule_regexp in \
+ "@3 match in on ${epair_tester}b .* Packets: 2 Bytes: 160 States: 0 " \
+ "@4 block drop in on ${epair_tester}b .* Packets: 2 Bytes: 160 States: 0 " \
+ ; do
+ grep -qE "${rule_regexp}" $rules || atf_fail "Rule regexp not found for '${rule_regexp}'"
+ done
+
+ # OpenBSD has (In|Out)/Match. We don't (yet) have it in FreeBSD
+ # so we follow the action of the "pass" rule ("block" for this test)
+ # in "match" rules.
+ for table_test in \
+ "tbl_in_match___Evaluations: NoMatch: 0 Match: 2 In/Block: Packets: 2 Bytes: 160 In/Pass: Packets: 0 Bytes: 0 In/XPass: Packets: 0 Bytes: 0 Out/Block: Packets: 0 Bytes: 0 Out/Pass: Packets: 0 Bytes: 0 Out/XPass: Packets: 0 Bytes: 0" \
+ "tbl_in_block___Evaluations: NoMatch: 0 Match: 2 In/Block: Packets: 2 Bytes: 160 In/Pass: Packets: 0 Bytes: 0 In/XPass: Packets: 0 Bytes: 0 Out/Block: Packets: 0 Bytes: 0 Out/Pass: Packets: 0 Bytes: 0 Out/XPass: Packets: 0 Bytes: 0" \
+ ; do
+ table_name=${table_test%%___*}
+ table_regexp=${table_test##*___}
+ table=$(mktemp) || exit 1
+ cat $tables | grep -A10 $table_name | tr '\n' ' ' | awk '{gsub("[\\[\\]]", " ", $0); gsub("[[:blank:]]+"," ",$0); print $0}' > ${table}
+ grep -qE "${table_regexp}" ${table} || atf_fail "Bad counters for table ${table_name}"
+ done;
+}
+
+match_block_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "match_fail" "cleanup"
+match_fail_head()
+{
+ atf_set descr 'Counters on match and failing pass rules'
+ atf_set require.user root
+}
+
+match_fail_body()
+{
+ setup_router_server_ipv6
+
+ # Statefull firewall with a failing "pass" rule.
+ # When the rule can't apply it will not have its counters increased.
+ pft_set_rules router \
+ "set state-policy if-bound" \
+ "table <tbl_in_match> { ${net_server_host_server} }" \
+ "table <tbl_in_fail> { ${net_server_host_server} }" \
+ "block" \
+ "pass inet6 proto icmp6 icmp6-type { neighbrsol, neighbradv }" \
+ "match in on ${epair_tester}b inet6 proto tcp to <tbl_in_match> scrub (random-id)" \
+ "pass in on ${epair_tester}b inet6 proto tcp to <tbl_in_fail> keep state (max 1)" \
+ "pass out on ${epair_server}a inet6 proto tcp keep state"
+
+ # The first test will pass and increase the counters for all rules.
+ echo 'This is a test' | nc -w3 ${net_server_host_server} echo
+ # The second test will go through the "match" rules but fail
+ # on the "pass" rule due to 'keep state (max 1)'.
+ # Wait 3 seconds, that will cause 2 SYNs to be sent out.
+ echo 'This is a test' | nc -w3 ${net_server_host_server} echo
+ sleep 1
+ get_counters
+
+ for rule_regexp in \
+ "@3 match in on ${epair_tester}b .* Packets: 10 Bytes: 766 States: 1 " \
+ "@4 pass in on ${epair_tester}b .* Packets: 10 Bytes: 766 States: 1 " \
+ ; do
+ grep -qE "${rule_regexp}" $rules || atf_fail "Rule regexp not found for '${rule_regexp}'"
+ done
+
+ $table_counters_single="Evaluations: NoMatch: 0 Match: 3 In/Block: Packets: 0 Bytes: 0 In/Pass: Packets: 6 Bytes: 455 In/XPass: Packets: 0 Bytes: 0 Out/Block: Packets: 0 Bytes: 0 Out/Pass: Packets: 4 Bytes: 311 Out/XPass: Packets: 0 Bytes: 0"
+ for table_test in \
+ "tbl_in_match___${table_counters_single}" \
+ "tbl_in_fail___${table_counters_single}" \
+ ; do
+ table_name=${table_test%%___*}
+ table_regexp=${table_test##*___}
+ table=$(mktemp) || exit 1
+ cat $tables | grep -A10 $table_name | tr '\n' ' ' | awk '{gsub("[\\[\\]]", " ", $0); gsub("[[:blank:]]+"," ",$0); print $0}' > ${table}
+ grep -qE "${table_regexp}" ${table} || atf_fail "Bad counters for table ${table_name}"
+ done;
+}
+
+match_fail_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "nat_natonly" "cleanup"
+nat_natonly_head()
+{
+ atf_set descr 'Counters on only a NAT rule creating state'
+ atf_set require.user root
+}
+
+nat_natonly_body()
+{
+ setup_router_server_ipv6
+
+ # NAT is applied on the "nat" rule.
+ # The "nat" rule matches on pre-NAT addresses. There is no separate
+ # "pass" rule so the "nat" rule creates the state.
+ pft_set_rules router \
+ "set state-policy if-bound" \
+ "table <tbl_src_nat> { ${net_tester_host_tester} }" \
+ "table <tbl_dst_nat> { ${net_server_host_server} }" \
+ "nat on ${epair_server}a inet6 proto tcp from <tbl_src_nat> to <tbl_dst_nat> -> ${net_server_host_router}"
+
+ # Use a real TCP connection so that it will be properly closed, guaranteeing the amount of packets.
+ atf_check -s exit:0 -o match:"This is a test" -x \
+ "echo 'This is a test' | nc -w3 ${net_server_host_server} echo"
+ sleep 1
+ get_counters
+
+ for rule_regexp in \
+ "@0 nat on ${epair_server}a .* Packets: 10 Bytes: 766 States: 1 " \
+ ; do
+ grep -qE "${rule_regexp}" $rules || atf_fail "Rule regexp not found for '${rule_regexp}'"
+ done
+
+ # All tables have counters increased for In/Pass and Out/Pass, not XPass.
+ table_counters="Evaluations: NoMatch: 0 Match: 1 In/Block: Packets: 0 Bytes: 0 In/Pass: Packets: 4 Bytes: 311 In/XPass: Packets: 0 Bytes: 0 Out/Block: Packets: 0 Bytes: 0 Out/Pass: Packets: 6 Bytes: 455 Out/XPass: Packets: 0 Bytes: 0"
+ for table_test in \
+ "tbl_src_nat___${table_counters}" \
+ "tbl_dst_nat___${table_counters}" \
+ ; do
+ table_name=${table_test%%___*}
+ table_regexp=${table_test##*___}
+ table=$(mktemp) || exit 1
+ cat $tables | grep -A10 $table_name | tr '\n' ' ' | awk '{gsub("[\\[\\]]", " ", $0); gsub("[[:blank:]]+"," ",$0); print $0}' > ${table}
+ grep -qE "${table_regexp}" ${table} || atf_fail "Bad counters for table ${table_name}"
+ done;
+
+ for state_regexp in \
+ "all tcp ${net_server_host_router}.* -> ${net_server_host_server}.* 6:4 pkts, 455:311 bytes" \
+ ; do
+ grep -qE "${state_regexp}" $states || atf_fail "State not found for '${state_regexp}'"
+ done
+}
+
+nat_natonly_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "nat_nat" "cleanup"
+nat_nat_head()
+{
+ atf_set descr 'Counters on NAT, match and pass rules with keep state'
+ atf_set require.user root
+}
+
+nat_nat_body()
+{
+ setup_router_server_ipv6
+
+ # NAT is applied in the NAT ruleset.
+ # The "nat" rule matches on pre-NAT addresses.
+ # The "match" rule matches on post-NAT addresses.
+ # The "pass" rule matches on post-NAT addresses and creates the state.
+ pft_set_rules router \
+ "set state-policy if-bound" \
+ "table <tbl_src_nat> { ${net_tester_host_tester} }" \
+ "table <tbl_dst_nat> { ${net_server_host_server} }" \
+ "table <tbl_src_match> { ${net_server_host_router} }" \
+ "table <tbl_dst_match> { ${net_server_host_server} }" \
+ "table <tbl_src_pass> { ${net_server_host_router} }" \
+ "table <tbl_dst_pass> { ${net_server_host_server} }" \
+ "nat on ${epair_server}a inet6 proto tcp from <tbl_src_nat> to <tbl_dst_nat> -> ${net_server_host_router}" \
+ "block" \
+ "pass inet6 proto icmp6 icmp6-type { neighbrsol, neighbradv }" \
+ "pass in on ${epair_tester}b inet6 proto tcp keep state" \
+ "match out on ${epair_server}a inet6 proto tcp from <tbl_src_match> to <tbl_dst_match> scrub (random-id)" \
+ "pass out on ${epair_server}a inet6 proto tcp from <tbl_src_pass> to <tbl_dst_pass> keep state"
+
+ # Use a real TCP connection so that it will be properly closed, guaranteeing the amount of packets.
+ atf_check -s exit:0 -o match:"This is a test" -x \
+ "echo 'This is a test' | nc -w3 ${net_server_host_server} echo"
+ sleep 1
+ get_counters
+
+ for rule_regexp in \
+ "@0 nat on ${epair_server}a .* Packets: 10 Bytes: 766 States: 1 " \
+ "@4 match out on ${epair_server}a .* Packets: 10 Bytes: 766 States: 1 " \
+ "@5 pass out on ${epair_server}a .* Packets: 10 Bytes: 766 States: 1 " \
+ ; do
+ grep -qE "${rule_regexp}" $rules || atf_fail "Rule regexp not found for '${rule_regexp}'"
+ done
+
+ # All tables have counters increased for In/Pass and Out/Pass, not XPass nor Block.
+ table_counters="Evaluations: NoMatch: 0 Match: 1 In/Block: Packets: 0 Bytes: 0 In/Pass: Packets: 4 Bytes: 311 In/XPass: Packets: 0 Bytes: 0 Out/Block: Packets: 0 Bytes: 0 Out/Pass: Packets: 6 Bytes: 455 Out/XPass: Packets: 0 Bytes: 0"
+ for table_test in \
+ "tbl_src_nat___${table_counters}" \
+ "tbl_dst_nat___${table_counters}" \
+ "tbl_src_match___${table_counters}" \
+ "tbl_dst_match___${table_counters}" \
+ "tbl_src_pass___${table_counters}" \
+ "tbl_dst_pass___${table_counters}" \
+ ; do
+ table_name=${table_test%%___*}
+ table_regexp=${table_test##*___}
+ table=$(mktemp) || exit 1
+ cat $tables | grep -A10 $table_name | tr '\n' ' ' | awk '{gsub("[\\[\\]]", " ", $0); gsub("[[:blank:]]+"," ",$0); print $0}' > ${table}
+ grep -qE "${table_regexp}" ${table} || atf_fail "Bad counters for table ${table_name}"
+ done;
+
+ for state_regexp in \
+ "${epair_server}a tcp ${net_server_host_router}.* -> ${net_server_host_server}.* 6:4 pkts, 455:311 bytes, rule 5," \
+ ; do
+ grep -qE "${state_regexp}" $states || atf_fail "State not found for '${state_regexp}'"
+ done
+}
+
+nat_nat_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "nat_match" "cleanup"
+nat_match_head()
+{
+ atf_set descr 'Counters on match with NAT and pass rules'
+ atf_set require.user root
+}
+
+nat_match_body()
+{
+ setup_router_server_ipv6
+
+ # NAT is applied on the "match" rule.
+ # The "match" rule up to and including the NAT rule match on pre-NAT addresses.
+ # The "match" rule after NAT matches on post-NAT addresses.
+ # The "pass" rule matches on post-NAT addresses and creates the state.
+ pft_set_rules router \
+ "set state-policy if-bound" \
+ "table <tbl_src_match1> { ${net_tester_host_tester} }" \
+ "table <tbl_dst_match1> { ${net_server_host_server} }" \
+ "table <tbl_src_match2> { ${net_tester_host_tester} }" \
+ "table <tbl_dst_match2> { ${net_server_host_server} }" \
+ "table <tbl_src_match3> { ${net_server_host_router} }" \
+ "table <tbl_dst_match3> { ${net_server_host_server} }" \
+ "table <tbl_src_pass> { ${net_server_host_router} }" \
+ "table <tbl_dst_pass> { ${net_server_host_server} }" \
+ "block" \
+ "pass inet6 proto icmp6 icmp6-type { neighbrsol, neighbradv }" \
+ "pass in on ${epair_tester}b inet6 proto tcp keep state" \
+ "match out on ${epair_server}a inet6 proto tcp from <tbl_src_match1> to <tbl_dst_match1> scrub (random-id)" \
+ "match out on ${epair_server}a inet6 proto tcp from <tbl_src_match2> to <tbl_dst_match2> nat-to ${net_server_host_router}" \
+ "match out on ${epair_server}a inet6 proto tcp from <tbl_src_match3> to <tbl_dst_match3> scrub (random-id)" \
+ "pass out on ${epair_server}a inet6 proto tcp from <tbl_src_pass> to <tbl_dst_pass> keep state"
+
+ # Use a real TCP connection so that it will be properly closed, guaranteeing the amount of packets.
+ atf_check -s exit:0 -o match:"This is a test" -x \
+ "echo 'This is a test' | nc -w3 ${net_server_host_server} echo"
+ sleep 1
+ get_counters
+
+ for rule_regexp in \
+ "@4 match out on ${epair_server}a .* Packets: 10 Bytes: 766 States: 1 " \
+ "@5 match out on ${epair_server}a .* Packets: 10 Bytes: 766 States: 1 " \
+ "@6 match out on ${epair_server}a .* Packets: 10 Bytes: 766 States: 1 " \
+ "@7 pass out on ${epair_server}a .* Packets: 10 Bytes: 766 States: 1 " \
+ ; do
+ grep -qE "${rule_regexp}" $rules || atf_fail "Rule regexp not found for '${rule_regexp}'"
+ done
+
+ # All tables have counters increased for In/Pass and Out/Pass, not XPass nor Block.
+ table_counters="Evaluations: NoMatch: 0 Match: 1 In/Block: Packets: 0 Bytes: 0 In/Pass: Packets: 4 Bytes: 311 In/XPass: Packets: 0 Bytes: 0 Out/Block: Packets: 0 Bytes: 0 Out/Pass: Packets: 6 Bytes: 455 Out/XPass: Packets: 0 Bytes: 0"
+ for table_test in \
+ "tbl_src_match1___${table_counters}" \
+ "tbl_dst_match1___${table_counters}" \
+ "tbl_src_match2___${table_counters}" \
+ "tbl_dst_match2___${table_counters}" \
+ "tbl_src_match3___${table_counters}" \
+ "tbl_dst_match3___${table_counters}" \
+ "tbl_src_pass___${table_counters}" \
+ "tbl_dst_pass___${table_counters}" \
+ ; do
+ table_name=${table_test%%___*}
+ table_regexp=${table_test##*___}
+ table=$(mktemp) || exit 1
+ cat $tables | grep -A10 $table_name | tr '\n' ' ' | awk '{gsub("[\\[\\]]", " ", $0); gsub("[[:blank:]]+"," ",$0); print $0}' > ${table}
+ grep -qE "${table_regexp}" ${table} || atf_fail "Bad counters for table ${table_name}"
+ done;
+
+ for state_regexp in \
+ "${epair_server}a tcp ${net_server_host_tester}.* -> ${net_server_host_server}.* 6:4 pkts, 455:311 bytes, rule 7, " \
+ ; do
+ grep -qE "${state_regexp}" $states || atf_fail "State not found for '${state_regexp}'"
+ done
+}
+
+nat_match_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "nat_pass" "cleanup"
+nat_pass_head()
+{
+ atf_set descr 'Counters on match, and pass with NAT rules'
+ atf_set require.user root
+}
+
+nat_pass_body()
+{
+ setup_router_server_ipv6
+
+ # NAT is applied on the "pass" rule which also creates the state.
+ # All rules match on pre-NAT addresses.
+ pft_set_rules router \
+ "set state-policy if-bound" \
+ "table <tbl_src_match> { ${net_tester_host_tester} }" \
+ "table <tbl_dst_match> { ${net_server_host_server} }" \
+ "table <tbl_src_pass> { ${net_tester_host_tester} }" \
+ "table <tbl_dst_pass> { ${net_server_host_server} }" \
+ "block" \
+ "pass inet6 proto icmp6 icmp6-type { neighbrsol, neighbradv }" \
+ "pass in on ${epair_tester}b inet6 proto tcp keep state" \
+ "match out on ${epair_server}a inet6 proto tcp from <tbl_src_match> to <tbl_dst_match> scrub (random-id)" \
+ "pass out on ${epair_server}a inet6 proto tcp from <tbl_src_pass> to <tbl_dst_pass> nat-to ${net_server_host_router} keep state"
+
+ # Use a real TCP connection so that it will be properly closed, guaranteeing the amount of packets.
+ atf_check -s exit:0 -o match:"This is a test" -x \
+ "echo 'This is a test' | nc -w3 ${net_server_host_server} echo"
+ sleep 1
+ get_counters
+
+ for rule_regexp in \
+ "@4 match out on ${epair_server}a .* Packets: 10 Bytes: 766 States: 1 " \
+ "@5 pass out on ${epair_server}a .* Packets: 10 Bytes: 766 States: 1 " \
+ ; do
+ grep -qE "${rule_regexp}" $rules || atf_fail "Rule regexp not found for '${rule_regexp}'"
+ done
+
+ table_counters="Evaluations: NoMatch: 0 Match: 1 In/Block: Packets: 0 Bytes: 0 In/Pass: Packets: 4 Bytes: 311 In/XPass: Packets: 0 Bytes: 0 Out/Block: Packets: 0 Bytes: 0 Out/Pass: Packets: 6 Bytes: 455 Out/XPass: Packets: 0 Bytes: 0"
+ for table_test in \
+ "tbl_src_match___${table_counters}" \
+ "tbl_dst_match___${table_counters}" \
+ "tbl_src_pass___${table_counters}" \
+ "tbl_dst_pass___${table_counters}" \
+ ; do
+ table_name=${table_test%%___*}
+ table_regexp=${table_test##*___}
+ table=$(mktemp) || exit 1
+ cat $tables | grep -A10 $table_name | tr '\n' ' ' | awk '{gsub("[\\[\\]]", " ", $0); gsub("[[:blank:]]+"," ",$0); print $0}' > ${table}
+ grep -qE "${table_regexp}" ${table} || atf_fail "Bad counters for table ${table_name}"
+ done;
+
+ for state_regexp in \
+ "${epair_server}a tcp ${net_server_host_router}.* -> ${net_server_host_server}.* 6:4 pkts, 455:311 bytes, rule 5," \
+ ; do
+ grep -qE "${state_regexp}" $states || atf_fail "State not found for '${state_regexp}'"
+ done
+}
+
+nat_pass_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "rdr_match" "cleanup"
+rdr_match_head()
+{
+ atf_set descr 'Counters on match with RDR and pass rules'
+ atf_set require.user root
+}
+
+rdr_match_body()
+{
+ setup_router_server_ipv6
+
+ # Similar to the nat_match test but for the RDR action.
+ # Hopefully we don't need all other tests duplicated for RDR.
+ # Send traffic to a non-existing host, RDR it to the server.
+ #
+ # The "match" rule up to and including the RDR rule match on pre-RDR dst address.
+ # The "match" rule after NAT matches on post-RDR dst address.
+ # The "pass" rule matches on post-RDR dst address.
+ net_server_host_notserver=${net_server_host_server%%::*}::3
+ pft_set_rules router \
+ "set state-policy if-bound" \
+ "table <tbl_src_match1> { ${net_tester_host_tester} }" \
+ "table <tbl_dst_match1> { ${net_server_host_notserver} }" \
+ "table <tbl_src_match2> { ${net_tester_host_tester} }" \
+ "table <tbl_dst_match2> { ${net_server_host_notserver} }" \
+ "table <tbl_src_match3> { ${net_tester_host_tester} }" \
+ "table <tbl_dst_match3> { ${net_server_host_server} }" \
+ "table <tbl_src_pass> { ${net_tester_host_tester} }" \
+ "table <tbl_dst_pass> { ${net_server_host_server} }" \
+ "block" \
+ "pass inet6 proto icmp6 icmp6-type { neighbrsol, neighbradv }" \
+ "pass out on ${epair_server}a inet6 proto tcp keep state" \
+ "match in on ${epair_tester}b inet6 proto tcp from <tbl_src_match1> to <tbl_dst_match1> scrub (random-id)" \
+ "match in on ${epair_tester}b inet6 proto tcp from <tbl_src_match2> to <tbl_dst_match2> rdr-to ${net_server_host_server}" \
+ "match in on ${epair_tester}b inet6 proto tcp from <tbl_src_match3> to <tbl_dst_match3> scrub (random-id)" \
+ "pass in on ${epair_tester}b inet6 proto tcp from <tbl_src_pass> to <tbl_dst_pass> keep state"
+
+ # Use a real TCP connection so that it will be properly closed, guaranteeing the amount of packets.
+ atf_check -s exit:0 -o match:"This is a test" -x \
+ "echo 'This is a test' | nc -w3 ${net_server_host_notserver} echo"
+ sleep 1
+ get_counters
+
+ for rule_regexp in \
+ "@4 match in on ${epair_tester}b .* Packets: 10 Bytes: 766 States: 1 " \
+ "@5 match in on ${epair_tester}b .* Packets: 10 Bytes: 766 States: 1 " \
+ "@6 match in on ${epair_tester}b .* Packets: 10 Bytes: 766 States: 1 " \
+ "@7 pass in on ${epair_tester}b .* Packets: 10 Bytes: 766 States: 1 " \
+ ; do
+ grep -qE "${rule_regexp}" $rules || atf_fail "Rule regexp not found for '${rule_regexp}'"
+ done
+
+ # All tables have counters increased for In/Pass and Out/Pass, not XPass nor Block.
+ table_counters="Evaluations: NoMatch: 0 Match: 1 In/Block: Packets: 0 Bytes: 0 In/Pass: Packets: 6 Bytes: 455 In/XPass: Packets: 0 Bytes: 0 Out/Block: Packets: 0 Bytes: 0 Out/Pass: Packets: 4 Bytes: 311 Out/XPass: Packets: 0 Bytes: 0"
+ for table_test in \
+ "tbl_src_match1___${table_counters}" \
+ "tbl_dst_match1___${table_counters}" \
+ "tbl_src_match2___${table_counters}" \
+ "tbl_dst_match2___${table_counters}" \
+ "tbl_src_match3___${table_counters}" \
+ "tbl_dst_match3___${table_counters}" \
+ "tbl_src_pass___${table_counters}" \
+ "tbl_dst_pass___${table_counters}" \
+ ; do
+ table_name=${table_test%%___*}
+ table_regexp=${table_test##*___}
+ table=$(mktemp) || exit 1
+ cat $tables | grep -A10 $table_name | tr '\n' ' ' | awk '{gsub("[\\[\\]]", " ", $0); gsub("[[:blank:]]+"," ",$0); print $0}' > ${table}
+ grep -qE "${table_regexp}" ${table} || atf_fail "Bad counters for table ${table_name}"
+ done;
+
+ for state_regexp in \
+ "${epair_tester}b tcp ${net_server_host_server}.* 6:4 pkts, 455:311 bytes, rule 7, " \
+ ; do
+ grep -qE "${state_regexp}" $states || atf_fail "State not found for '${state_regexp}'"
+ done
+}
+
+rdr_match_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "nat64_in" "cleanup"
+nat64_in_head()
+{
+ atf_set descr 'Counters on match and inbound af-to rules'
+ atf_set require.user root
+}
+
+nat64_in_body()
+{
+ setup_router_server_nat64
+
+ pft_set_rules router \
+ "set state-policy if-bound" \
+ "table <tbl_src_match> { ${net_tester_6_host_tester} }" \
+ "table <tbl_dst_match> { 64:ff9b::${net_server1_4_host_server} }" \
+ "table <tbl_src_pass> { ${net_tester_6_host_tester} }" \
+ "table <tbl_dst_pass> { 64:ff9b::${net_server1_4_host_server} }" \
+ "block log" \
+ "pass inet6 proto icmp6 icmp6-type { neighbrsol, neighbradv }" \
+ "match in on ${epair_tester}b inet6 proto tcp from <tbl_src_match> to <tbl_dst_match> scrub (random-id)" \
+ "pass in on ${epair_tester}b inet6 proto tcp from <tbl_src_pass> to <tbl_dst_pass> \
+ af-to inet from (${epair_server1}a) \
+ keep state"
+
+ # Use a real TCP connection so that it will be properly closed, guaranteeing the amount of packets.
+ atf_check -s exit:0 -o match:"This is a test" -x \
+ "echo 'This is a test' | nc -w3 64:ff9b::${net_server1_4_host_server} echo"
+ sleep 1
+ get_counters
+
+ # The amount of packets is counted properly but sizes are not because
+ # pd->tot_len is always post-nat, even when updating pre-nat counters.
+ for rule_regexp in \
+ "@3 match in on ${epair_tester}b .* Packets: 10 Bytes: 686 States: 1 " \
+ "@4 pass in on ${epair_tester}b .* Packets: 10 Bytes: 686 States: 1 " \
+ ; do
+ grep -qE "${rule_regexp}" $rules || atf_fail "Rule regexp not found for '${rule_regexp}'"
+ done
+
+ # All tables have counters increased for In/Pass and Out/Pass, not XPass nor Block.
+ table_counters="Evaluations: NoMatch: 0 Match: 1 In/Block: Packets: 0 Bytes: 0 In/Pass: Packets: 4 Bytes: 231 In/XPass: Packets: 0 Bytes: 0 Out/Block: Packets: 0 Bytes: 0 Out/Pass: Packets: 6 Bytes: 455 Out/XPass: Packets: 0 Bytes: 0"
+ for table_test in \
+ "tbl_src_match___${table_counters}" \
+ "tbl_dst_match___${table_counters}" \
+ "tbl_src_pass___${table_counters}" \
+ "tbl_dst_pass___${table_counters}" \
+ ; do
+ table_name=${table_test%%___*}
+ table_regexp=${table_test##*___}
+ table=$(mktemp) || exit 1
+ cat $tables | grep -A10 $table_name | tr '\n' ' ' | awk '{gsub("[\\[\\]]", " ", $0); gsub("[[:blank:]]+"," ",$0); print $0}' > ${table}
+ grep -qE "${table_regexp}" ${table} || atf_fail "Bad counters for table ${table_name}"
+ done;
+
+ for state_regexp in \
+ "${epair_server1}a tcp ${net_server_host_tester}.* 6:4 pkts, 455:231 bytes, rule 4, " \
+ ; do
+ grep -qE "${state_regexp}" $states || atf_fail "State not found for '${state_regexp}'"
+ done
+
+ echo " === interfaces === "
+ echo " === tester === "
+ jexec router pfctl -qvvsI -i ${epair_tester}b
+ echo " === server === "
+ jexec router pfctl -qvvsI -i ${epair_server1}a
+ echo " === "
+}
+
+nat64_in_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "nat64_out" "cleanup"
+nat64_out_head()
+{
+ atf_set descr 'Counters on match and outbound af-to rules'
+ atf_set require.user root
+}
+
+nat64_out_body()
+{
+ setup_router_server_nat64
+
+ # af-to in outbound path requires routes for the pre-af-to traffic.
+ jexec router route add -inet6 64:ff9b::/96 -iface ${epair_server1}a
+
+ pft_set_rules router \
+ "set state-policy if-bound" \
+ "table <tbl_src_match> { ${net_tester_6_host_tester} }" \
+ "table <tbl_dst_match> { 64:ff9b::${net_server1_4_host_server} }" \
+ "table <tbl_src_pass> { ${net_tester_6_host_tester} }" \
+ "table <tbl_dst_pass> { 64:ff9b::${net_server1_4_host_server} }" \
+ "block log " \
+ "pass inet6 proto icmp6 icmp6-type { neighbrsol, neighbradv }" \
+ "pass in on ${epair_tester}b inet6 proto tcp keep state" \
+ "match out on ${epair_server1}a inet6 proto tcp from <tbl_src_match> to <tbl_dst_match> scrub (random-id)" \
+ "pass out on ${epair_server1}a inet6 proto tcp from <tbl_src_pass> to <tbl_dst_pass> \
+ af-to inet from (${epair_server1}a) \
+ keep state"
+
+ # Use a real TCP connection so that it will be properly closed, guaranteeing the amount of packets.
+ atf_check -s exit:0 -o match:"This is a test" -x \
+ "echo 'This is a test' | nc -w3 64:ff9b::${net_server1_4_host_server} echo"
+ sleep 1
+ get_counters
+
+ for rule_regexp in \
+ "@4 match out on ${epair_server1}a .* Packets: 10 Bytes: 686 States: 1 " \
+ "@5 pass out on ${epair_server1}a .* Packets: 10 Bytes: 686 States: 1 " \
+ ; do
+ grep -qE "${rule_regexp}" $rules || atf_fail "Rule regexp not found for '${rule_regexp}'"
+ done
+
+ # All tables have counters increased for In/Pass and Out/Pass, not XPass nor Block.
+ table_counters="Evaluations: NoMatch: 0 Match: 1 In/Block: Packets: 0 Bytes: 0 In/Pass: Packets: 4 Bytes: 231 In/XPass: Packets: 0 Bytes: 0 Out/Block: Packets: 0 Bytes: 0 Out/Pass: Packets: 6 Bytes: 455 Out/XPass: Packets: 0 Bytes: 0"
+ for table_test in \
+ "tbl_src_match___${table_counters}" \
+ "tbl_dst_match___${table_counters}" \
+ "tbl_src_pass___${table_counters}" \
+ "tbl_dst_pass___${table_counters}" \
+ ; do
+ table_name=${table_test%%___*}
+ table_regexp=${table_test##*___}
+ table=$(mktemp) || exit 1
+ cat $tables | grep -A10 $table_name | tr '\n' ' ' | awk '{gsub("[\\[\\]]", " ", $0); gsub("[[:blank:]]+"," ",$0); print $0}' > ${table}
+ grep -qE "${table_regexp}" ${table} || atf_fail "Bad counters for table ${table_name}"
+ done;
+
+ for state_regexp in \
+ "${epair_server1}a tcp 198.51.100.17:[0-9]+ \(64:ff9b::c633:6412\[7\]\) -> 198.51.100.18:7 \(2001:db8:4200::2\[[0-9]+\]\) .* 6:4 pkts, 455:231 bytes, rule 5," \
+ ; do
+ grep -qE "${state_regexp}" $states || atf_fail "State not found for '${state_regexp}'"
+ done
+
+ echo " === interfaces === "
+ echo " === tester === "
+ jexec router pfctl -qvvsI -i ${epair_tester}b
+ echo " === server === "
+ jexec router pfctl -qvvsI -i ${epair_server1}a
+ echo " === "
+}
+
+nat64_out_cleanup()
+{
+ pft_cleanup
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case "match_pass_state"
+ atf_add_test_case "match_pass_no_state"
+ atf_add_test_case "match_block"
+ atf_add_test_case "match_fail"
+ atf_add_test_case "nat_natonly"
+ atf_add_test_case "nat_nat"
+ atf_add_test_case "nat_match"
+ atf_add_test_case "nat_pass"
+ atf_add_test_case "rdr_match"
+ atf_add_test_case "nat64_in"
+ atf_add_test_case "nat64_out"
+}
diff --git a/tests/sys/netpfil/pf/mld.py b/tests/sys/netpfil/pf/mld.py
index d118a34c8a7d..b3ef6c21b3de 100644
--- a/tests/sys/netpfil/pf/mld.py
+++ b/tests/sys/netpfil/pf/mld.py
@@ -32,23 +32,22 @@ from atf_python.sys.net.vnet import VnetTestTemplate
class TestMLD(VnetTestTemplate):
REQUIRED_MODULES = [ "pf" ]
TOPOLOGY = {
- "vnet1": {"ifaces": ["if1"]},
+ "vnet1": {"ifaces": ["if1"], "opts": ["allow.read_msgbuf"]},
"vnet2": {"ifaces": ["if1"]},
"if1": {"prefixes6": [("2001:db8::2/64", "2001:db8::1/64")]},
}
def vnet2_handler(self, vnet):
ifname = vnet.iface_alias_map["if1"].name
- #ToolsHelper.print_output("/sbin/pfctl -e")
+ ToolsHelper.print_output("/sbin/pfctl -e")
ToolsHelper.pf_rules([
"pass",
])
ToolsHelper.print_output("/sbin/pfctl -x loud")
- #ToolsHelper.print_output("echo \"j 230.0.0.1 %s\ns 3600\nq\" | /usr/sbin/mtest" % ifname)
def find_mld_reply(self, pkt, ifname):
pkt.show()
- s = DelayedSend(pkt)
+ s = DelayedSend(pkt, ifname)
found = False
packets = self.sp.sniff(iface=ifname, timeout=5)
@@ -66,7 +65,6 @@ class TestMLD(VnetTestTemplate):
def test_router_alert(self):
"""Verify that we allow MLD packets with router alert extension header"""
ifname = self.vnet.iface_alias_map["if1"].name
- #ToolsHelper.print_output("/sbin/ifconfig %s inet6 -ifdisable" % ifname)
ToolsHelper.print_output("/sbin/ifconfig")
# Import in the correct vnet, so at to not confuse Scapy
@@ -76,20 +74,17 @@ class TestMLD(VnetTestTemplate):
self.sp = sp
self.sc = sc
- # A correct MLD query gets a reply
- pkt = sp.IPv6(src="fe80::1%%%s" % ifname, dst="ff02::1", hlim=1) \
- / sp.RouterAlert(value=0) \
+ # MLD packets with an incorrect hop limit get dropped.
+ pkt = sp.Ether() \
+ / sp.IPv6(src="fe80::1%%%s" % ifname, dst="ff02::1", hlim=2) \
+ / sp.IPv6ExtHdrHopByHop(options=[ \
+ sp.RouterAlert(value=0) \
+ ]) \
/ sp.ICMPv6MLQuery2()
- assert self.find_mld_reply(pkt, ifname)
+ # We can't reliably test this by checking for a reply, because
+ # the other jail may just send a spontaneous MLD reply.
+ self.find_mld_reply(pkt, ifname)
- # The wrong extension header does not
- pkt = sp.IPv6(src="fe80::1%%%s" % ifname, dst="ff02::1", hlim=1) \
- / sp.IPv6ExtHdrRouting() \
- / sp.ICMPv6MLQuery2()
- assert not self.find_mld_reply(pkt, ifname)
-
- # Neither does an incorrect hop limit
- pkt = sp.IPv6(src="fe80::1%%%s" % ifname, dst="ff02::1", hlim=2) \
- / sp.RouterAlert(value=0) \
- / sp.ICMPv6MLQuery2()
- assert not self.find_mld_reply(pkt, ifname)
+ # Check if we logged dropping the MLD paacket
+ dmesg = ToolsHelper.get_output("/sbin/dmesg")
+ assert dmesg.find("Invalid MLD") != -1
diff --git a/tests/sys/netpfil/pf/nat.sh b/tests/sys/netpfil/pf/nat.sh
index 170d813d57fe..e55f46418221 100644
--- a/tests/sys/netpfil/pf/nat.sh
+++ b/tests/sys/netpfil/pf/nat.sh
@@ -838,7 +838,7 @@ dummynet_mask_body()
jexec gw dnctl pipe 1 config delay 100 mask src-ip 0xffffff00
jexec gw pfctl -e
pft_set_rules gw \
- "nat pass on ${epair_srv}b inet from 192.0.2.0/24 to any -> (${epair_srv}b)" \
+ "nat on ${epair_srv}b inet from 192.0.2.0/24 to any -> (${epair_srv}b)" \
"pass out dnpipe 1"
atf_check -s exit:0 -o ignore \
diff --git a/tests/sys/netpfil/pf/nat64.sh b/tests/sys/netpfil/pf/nat64.sh
index d930e2ee5763..6631e3eca2c7 100644
--- a/tests/sys/netpfil/pf/nat64.sh
+++ b/tests/sys/netpfil/pf/nat64.sh
@@ -213,12 +213,14 @@ tcp_in_if_bound_body()
atf_fail "Failed to connect to TCP server"
fi
+ sleep 1
+
# Interfaces of the state are reversed when doing inbound NAT64!
- # FIXME: Packets counters seem wrong!
+ # FIXME: Packets from both directions are counted only on the inbound direction!
states=$(mktemp) || exit 1
jexec rtr pfctl -qvvss | normalize_pfctl_s > $states
for state_regexp in \
- "${epair_link}a tcp 192.0.2.1:[0-9]+ \(2001:db8::2\[[0-9]+\]\) -> 192.0.2.2:1234 \(64:ff9b::c000:202\[1234\]\) .* 9:9 pkts.* rule 3 .* origif: ${epair}b" \
+ "${epair_link}a tcp 192.0.2.1:[0-9]+ \(2001:db8::2\[[0-9]+\]\) -> 192.0.2.2:1234 \(64:ff9b::c000:202\[1234\]\) .* 5:4 pkts.* rule 3 .* origif: ${epair}b" \
; do
grep -qE "${state_regexp}" $states || atf_fail "State not found for '${state_regexp}'"
done
@@ -254,6 +256,8 @@ tcp_out_if_bound_body()
atf_fail "Failed to connect to TCP server"
fi
+ sleep 1
+
# Origif is not printed when identical as if.
states=$(mktemp) || exit 1
jexec rtr pfctl -qvvss | normalize_pfctl_s > $states
@@ -295,12 +299,14 @@ tcp_in_floating_body()
atf_fail "Failed to connect to TCP server"
fi
+ sleep 1
+
# Interfaces of the state are reversed when doing inbound NAT64!
- # FIXME: Packets counters seem wrong!
+ # FIXME: Packets from both directions are counted only on the inbound direction!
states=$(mktemp) || exit 1
jexec rtr pfctl -qvvss | normalize_pfctl_s > $states
for state_regexp in \
- "all tcp 192.0.2.1:[0-9]+ \(2001:db8::2\[[0-9]+\]\) -> 192.0.2.2:1234 \(64:ff9b::c000:202\[1234\]\).* 9:9 pkts.* rule 3 .* origif: ${epair}b" \
+ "all tcp 192.0.2.1:[0-9]+ \(2001:db8::2\[[0-9]+\]\) -> 192.0.2.2:1234 \(64:ff9b::c000:202\[1234\]\).* 5:4 pkts.* rule 3 .* origif: ${epair}b" \
; do
grep -qE "${state_regexp}" $states || atf_fail "State not found for '${state_regexp}'"
done
@@ -336,6 +342,8 @@ tcp_out_floating_body()
atf_fail "Failed to connect to TCP server"
fi
+ sleep 1
+
# Origif is not printed when identical as if.
states=$(mktemp) || exit 1
jexec rtr pfctl -qvvss | normalize_pfctl_s > $states
@@ -1011,20 +1019,19 @@ route_to_body()
pft_init
epair_link=$(vnet_mkepair)
- epair_null=$(vnet_mkepair)
epair=$(vnet_mkepair)
ifconfig ${epair}a inet6 2001:db8::2/64 up no_dad
route -6 add default 2001:db8::1
- vnet_mkjail rtr ${epair}b ${epair_link}a ${epair_null}a
+ vnet_mkjail rtr ${epair}b ${epair_link}a
jexec rtr ifconfig ${epair}b inet6 2001:db8::1/64 up no_dad
- jexec rtr ifconfig ${epair_null}a 192.0.2.3/24 up
jexec rtr ifconfig ${epair_link}a 192.0.2.1/24 up
vnet_mkjail dst ${epair_link}b
jexec dst ifconfig ${epair_link}b 192.0.2.2/24 up
- jexec dst route add default 192.0.2.1
+ jexec dst ifconfig lo0 203.0.113.1/32 alias
+ jexec dst route add default 192.0.2.2
# Sanity checks
atf_check -s exit:0 -o ignore \
@@ -1033,8 +1040,9 @@ route_to_body()
jexec rtr pfctl -e
pft_set_rules rtr \
"set reassemble yes" \
+ "set debug loud" \
"set state-policy if-bound" \
- "block" \
+ "block log (all)" \
"pass inet6 proto icmp6 icmp6-type { neighbrsol, neighbradv }" \
"pass in on ${epair}b route-to (${epair_link}a 192.0.2.2) inet6 from any to 64:ff9b::/96 af-to inet from (${epair_link}a)"
@@ -1044,9 +1052,11 @@ route_to_body()
states=$(mktemp) || exit 1
jexec rtr pfctl -qvvss | normalize_pfctl_s > $states
+ cat $states
+ # Interfaces of the state are reversed when doing inbound NAT64!
for state_regexp in \
- "${epair_link}a ipv6-icmp 192.0.2.1:.* \(2001:db8::2\[[0-9]+\]\) -> 192.0.2.2:8 \(64:ff9b::c000:202\[[0-9]+\]\).*6:6 pkts.*route-to: 192.0.2.2@${epair_link}a origif: ${epair}b" \
+ "${epair_link}a ipv6-icmp 192.0.2.1:.* \(2001:db8::2\[[0-9]+\]\) -> 192.0.2.2:8 \(64:ff9b::c000:202\[[0-9]+\]\).* 3:3 pkts.*route-to: 192.0.2.2@${epair_link}a origif: ${epair}b" \
; do
grep -qE "${state_regexp}" $states || atf_fail "State not found for '${state_regexp}'"
done
diff --git a/tests/sys/netpfil/pf/rdr.sh b/tests/sys/netpfil/pf/rdr.sh
index f7c920bbfa8f..24b95b2047f4 100644
--- a/tests/sys/netpfil/pf/rdr.sh
+++ b/tests/sys/netpfil/pf/rdr.sh
@@ -281,8 +281,66 @@ srcport_pass_cleanup()
pft_cleanup
}
+atf_test_case "natpass" "cleanup"
+natpass_head()
+{
+ atf_set descr 'Test rdr pass'
+ atf_set require.user root
+}
+
+natpass_body()
+{
+ pft_init
+
+ epair=$(vnet_mkepair)
+ epair_link=$(vnet_mkepair)
+
+ ifconfig ${epair}a 192.0.2.2/24 up
+
+ vnet_mkjail alcatraz ${epair}b ${epair_link}a
+ jexec alcatraz ifconfig lo0 inet 127.0.0.1/8 up
+ jexec alcatraz ifconfig ${epair}b inet 192.0.2.1/24 up
+ jexec alcatraz ifconfig ${epair_link}a 198.51.100.1/24 up
+ jexec alcatraz sysctl net.inet.ip.forwarding=1
+
+ vnet_mkjail srv ${epair_link}b
+ jexec srv ifconfig ${epair_link}b inet 198.51.100.2/24 up
+ jexec srv route add default 198.51.100.1
+
+ # Sanity check
+ atf_check -s exit:0 -o ignore \
+ ping -c 1 192.0.2.1
+ atf_check -s exit:0 -o ignore \
+ jexec alcatraz ping -c 1 198.51.100.2
+
+ jexec alcatraz pfctl -e
+ pft_set_rules alcatraz \
+ "rdr pass on ${epair}b proto udp from any to 192.0.2.1 port 80 -> 198.51.100.2" \
+ "nat on ${epair}b inet from 198.51.100.0/24 to any -> 192.0.2.1" \
+ "block in proto udp from any to any port 80" \
+ "pass in proto icmp"
+
+ echo "foo" | jexec srv nc -u -l 80 &
+ sleep 1 # Give the above a moment to start
+
+ out=$(echo 1 | nc -u -w 1 192.0.2.1 80)
+ echo "out ${out}"
+ if [ "${out}" != "foo" ];
+ then
+ jexec alcatraz pfctl -sn -vv
+ jexec alcatraz pfctl -ss -vv
+ atf_fail "rdr failed"
+ fi
+}
+
+natpass_cleanup()
+{
+ pft_cleanup
+}
+
atf_init_test_cases()
{
+ atf_add_test_case "natpass"
atf_add_test_case "tcp_v6_compat"
atf_add_test_case "tcp_v6_pass"
atf_add_test_case "srcport_compat"
diff --git a/tests/sys/netpfil/pf/utils.subr b/tests/sys/netpfil/pf/utils.subr
index a48f26653f8c..8b3b06bf3bba 100644
--- a/tests/sys/netpfil/pf/utils.subr
+++ b/tests/sys/netpfil/pf/utils.subr
@@ -217,6 +217,7 @@ setup_router_server_ipv4()
jexec server route add -net ${net_tester} ${net_server_host_router}
inetd_conf=$(mktemp)
echo "discard stream tcp nowait root internal" > $inetd_conf
+ echo "echo stream tcp nowait root internal" >> $inetd_conf
jexec server inetd -p ${PWD}/inetd.pid $inetd_conf
}
@@ -271,6 +272,7 @@ setup_router_server_ipv6()
jexec server route add -6 ${net_tester} ${net_server_host_router}
inetd_conf=$(mktemp)
echo "discard stream tcp6 nowait root internal" > $inetd_conf
+ echo "echo stream tcp6 nowait root internal" >> $inetd_conf
jexec server inetd -p ${PWD}/inetd.pid $inetd_conf
}
@@ -353,6 +355,7 @@ setup_router_server_nat64()
inetd_conf=$(mktemp)
echo "discard stream tcp46 nowait root internal" >> $inetd_conf
+ echo "echo stream tcp46 nowait root internal" >> $inetd_conf
vnet_mkjail server1 ${epair_server1}b
jexec server1 /etc/rc.d/netif start lo0
diff --git a/tests/sys/sys/bitstring_test.c b/tests/sys/sys/bitstring_test.c
index a48042a4a063..bf436040c00f 100644
--- a/tests/sys/sys/bitstring_test.c
+++ b/tests/sys/sys/bitstring_test.c
@@ -559,14 +559,13 @@ BITSTRING_TC_DEFINE(bit_nclear)
bit_nclear(bitstr, i, j);
bit_ffc(bitstr, nbits, &found_clear_bit);
- ATF_REQUIRE_MSG(
- found_clear_bit == i,
+ ATF_REQUIRE_INTEQ_MSG(i, found_clear_bit,
"bit_nclear_%d_%d_%d%s: Failed with result %d",
nbits, i, j, memloc, found_clear_bit);
bit_ffs_at(bitstr, i, nbits, &found_set_bit);
- ATF_REQUIRE_MSG(
- (j + 1 < nbits) ? found_set_bit == j + 1 : -1,
+ ATF_REQUIRE_INTEQ_MSG((j + 1 < nbits) ? j + 1 : -1,
+ found_set_bit,
"bit_nset_%d_%d_%d%s: Failed with result %d",
nbits, i, j, memloc, found_set_bit);
}
@@ -586,14 +585,13 @@ BITSTRING_TC_DEFINE(bit_nset)
bit_nset(bitstr, i, j);
bit_ffs(bitstr, nbits, &found_set_bit);
- ATF_REQUIRE_MSG(
- found_set_bit == i,
+ ATF_REQUIRE_INTEQ_MSG(i, found_set_bit,
"bit_nset_%d_%d_%d%s: Failed with result %d",
nbits, i, j, memloc, found_set_bit);
bit_ffc_at(bitstr, i, nbits, &found_clear_bit);
- ATF_REQUIRE_MSG(
- (j + 1 < nbits) ? found_clear_bit == j + 1 : -1,
+ ATF_REQUIRE_INTEQ_MSG((j + 1 < nbits) ? j + 1 : -1,
+ found_clear_bit,
"bit_nset_%d_%d_%d%s: Failed with result %d",
nbits, i, j, memloc, found_clear_bit);
}