git: c71487dfba1f - stable/14 - pfctl: fix recursive printing of nat anchors

From: Kristof Provost <kp_at_FreeBSD.org>
Date: Tue, 18 Jun 2024 14:47:22 UTC
The branch stable/14 has been updated by kp:

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

commit c71487dfba1f34bf06e3ae35890849bf0b454afc
Author:     Kristof Provost <kp@FreeBSD.org>
AuthorDate: 2024-05-28 14:26:28 +0000
Commit:     Kristof Provost <kp@FreeBSD.org>
CommitDate: 2024-06-18 07:57:42 +0000

    pfctl: fix recursive printing of nat anchors
    
    Similar to the preceding fix for rules, ensure that we
    recursively list wildcard anchors for nat rules.
    
    MFC after:      3 weeks
    Sponsored by:   Rubicon Communications, LLC ("Netgate")
    
    (cherry picked from commit 8ddd0359bca5c5fc38189000a80a3180854a8a2e)
---
 sbin/pfctl/pfctl.c | 103 +++++++++++++++++++++++++++++++++++++----------------
 1 file changed, 72 insertions(+), 31 deletions(-)

diff --git a/sbin/pfctl/pfctl.c b/sbin/pfctl/pfctl.c
index d5541571a135..621d97d6c555 100644
--- a/sbin/pfctl/pfctl.c
+++ b/sbin/pfctl/pfctl.c
@@ -98,7 +98,7 @@ 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	 pfctl_show_nat(int, 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);
@@ -1398,7 +1398,8 @@ 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, char *path, int opts, char *anchorname, int depth,
+    int wildcard)
 {
 	struct pfctl_rules_info ri;
 	struct pfctl_rule rule;
@@ -1406,14 +1407,65 @@ pfctl_show_nat(int dev, char *path, int opts, char *anchorname, int depth)
 	u_int32_t nr;
 	static int nattype[3] = { PF_NAT, PF_RDR, PF_BINAT };
 	int i, dotitle = opts & PF_OPT_SHOWALL;
-	int brace, ret;
+	int ret;
 	int len = strlen(path);
-	char *p;
+	char *npath, *p;
 
-	if (path[0])
-		snprintf(&path[len], MAXPATHLEN - len, "/%s", anchorname);
-	else
-		snprintf(&path[len], MAXPATHLEN - len, "%s", anchorname);
+	/*
+	 * Truncate a trailing / and * on an anchorname before searching for
+	 * the ruleset, this is syntactic sugar that doesn't actually make it
+	 * to the kernel.
+	 */
+	if ((p = strrchr(anchorname, '/')) != NULL &&
+	    p[1] == '*' && p[2] == '\0') {
+		p[0] = '\0';
+	}
+
+	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);
+		else
+			snprintf(&path[len], MAXPATHLEN - len, "%s", anchorname);
+		npath = path;
+	}
+
+	/*
+	 * If this anchor was called with a wildcard path, go through
+	 * the rulesets in the anchor rather than the rules.
+	 */
+	if (wildcard && (opts & PF_OPT_RECURSE)) {
+		struct pfioc_ruleset     prs;
+		u_int32_t                mnr, nr;
+		memset(&prs, 0, sizeof(prs));
+		memcpy(prs.path, npath, sizeof(prs.path));
+		if (ioctl(dev, DIOCGETRULESETS, &prs)) {
+			if (errno == EINVAL)
+				fprintf(stderr, "NAT anchor '%s' "
+				    "not found.\n", anchorname);
+			else
+				err(1, "DIOCGETRULESETS");
+		}
+		mnr = prs.nr;
+
+		pfctl_print_rule_counters(&rule, opts);
+		for (nr = 0; nr < mnr; ++nr) {
+			prs.nr = nr;
+			if (ioctl(dev, DIOCGETRULESET, &prs))
+				err(1, "DIOCGETRULESET");
+			INDENT(depth, !(opts & PF_OPT_VERBOSE));
+			printf("nat-anchor \"%s\" all {\n", prs.name);
+			pfctl_show_nat(dev, npath, opts,
+			    prs.name, depth + 1, 0);
+			INDENT(depth, !(opts & PF_OPT_VERBOSE));
+			printf("}\n");
+		}
+		path[len] = '\0';
+		return (0);
+	}
 
 	for (i = 0; i < 3; i++) {
 		ret = pfctl_get_rules_info(dev, &ri, nattype[i], path);
@@ -1422,7 +1474,6 @@ pfctl_show_nat(int dev, char *path, int opts, char *anchorname, int depth)
 			return (-1);
 		}
 		for (nr = 0; nr < ri.nr; ++nr) {
-			brace = 0;
 			INDENT(depth, !(opts & PF_OPT_VERBOSE));
 
 			if (pfctl_get_rule(dev, nr, ri.ticket, path,
@@ -1434,35 +1485,25 @@ pfctl_show_nat(int dev, char *path, int opts, char *anchorname, int depth)
 			    ri.ticket, nattype[i], path) != 0)
 				return (-1);
 
-			if (anchor_call[0] &&
-			   ((((p = strrchr(anchor_call, '_')) != NULL) &&
-			   (p == anchor_call ||
-			   *(--p) == '/')) || (opts & PF_OPT_RECURSE))) {
-				brace++;
-				if ((p = strrchr(anchor_call, '/')) !=
-				    NULL)
-					p++;
-				else
-					p = &anchor_call[0];
-			} else
-				p = &anchor_call[0];
-
 			if (dotitle) {
 				pfctl_print_title("TRANSLATION RULES:");
 				dotitle = 0;
 			}
 			print_rule(&rule, anchor_call,
 			    opts & PF_OPT_VERBOSE2, opts & PF_OPT_NUMERIC);
-			if (brace)
+			if (anchor_call[0] &&
+			    (((p = strrchr(anchor_call, '/')) ?
+			      p[1] == '_' : anchor_call[0] == '_') ||
+			     opts & PF_OPT_RECURSE)) {
 				printf(" {\n");
-			else
-				printf("\n");
-			pfctl_print_rule_counters(&rule, opts);
-			pfctl_clear_pool(&rule.rpool);
-			if (brace) {
-				pfctl_show_nat(dev, path, opts, p, depth + 1);
+				pfctl_print_rule_counters(&rule, opts);
+				pfctl_show_nat(dev, npath, opts, anchor_call,
+				    depth + 1, rule.anchor_wildcard);
 				INDENT(depth, !(opts & PF_OPT_VERBOSE));
 				printf("}\n");
+			} else {
+				printf("\n");
+				pfctl_print_rule_counters(&rule, opts);
 			}
 		}
 	}
@@ -3058,7 +3099,7 @@ main(int argc, char *argv[])
 			break;
 		case 'n':
 			pfctl_load_fingerprints(dev, opts);
-			pfctl_show_nat(dev, path, opts, anchorname, 0);
+			pfctl_show_nat(dev, path, opts, anchorname, 0, 0);
 			break;
 		case 'q':
 			pfctl_show_altq(dev, ifaceopt, opts,
@@ -3093,7 +3134,7 @@ main(int argc, char *argv[])
 			pfctl_show_eth_rules(dev, path, opts, 0, anchorname, 0,
 			    0);
 
-			pfctl_show_nat(dev, path, opts, anchorname, 0);
+			pfctl_show_nat(dev, path, opts, anchorname, 0, 0);
 			pfctl_show_rules(dev, path, opts, 0, anchorname, 0, 0);
 			pfctl_show_altq(dev, ifaceopt, opts, 0);
 			pfctl_show_states(dev, ifaceopt, opts);