Skip site navigation (1)Skip section navigation (2)
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>