From 70cd0b4b03b0eee2668b1a88b2f1076407753dbf Mon Sep 17 00:00:00 2001 From: Kristof Provost Date: Tue, 28 May 2024 16:26:28 +0200 Subject: [PATCH] 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 985cbe0771c0..1cdcd1d0b912 100644 --- a/sbin/pfctl/pfctl.c +++ b/sbin/pfctl/pfctl.c @@ -96,7 +96,7 @@ int pfctl_get_pool(int, struct pfctl_pool *, u_int32_t, u_int32_t, int, char *); void pfctl_print_rule_counters(struct pfctl_rule *, 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); @@ -1220,7 +1220,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; @@ -1228,14 +1229,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); @@ -1244,7 +1296,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, @@ -1256,35 +1307,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); } } } @@ -2601,7 +2642,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, @@ -2629,7 +2670,7 @@ main(int argc, char *argv[]) opts |= PF_OPT_SHOWALL; pfctl_load_fingerprints(dev, opts); - 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);