git: fe3bb40b9e80 - main - pf: fix dummynet + ipdivert use case
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Fri, 17 Nov 2023 17:06:55 UTC
The branch main has been updated by kp: URL: https://cgit.FreeBSD.org/src/commit/?id=fe3bb40b9e807d4010617de1ef040ba3aa623487 commit fe3bb40b9e807d4010617de1ef040ba3aa623487 Author: Igor Ostapenko <pm@igoro.pro> AuthorDate: 2023-11-17 16:04:01 +0000 Commit: Kristof Provost <kp@FreeBSD.org> CommitDate: 2023-11-17 16:06:16 +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 --- 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" }