diff --git a/sys/netpfil/pf/pf.c b/sys/netpfil/pf/pf.c --- a/sys/netpfil/pf/pf.c +++ b/sys/netpfil/pf/pf.c @@ -303,6 +303,8 @@ 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 @@ /* 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 @@ 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 @@ 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 @@ 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 @@ /* 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 @@ 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 --- 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 @@ 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 @@ 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_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" }