Skip site navigation (1)Skip section navigation (2)
Date:      Mon, 20 Nov 2023 13:04:27 GMT
From:      Kristof Provost <kp@FreeBSD.org>
To:        src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-branches@FreeBSD.org
Subject:   git: f831517d862d - stable/14 - pf: fix dummynet + ipdivert use case
Message-ID:  <202311201304.3AKD4RjV089636@gitrepo.freebsd.org>

next in thread | raw e-mail | index | archive | help
The branch stable/14 has been updated by kp:

URL: https://cgit.FreeBSD.org/src/commit/?id=f831517d862dac2df3110c569b44e8417c3f0afa

commit f831517d862dac2df3110c569b44e8417c3f0afa
Author:     Igor Ostapenko <pm@igoro.pro>
AuthorDate: 2023-11-17 16:04:01 +0000
Commit:     Kristof Provost <kp@FreeBSD.org>
CommitDate: 2023-11-20 10:30:19 +0000

    pf: fix dummynet + ipdivert use case
    
    Dummynet re-injects an mbuf with MTAG_IPFW_RULE added, and the same mtag
    is used by divert(4) as parameters for packet diversion.
    
    If according to pf rule set a packet should go through dummynet first
    and through ipdivert after then mentioned mtag must be removed after
    dummynet not to make ipdivert think that this is its input parameters.
    
    At the very beginning ipfw consumes this mtag what means the same
    behavior with tag clearing after dummynet.
    
    And after fabf705f4b5a pf passes parameters to ipdivert using its
    personal MTAG_PF_DIVERT mtag.
    
    PR:             274850
    Reviewed by:    kp
    Differential Revision:  https://reviews.freebsd.org/D42609
    
    (cherry picked from commit fe3bb40b9e807d4010617de1ef040ba3aa623487)
---
 sys/netpfil/pf/pf.c               |  27 +++++++--
 tests/sys/netpfil/pf/divert-to.sh | 118 +++++++++++++++++++++++++++++++++++++-
 2 files changed, 139 insertions(+), 6 deletions(-)

diff --git a/sys/netpfil/pf/pf.c b/sys/netpfil/pf/pf.c
index 6e6b0b20130d..9cb5fe03b5c2 100644
--- a/sys/netpfil/pf/pf.c
+++ b/sys/netpfil/pf/pf.c
@@ -303,6 +303,8 @@ static int		 pf_state_key_attach(struct pf_state_key *,
 static void		 pf_state_key_detach(struct pf_kstate *, int);
 static int		 pf_state_key_ctor(void *, int, void *, int);
 static u_int32_t	 pf_tcp_iss(struct pf_pdesc *);
+static __inline void	 pf_dummynet_flag_remove(struct mbuf *m,
+			    struct pf_mtag *pf_mtag);
 static int		 pf_dummynet(struct pf_pdesc *, struct pf_kstate *,
 			    struct pf_krule *, struct mbuf **);
 static int		 pf_dummynet_route(struct pf_pdesc *,
@@ -4140,7 +4142,7 @@ pf_test_eth_rule(int dir, struct pfi_kkif *kif, struct mbuf **m0)
 
 		/* But only once. We may see the packet multiple times (e.g.
 		 * PFIL_IN/PFIL_OUT). */
-		mtag->flags &= ~PF_MTAG_FLAG_DUMMYNET;
+		pf_dummynet_flag_remove(m, mtag);
 
 		return (PF_PASS);
 	}
@@ -4361,7 +4363,7 @@ pf_test_eth_rule(int dir, struct pfi_kkif *kif, struct mbuf **m0)
 		mtag->flags |= PF_MTAG_FLAG_DUMMYNET;
 		ip_dn_io_ptr(m0, &dnflow);
 		if (*m0 != NULL)
-			mtag->flags &= ~PF_MTAG_FLAG_DUMMYNET;
+			pf_dummynet_flag_remove(m, mtag);
 	} else {
 		PF_RULES_RUNLOCK();
 	}
@@ -7794,6 +7796,21 @@ pf_test_eth(int dir, int pflags, struct ifnet *ifp, struct mbuf **m0,
 	return (pf_test_eth_rule(dir, kif, m0));
 }
 
