aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sys/netinet/ip_divert.c31
-rw-r--r--sys/netinet/ip_var.h10
-rw-r--r--sys/netpfil/pf/pf.c32
-rw-r--r--tests/sys/netpfil/pf/Makefile4
-rw-r--r--tests/sys/netpfil/pf/divapp.c149
-rw-r--r--tests/sys/netpfil/pf/divert-to.sh413
6 files changed, 625 insertions, 14 deletions
diff --git a/sys/netinet/ip_divert.c b/sys/netinet/ip_divert.c
index dd0a8b74c013..ad95a1ce0d76 100644
--- a/sys/netinet/ip_divert.c
+++ b/sys/netinet/ip_divert.c
@@ -171,11 +171,19 @@ divert_packet(struct mbuf *m, bool incoming)
u_int16_t nport;
struct sockaddr_in divsrc;
struct m_tag *mtag;
+ uint16_t cookie;
NET_EPOCH_ASSERT();
mtag = m_tag_locate(m, MTAG_IPFW_RULE, 0, NULL);
- if (mtag == NULL) {
+ if (mtag != NULL) {
+ cookie = ((struct ipfw_rule_ref *)(mtag+1))->rulenum;
+ nport = htons((uint16_t)
+ (((struct ipfw_rule_ref *)(mtag+1))->info));
+ } else if ((mtag = m_tag_locate(m, MTAG_PF_DIVERT, 0, NULL)) != NULL) {
+ cookie = ((struct pf_divert_mtag *)(mtag+1))->idir;
+ nport = htons(((struct pf_divert_mtag *)(mtag+1))->ndir);
+ } else {
m_freem(m);
return;
}
@@ -216,7 +224,7 @@ divert_packet(struct mbuf *m, bool incoming)
divsrc.sin_len = sizeof(divsrc);
divsrc.sin_family = AF_INET;
/* record matching rule, in host format */
- divsrc.sin_port = ((struct ipfw_rule_ref *)(mtag+1))->rulenum;
+ divsrc.sin_port = cookie;
/*
* Record receive interface address, if any.
* But only for incoming packets.
@@ -265,7 +273,6 @@ divert_packet(struct mbuf *m, bool incoming)
}
/* Put packet on socket queue, if any */
- nport = htons((uint16_t)(((struct ipfw_rule_ref *)(mtag+1))->info));
SLIST_FOREACH(dcb, &V_divhash[DIVHASH(nport)], dcb_next)
if (dcb->dcb_port == nport)
break;
@@ -304,6 +311,7 @@ div_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *nam,
const struct ip *ip;
struct m_tag *mtag;
struct ipfw_rule_ref *dt;
+ struct pf_divert_mtag *pfdt;
int error, family;
if (control)
@@ -390,13 +398,30 @@ div_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *nam,
return (EAFNOSUPPORT);
}
+ mtag = m_tag_locate(m, MTAG_PF_DIVERT, 0, NULL);
+ if (mtag == NULL) {
+ /* this should be normal */
+ mtag = m_tag_alloc(MTAG_PF_DIVERT, 0,
+ sizeof(struct pf_divert_mtag), M_NOWAIT | M_ZERO);
+ if (mtag == NULL) {
+ m_freem(m);
+ return (ENOBUFS);
+ }
+ m_tag_prepend(m, mtag);
+ }
+ pfdt = (struct pf_divert_mtag *)(mtag+1);
+ if (sin)
+ pfdt->idir = sin->sin_port;
+
/* Reinject packet into the system as incoming or outgoing */
NET_EPOCH_ENTER(et);
if (!sin || sin->sin_addr.s_addr == 0) {
dt->info |= IPFW_IS_DIVERT | IPFW_INFO_OUT;
+ pfdt->ndir = PF_DIVERT_MTAG_DIR_OUT;
error = div_output_outbound(family, so, m);
} else {
dt->info |= IPFW_IS_DIVERT | IPFW_INFO_IN;
+ pfdt->ndir = PF_DIVERT_MTAG_DIR_IN;
error = div_output_inbound(family, so, m, sin);
}
NET_EPOCH_EXIT(et);
diff --git a/sys/netinet/ip_var.h b/sys/netinet/ip_var.h
index 06560fb52afe..a8c687682af9 100644
--- a/sys/netinet/ip_var.h
+++ b/sys/netinet/ip_var.h
@@ -326,6 +326,16 @@ extern void (*ip_divert_ptr)(struct mbuf *m, bool incoming);
extern int (*ng_ipfw_input_p)(struct mbuf **, struct ip_fw_args *, bool);
extern int (*ip_dn_ctl_ptr)(struct sockopt *);
extern int (*ip_dn_io_ptr)(struct mbuf **, struct ip_fw_args *);
+
+/* pf specific mtag for divert(4) support */
+enum { PF_DIVERT_MTAG_DIR_IN=1, PF_DIVERT_MTAG_DIR_OUT=2 };
+struct pf_divert_mtag {
+ uint16_t idir; // initial pkt direction
+ uint16_t ndir; // a) divert(4) port upon initial diversion
+ // b) new direction upon pkt re-enter
+};
+#define MTAG_PF_DIVERT 1262273569
+
#endif /* _KERNEL */
#endif /* !_NETINET_IP_VAR_H_ */
diff --git a/sys/netpfil/pf/pf.c b/sys/netpfil/pf/pf.c
index b80ec2bb303d..eb2e09b2e6f2 100644
--- a/sys/netpfil/pf/pf.c
+++ b/sys/netpfil/pf/pf.c
@@ -7855,7 +7855,7 @@ pf_test(int dir, int pflags, struct ifnet *ifp, struct mbuf **m0,
u_short action, reason = 0;
struct mbuf *m = *m0;
struct ip *h = NULL;
- struct m_tag *ipfwtag;
+ struct m_tag *mtag;
struct pf_krule *a = NULL, *r = &V_pf_default_rule, *tr, *nr;
struct pf_kstate *s = NULL;
struct pf_kruleset *ruleset = NULL;
@@ -7945,21 +7945,26 @@ pf_test(int dir, int pflags, struct ifnet *ifp, struct mbuf **m0,
off = h->ip_hl << 2;
if (__predict_false(ip_divert_ptr != NULL) &&
- ((ipfwtag = m_tag_locate(m, MTAG_IPFW_RULE, 0, NULL)) != NULL)) {
- struct ipfw_rule_ref *rr = (struct ipfw_rule_ref *)(ipfwtag+1);
- if (rr->info & IPFW_IS_DIVERT && rr->rulenum == 0) {
+ ((mtag = m_tag_locate(m, MTAG_PF_DIVERT, 0, NULL)) != NULL)) {
+ struct pf_divert_mtag *dt = (struct pf_divert_mtag *)(mtag+1);
+ if ((dt->idir == PF_DIVERT_MTAG_DIR_IN && dir == PF_IN) ||
+ (dt->idir == PF_DIVERT_MTAG_DIR_OUT && dir == PF_OUT)) {
if (pd.pf_mtag == NULL &&
((pd.pf_mtag = pf_get_mtag(m)) == NULL)) {
action = PF_DROP;
goto done;
}
pd.pf_mtag->flags |= PF_MTAG_FLAG_PACKET_LOOPED;
- m_tag_delete(m, ipfwtag);
}
if (pd.pf_mtag && pd.pf_mtag->flags & PF_MTAG_FLAG_FASTFWD_OURS_PRESENT) {
m->m_flags |= M_FASTFWD_OURS;
pd.pf_mtag->flags &= ~PF_MTAG_FLAG_FASTFWD_OURS_PRESENT;
}
+ m_tag_delete(m, mtag);
+
+ mtag = m_tag_locate(m, MTAG_IPFW_RULE, 0, NULL);
+ if (mtag != NULL)
+ m_tag_delete(m, mtag);
} else if (pf_normalize_ip(m0, kif, &reason, &pd) != PF_PASS) {
/* We do IP header normalization and packet reassembly here */
action = PF_DROP;
@@ -8241,17 +8246,19 @@ done:
if (__predict_false(ip_divert_ptr != NULL) && action == PF_PASS &&
r->divert.port && !PACKET_LOOPED(&pd)) {
- ipfwtag = m_tag_alloc(MTAG_IPFW_RULE, 0,
- sizeof(struct ipfw_rule_ref), M_NOWAIT | M_ZERO);
- if (ipfwtag != NULL) {
- ((struct ipfw_rule_ref *)(ipfwtag+1))->info =
+ mtag = m_tag_alloc(MTAG_PF_DIVERT, 0,
+ sizeof(struct pf_divert_mtag), M_NOWAIT | M_ZERO);
+ if (mtag != NULL) {
+ ((struct pf_divert_mtag *)(mtag+1))->ndir =
ntohs(r->divert.port);
- ((struct ipfw_rule_ref *)(ipfwtag+1))->rulenum = dir;
+ ((struct pf_divert_mtag *)(mtag+1))->idir =
+ (dir == PF_IN) ? PF_DIVERT_MTAG_DIR_IN :
+ PF_DIVERT_MTAG_DIR_OUT;
if (s)
PF_STATE_UNLOCK(s);
- m_tag_prepend(m, ipfwtag);
+ m_tag_prepend(m, mtag);
if (m->m_flags & M_FASTFWD_OURS) {
if (pd.pf_mtag == NULL &&
((pd.pf_mtag = pf_get_mtag(m)) == NULL)) {
@@ -8279,6 +8286,9 @@ done:
("pf: failed to allocate divert tag\n"));
}
}
+ /* this flag will need revising if the pkt is forwarded */
+ if (pd.pf_mtag)
+ pd.pf_mtag->flags &= ~PF_MTAG_FLAG_PACKET_LOOPED;
if (pd.act.log) {
struct pf_krule *lr;
diff --git a/tests/sys/netpfil/pf/Makefile b/tests/sys/netpfil/pf/Makefile
index 44fe95680dfb..c9adab5626d4 100644
--- a/tests/sys/netpfil/pf/Makefile
+++ b/tests/sys/netpfil/pf/Makefile
@@ -2,10 +2,12 @@
PACKAGE= tests
TESTSDIR= ${TESTSBASE}/sys/netpfil/pf
+BINDIR= ${TESTSDIR}
TESTS_SUBDIRS+= ioctl
ATF_TESTS_SH+= altq \
anchor \
+ divert-to \
dup \
ether \
forward \
@@ -45,6 +47,8 @@ ATF_TESTS_PYTEST+= sctp.py
# Tests reuse jail names and so cannot run in parallel.
TEST_METADATA+= is_exclusive=true
+PROGS= divapp
+
${PACKAGE}FILES+= CVE-2019-5597.py \
CVE-2019-5598.py \
daytime_inetd.conf \
diff --git a/tests/sys/netpfil/pf/divapp.c b/tests/sys/netpfil/pf/divapp.c
new file mode 100644
index 000000000000..908c41eaa67f
--- /dev/null
+++ b/tests/sys/netpfil/pf/divapp.c
@@ -0,0 +1,149 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2023 Igor Ostapenko <pm@igoro.pro>
+ *
+ * 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 PROJECT 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 PROJECT 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.
+ */
+
+/* Used by tests like divert-to.sh */
+
+#include <errno.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <err.h>
+#include <sysexits.h>
+#include <string.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+
+
+struct context {
+ unsigned short divert_port;
+ bool divert_back;
+
+ int fd;
+ struct sockaddr_in sin;
+ socklen_t sin_len;
+ char pkt[IP_MAXPACKET];
+ ssize_t pkt_n;
+};
+
+static void
+init(struct context *c)
+{
+ c->fd = socket(PF_DIVERT, SOCK_RAW, 0);
+ if (c->fd == -1)
+ errx(EX_OSERR, "init: Cannot create divert socket.");
+
+ memset(&c->sin, 0, sizeof(c->sin));
+ c->sin.sin_family = AF_INET;
+ c->sin.sin_port = htons(c->divert_port);
+ c->sin.sin_addr.s_addr = INADDR_ANY;
+ c->sin_len = sizeof(struct sockaddr_in);
+
+ if (bind(c->fd, (struct sockaddr *) &c->sin, c->sin_len) != 0)
+ errx(EX_OSERR, "init: Cannot bind divert socket.");
+}
+
+static ssize_t
+recv_pkt(struct context *c)
+{
+ fd_set readfds;
+ struct timeval timeout;
+ int s;
+
+ FD_ZERO(&readfds);
+ FD_SET(c->fd, &readfds);
+ timeout.tv_sec = 3;
+ timeout.tv_usec = 0;
+
+ s = select(c->fd + 1, &readfds, 0, 0, &timeout);
+ if (s == -1)
+ errx(EX_IOERR, "recv_pkt: select() errors.");
+ if (s != 1) // timeout
+ return -1;
+
+ c->pkt_n = recvfrom(c->fd, c->pkt, sizeof(c->pkt), 0,
+ (struct sockaddr *) &c->sin, &c->sin_len);
+ if (c->pkt_n == -1)
+ errx(EX_IOERR, "recv_pkt: recvfrom() errors.");
+
+ return (c->pkt_n);
+}
+
+static void
+send_pkt(struct context *c)
+{
+ ssize_t n;
+ char errstr[32];
+
+ n = sendto(c->fd, c->pkt, c->pkt_n, 0,
+ (struct sockaddr *) &c->sin, c->sin_len);
+ if (n == -1) {
+ strerror_r(errno, errstr, sizeof(errstr));
+ errx(EX_IOERR, "send_pkt: sendto() errors: %d %s.", errno, errstr);
+ }
+ if (n != c->pkt_n)
+ errx(EX_IOERR, "send_pkt: sendto() sent %zd of %zd bytes.",
+ n, c->pkt_n);
+}
+
+int
+main(int argc, char *argv[])
+{
+ struct context c;
+ int npkt;
+
+ if (argc < 2)
+ errx(EX_USAGE,
+ "Usage: %s <divert-port> [divert-back]", argv[0]);
+
+ memset(&c, 0, sizeof(struct context));
+
+ c.divert_port = (unsigned short) strtol(argv[1], NULL, 10);
+ if (c.divert_port == 0)
+ errx(EX_USAGE, "divert port is not defined.");
+
+ if (argc >= 3 && strcmp(argv[2], "divert-back") == 0)
+ c.divert_back = true;
+
+
+ init(&c);
+
+ npkt = 0;
+ while (recv_pkt(&c) > 0) {
+ if (c.divert_back)
+ send_pkt(&c);
+ npkt++;
+ if (npkt >= 10)
+ break;
+ }
+
+ if (npkt != 1)
+ errx(EXIT_FAILURE, "%d: npkt=%d.", c.divert_port, npkt);
+
+ return EXIT_SUCCESS;
+}
diff --git a/tests/sys/netpfil/pf/divert-to.sh b/tests/sys/netpfil/pf/divert-to.sh
new file mode 100644
index 000000000000..0a37cea78ad3
--- /dev/null
+++ b/tests/sys/netpfil/pf/divert-to.sh
@@ -0,0 +1,413 @@
+#
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2023 Igor Ostapenko <pm@igoro.pro>
+#
+# 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.
+
+#
+# pf divert-to action test cases
+#
+# -----------| |-- |----| ----| |-----------
+# ( ) inbound |pf_check_in| ) -> |host| -> ( ) |pf_check_out| outbound )
+# -----------| | |-- |----| ----| | |-----------
+# | |
+# \|/ \|/
+# |------| |------|
+# |divapp| |divapp|
+# |------| |------|
+#
+# The basic cases:
+# - inbound > diverted | divapp terminated
+# - inbound > diverted > inbound | host terminated
+# - inbound > diverted > outbound | network terminated
+# - outbound > diverted | divapp terminated
+# - outbound > diverted > outbound | network terminated
+# - outbound > diverted > inbound | e.g. host terminated
+#
+# When a packet is diverted, forwarded, and possibly diverted again:
+# - inbound > diverted > inbound > forwarded
+# > outbound | network terminated
+# - inbound > diverted > inbound > forwarded
+# > outbound > diverted > outbound | network terminated
+#
+# Test case naming legend:
+# in - inbound
+# div - diverted
+# out - outbound
+# fwd - forwarded
+# ipfwon - with ipfw enabled, which allows all
+#
+
+. $(atf_get_srcdir)/utils.subr
+
+divert_init()
+{
+ if ! kldstat -q -m ipdivert; then
+ atf_skip "This test requires ipdivert"
+ fi
+}
+
+ipfw_init()
+{
+ if ! kldstat -q -m ipfw; then
+ atf_skip "This test requires ipfw"
+ fi
+}
+
+assert_ipfw_is_off()
+{
+ if kldstat -q -m ipfw; then
+ atf_skip "This test is for the case when ipfw is not loaded"
+ fi
+}
+
+atf_test_case "ipfwoff_in_div" "cleanup"
+ipfwoff_in_div_head()
+{
+ atf_set descr 'Test inbound > diverted | divapp terminated'
+ atf_set require.user root
+}
+ipfwoff_in_div_body()
+{
+ local ipfwon
+
+ pft_init
+ divert_init
+ test "$1" == "ipfwon" && ipfwon="yes"
+ test $ipfwon && ipfw_init || assert_ipfw_is_off
+
+ epair=$(vnet_mkepair)
+ vnet_mkjail div ${epair}b
+ ifconfig ${epair}a 192.0.2.1/24 up
+ jexec div ifconfig ${epair}b 192.0.2.2/24 up
+ test $ipfwon && jexec div ipfw add 65534 allow all from any to any
+
+ # Sanity check
+ atf_check -s exit:0 -o ignore ping -c3 192.0.2.2
+
+ jexec div pfctl -e
+ pft_set_rules div \
+ "pass all" \
+ "pass in inet proto icmp icmp-type echoreq divert-to 127.0.0.1 port 2000"
+
+ jexec div $(atf_get_srcdir)/divapp 2000 &
+ divapp_pid=$!
+ # Wait for the divapp to be ready
+ sleep 1
+
+ # divapp is expected to "eat" the packet
+ atf_check -s not-exit:0 -o ignore ping -c1 192.0.2.2
+
+ wait $divapp_pid
+}
+ipfwoff_in_div_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "ipfwon_in_div" "cleanup"
+ipfwon_in_div_head()
+{
+ atf_set descr 'Test inbound > diverted | divapp terminated, with ipfw enabled'
+ atf_set require.user root
+}
+ipfwon_in_div_body()
+{
+ ipfwoff_in_div_body "ipfwon"
+}
+ipfwon_in_div_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "ipfwoff_in_div_in" "cleanup"
+ipfwoff_in_div_in_head()
+{
+ atf_set descr 'Test inbound > diverted > inbound | host terminated'
+ atf_set require.user root
+}
+ipfwoff_in_div_in_body()
+{
+ local ipfwon
+
+ pft_init
+ divert_init
+ test "$1" == "ipfwon" && ipfwon="yes"
+ test $ipfwon && ipfw_init || assert_ipfw_is_off
+
+ epair=$(vnet_mkepair)
+ vnet_mkjail div ${epair}b
+ ifconfig ${epair}a 192.0.2.1/24 up
+ jexec div ifconfig ${epair}b 192.0.2.2/24 up
+ test $ipfwon && jexec div ipfw add 65534 allow all from any to any
+
+ # Sanity check
+ atf_check -s exit:0 -o ignore ping -c3 192.0.2.2
+
+ jexec div pfctl -e
+ pft_set_rules div \
+ "pass all" \
+ "pass in inet proto icmp icmp-type echoreq divert-to 127.0.0.1 port 2000 no state"
+
+ jexec div $(atf_get_srcdir)/divapp 2000 divert-back &
+ divapp_pid=$!
+ # Wait for the divapp to be ready
+ sleep 1
+
+ # divapp is NOT expected to "eat" the packet
+ atf_check -s exit:0 -o ignore ping -c1 192.0.2.2
+
+ wait $divapp_pid
+}
+ipfwoff_in_div_in_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "ipfwon_in_div_in" "cleanup"
+ipfwon_in_div_in_head()
+{
+ atf_set descr 'Test inbound > diverted > inbound | host terminated, with ipfw enabled'
+ atf_set require.user root
+}
+ipfwon_in_div_in_body()
+{
+ ipfwoff_in_div_in_body "ipfwon"
+}
+ipfwon_in_div_in_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "ipfwoff_out_div" "cleanup"
+ipfwoff_out_div_head()
+{
+ atf_set descr 'Test outbound > diverted | divapp terminated'
+ atf_set require.user root
+}
+ipfwoff_out_div_body()
+{
+ local ipfwon
+
+ pft_init
+ divert_init
+ test "$1" == "ipfwon" && ipfwon="yes"
+ test $ipfwon && ipfw_init || assert_ipfw_is_off
+
+ epair=$(vnet_mkepair)
+ vnet_mkjail div ${epair}b
+ ifconfig ${epair}a 192.0.2.1/24 up
+ jexec div ifconfig ${epair}b 192.0.2.2/24 up
+ test $ipfwon && jexec div ipfw add 65534 allow all from any to any
+
+ # Sanity check
+ atf_check -s exit:0 -o ignore ping -c3 192.0.2.2
+
+ jexec div pfctl -e
+ pft_set_rules div \
+ "pass all" \
+ "pass in inet proto icmp icmp-type echoreq no state" \
+ "pass out inet proto icmp icmp-type echorep divert-to 127.0.0.1 port 2000 no state"
+
+ jexec div $(atf_get_srcdir)/divapp 2000 &
+ divapp_pid=$!
+ # Wait for the divapp to be ready
+ sleep 1
+
+ # divapp is expected to "eat" the packet
+ atf_check -s not-exit:0 -o ignore ping -c1 192.0.2.2
+
+ wait $divapp_pid
+}
+ipfwoff_out_div_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "ipfwon_out_div" "cleanup"
+ipfwon_out_div_head()
+{
+ atf_set descr 'Test outbound > diverted | divapp terminated, with ipfw enabled'
+ atf_set require.user root
+}
+ipfwon_out_div_body()
+{
+ ipfwoff_out_div_body "ipfwon"
+}
+ipfwon_out_div_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "ipfwoff_out_div_out" "cleanup"
+ipfwoff_out_div_out_head()
+{
+ atf_set descr 'Test outbound > diverted > outbound | network terminated'
+ atf_set require.user root
+}
+ipfwoff_out_div_out_body()
+{
+ local ipfwon
+
+ pft_init
+ divert_init
+ test "$1" == "ipfwon" && ipfwon="yes"
+ test $ipfwon && ipfw_init || assert_ipfw_is_off
+
+ epair=$(vnet_mkepair)
+ vnet_mkjail div ${epair}b
+ ifconfig ${epair}a 192.0.2.1/24 up
+ jexec div ifconfig ${epair}b 192.0.2.2/24 up
+ test $ipfwon && jexec div ipfw add 65534 allow all from any to any
+
+ # Sanity check
+ atf_check -s exit:0 -o ignore ping -c3 192.0.2.2
+
+ jexec div pfctl -e
+ pft_set_rules div \
+ "pass all" \
+ "pass in inet proto icmp icmp-type echoreq no state" \
+ "pass out inet proto icmp icmp-type echorep divert-to 127.0.0.1 port 2000 no state"
+
+ jexec div $(atf_get_srcdir)/divapp 2000 divert-back &
+ divapp_pid=$!
+ # Wait for the divapp to be ready
+ sleep 1
+
+ # divapp is NOT expected to "eat" the packet
+ atf_check -s exit:0 -o ignore ping -c1 192.0.2.2
+
+ wait $divapp_pid
+}
+ipfwoff_out_div_out_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "ipfwon_out_div_out" "cleanup"
+ipfwon_out_div_out_head()
+{
+ atf_set descr 'Test outbound > diverted > outbound | network terminated, with ipfw enabled'
+ atf_set require.user root
+}
+ipfwon_out_div_out_body()
+{
+ ipfwoff_out_div_out_body "ipfwon"
+}
+ipfwon_out_div_out_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "ipfwoff_in_div_in_fwd_out_div_out" "cleanup"
+ipfwoff_in_div_in_fwd_out_div_out_head()
+{
+ atf_set descr 'Test inbound > diverted > inbound > forwarded > outbound > diverted > outbound | network terminated'
+ atf_set require.user root
+}
+ipfwoff_in_div_in_fwd_out_div_out_body()
+{
+ local ipfwon
+
+ pft_init
+ divert_init
+ test "$1" == "ipfwon" && ipfwon="yes"
+ test $ipfwon && ipfw_init || assert_ipfw_is_off
+
+ # host <a--epair0--b> router <a--epair1--b> site
+ epair0=$(vnet_mkepair)
+ epair1=$(vnet_mkepair)
+
+ vnet_mkjail router ${epair0}b ${epair1}a
+ ifconfig ${epair0}a 192.0.2.1/24 up
+ jexec router sysctl net.inet.ip.forwarding=1
+ jexec router ifconfig ${epair0}b 192.0.2.2/24 up
+ jexec router ifconfig ${epair1}a 198.51.100.1/24 up
+ test $ipfwon && jexec router ipfw add 65534 allow all from any to any
+
+ vnet_mkjail site ${epair1}b
+ jexec site ifconfig ${epair1}b 198.51.100.2/24 up
+ jexec site route add default 198.51.100.1
+ test $ipfwon && jexec site ipfw add 65534 allow all from any to any
+
+ route add -net 198.51.100.0/24 192.0.2.2
+
+ # Sanity check
+ atf_check -s exit:0 -o ignore ping -c3 192.0.2.2
+
+ # Should be routed without pf
+ atf_check -s exit:0 -o ignore ping -c3 198.51.100.2
+
+ jexec router pfctl -e
+ pft_set_rules router \
+ "pass all" \
+ "pass in inet proto icmp icmp-type echoreq divert-to 127.0.0.1 port 2001 no state" \
+ "pass out inet proto icmp icmp-type echoreq divert-to 127.0.0.1 port 2002 no state"
+
+ jexec router $(atf_get_srcdir)/divapp 2001 divert-back &
+ indivapp_pid=$!
+ jexec router $(atf_get_srcdir)/divapp 2002 divert-back &
+ outdivapp_pid=$!
+ # Wait for the divappS to be ready
+ sleep 1
+
+ # Both divappS are NOT expected to "eat" the packet
+ atf_check -s exit:0 -o ignore ping -c1 198.51.100.2
+
+ wait $indivapp_pid && wait $outdivapp_pid
+}
+ipfwoff_in_div_in_fwd_out_div_out_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "ipfwon_in_div_in_fwd_out_div_out" "cleanup"
+ipfwon_in_div_in_fwd_out_div_out_head()
+{
+ atf_set descr 'Test inbound > diverted > inbound > forwarded > outbound > diverted > outbound | network terminated, with ipfw enabled'
+ atf_set require.user root
+}
+ipfwon_in_div_in_fwd_out_div_out_body()
+{
+ ipfwoff_in_div_in_fwd_out_div_out_body "ipfwon"
+}
+ipfwon_in_div_in_fwd_out_div_out_cleanup()
+{
+ pft_cleanup
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case "ipfwoff_in_div"
+ atf_add_test_case "ipfwoff_in_div_in"
+ atf_add_test_case "ipfwon_in_div"
+ atf_add_test_case "ipfwon_in_div_in"
+
+ atf_add_test_case "ipfwoff_out_div"
+ atf_add_test_case "ipfwoff_out_div_out"
+ atf_add_test_case "ipfwon_out_div"
+ atf_add_test_case "ipfwon_out_div_out"
+
+ atf_add_test_case "ipfwoff_in_div_in_fwd_out_div_out"
+ atf_add_test_case "ipfwon_in_div_in_fwd_out_div_out"
+}