The branch main has been updated by kp:

URL: 
https://cgit.FreeBSD.org/src/commit/?id=58164dcb55d62ca73b5e550b8344bf61e2d8a47a

commit 58164dcb55d62ca73b5e550b8344bf61e2d8a47a
Author:     Kristof Provost <k...@freebsd.org>
AuthorDate: 2025-04-02 16:04:46 +0000
Commit:     Kristof Provost <k...@freebsd.org>
CommitDate: 2025-04-02 19:11:47 +0000

    pfctl: fix recursive printing of NAT rules
    
    pfctl_show_nat() is called recursively to print nat anchors. This passes the
    anchor path, but this path was modified by pfctl_show_nat(), leading to 
issues
    printing the anchors.
    
    Make a copy of the path ('npath') before we modify it. Ensure we do this
    correctly by sprinking in 'const', and add a test case to verify that we do 
now
    print things correctly.
    
    Reported by:    Thomas Pasqualini <thomas.pasqual...@orange.com>
    MFC after:      2 weeks
    Sponsored by:   Rubicon Communications, LLC ("Netgate")
---
 sbin/pfctl/pfctl.c             | 33 +++++++++++++++---------------
 tests/sys/netpfil/pf/anchor.sh | 46 ++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 63 insertions(+), 16 deletions(-)

diff --git a/sbin/pfctl/pfctl.c b/sbin/pfctl/pfctl.c
index 7e06f4136686..1c5b7f5e1fc0 100644
--- a/sbin/pfctl/pfctl.c
+++ b/sbin/pfctl/pfctl.c
@@ -93,12 +93,12 @@ int  pfctl_load_hostid(struct pfctl *, u_int32_t);
 int     pfctl_load_reassembly(struct pfctl *, u_int32_t);
 int     pfctl_load_syncookies(struct pfctl *, u_int8_t);
 int     pfctl_get_pool(int, struct pfctl_pool *, u_int32_t, u_int32_t, int,
-           char *, int);
+           const char *, int);
 void    pfctl_print_eth_rule_counters(struct pfctl_eth_rule *, int);
 void    pfctl_print_rule_counters(struct pfctl_rule *, int);
 int     pfctl_show_eth_rules(int, char *, int, enum pfctl_show, char *, int, 
int);
 int     pfctl_show_rules(int, char *, int, enum pfctl_show, char *, int, int);
-int     pfctl_show_nat(int, char *, int, char *, int, int);
+int     pfctl_show_nat(int, const char *, int, char *, int, int);
 int     pfctl_show_src_nodes(int, int);
 int     pfctl_show_states(int, const char *, int);
 int     pfctl_show_status(int, int);