+static __inline void
+pf_dummynet_flag_remove(struct mbuf *m, struct pf_mtag *pf_mtag)
+{
+	struct m_tag *mtag;
+
+	pf_mtag->flags &= ~PF_MTAG_FLAG_DUMMYNET;
+
+	/* dummynet adds this tag, but pf does not need it,
+	 * and keeping it creates unexpected behavior,
+	 * e.g. in case of divert(4) usage right after dummynet. */
+	mtag = m_tag_locate(m, MTAG_IPFW_RULE, 0, NULL);
+	if (mtag != NULL)
+		m_tag_delete(m, mtag);
+}
+
 static int
 pf_dummynet(struct pf_pdesc *pd, struct pf_kstate *s,
     struct pf_krule *r, struct mbuf **m0)
@@ -7844,7 +7861,7 @@ pf_dummynet_route(struct pf_pdesc *pd, struct pf_kstate *s,
 			ip_dn_io_ptr(m0, &dnflow);
 			if (*m0 != NULL) {
 				pd->pf_mtag->flags &= ~PF_MTAG_FLAG_ROUTE_TO;
-				pd->pf_mtag->flags &= ~PF_MTAG_FLAG_DUMMYNET;
+				pf_dummynet_flag_remove(*m0, pd->pf_mtag);
 			}
 		}
 	}
