The branch main has been updated by kp:

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

commit 041ce1d690f1c58bd4a93ab808ded53444af2092
Author:     Kristof Provost <k...@freebsd.org>
AuthorDate: 2025-07-04 21:34:48 +0000
Commit:     Kristof Provost <k...@freebsd.org>
CommitDate: 2025-07-09 08:57:50 +0000

    pfctl: recursively flush rules and tables
    
    The recursive operation ("pfctl -a '*' ...") works for '-s' option already. 
This
    change enables the same thing for '-F' option, so "pfctl -a '*' -Fa" will
    flush everything from PF driver.
    
    The idea was discussed with many on tech@ in spring 2019.
    
    OK kn@
    
    Obtained from:  OpenBSD, sashan <sas...@openbsd.org>, ae711728d4
    Obtained from:  OpenBSD, sashan <sas...@openbsd.org>, 7abd52e24a
    Sponsored by:   Rubicon Communications, LLC ("Netgate")
---
 sbin/pfctl/pfctl.8        |   9 +-
 sbin/pfctl/pfctl.c        | 289 +++++++++++++++++++++++++++++++++++++++-------
 sbin/pfctl/pfctl.h        |  12 +-
 sbin/pfctl/pfctl_osfp.c   |   2 +-
 sbin/pfctl/pfctl_parser.h |   1 +
 sbin/pfctl/pfctl_table.c  |  15 ++-
 6 files changed, 277 insertions(+), 51 deletions(-)

diff --git a/sbin/pfctl/pfctl.8 b/sbin/pfctl/pfctl.8
index 28efff896956..2566bca57a28 100644
--- a/sbin/pfctl/pfctl.8
+++ b/sbin/pfctl/pfctl.8
@@ -24,7 +24,7 @@
 .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 .\"
-.Dd July 2, 2025
+.Dd July 4, 2025
 .Dt PFCTL 8
 .Os
 .Sh NAME
@@ -186,6 +186,13 @@ as the anchor name:
 .Bd -literal -offset indent
 # pfctl -a '*' -sr
 .Ed
+.Pp
+To flush all rulesets and tables recursively, specify only
+.Sq *
+as the anchor name:
+.Bd -literal -offset indent
+# pfctl -a '*' -Fa
+.Ed
 .It Fl D Ar macro Ns = Ns Ar value
 Define
 .Ar macro
diff --git a/sbin/pfctl/pfctl.c b/sbin/pfctl/pfctl.c
index 8c6d6833dd3b..254bd054ae4a 100644
--- a/sbin/pfctl/pfctl.c
+++ b/sbin/pfctl/pfctl.c
@@ -59,6 +59,8 @@
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
+#include <stdarg.h>
+#include <libgen.h>
 
 #include "pfctl_parser.h"
 #include "pfctl.h"
@@ -72,7 +74,7 @@ void   pfctl_check_skip_ifaces(char *);
 void    pfctl_adjust_skip_ifaces(struct pfctl *);
 void    pfctl_clear_interface_flags(int, int);
 void    pfctl_flush_eth_rules(int, int, char *);
-void    pfctl_flush_rules(int, int, char *);
+int     pfctl_flush_rules(int, int, char *);
 void    pfctl_flush_nat(int, int, char *);
 int     pfctl_clear_altq(int, int);
 void    pfctl_clear_src_nodes(int, int);
