Hello,

I received a feedback suggesting to come up with better name than 'Nuke'

> not sure about the name.
> 
> i've often want a similar operation for network interfaces, which
> returns them to 'raw' state.
> 
> and there's also been people who want the same for the routing
> table
> 
> if we ever do such a thing for all of these things, i think we should
> use the same word
> 
> 'nuke' sounds like the wrong word, it is a 'destroy' operation rather
> than 'return to unitialized'.

I think all the above calls for a new standalone option, which I named as
'Unconfigure'.  Patch below suggest unconfigure behavior for PF.
Doing 'pfctl -U' will bring PF back to its initial state (e.g. right before
pf.conf got processed during the system boot). In case of PF the proposed -U
will do following:
    - remove all rulesets and tables
    - remove all states and source nodes
    - remove all OS fingerprints
    - set all limits, timeouts and options to their defaults

thanks and
regards
sashan

--------8<---------------8<---------------8<------------------8<--------
diff --git a/sbin/pfctl/pfctl.8 b/sbin/pfctl/pfctl.8
index 48b2893cfcd..562e03b3477 100644
--- a/sbin/pfctl/pfctl.8
+++ b/sbin/pfctl/pfctl.8
@@ -644,6 +644,9 @@ This flag is set when per-address counters are enabled on 
the table.
 .El
 .It Fl t Ar table
 Specify the name of the table.
+.It Fl U
+Unconfigure firewall. All rules, states and tables are removed. All limits
+and timeouts are reset to default values.
 .It Fl V Ar rdomain
 Select the routing domain to be used to kill states by host or by label.
 The rdomain of a state is displayed in parentheses before the host by
diff --git a/sbin/pfctl/pfctl.c b/sbin/pfctl/pfctl.c
index 493ff47af2f..19f63db56f2 100644
--- a/sbin/pfctl/pfctl.c
+++ b/sbin/pfctl/pfctl.c
@@ -54,6 +54,8 @@
 
 #include <syslog.h>
 
+#include <stdarg.h>
+
 #include "pfctl_parser.h"
 #include "pfctl.h"
 
@@ -63,7 +65,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 +107,14 @@ 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);
+void   pfctl_set_defaults(int, int);
 
 const char     *clearopt;
 char           *rulesopt;
@@ -124,6 +134,7 @@ char                *state_kill[2];
 int             dev = -1;
 int             first_title = 1;
 int             labels = 0;
+int             exit_val = 0;
 
 #define INDENT(d, o)   do {                                            \
                                if (o) {                                \
@@ -232,7 +243,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)
 {
@@ -249,6 +259,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(1, 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(1, fmt, ap);
+       else
+               vwarnx(fmt, ap);
+
+       va_end(ap);
+
+       exit_val = eval;
+}
+
 int
 pfctl_enable(int dev, int opts)
 {
@@ -287,10 +331,10 @@ pfctl_clear_stats(int dev, const char *iface, int opts)
        memset(&pi, 0, sizeof(pi));
        if (iface != NULL && strlcpy(pi.pfiio_name, iface,
            sizeof(pi.pfiio_name)) >= sizeof(pi.pfiio_name))
-               errx(1, "invalid interface: %s", iface);
+               pfctl_errx(opts, 1, "invalid interface: %s", iface);
 
        if (ioctl(dev, DIOCCLRSTATUS, &pi))
-               err(1, "DIOCCLRSTATUS");
+               pfctl_err(opts, 1, "DIOCCLRSTATUS");
        if ((opts & PF_OPT_QUIET) == 0) {
                fprintf(stderr, "pf: statistics cleared");
                if (iface != NULL)
@@ -309,32 +353,36 @@ pfctl_clear_interface_flags(int dev, int opts)
                pi.pfiio_flags = PFI_IFLAG_SKIP;
 
                if (ioctl(dev, DIOCCLRIFFLAG, &pi))
-                       err(1, "DIOCCLRIFFLAG");
+                       pfctl_err(opts, 1, "DIOCCLRIFFLAG");
                if ((opts & PF_OPT_QUIET) == 0)
                        fprintf(stderr, "pf: interface flags reset\n");
        }
 }
 
-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)) {
+               pfctl_err(opts, 1, "%s", __func__);
+               rv = 1;
+       } else if ((opts & PF_OPT_QUIET) == 0)
                fprintf(stderr, "rules cleared\n");
+
+       return (rv);
 }
 
 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");
 }
@@ -347,10 +395,10 @@ pfctl_clear_states(int dev, const char *iface, int opts)
        memset(&psk, 0, sizeof(psk));
        if (iface != NULL && strlcpy(psk.psk_ifname, iface,
            sizeof(psk.psk_ifname)) >= sizeof(psk.psk_ifname))
-               errx(1, "invalid interface: %s", iface);
+               pfctl_errx(opts, 1, "invalid interface: %s", iface);
 
        if (ioctl(dev, DIOCCLRSTATES, &psk))