@@ -7933,7 +7950,7 @@ pf_test(int dir, int pflags, struct ifnet *ifp, struct mbuf **m0,
 
 		/* But only once. We may see the packet multiple times (e.g.
 		 * PFIL_IN/PFIL_OUT). */
-		pd.pf_mtag->flags &= ~PF_MTAG_FLAG_DUMMYNET;
+		pf_dummynet_flag_remove(m, pd.pf_mtag);
 		PF_RULES_RUNLOCK();
 
 		return (PF_PASS);
@@ -8494,7 +8511,7 @@ pf_test6(int dir, int pflags, struct ifnet *ifp, struct mbuf **m0, struct inpcb
 
 	if (ip_dn_io_ptr != NULL && pd.pf_mtag != NULL &&
 	    pd.pf_mtag->flags & PF_MTAG_FLAG_DUMMYNET) {
-		pd.pf_mtag->flags &= ~PF_MTAG_FLAG_DUMMYNET;
+		pf_dummynet_flag_remove(m, pd.pf_mtag);
 		/* Dummynet re-injects packets after they've
 		 * completed their delay. We've already
 		 * processed them, so pass unconditionally. */
diff --git a/tests/sys/netpfil/pf/divert-to.sh b/tests/sys/netpfil/pf/divert-to.sh
index 0a37cea78ad3..965464b35810 100644
--- a/tests/sys/netpfil/pf/divert-to.sh
+++ b/tests/sys/netpfil/pf/divert-to.sh
@@ -51,11 +51,13 @@
 #         > outbound > diverted > outbound | network terminated
 #
 # Test case naming legend:
+# ipfwon - with ipfw enabled
+# ipfwoff - with ipfw disabled
 # in - inbound
 # div - diverted
 # out - outbound
 # fwd - forwarded
-# ipfwon - with ipfw enabled, which allows all
+# dn - delayed by dummynet
 #
 
 . $(atf_get_srcdir)/utils.subr
@@ -67,6 +69,13 @@ divert_init()
 	fi
 }
 
+dummynet_init()
+{
+	if ! kldstat -q -m dummynet; then
+		atf_skip "This test requires dummynet"
+	fi
+}
+
 ipfw_init()
 {
 	if ! kldstat -q -m ipfw; then
@@ -396,6 +405,110 @@ ipfwon_in_div_in_fwd_out_div_out_cleanup()
 	pft_cleanup
 }
 
+atf_test_case "ipfwoff_in_dn_in_div_in_out_dn_out_div_out" "cleanup"
+ipfwoff_in_dn_in_div_in_out_dn_out_div_out_head()
+{
+	atf_set descr 'Test inbound > delayed+diverted > outbound > delayed+diverted > outbound | network terminated'
+	atf_set require.user root
+}
+ipfwoff_in_dn_in_div_in_out_dn_out_div_out_body()
+{
+	local ipfwon
+
+	pft_init
+	divert_init
+	dummynet_init
+	test "$1" == "ipfwon" && ipfwon="yes"
+	test $ipfwon && ipfw_init || assert_ipfw_is_off
+
+	epair=$(vnet_mkepair)
+	vnet_mkjail alcatraz ${epair}b
+	ifconfig ${epair}a 192.0.2.1/24 up
+	ifconfig ${epair}a ether 02:00:00:00:00:01
+	jexec alcatraz ifconfig ${epair}b 192.0.2.2/24 up
+	test $ipfwon && jexec alcatraz ipfw add 65534 allow all from any to any
+
+	# Sanity check
+	atf_check -s exit:0 -o ignore ping -c3 192.0.2.2
+
+	# a) ping should time out due to very narrow dummynet pipes {
+
+	jexec alcatraz dnctl pipe 1001 config bw 1Byte/s
+	jexec alcatraz dnctl pipe 1002 config bw 1Byte/s
+
+	jexec alcatraz pfctl -e
+	pft_set_rules alcatraz \
+		"ether pass in from 02:00:00:00:00:01 l3 all dnpipe 1001" \
+		"ether pass out to 02:00:00:00:00:01 l3 all dnpipe 1002 " \
+		"pass all" \
+		"pass in inet proto icmp icmp-type echoreq divert-to 127.0.0.1 port 1001 no state" \
+		"pass out inet proto icmp icmp-type echorep divert-to 127.0.0.1 port 1002 no state"
+
+	jexec alcatraz $(atf_get_srcdir)/divapp 1001 divert-back &
+	indivapp_pid=$!
+	jexec alcatraz $(atf_get_srcdir)/divapp 1002 divert-back &
+	outdivapp_pid=$!
+	# Wait for the divappS to be ready
+	sleep 1
+
+	atf_check -s not-exit:0 -o ignore ping -c1 -s56 -t1 192.0.2.2
+
+	wait $indivapp_pid
+	atf_check_not_equal 0 $?
+	wait $outdivapp_pid
+	atf_check_not_equal 0 $?
+
+	# }
+
+	# b) ping should NOT time out due to wide enough dummynet pipes {
+
+	jexec alcatraz dnctl pipe 2001 config bw 100KByte/s
+	jexec alcatraz dnctl pipe 2002 config bw 100KByte/s
+
+	jexec alcatraz pfctl -e
+	pft_set_rules alcatraz \
+		"ether pass in from 02:00:00:00:00:01 l3 all dnpipe 2001" \
+		"ether pass out to 02:00:00:00:00:01 l3 all dnpipe 2002 " \
+		"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 echorep divert-to 127.0.0.1 port 2002 no state"
+
+	jexec alcatraz $(atf_get_srcdir)/divapp 2001 divert-back &
+	indivapp_pid=$!
+	jexec alcatraz $(atf_get_srcdir)/divapp 2002 divert-back &
+	outdivapp_pid=$!
+	# Wait for the divappS to be ready
+	sleep 1
+
+	atf_check -s exit:0 -o ignore ping -c1 -s56 -t1 192.0.2.2
+
+	wait $indivapp_pid
+	atf_check_equal 0 $?
+	wait $outdivapp_pid
+	atf_check_equal 0 $?
+
+	# }
+}
+ipfwoff_in_dn_in_div_in_out_dn_out_div_out_cleanup()
+{
+	pft_cleanup
+}
+
+atf_test_case "ipfwon_in_dn_in_div_in_out_dn_out_div_out" "cleanup"
+ipfwon_in_dn_in_div_in_out_dn_out_div_out_head()
+{
+	atf_set descr 'Test inbound > delayed+diverted > outbound > delayed+diverted > outbound | network terminated, with ipfw enabled'
+	atf_set require.user root
+}
+ipfwon_in_dn_in_div_in_out_dn_out_div_out_body()
+{
+	ipfwoff_in_dn_in_div_in_out_dn_out_div_out_body "ipfwon"
+}
+ipfwon_in_dn_in_div_in_out_dn_out_div_out_cleanup()
+{
+	pft_cleanup
+}
+
 atf_init_test_cases()
 {
 	atf_add_test_case "ipfwoff_in_div"
@@ -410,4 +523,7 @@ atf_init_test_cases()
 
 	atf_add_test_case "ipfwoff_in_div_in_fwd_out_div_out"
 	atf_add_test_case "ipfwon_in_div_in_fwd_out_div_out"
+
+	atf_add_test_case "ipfwoff_in_dn_in_div_in_out_dn_out_div_out"
+	atf_add_test_case "ipfwon_in_dn_in_div_in_out_dn_out_div_out"
 }



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?202311201304.3AKD4RjV089636>