@@ -124,6 +126,17 @@ int         pfctl_load_ruleset(struct pfctl *, char *,
 int     pfctl_load_rule(struct pfctl *, char *, struct pfctl_rule *, int);
 const char     *pfctl_lookup_option(char *, const char * const *);
 void    pfctl_reset(int, int);
+int     pfctl_walk_show(int, struct pfioc_ruleset *, void *);
+int     pfctl_walk_get(int, struct pfioc_ruleset *, void *);
+int     pfctl_walk_anchors(int, int, const char *,
+           int(*)(int, struct pfioc_ruleset *, void *), void *);
+struct pfr_anchors *
+        pfctl_get_anchors(int, char *, int);
+int     pfctl_recurse(int, int, char *,
+           int(*)(int, int, struct pfr_anchoritem *));
+int     pfctl_call_clearrules(int, int, struct pfr_anchoritem *);
+int     pfctl_call_cleartables(int, int, struct pfr_anchoritem *);
+int     pfctl_call_clearanchors(int, int, struct pfr_anchoritem *);
 
 static struct pfctl_anchor_global       pf_anchors;
 struct pfctl_anchor     pf_main_anchor;
@@ -151,6 +164,7 @@ int                  dev = -1;
 struct pfctl_handle    *pfh = NULL;
 static int              first_title = 1;
 static int              labels = 0;
+static int              exit_val = 0;
 
 #define INDENT(d, o)   do {                                            \
                                if (o) {                                \
@@ -269,6 +283,40 @@ usage(void)
        exit(1);
 }
 
+void
+pfctl_err(int opts, int eval, const char *fmt, ...)
+{
+       va_list ap;
+
+       va_start(ap, fmt);
+
+       if ((opts & PF_OPT_IGNFAIL) == 0)
+               verr(eval, fmt, ap);
+       else
+               vwarn(fmt, ap);
+
+       va_end(ap);
+
+       exit_val = eval;
+}
+
+void
+pfctl_errx(int opts, int eval, const char *fmt, ...)
+{
+       va_list ap;
+
+       va_start(ap, fmt);
+
+       if ((opts & PF_OPT_IGNFAIL) == 0)
+               verrx(eval, fmt, ap);
+       else
+               vwarnx(fmt, ap);
+
+       va_end(ap);
+
+       exit_val = eval;
+}
+
 /*
  * Cache protocol number to name translations.
  *
@@ -361,7 +409,7 @@ pfctl_clear_stats(struct pfctl_handle *h, int opts)
 {
        int ret;
        if ((ret = pfctl_clear_status(h)) != 0)
-               errc(1, ret, "DIOCCLRSTATUS");
+               pfctl_err(opts, 1, "DIOCCLRSTATUS");
        if ((opts & PF_OPT_QUIET) == 0)
                fprintf(stderr, "pf: statistics cleared\n");
 }
@@ -469,16 +517,19 @@ pfctl_flush_eth_rules(int dev, int opts, char *anchorname)
                fprintf(stderr, "Ethernet rules cleared\n");
 }
 
-void
+int
 pfctl_flush_rules(int dev, int opts, char *anchorname)
 {
        int ret;
 
        ret = pfctl_clear_rules(dev, anchorname);
-       if (ret != 0)
-               err(1, "pfctl_clear_rules");
-       if ((opts & PF_OPT_QUIET) == 0)
+       if (ret != 0) {
+               pfctl_err(opts, 1, "%s", __func__);
+               return (1);
+       } else if ((opts & PF_OPT_QUIET) == 0)
                fprintf(stderr, "rules cleared\n");
+
+       return (0);
 }
 
 void
@@ -515,7 +566,7 @@ void
 pfctl_clear_src_nodes(int dev, int opts)
 {
        if (ioctl(dev, DIOCCLRSRCNODES))
-               err(1, "DIOCCLRSRCNODES");
+               pfctl_err(opts, 1, "DIOCCLRSRCNODES");
        if ((opts & PF_OPT_QUIET) == 0)
                fprintf(stderr, "source tracking entries cleared\n");
 }
@@ -530,13 +581,13 @@ pfctl_clear_iface_states(int dev, const char *iface, int 
opts)
        memset(&kill, 0, sizeof(kill));
        if (iface != NULL && strlcpy(kill.ifname, iface,
            sizeof(kill.ifname)) >= sizeof(kill.ifname))
-               errx(1, "invalid interface: %s", iface);
+               pfctl_errx(opts, 1, "invalid interface: %s", iface);
 
        if (opts & PF_OPT_KILLMATCH)
                kill.kill_match = true;
 
        if ((ret = pfctl_clear_states_h(pfh, &kill, &killed)) != 0)
-               errc(1, ret, "DIOCCLRSTATES");
+               pfctl_err(opts, 1, "DIOCCLRSTATUS");
        if ((opts & PF_OPT_QUIET) == 0)
                fprintf(stderr, "%d states cleared\n", killed);
 }
@@ -687,7 +738,7 @@ pfctl_net_kill_states(int dev, const char *iface, int opts)
        memset(&last_dst, 0xff, sizeof(last_dst));
        if (iface != NULL && strlcpy(kill.ifname, iface,
            sizeof(kill.ifname)) >= sizeof(kill.ifname))
-               errx(1, "invalid interface: %s", iface);
+               pfctl_errx(opts, 1, "invalid interface: %s", iface);
 
        if (state_killers == 2 && (strcmp(state_kill[0], "nat") == 0)) {
                kill.nat = true;
@@ -740,13 +791,13 @@ pfctl_net_kill_states(int dev, const char *iface, int 
opts)
                                    resp[1]->ai_addr);
 
                                if ((ret = pfctl_kill_states_h(pfh, &kill, 
&newkilled)) != 0)
-                                       errc(1, ret, "DIOCKILLSTATES");
+                                       pfctl_errx(opts, 1, "DIOCKILLSTATES");
                                killed += newkilled;
                        }
                        freeaddrinfo(res[1]);
                } else {
                        if ((ret = pfctl_kill_states_h(pfh, &kill, &newkilled)) 
!= 0)
-                               errc(1, ret, "DIOCKILLSTATES");
+                               pfctl_errx(opts, 1, "DIOCKILLSTATES");
                        killed += newkilled;
                }
        }
@@ -778,7 +829,7 @@ pfctl_gateway_kill_states(int dev, const char *iface, int 
opts)
        memset(&last_src, 0xff, sizeof(last_src));
        if (iface != NULL && strlcpy(kill.ifname, iface,
            sizeof(kill.ifname)) >= sizeof(kill.ifname))
-               errx(1, "invalid interface: %s", iface);
+               pfctl_errx(opts, 1, "invalid interface: %s", iface);
 
        if (opts & PF_OPT_KILLMATCH)
                kill.kill_match = true;
@@ -799,7 +850,7 @@ pfctl_gateway_kill_states(int dev, const char *iface, int 
opts)
                copy_satopfaddr(&kill.rt_addr.addr.v.a.addr,
                    resp->ai_addr);
                if (pfctl_kill_states_h(pfh, &kill, &newkilled))
-                       err(1, "DIOCKILLSTATES");
+                       pfctl_errx(opts, 1, "DIOCKILLSTATES");
                killed += newkilled;
        }
 
@@ -823,7 +874,7 @@ pfctl_label_kill_states(int dev, const char *iface, int 
opts)
        memset(&kill, 0, sizeof(kill));
        if (iface != NULL && strlcpy(kill.ifname, iface,
            sizeof(kill.ifname)) >= sizeof(kill.ifname))
-               errx(1, "invalid interface: %s", iface);
+               pfctl_errx(opts, 1, "invalid interface: %s", iface);
 
        if (opts & PF_OPT_KILLMATCH)
                kill.kill_match = true;
@@ -833,7 +884,7 @@ pfctl_label_kill_states(int dev, const char *iface, int 
opts)
                errx(1, "label too long: %s", state_kill[1]);
 
        if ((ret = pfctl_kill_states_h(pfh, &kill, &killed)) != 0)
-               errc(1, ret, "DIOCKILLSTATES");
+               pfctl_errx(opts, 1, "DIOCKILLSTATES");
 
        if ((opts & PF_OPT_QUIET) == 0)
                fprintf(stderr, "killed %d states\n", killed);
@@ -871,7 +922,7 @@ pfctl_id_kill_states(int dev, const char *iface, int opts)
        }
 
        if ((ret = pfctl_kill_states_h(pfh, &kill, &killed)) != 0)
-               errc(1, ret, "DIOCKILLSTATES");
+               pfctl_errx(opts, 1, "DIOCKILLSTATES");
 
        if ((opts & PF_OPT_QUIET) == 0)
                fprintf(stderr, "killed %d states\n", killed);
@@ -895,7 +946,7 @@ pfctl_key_kill_states(int dev, const char *iface, int opts)
        if (iface != NULL &&
            strlcpy(kill.ifname, iface, sizeof(kill.ifname)) >=
            sizeof(kill.ifname))
-               errx(1, "invalid interface: %s", iface);
+               pfctl_errx(opts, 1, "invalid interface: %s", iface);
 
        s = strdup(state_kill[1]);
        if (!s)
@@ -931,7 +982,7 @@ pfctl_key_kill_states(int dev, const char *iface, int opts)
                errx(1, "invalid host: %s", tokens[didx]);
 
        if ((ret = pfctl_kill_states_h(pfh, &kill, &killed)) != 0)
-               errc(1, ret, "DIOCKILLSTATES");
+               pfctl_errx(opts, 1, "DIOCKILLSTATES");
 
        if ((opts & PF_OPT_QUIET) == 0)
                fprintf(stderr, "killed %d states\n", killed);
@@ -2805,7 +2856,7 @@ pfctl_set_interface_flags(struct pfctl *pf, char *ifname, 
int flags, int how)
        if ((pf->opts & PF_OPT_NOACTION) == 0) {
                if (how == 0) {
                        if (ioctl(pf->dev, DIOCCLRIFFLAG, &pi))
-                               err(1, "DIOCCLRIFFLAG");
+                               pfctl_err(pf->opts, 1, "DIOCCLRIFFLAG");
                } else {
                        if (ioctl(pf->dev, DIOCSETIFFLAG, &pi))
                                err(1, "DIOCSETIFFLAG");
@@ -2864,17 +2915,56 @@ pfctl_test_altqsupport(int dev, int opts)
 }
 
 int
-pfctl_show_anchors(int dev, int opts, char *anchorname)
+pfctl_walk_show(int opts, struct pfioc_ruleset *pr, void *warg)
+{
+       if (pr->path[0]) {
+               if (pr->path[0] != '_' || (opts & PF_OPT_VERBOSE))
+                       printf("  %s/%s\n", pr->path, pr->name);
+       } else if (pr->name[0] != '_' || (opts & PF_OPT_VERBOSE))
+               printf("  %s\n", pr->name);
+
+       return (0);
+}
+
+int
+pfctl_walk_get(int opts, struct pfioc_ruleset *pr, void *warg)
+{
+       struct pfr_anchoritem *pfra;
+       struct pfr_anchors *anchors;
+       int e;
+
+       anchors = (struct pfr_anchors *)warg;
+
+       pfra = malloc(sizeof(*pfra));
+       if (pfra == NULL)
+               err(1, "%s", __func__);
+
+       if (pr->path[0])
+               e = asprintf(&pfra->pfra_anchorname, "%s/%s", pr->path,
+                   pr->name);
+       else
+               e = asprintf(&pfra->pfra_anchorname, "%s", pr->name);
+
+       if (e == -1)
+               err(1, "%s", __func__);
+
+       SLIST_INSERT_HEAD(anchors, pfra, pfra_sle);
+
+       return (0);
+}
+
+int
+pfctl_walk_anchors(int dev, int opts, const char *anchor,
+    int(walkf)(int, struct pfioc_ruleset *, void *), void *warg)
 {
        struct pfioc_ruleset     pr;
        u_int32_t                mnr, nr;
        int                      ret;
 
        memset(&pr, 0, sizeof(pr));
-       if ((ret = pfctl_get_rulesets(pfh, anchorname, &mnr)) != 0) {
+       if ((ret = pfctl_get_rulesets(pfh, anchor, &mnr)) != 0) {
                if (ret == EINVAL)
-                       fprintf(stderr, "Anchor '%s' not found.\n",
-                           anchorname);
+                       fprintf(stderr, "Anchor '%s' not found.\n", anchor);
                else
                        errc(1, ret, "DIOCGETRULESETS");
                return (-1);
@@ -2882,24 +2972,122 @@ pfctl_show_anchors(int dev, int opts, char *anchorname)
        for (nr = 0; nr < mnr; ++nr) {
                char sub[MAXPATHLEN];
 
-               if ((ret = pfctl_get_ruleset(pfh, anchorname, nr, &pr)) != 0)
+               if ((ret = pfctl_get_ruleset(pfh, anchor, nr, &pr)) != 0)
                        errc(1, ret, "DIOCGETRULESET");
                if (!strcmp(pr.name, PF_RESERVED_ANCHOR))
                        continue;
                sub[0] = '\0';
-               if (pr.path[0]) {
-                       strlcat(sub, pr.path, sizeof(sub));
-                       strlcat(sub, "/", sizeof(sub));
-               }
-               strlcat(sub, pr.name, sizeof(sub));
-               if (sub[0] != '_' || (opts & PF_OPT_VERBOSE))
-                       printf("  %s\n", sub);
-               if ((opts & PF_OPT_VERBOSE) && pfctl_show_anchors(dev, opts, 
sub))
+               if (walkf(opts, &pr, warg))
+                       return (-1);
+
+               if (pr.path[0])
+                       snprintf(sub, sizeof(sub), "%s/%s", pr.path, pr.name);
+               else
+                       snprintf(sub, sizeof(sub), "%s", pr.name);
+               if (pfctl_walk_anchors(dev, opts, sub, walkf, warg))
                        return (-1);
        }
        return (0);
 }
 
+int
+pfctl_show_anchors(int dev, int opts, char *anchor)
+{
+       return (
+           pfctl_walk_anchors(dev, opts, anchor, pfctl_walk_show, NULL));
+}
+
+struct pfr_anchors *
+pfctl_get_anchors(int dev, char *anchor, int opts)
+{
+       struct pfioc_ruleset pr;
+       static struct pfr_anchors anchors;
+       char *n;
+
+       SLIST_INIT(&anchors);
+
+       memset(&pr, 0, sizeof(pr));
+       if (*anchor != '\0') {
+               n = dirname(anchor);
+               if (n[0] != '.' && n[1] != '\0')
+                       strlcpy(pr.path, n, sizeof(pr.path));
+               n = basename(anchor);
+               if (n != NULL)
+                       strlcpy(pr.name, n, sizeof(pr.name));
+       }
+
+       /* insert a root anchor first. */
+       pfctl_walk_get(opts, &pr, &anchors);
+
+       if (pfctl_walk_anchors(dev, opts, anchor, pfctl_walk_get, &anchors))
+               errx(1, "%s failed to retrieve list of anchors, can't continue",
+                   __func__);
+
+       return (&anchors);
+}
+
+int
+pfctl_call_cleartables(int dev, int opts, struct pfr_anchoritem *pfra)
+{
+       /*
+        * PF_OPT_QUIET makes pfctl_clear_tables() to stop printing number of
+        * tables cleared for given anchor.
+        */
+       opts |= PF_OPT_QUIET;
+       return ((pfctl_do_clear_tables(pfra->pfra_anchorname, opts) == -1) ?
+           1 : 0);
+}
+
+int
+pfctl_call_clearrules(int dev, int opts, struct pfr_anchoritem *pfra)
+{
+       /*
+        * PF_OPT_QUIET makes pfctl_clear_rules() to stop printing a 'rules
+        * cleared' message for every anchor it deletes.
+        */
+       opts |= PF_OPT_QUIET;
+       return (pfctl_flush_rules(dev, opts, pfra->pfra_anchorname));
+}
+
+int
+pfctl_call_clearanchors(int dev, int opts, struct pfr_anchoritem *pfra)
+{
+       int rv = 0;
+
+       rv |= pfctl_call_cleartables(dev, opts, pfra);
+       rv |= pfctl_call_clearrules(dev, opts, pfra);
+
+       return (rv);
+}
+
+int
+pfctl_recurse(int dev, int opts, char *anchorname,
+    int(*walkf)(int, int, struct pfr_anchoritem *))
+{
+       int                      rv = 0;
+       struct pfr_anchors      *anchors;
+       struct pfr_anchoritem   *pfra, *pfra_save;
+
+       anchors = pfctl_get_anchors(dev, anchorname, opts);
+       /*
+        * While traversing the list, pfctl_clear_*() must always return
+        * so that failures on one anchor do not prevent clearing others.
+        */
+       opts |= PF_OPT_IGNFAIL;
+       printf("Removing:\n");
+       SLIST_FOREACH_SAFE(pfra, anchors, pfra_sle, pfra_save) {
+               printf("  %s\n",
+                   (*pfra->pfra_anchorname == '\0') ? "<root>" :
+                                                      pfra->pfra_anchorname);
+               rv |= walkf(dev, opts, pfra);
+               SLIST_REMOVE(anchors, pfra, pfr_anchoritem, pfra_sle);
+               free(pfra->pfra_anchorname);
+               free(pfra);
+       }
+
+       return (rv);
+}
+
 int
 pfctl_show_eth_anchors(int dev, int opts, char *anchorname)
 {
@@ -2990,7 +3178,6 @@ pfctl_reset(int dev, int opts)
 int
 main(int argc, char *argv[])
 {
-       int      error = 0;
        int      ch;
        int      mode = O_RDONLY;
        int      opts = 0;
@@ -3218,7 +3405,7 @@ main(int argc, char *argv[])
 
        if (opts & PF_OPT_DISABLE)
                if (pfctl_disable(dev, opts))
-                       error = 1;
+                       exit_val = 1;
 
        if ((path = calloc(1, MAXPATHLEN)) == NULL)
                errx(1, "%s: calloc", __func__);
@@ -3259,7 +3446,7 @@ main(int argc, char *argv[])
                        pfctl_show_status(dev, opts);
                        break;
                case 'R':
-                       error = pfctl_show_running(dev);
+                       exit_val = pfctl_show_running(dev);
                        break;
                case 't':
                        pfctl_show_timeouts(dev, opts);
@@ -3321,7 +3508,11 @@ main(int argc, char *argv[])
                        pfctl_flush_eth_rules(dev, opts, anchorname);
                        break;
                case 'r':
-                       pfctl_flush_rules(dev, opts, anchorname);
+                       if (opts & PF_OPT_RECURSE)
+                               pfctl_recurse(dev, opts, anchorname,
+                                   pfctl_call_clearrules);
+                       else
+                               pfctl_flush_rules(dev, opts, anchorname);
                        break;
                case 'n':
                        pfctl_flush_nat(dev, opts, anchorname);
@@ -3347,7 +3538,13 @@ main(int argc, char *argv[])
                        pfctl_flush_eth_rules(dev, opts, anchorname);
                        pfctl_flush_rules(dev, opts, anchorname);
                        pfctl_flush_nat(dev, opts, anchorname);
-                       pfctl_do_clear_tables(anchorname, opts);
+                       if (opts & PF_OPT_RECURSE)
+                               pfctl_recurse(dev, opts, anchorname,
+                                   pfctl_call_clearanchors);
+                       else {
+                               pfctl_do_clear_tables(anchorname, opts);
+                               pfctl_flush_rules(dev, opts, anchorname);
+                       }
                        if (!*anchorname) {
                                pfctl_clear_altq(dev, opts);
                                pfctl_clear_iface_states(dev, ifaceopt, opts);
@@ -3361,7 +3558,11 @@ main(int argc, char *argv[])
                        pfctl_clear_fingerprints(dev, opts);
                        break;
                case 'T':
-                       pfctl_do_clear_tables(anchorname, opts);
+                       if ((opts & PF_OPT_RECURSE) == 0)
+                               pfctl_do_clear_tables(anchorname, opts);
+                       else
+                               pfctl_recurse(dev, opts, anchorname,
+                                   pfctl_call_cleartables);
                        break;
                case 'R':
                        pfctl_reset(dev, opts);
@@ -3385,7 +3586,7 @@ main(int argc, char *argv[])
                pfctl_kill_src_nodes(dev, opts);
 
        if (tblcmdopt != NULL) {
-               error = pfctl_table(argc, argv, tableopt,
+               exit_val = pfctl_table(argc, argv, tableopt,
                    tblcmdopt, rulesopt, anchorname, opts);
                rulesopt = NULL;
        }
@@ -3411,17 +3612,17 @@ main(int argc, char *argv[])
        if (rulesopt != NULL && !(opts & PF_OPT_MERGE) &&
            !anchorname[0] && (loadopt & PFCTL_FLAG_OPTION))
                if (pfctl_file_fingerprints(dev, opts, PF_OSFP_FILE))
-                       error = 1;
+                       exit_val = 1;
 
        if (rulesopt != NULL) {
                if (pfctl_rules(dev, rulesopt, opts, optimize,
                    anchorname, NULL))
-                       error = 1;
+                       exit_val = 1;
        }
 
        if (opts & PF_OPT_ENABLE)
                if (pfctl_enable(dev, opts))
-                       error = 1;
+                       exit_val = 1;
 
        if (debugopt != NULL) {
                switch (*debugopt) {
@@ -3440,5 +3641,5 @@ main(int argc, char *argv[])
                }
        }
 
-       exit(error);
+       exit(exit_val);
 }
diff --git a/sbin/pfctl/pfctl.h b/sbin/pfctl/pfctl.h
index d8196c129187..5b5b3d3e5fff 100644
--- a/sbin/pfctl/pfctl.h
+++ b/sbin/pfctl/pfctl.h
@@ -55,6 +55,13 @@ struct pfr_buffer {
            (var) != NULL;                              \
            (var) = pfr_buf_next((buf), (var)))
 
+struct pfr_anchoritem {
+       SLIST_ENTRY(pfr_anchoritem)      pfra_sle;
+       char                            *pfra_anchorname;
+};
+
+SLIST_HEAD(pfr_anchors, pfr_anchoritem);
+
 int     pfr_get_fd(void);
 int     pfr_add_table(struct pfr_table *, int *, int);
 int     pfr_del_table(struct pfr_table *, int *, int);
@@ -81,7 +88,7 @@ int    pfi_get_ifaces(const char *, struct pfi_kif *, int *);
 int     pfi_clr_istats(const char *, int *, int);
 
 void    pfctl_print_title(char *);
-void    pfctl_do_clear_tables(const char *, int);
+int     pfctl_do_clear_tables(const char *, int);
 void    pfctl_show_tables(const char *, int);
 int     pfctl_table(int, char *[], char *, const char *, char *,
            const char *, int);
@@ -150,4 +157,7 @@ void                 expand_label(char *, size_t, struct 
pfctl_rule *);
 
 const char *pfctl_proto2name(int);
 
+void    pfctl_err(int, int, const char *, ...);
+void    pfctl_errx(int, int, const char *, ...);
+
 #endif /* _PFCTL_H_ */
diff --git a/sbin/pfctl/pfctl_osfp.c b/sbin/pfctl/pfctl_osfp.c
index 3a94c2e8c81b..5770c8343a46 100644
--- a/sbin/pfctl/pfctl_osfp.c
+++ b/sbin/pfctl/pfctl_osfp.c
@@ -264,7 +264,7 @@ void
 pfctl_clear_fingerprints(int dev, int opts)
 {
        if (ioctl(dev, DIOCOSFPFLUSH))
-               err(1, "DIOCOSFPFLUSH");
+               pfctl_err(opts, 1, "DIOCOSFPFLUSH");
 }
 
 /* flush pfctl's view of the fingerprints */
diff --git a/sbin/pfctl/pfctl_parser.h b/sbin/pfctl/pfctl_parser.h
index b91d37c791ae..778105c2b96d 100644
--- a/sbin/pfctl/pfctl_parser.h
+++ b/sbin/pfctl/pfctl_parser.h
@@ -55,6 +55,7 @@
 #define PF_OPT_RECURSE         0x04000
 #define PF_OPT_KILLMATCH       0x08000
 #define PF_OPT_NODNS           0x10000
+#define PF_OPT_IGNFAIL         0x20000
 
 #define PF_NAT_PROXY_PORT_LOW  50001
 #define PF_NAT_PROXY_PORT_HIGH 65535
diff --git a/sbin/pfctl/pfctl_table.c b/sbin/pfctl/pfctl_table.c
index 0842b042df41..0b52f88eafbb 100644
--- a/sbin/pfctl/pfctl_table.c
+++ b/sbin/pfctl/pfctl_table.c
@@ -79,7 +79,8 @@ static const char     *istats_text[2][2][2] = {
                if ((!(opts & PF_OPT_NOACTION) ||       \
                    (opts & PF_OPT_DUMMYACTION)) &&     \
                    (fct)) {                            \
-                       radix_perror();                 \
+               if ((opts & PF_OPT_RECURSE) == 0)       \
+                               radix_perror();         \
                        goto _error;                    \
                }                                       \
        } while (0)
@@ -103,11 +104,17 @@ static const char *istats_text[2][2][2] = {
                table.pfrt_flags &= ~PFR_TFLAG_PERSIST;                 \
        } while(0)
 
-void
+int
 pfctl_do_clear_tables(const char *anchor, int opts)
 {
-       if (pfctl_table(0, NULL, NULL, "-F", NULL, anchor, opts))
-               exit(1);
+       int     rv;
+
+       if ((rv = pfctl_table(0, NULL, NULL, "-F", NULL, anchor, opts)) == -1) {
+               if ((opts & PF_OPT_IGNFAIL) == 0)
+                       exit(1);
+       }
+
+       return (rv);
 }
 
 void

Reply via email to