-               err(1, "DIOCCLRSTATES");
+               pfctl_err(opts, 1, "DIOCCLRSTATES");
        if ((opts & PF_OPT_QUIET) == 0)
                fprintf(stderr, "%d states cleared\n", psk.psk_killed);
 }
@@ -2107,13 +2155,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 +2226,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)
 {
@@ -2232,6 +2398,39 @@ pfctl_state_load(int dev, const char *file)
        fclose(f);
 }
 
+void
+pfctl_set_defaults(int dev, int opts)
+{
+       struct pfctl    pf;
+       struct pfr_buffer t;
+       int             i;
+
+       pf.dev = dev;
+       pfctl_init_options(&pf);
+
+       pf.debug_set = 1;
+       pf.reass_set = 1;
+       pf.syncookieswat_set = 1;
+       pf.ifname_set = 1;
+
+       memset(&t, 0, sizeof(t));
+       t.pfrb_type = PFRB_TRANS;
+       if (pfctl_trans(dev, &t, DIOCXBEGIN, 0))
+               warn("%s, DIOCXBEGIN", __func__);
+
+
+       for (i = 0; pf_limits[i].name; i++)
+               pf.limit_set[pf_limits[i].index] = 1;
+
+       for (i = 0; pf_timeouts[i].name; i++)
+               pf.timeout_set[pf_timeouts[i].timeout] = 1;
+
+       pfctl_load_options(&pf);
+
+       if (pfctl_trans(dev, &t, DIOCXCOMMIT, 0))
+               warn("%s, DIOCXCOMMIT", __func__);
+}
+
 int
 main(int argc, char *argv[])
 {
@@ -2248,12 +2447,13 @@ main(int argc, char *argv[])
        char    *lfile = NULL, *sfile = NULL;
        const char *errstr;
        long     shownr = -1;
+       int      unconfigure = 0;
 
        if (argc < 2)
                usage();
 
        while ((ch = getopt(argc, argv,
-           "a:dD:eqf:F:ghi:k:K:L:Nno:Pp:R:rS:s:t:T:vV:x:z")) != -1) {
+           "a:dD:eqf:F:ghi:k:K:L:Nno:Pp:R:rS:s:t:T:UvV:x:z")) != -1) {
                switch (ch) {
                case 'a':
                        anchoropt = optarg;
@@ -2388,6 +2588,10 @@ main(int argc, char *argv[])
                        mode = O_RDWR;
                        lfile = optarg;
                        break;
+               case 'U':
+                       unconfigure = 1;
+                       mode = O_RDWR;
+                       break;
                case 'h':
                        /* FALLTHROUGH */
                default:
@@ -2399,6 +2603,15 @@ main(int argc, char *argv[])
        if ((opts & PF_OPT_NODNS) && (opts & PF_OPT_USEDNS))
                errx(1, "-N and -r are mutually exclusive");
 
+       if (unconfigure) {
+               if ((tblcmdopt || tableopt || anchoropt || clearopt ||
+                   rdomain || showopt || optiopt || src_node_killers ||
+                   lfile || sfile || (opts & PF_OPT_DISABLE) ||
+                   (opts & PF_OPT_ENABLE) || (opts & PF_OPT_CLRRULECTRS)))
+                       errx(1,
+                           "-U option can be combined with -x and -v only");
+       }
+
        if (tblcmdopt == NULL ^ tableopt == NULL)
                usage();
 
@@ -2454,6 +2667,22 @@ main(int argc, char *argv[])
                clearopt = showopt = debugopt = NULL;
        }
 
+       if (unconfigure) {
+               exit_val = pfctl_nuke_anchors(dev, opts);
+               /*
+                * don't let pfctl_clear_*() functions to fail with exit
+                */
+               opts |= PF_OPT_IGNFAIL;
+               pfctl_clear_states(dev, NULL, opts);
+               pfctl_clear_src_nodes(dev, opts);
+               pfctl_clear_stats(dev, NULL, opts);
+               pfctl_clear_fingerprints(dev, opts);
+               pfctl_clear_interface_flags(dev, opts);
+               pfctl_set_defaults(dev, opts);
+
+               exit(exit_val);
+       }
+
        if (opts & PF_OPT_DISABLE)
                if (pfctl_disable(dev, opts))
                        error = 1;
diff --git a/sbin/pfctl/pfctl.h b/sbin/pfctl/pfctl.h
index 7981cf66fdb..8709c90d710 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);
@@ -96,5 +104,7 @@ u_int32_t
 int     pfctl_trans(int, struct pfr_buffer *, u_long, int);
 
 int     pfctl_show_queues(int, const char *, int, 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 9c51d7462eb..e54b113d7e0 100644
--- a/sbin/pfctl/pfctl_osfp.c
+++ b/sbin/pfctl/pfctl_osfp.c
@@ -260,7 +260,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 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