@@ -956,7 +956,7 @@ pfctl_id_kill_states(int dev, const char *iface, int opts)
 
 int
 pfctl_get_pool(int dev, struct pfctl_pool *pool, u_int32_t nr,
-    u_int32_t ticket, int r_action, char *anchorname, int which)
+    u_int32_t ticket, int r_action, const char *anchorname, int which)
 {
        struct pfioc_pooladdr pp;
        struct pf_pooladdr *pa;
@@ -1438,7 +1438,7 @@ pfctl_show_rules(int dev, char *path, int opts, enum 
pfctl_show format,
 }
 
 int
-pfctl_show_nat(int dev, char *path, int opts, char *anchorname, int depth,
+pfctl_show_nat(int dev, const char *path, int opts, char *anchorname, int 
depth,
     int wildcard)
 {
        struct pfctl_rules_info ri;
@@ -1461,16 +1461,17 @@ pfctl_show_nat(int dev, char *path, int opts, char 
*anchorname, int depth,
                p[0] = '\0';
        }
 
+       if ((npath = calloc(1, MAXPATHLEN)) == NULL)
+               errx(1, "pfctl_rules: calloc");
+
        if (anchorname[0] == '/') {
-               if ((npath = calloc(1, MAXPATHLEN)) == NULL)
-                       errx(1, "pfctl_rules: calloc");
                snprintf(npath, MAXPATHLEN, "%s", anchorname);
        } else {
-               if (path[0])
-                       snprintf(&path[len], MAXPATHLEN - len, "/%s", 
anchorname);
+               snprintf(npath, MAXPATHLEN, "%s", path);
+               if (npath[0])
+                       snprintf(&npath[len], MAXPATHLEN - len, "/%s", 
anchorname);
                else
-                       snprintf(&path[len], MAXPATHLEN - len, "%s", 
anchorname);
-               npath = path;
+                       snprintf(&npath[len], MAXPATHLEN - len, "%s", 
anchorname);
        }
 
        /*
@@ -1500,12 +1501,12 @@ pfctl_show_nat(int dev, char *path, int opts, char 
*anchorname, int depth,
                        INDENT(depth, !(opts & PF_OPT_VERBOSE));
                        printf("}\n");
                }
-               path[len] = '\0';
+               npath[len] = '\0';
                return (0);
        }
 
        for (i = 0; i < 3; i++) {
-               ret = pfctl_get_rules_info_h(pfh, &ri, nattype[i], path);
+               ret = pfctl_get_rules_info_h(pfh, &ri, nattype[i], npath);
                if (ret != 0) {
                        warnc(ret, "DIOCGETRULES");
                        return (-1);
@@ -1513,19 +1514,19 @@ pfctl_show_nat(int dev, char *path, int opts, char 
*anchorname, int depth,
                for (nr = 0; nr < ri.nr; ++nr) {
                        INDENT(depth, !(opts & PF_OPT_VERBOSE));
 
-                       if ((ret = pfctl_get_rule_h(pfh, nr, ri.ticket, path,
+                       if ((ret = pfctl_get_rule_h(pfh, nr, ri.ticket, npath,
                            nattype[i], &rule, anchor_call)) != 0) {
                                warnc(ret, "DIOCGETRULE");
                                return (-1);
                        }
                        if (pfctl_get_pool(dev, &rule.rdr, nr,
-                           ri.ticket, nattype[i], path, PF_RDR) != 0)
+                           ri.ticket, nattype[i], npath, PF_RDR) != 0)
                                return (-1);
                        if (pfctl_get_pool(dev, &rule.nat, nr,
-                           ri.ticket, nattype[i], path, PF_NAT) != 0)
+                           ri.ticket, nattype[i], npath, PF_NAT) != 0)
                                return (-1);
                        if (pfctl_get_pool(dev, &rule.route, nr,
-                           ri.ticket, nattype[i], path, PF_RT) != 0)
+                           ri.ticket, nattype[i], npath, PF_RT) != 0)
                                return (-1);
 
                        if (dotitle) {
diff --git a/tests/sys/netpfil/pf/anchor.sh b/tests/sys/netpfil/pf/anchor.sh
index 40e8354a2343..463cd4d475e3 100644
--- a/tests/sys/netpfil/pf/anchor.sh
+++ b/tests/sys/netpfil/pf/anchor.sh
@@ -316,6 +316,51 @@ counter_cleanup()
        pft_cleanup
 }
 
+atf_test_case "nat" "cleanup"
+nat_head()
+{
+       atf_set descr 'Test nested nat anchors'
+       atf_set require.user root
+}
+
+nat_body()
+{
+       pft_init
+
+       epair=$(vnet_mkepair)
+       vnet_mkjail alcatraz ${epair}a
+
+       ifconfig ${epair}b 192.0.2.2/24 up
+       jexec alcatraz ifconfig ${epair}a 192.0.2.1/24 up
+
+       # Sanity check
+       atf_check -s exit:0 -o ignore ping -c 1 192.0.2.1
+
+       jexec alcatraz pfctl -e
+       pft_set_rules alcatraz \
+           "nat-anchor \"foo/*\"" \
+           "pass"
+
+       echo "nat log on ${epair}a inet from 192.0.2.0/24 to any port = 53 -> 
192.0.2.1" \
+           | jexec alcatraz pfctl -a "foo/bar" -g -f -
+       echo "rdr on ${epair}a proto tcp to port echo -> 127.0.0.1 port echo" \
+           | jexec alcatraz pfctl -a "foo/baz" -g -f -
+
+       jexec alcatraz pfctl -sn -a "*"
+       jexec alcatraz pfctl -sn -a "foo/bar"
+       jexec alcatraz pfctl -sn -a "foo/baz"
+
+       atf_check -s exit:0 -o match:"nat log on epair0a inet from 192.0.2.0/24 
to any port = domain -> 192.0.2.1" \
+           jexec alcatraz pfctl -sn -a "*"
+       atf_check -s exit:0 -o match:"rdr on epair0a inet proto tcp from any to 
any port = echo -> 127.0.0.1 port 7" \
+           jexec alcatraz pfctl -sn -a "*"
+}
+
+nat_cleanup()
+{
+       pft_cleanup
+}
+
 atf_init_test_cases()
 {
        atf_add_test_case "pr183198"
@@ -326,4 +371,5 @@ atf_init_test_cases()
        atf_add_test_case "quick"
        atf_add_test_case "quick_nested"
        atf_add_test_case "counter"
+       atf_add_test_case "nat"
 }

Reply via email to