git: 041ce1d690f1 - main - pfctl: recursively flush rules and tables
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Wed, 09 Jul 2025 08:59:12 UTC
The branch main has been updated by kp:
URL: https://cgit.FreeBSD.org/src/commit/?id=041ce1d690f1c58bd4a93ab808ded53444af2092
commit 041ce1d690f1c58bd4a93ab808ded53444af2092
Author: Kristof Provost <kp@FreeBSD.org>
AuthorDate: 2025-07-04 21:34:48 +0000
Commit: Kristof Provost <kp@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 <sashan@openbsd.org>, ae711728d4
Obtained from: OpenBSD, sashan <sashan@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