Hello,

I'm giving up on identifying unreferenced/orphaned rulesets. It seems to me
like too much work/complexity, which invites more troubles. I prefer to keep
things simple, hence I'm proposing new modifier for Flush option:

    -F Nuke       Flush all rulesets and tables.

The option is meant to be used when things will get too messy.  It removes all
tables and rulesets from kernel allowing admin to quickly start from scratch
without doing reboot.

so here is my first question:

    does proposal for the 'Nuke' flush modifier sound good?
    or is it completely off?

Proposed change below splits current pfctl_show_anchors() to two functions:

    pfctl_walk_anchors(), which implements ruleset/anchor traversal

    pfctl_walk_show(), which prints full path to every anchor, while
        anchors are traversed. the pfctl_walk_show() is passed
        as argument to pfctl_walk_anchors()

The implementation of Nuke modifier comes with its own callback
pfctl_walk_get(), which collects anchors to SLIST during anchor tree
traversal. As soon as all anchors/rulesets are collected
we effectively apply:
    pfctl -a ... -FT
    pfctl -a ... -Fr
to every anchor/ruleset found in the list.

If you think the change is too big to be done in one step, then
I can split the diff to two steps:

    split pfctl_show_anchors() in the first step

    then send an updated diff, which just adds 'Nuke' modifier to F
    option.

thanks and
regards
sashan

--------8<---------------8<---------------8<------------------8<--------
diff --git a/sbin/pfctl/pfctl.8 b/sbin/pfctl/pfctl.8
index 48b2893cfcd..06e76e35b68 100644
--- a/sbin/pfctl/pfctl.8
+++ b/sbin/pfctl/pfctl.8
@@ -199,6 +199,8 @@ Flush the tables.
 Flush the passive operating system fingerprints.
 .It Fl F Cm all
 Flush all of the above.
+.It Fl F Cm Nuke
+Flush all rulesets and tables.
 .El
 .It Fl f Ar file
 Replace the current ruleset with
diff --git a/sbin/pfctl/pfctl.c b/sbin/pfctl/pfctl.c
index 493ff47af2f..fc909ade507 100644
--- a/sbin/pfctl/pfctl.c
+++ b/sbin/pfctl/pfctl.c
@@ -63,7 +63,7 @@ int    pfctl_disable(int, int);
 void    pfctl_clear_queues(struct pf_qihead *);
 void    pfctl_clear_stats(int, const char *, int);
 void    pfctl_clear_interface_flags(int, int);
-void    pfctl_clear_rules(int, int, char *);
+int     pfctl_clear_rules(int, int, char *);
 void    pfctl_clear_src_nodes(int, int);
 void    pfctl_clear_states(int, const char *, int);
 struct addrinfo *
@@ -105,6 +105,13 @@ int         pfctl_load_rule(struct pfctl *, char *, struct 
pf_rule *, int);
 const char     *pfctl_lookup_option(char *, const char **);
 void   pfctl_state_store(int, const char *);
 void   pfctl_state_load(int, const char *);
+int    pfctl_walk_show(int, struct pfioc_ruleset *, void *);
+int    pfctl_walk_get(int, struct pfioc_ruleset *, void *);
+int    pfctl_walk_anchors(int, int, char *, void *,
+    int(*)(int, struct pfioc_ruleset *, void *));
+struct pfr_anchors *
+       pfctl_get_anchors(int, int);
+int    pfctl_nuke_anchors(int, int);
 
 const char     *clearopt;
 char           *rulesopt;
@@ -205,7 +212,8 @@ static const struct {
 };
 
 static const char *clearopt_list[] = {
-       "rules", "Sources", "states", "info", "Tables", "osfp", "all", NULL
+       "rules", "Sources", "states", "info", "Tables", "osfp", "all",
+       "Nuke", NULL
 };
 
 static const char *showopt_list[] = {
@@ -232,7 +240,6 @@ static const char *optiopt_list[] = {
 struct pf_qihead qspecs = TAILQ_HEAD_INITIALIZER(qspecs);
 struct pf_qihead rootqs = TAILQ_HEAD_INITIALIZER(rootqs);
 
-
 __dead void
 usage(void)
 {
@@ -315,19 +322,29 @@ pfctl_clear_interface_flags(int dev, int opts)
        }
 }
 
-void
+int
 pfctl_clear_rules(int dev, int opts, char *anchorname)
 {
-       struct pfr_buffer t;
+       struct pfr_buffer       t;
+       int                     rv = 0;
 
        memset(&t, 0, sizeof(t));
        t.pfrb_type = PFRB_TRANS;
        if (pfctl_add_trans(&t, PF_TRANS_RULESET, anchorname) ||
            pfctl_trans(dev, &t, DIOCXBEGIN, 0) ||
-           pfctl_trans(dev, &t, DIOCXCOMMIT, 0))
-               err(1, "pfctl_clear_rules");
-       if ((opts & PF_OPT_QUIET) == 0)
+           pfctl_trans(dev, &t, DIOCXCOMMIT, 0)) {
+               if (opts & PF_OPT_IGNFAIL) {
+                       if ((opts & PF_OPT_QUIET) == 0) {
+                               warn("%s", __func__);
+                       }
+                       rv = 1;
+               } else {
+                       err(1, "%s", __func__);
+               }
+       } else if ((opts & PF_OPT_QUIET) == 0)
                fprintf(stderr, "rules cleared\n");
+
+       return (rv);
 }
 
 void
