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