Date: Wed, 24 Sep 2025 11:45:20 GMT From: Kristof Provost <kp@FreeBSD.org> To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org Subject: git: 7ec06143964a - main - pf: pass pre-NAT addresses to dummynet Message-ID: <202509241145.58OBjKI8083150@gitrepo.freebsd.org>
next in thread | raw e-mail | index | archive | help
The branch main has been updated by kp: URL: https://cgit.FreeBSD.org/src/commit/?id=7ec06143964a949ebf6885ac120fdf839ad29eab commit 7ec06143964a949ebf6885ac120fdf839ad29eab Author: Kristof Provost <kp@FreeBSD.org> AuthorDate: 2025-09-04 12:49:00 +0000 Commit: Kristof Provost <kp@FreeBSD.org> CommitDate: 2025-09-24 11:44:54 +0000 pf: pass pre-NAT addresses to dummynet When we're NAT-ing give dummynet (via its struct ip_fw_args) the pre-NAT source and destination addresses. That's what we used to do, but that got unintentionally changed during the nat64 work. The pre-NAT addresses make somewhat more sense, in that it enables limiting based on specific LAN clients. See also: https://redmine.pfsense.org/issues/15770 Sponsored by: Rubicon Communications, LLC ("Netgate") --- sys/netpfil/pf/pf.c | 39 +++++++++++++++++++++++++++++++++------ tests/sys/netpfil/pf/nat.sh | 45 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 78 insertions(+), 6 deletions(-) diff --git a/sys/netpfil/pf/pf.c b/sys/netpfil/pf/pf.c index a57905df0c69..be00aff1f5cb 100644 --- a/sys/netpfil/pf/pf.c +++ b/sys/netpfil/pf/pf.c @@ -9760,6 +9760,7 @@ pf_pdesc_to_dnflow(const struct pf_pdesc *pd, const struct pf_krule *r, const struct pf_kstate *s, struct ip_fw_args *dnflow) { int dndir = r->direction; + sa_family_t af = pd->naf; if (s && dndir == PF_INOUT) { dndir = s->direction; @@ -9800,20 +9801,46 @@ pf_pdesc_to_dnflow(const struct pf_pdesc *pd, const struct pf_krule *r, dnflow->f_id.proto = pd->proto; dnflow->f_id.extra = dnflow->rule.info; - switch (pd->naf) { + if (s) + af = s->key[PF_SK_STACK]->af; + + switch (af) { case AF_INET: dnflow->f_id.addr_type = 4; - dnflow->f_id.src_ip = ntohl(pd->src->v4.s_addr); - dnflow->f_id.dst_ip = ntohl(pd->dst->v4.s_addr); + if (s) { + dnflow->f_id.src_ip = htonl( + s->key[PF_SK_STACK]->addr[pd->sidx].v4.s_addr); + dnflow->f_id.dst_ip = htonl( + s->key[PF_SK_STACK]->addr[pd->didx].v4.s_addr); + } else { + dnflow->f_id.src_ip = ntohl(pd->src->v4.s_addr); + dnflow->f_id.dst_ip = ntohl(pd->dst->v4.s_addr); + } break; case AF_INET6: - dnflow->flags |= IPFW_ARGS_IP6; dnflow->f_id.addr_type = 6; - dnflow->f_id.src_ip6 = pd->src->v6; - dnflow->f_id.dst_ip6 = pd->dst->v6; + + if (s) { + dnflow->f_id.src_ip6 = + s->key[PF_SK_STACK]->addr[pd->sidx].v6; + dnflow->f_id.dst_ip6 = + s->key[PF_SK_STACK]->addr[pd->didx].v6; + } else { + dnflow->f_id.src_ip6 = pd->src->v6; + dnflow->f_id.dst_ip6 = pd->dst->v6; + } break; } + /* + * Separate this out, because while we pass the pre-NAT addresses to + * dummynet we want the post-nat address family in case of nat64. + * Dummynet may call ip_output/ip6_output itself, and we need it to + * call the correct one. + */ + if (pd->naf == AF_INET6) + dnflow->flags |= IPFW_ARGS_IP6; + return (true); } diff --git a/tests/sys/netpfil/pf/nat.sh b/tests/sys/netpfil/pf/nat.sh index 5ea1dd6d8b2f..170d813d57fe 100644 --- a/tests/sys/netpfil/pf/nat.sh +++ b/tests/sys/netpfil/pf/nat.sh @@ -810,6 +810,50 @@ empty_pool_cleanup() pft_cleanup } +atf_test_case "dummynet_mask" "cleanup" +dummynet_mask_head() +{ + atf_set descr 'Verify that dummynet uses the pre-nat address for masking' + atf_set require.user root +} + +dummynet_mask_body() +{ + dummynet_init + + epair_srv=$(vnet_mkepair) + epair_cl=$(vnet_mkepair) + + ifconfig ${epair_cl}b 192.0.2.2/24 up + route add default 192.0.2.1 + + vnet_mkjail srv ${epair_srv}a + jexec srv ifconfig ${epair_srv}a 198.51.100.2/24 up + + vnet_mkjail gw ${epair_srv}b ${epair_cl}a + jexec gw ifconfig ${epair_srv}b 198.51.100.1/24 up + jexec gw ifconfig ${epair_cl}a 192.0.2.1/24 up + jexec gw sysctl net.inet.ip.forwarding=1 + + jexec gw dnctl pipe 1 config delay 100 mask src-ip 0xffffff00 + jexec gw pfctl -e + pft_set_rules gw \ + "nat pass on ${epair_srv}b inet from 192.0.2.0/24 to any -> (${epair_srv}b)" \ + "pass out dnpipe 1" + + atf_check -s exit:0 -o ignore \ + ping -c 3 198.51.100.2 + + # Now check that dummynet looked at the correct address + atf_check -s exit:0 -o match:"ip.*192.0.2.0/0" \ + jexec gw dnctl pipe show +} + +dummynet_mask_cleanup() +{ + pft_cleanup +} + atf_init_test_cases() { atf_add_test_case "exhaust" @@ -828,4 +872,5 @@ atf_init_test_cases() atf_add_test_case "binat_compat" atf_add_test_case "binat_match" atf_add_test_case "empty_pool" + atf_add_test_case "dummynet_mask" }
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?202509241145.58OBjKI8083150>