git: a983cea4e9a8 - main - pf: fix reply-to after rdr and dummynet
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Thu, 28 Mar 2024 16:06:41 UTC
The branch main has been updated by kp: URL: https://cgit.FreeBSD.org/src/commit/?id=a983cea4e9a8dcd52cfd6a3141d7aa03306b057b commit a983cea4e9a8dcd52cfd6a3141d7aa03306b057b Author: Kristof Provost <kp@FreeBSD.org> AuthorDate: 2024-03-27 14:47:21 +0000 Commit: Kristof Provost <kp@FreeBSD.org> CommitDate: 2024-03-28 16:06:01 +0000 pf: fix reply-to after rdr and dummynet If we redirect a packet to localhost and it gets dummynet'd it may be re-injected later (e.g. when delayed) which means it will be passed through ip_input() again. ip_input() will then reject the packet because it's directed to the loopback address, but did not arrive on a loopback interface. Fix this by having pf set the rcvif to V_iflo if we redirect to loopback. See also: https://redmine.pfsense.org/issues/15363 Sponsored by: Rubicon Communications, LLC ("Netgate") --- sys/netpfil/pf/pf.c | 12 ++++++++ tests/sys/netpfil/pf/route_to.sh | 61 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 73 insertions(+) diff --git a/sys/netpfil/pf/pf.c b/sys/netpfil/pf/pf.c index 50dc67b72439..4cec0936539e 100644 --- a/sys/netpfil/pf/pf.c +++ b/sys/netpfil/pf/pf.c @@ -7954,6 +7954,18 @@ pf_dummynet_route(struct pf_pdesc *pd, struct pf_kstate *s, sizeof(struct sockaddr_in6)); } + if (s != NULL && s->nat_rule.ptr != NULL && + s->nat_rule.ptr->action == PF_RDR && + ((pd->af == AF_INET && IN_LOOPBACK(ntohl(pd->dst->v4.s_addr))) || + (pd->af == AF_INET6 && IN6_IS_ADDR_LOOPBACK(&pd->dst->v6)))) { + /* + * If we're redirecting to loopback mark this packet + * as being local. Otherwise it might get dropped + * if dummynet re-injects. + */ + (*m0)->m_pkthdr.rcvif = V_loif; + } + if (pf_pdesc_to_dnflow(pd, r, s, &dnflow)) { pd->pf_mtag->flags |= PF_MTAG_FLAG_DUMMYNET; pd->pf_mtag->flags |= PF_MTAG_FLAG_DUMMYNETED; diff --git a/tests/sys/netpfil/pf/route_to.sh b/tests/sys/netpfil/pf/route_to.sh index 44fe6786e896..df95eaecc12e 100644 --- a/tests/sys/netpfil/pf/route_to.sh +++ b/tests/sys/netpfil/pf/route_to.sh @@ -626,6 +626,66 @@ ifbound_reply_to_v6_cleanup() pft_cleanup } +atf_test_case "ifbound_reply_to_rdr_dummynet" "cleanup" +ifbound_reply_to_rdr_dummynet_head() +{ + atf_set descr 'Test that reply-to states bind to the expected non-default-route interface after rdr and dummynet' + atf_set require.user root +} + +ifbound_reply_to_rdr_dummynet_body() +{ + dummynet_init + + j="route_to:ifbound_reply_to_rdr_dummynet" + + epair_one=$(vnet_mkepair) + epair_two=$(vnet_mkepair) + ifconfig ${epair_one}b inet 192.0.2.2/24 up + ifconfig ${epair_two}b up + + vnet_mkjail $j ${epair_one}a ${epair_two}a + jexec $j ifconfig lo0 inet 127.0.0.1/8 up + jexec $j ifconfig ${epair_one}a 192.0.2.1/24 up + jexec $j ifconfig ${epair_two}a 198.51.100.1/24 up + jexec $j route add default 198.51.100.254 + + jexec $j pfctl -e + jexec $j dnctl pipe 1 config delay 1 + pft_set_rules $j \ + "set state-policy if-bound" \ + "rdr on ${epair_one}a proto icmp from any to 192.0.2.1 -> 127.0.0.1" \ + "rdr on ${epair_two}a proto icmp from any to 198.51.100.1 -> 127.0.0.1" \ + "match in on ${epair_one}a inet all dnpipe (1, 1)" \ + "pass in on ${epair_one}a reply-to (${epair_one}a 192.0.2.2) inet from any to 127.0.0.1 keep state" + + atf_check -s exit:0 -o ignore \ + ping -c 3 192.0.2.1 + + atf_check -s exit:0 \ + ${common_dir}/pft_ping.py \ + --to 192.0.2.1 \ + --from 203.0.113.2 \ + --sendif ${epair_one}b \ + --replyif ${epair_one}b + + # pft_ping uses the same ID every time, so this will look like more traffic in the same state + atf_check -s exit:0 \ + ${common_dir}/pft_ping.py \ + --to 192.0.2.1 \ + --from 203.0.113.2 \ + --sendif ${epair_one}b \ + --replyif ${epair_one}b + + jexec $j pfctl -sr -vv + jexec $j pfctl -ss -vv +} + +ifbound_reply_to_rdr_dummynet_cleanup() +{ + pft_cleanup +} + atf_test_case "dummynet_frag" "cleanup" dummynet_frag_head() { @@ -740,6 +800,7 @@ atf_init_test_cases() atf_add_test_case "ifbound_v6" atf_add_test_case "ifbound_reply_to" atf_add_test_case "ifbound_reply_to_v6" + atf_add_test_case "ifbound_reply_to_rdr_dummynet" atf_add_test_case "dummynet_frag" atf_add_test_case "dummynet_double" }