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