@@ -2107,13 +2124,59 @@ pfctl_debug(int dev, u_int32_t level, 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;
+       unsigned int len;
+       struct {
+               struct pfr_anchoritem *pfra;
+       } *tail_pfra = warg;
+
+       pfra = malloc(sizeof(*pfra));
+       if (pfra == NULL)
+               err(1, "%s", __func__);
+
+       len = strlen(pr->path) + 1;
+       len += strlen(pr->name) + 1;
+       pfra->pfra_anchorname = malloc(len);
+       if (pfra->pfra_anchorname == NULL)
+               err(1, "%s", __func__);
+
+       if (pr->path[0])
+               snprintf(pfra->pfra_anchorname, len, "%s/%s",
+                   pr->path, pr->name);
+       else
+               snprintf(pfra->pfra_anchorname, len, "%s", pr->name);
+
+       if (tail_pfra->pfra != NULL)
+               SLIST_INSERT_AFTER(tail_pfra->pfra, pfra, pfra_sle);
+
+       tail_pfra->pfra = pfra;
+
+       return (0);
+}
+
+int
+pfctl_walk_anchors(int dev, int opts, char *anchorname, void *warg,
+    int(walkf)(int, struct pfioc_ruleset *, void *))
 {
        struct pfioc_ruleset     pr;
        u_int32_t                mnr, nr;
 
        memset(&pr, 0, sizeof(pr));
-       memcpy(pr.path, anchorname, sizeof(pr.path));
+       strlcpy(pr.path, anchorname, sizeof(pr.path));
        if (ioctl(dev, DIOCGETRULESETS, &pr)) {
                if (errno == EINVAL)
                        fprintf(stderr, "Anchor '%s' not found.\n",
@@ -2132,19 +2195,91 @@ pfctl_show_anchors(int dev, int opts, char *anchorname)
                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 (opts & PF_OPT_VERBOSE) {
+                       char    sub[PATH_MAX];
+
+                       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, warg, walkf))
+                               return (-1);
+               }
        }
        return (0);
 }
 
+int
+pfctl_show_anchors(int dev, int opts, char *anchorname)
+{
+       int     rv;
+
+       rv = pfctl_walk_anchors(dev, opts, anchorname, NULL, pfctl_walk_show);
+       return (rv);
+}
+
+struct pfr_anchors *
+pfctl_get_anchors(int dev, int opts)
+{
+       struct pfioc_ruleset    pr;
+       struct {
+               struct pfr_anchoritem *pfra;
+       } pfra;
+       static struct pfr_anchors anchors;
+
+       SLIST_INIT(&anchors);
+
+       memset(&pr, 0, sizeof(pr));
+       pfra.pfra = NULL;
+       pfctl_walk_get(opts, &pr, &pfra);
+       if (pfra.pfra == NULL) {
+               fprintf(stderr, "%s failed to allocate main anchor\n",
+                   __func__);
+               exit(1);
+       }
+       SLIST_INSERT_HEAD(&anchors, pfra.pfra, pfra_sle);
+
+       opts |= PF_OPT_VERBOSE;
+       if (pfctl_walk_anchors(dev, opts, "", &pfra, pfctl_walk_get)) {
+               fprintf(stderr,
+                   "%s failed to retreive list of anchors, can't continue\n",
+                   __func__);
+               exit(1);
+       }
+
+       return (&anchors);
+}
+
+int
+pfctl_nuke_anchors(int dev, int opts)
+{
+       int                      rv = 0;
+       struct pfr_anchors      *anchors;
+       struct pfr_anchoritem   *pfra, *pfra_save;
+
+       anchors = pfctl_get_anchors(dev, opts);
+       /*
+        * don't let pfctl_clear_*() function to fail with exit
+        */
+       opts |= PF_OPT_IGNFAIL;
+       SLIST_FOREACH_SAFE(pfra, anchors, pfra_sle, pfra_save) {
+               rv |= (pfctl_clear_tables(pfra->pfra_anchorname, opts) == -1) ?
+                   1 : 0;
+               rv |= pfctl_clear_rules(dev, opts, pfra->pfra_anchorname);
+               SLIST_REMOVE(anchors, pfra, pfr_anchoritem, pfra_sle);
+               free(pfra->pfra_anchorname);
+               free(pfra);
+       }
+
+       return (rv);
+}
+
 const char *
 pfctl_lookup_option(char *cmd, const char **list)
 {
@@ -2566,6 +2701,14 @@ main(int argc, char *argv[])
                case 'T':
                        pfctl_clear_tables(anchorname, opts);
                        break;
+               case 'N':
+                       if (*anchorname) {
+                               warnx("-a can not be used with -FN");
+                               usage();
+                               /* NOTREACHED */
+                       }
+                       pfctl_nuke_anchors(dev, opts);
+                       break;
                }
        }
        if (state_killers) {
diff --git a/sbin/pfctl/pfctl.h b/sbin/pfctl/pfctl.h
index 7981cf66fdb..796c894c335 100644
--- a/sbin/pfctl/pfctl.h
+++ b/sbin/pfctl/pfctl.h
@@ -48,6 +48,14 @@ 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_clr_tables(struct pfr_table *, int *, int);
 int     pfr_add_tables(struct pfr_table *, int, int *, int);
@@ -75,7 +83,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_clear_tables(const char *, int);
+int     pfctl_clear_tables(const char *, int);
 void    pfctl_show_tables(const char *, int);
 int     pfctl_table(int, char *[], char *, const char *, char *,
            const char *, int);
diff --git a/sbin/pfctl/pfctl_parser.h b/sbin/pfctl/pfctl_parser.h
index be0ccee1897..993b1fe1c77 100644
--- a/sbin/pfctl/pfctl_parser.h
+++ b/sbin/pfctl/pfctl_parser.h
@@ -36,21 +36,22 @@
 
 #define PF_OSFP_FILE           "/etc/pf.os"
 
-#define PF_OPT_DISABLE         0x0001
-#define PF_OPT_ENABLE          0x0002
-#define PF_OPT_VERBOSE         0x0004
-#define PF_OPT_NOACTION                0x0008
-#define PF_OPT_QUIET           0x0010
-#define PF_OPT_CLRRULECTRS     0x0020
-#define PF_OPT_USEDNS          0x0040
-#define PF_OPT_VERBOSE2                0x0080
-#define PF_OPT_DUMMYACTION     0x0100
-#define PF_OPT_DEBUG           0x0200
-#define PF_OPT_SHOWALL         0x0400
-#define PF_OPT_OPTIMIZE                0x0800
-#define PF_OPT_NODNS           0x1000
-#define PF_OPT_RECURSE         0x4000
-#define PF_OPT_PORTNAMES       0x8000
+#define PF_OPT_DISABLE         0x00001
+#define PF_OPT_ENABLE          0x00002
+#define PF_OPT_VERBOSE         0x00004
+#define PF_OPT_NOACTION                0x00008
+#define PF_OPT_QUIET           0x00010
+#define PF_OPT_CLRRULECTRS     0x00020
+#define PF_OPT_USEDNS          0x00040
+#define PF_OPT_VERBOSE2                0x00080
+#define PF_OPT_DUMMYACTION     0x00100
+#define PF_OPT_DEBUG           0x00200
+#define PF_OPT_SHOWALL         0x00400
+#define PF_OPT_OPTIMIZE                0x00800
+#define PF_OPT_NODNS           0x01000
+#define PF_OPT_RECURSE         0x04000
+#define PF_OPT_PORTNAMES       0x08000
+#define PF_OPT_IGNFAIL         0x10000
 
 #define PF_TH_ALL              0xFF
 
diff --git a/sbin/pfctl/pfctl_table.c b/sbin/pfctl/pfctl_table.c
index 6ed4024da4e..922bd78aae4 100644
--- a/sbin/pfctl/pfctl_table.c
+++ b/sbin/pfctl/pfctl_table.c
@@ -101,11 +101,17 @@ static const char *istats_text[2][2][2] = {
                table.pfrt_flags &= ~PFR_TFLAG_PERSIST;                 \
        } while(0)
 
-void
+int
 pfctl_clear_tables(const char *anchor, int opts)
 {
-       if (pfctl_table(0, NULL, NULL, "-F", NULL, anchor, opts) == -1)
-               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