jcs's openbsd hax
openbsd

Enable pfctl(8) to recursively flush rules and tables from PF driver. 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@

sashan ae711728 ca89ad4b

+285 -63
+241 -38
sbin/pfctl/pfctl.c
··· 1 - /* $OpenBSD: pfctl.c,v 1.377 2019/11/27 19:42:56 kn Exp $ */ 1 + /* $OpenBSD: pfctl.c,v 1.378 2020/01/15 11:52:50 sashan Exp $ */ 2 2 3 3 /* 4 4 * Copyright (c) 2001 Daniel Hartmeier ··· 51 51 #include <stdlib.h> 52 52 #include <string.h> 53 53 #include <unistd.h> 54 - 55 54 #include <syslog.h> 55 + #include <stdarg.h> 56 + #include <libgen.h> 56 57 57 58 #include "pfctl_parser.h" 58 59 #include "pfctl.h" ··· 63 64 void pfctl_clear_queues(struct pf_qihead *); 64 65 void pfctl_clear_stats(int, const char *, int); 65 66 void pfctl_clear_interface_flags(int, int); 66 - void pfctl_clear_rules(int, int, char *); 67 + int pfctl_clear_rules(int, int, char *); 67 68 void pfctl_clear_src_nodes(int, int); 68 69 void pfctl_clear_states(int, const char *, int); 69 70 struct addrinfo * ··· 106 107 void pfctl_state_store(int, const char *); 107 108 void pfctl_state_load(int, const char *); 108 109 void pfctl_reset(int, int); 110 + int pfctl_walk_show(int, struct pfioc_ruleset *, void *); 111 + int pfctl_walk_get(int, struct pfioc_ruleset *, void *); 112 + int pfctl_walk_anchors(int, int, const char *, 113 + int(*)(int, struct pfioc_ruleset *, void *), void *); 114 + struct pfr_anchors * 115 + pfctl_get_anchors(int, const char *, int); 116 + int pfctl_recurse(int, int, const char *, 117 + int(*)(int, int, struct pfr_anchoritem *)); 118 + int pfctl_call_clearrules(int, int, struct pfr_anchoritem *); 119 + int pfctl_call_cleartables(int, int, struct pfr_anchoritem *); 120 + int pfctl_call_clearanchors(int, int, struct pfr_anchoritem *); 109 121 110 122 const char *clearopt; 111 123 char *rulesopt; ··· 125 137 int dev = -1; 126 138 int first_title = 1; 127 139 int labels = 0; 140 + int exit_val = 0; 128 141 129 142 #define INDENT(d, o) do { \ 130 143 if (o) { \ ··· 233 246 234 247 struct pf_qihead qspecs = TAILQ_HEAD_INITIALIZER(qspecs); 235 248 struct pf_qihead rootqs = TAILQ_HEAD_INITIALIZER(rootqs); 236 - 237 249 238 250 __dead void 239 251 usage(void) ··· 251 263 exit(1); 252 264 } 253 265 266 + void 267 + pfctl_err(int opts, int eval, const char *fmt, ...) 268 + { 269 + va_list ap; 270 + 271 + va_start(ap, fmt); 272 + 273 + if ((opts & PF_OPT_IGNFAIL) == 0) 274 + verr(eval, fmt, ap); 275 + else 276 + vwarn(fmt, ap); 277 + 278 + va_end(ap); 279 + 280 + exit_val = eval; 281 + } 282 + 283 + void 284 + pfctl_errx(int opts, int eval, const char *fmt, ...) 285 + { 286 + va_list ap; 287 + 288 + va_start(ap, fmt); 289 + 290 + if ((opts & PF_OPT_IGNFAIL) == 0) 291 + verrx(eval, fmt, ap); 292 + else 293 + vwarnx(fmt, ap); 294 + 295 + va_end(ap); 296 + 297 + exit_val = eval; 298 + } 299 + 254 300 int 255 301 pfctl_enable(int dev, int opts) 256 302 { ··· 289 335 memset(&pi, 0, sizeof(pi)); 290 336 if (iface != NULL && strlcpy(pi.pfiio_name, iface, 291 337 sizeof(pi.pfiio_name)) >= sizeof(pi.pfiio_name)) 292 - errx(1, "invalid interface: %s", iface); 338 + pfctl_errx(opts, 1, "invalid interface: %s", iface); 293 339 294 340 if (ioctl(dev, DIOCCLRSTATUS, &pi) == -1) 295 - err(1, "DIOCCLRSTATUS"); 341 + pfctl_err(opts, 1, "DIOCCLRSTATUS"); 296 342 if ((opts & PF_OPT_QUIET) == 0) { 297 343 fprintf(stderr, "pf: statistics cleared"); 298 344 if (iface != NULL) ··· 311 357 pi.pfiio_flags = PFI_IFLAG_SKIP; 312 358 313 359 if (ioctl(dev, DIOCCLRIFFLAG, &pi) == -1) 314 - err(1, "DIOCCLRIFFLAG"); 360 + pfctl_err(opts, 1, "DIOCCLRIFFLAG"); 315 361 if ((opts & PF_OPT_QUIET) == 0) 316 362 fprintf(stderr, "pf: interface flags reset\n"); 317 363 } 318 364 } 319 365 320 - void 366 + int 321 367 pfctl_clear_rules(int dev, int opts, char *anchorname) 322 368 { 323 - struct pfr_buffer t; 369 + struct pfr_buffer t; 324 370 325 371 memset(&t, 0, sizeof(t)); 326 372 t.pfrb_type = PFRB_TRANS; 327 373 if (pfctl_add_trans(&t, PF_TRANS_RULESET, anchorname) || 328 374 pfctl_trans(dev, &t, DIOCXBEGIN, 0) || 329 - pfctl_trans(dev, &t, DIOCXCOMMIT, 0)) 330 - err(1, "pfctl_clear_rules"); 331 - if ((opts & PF_OPT_QUIET) == 0) 375 + pfctl_trans(dev, &t, DIOCXCOMMIT, 0)) { 376 + pfctl_err(opts, 1, "%s", __func__); 377 + return (1); 378 + } else if ((opts & PF_OPT_QUIET) == 0) 332 379 fprintf(stderr, "rules cleared\n"); 380 + 381 + return (0); 333 382 } 334 383 335 384 void 336 385 pfctl_clear_src_nodes(int dev, int opts) 337 386 { 338 387 if (ioctl(dev, DIOCCLRSRCNODES) == -1) 339 - err(1, "DIOCCLRSRCNODES"); 388 + pfctl_err(opts, 1, "DIOCCLRSRCNODES"); 340 389 if ((opts & PF_OPT_QUIET) == 0) 341 390 fprintf(stderr, "source tracking entries cleared\n"); 342 391 } ··· 349 398 memset(&psk, 0, sizeof(psk)); 350 399 if (iface != NULL && strlcpy(psk.psk_ifname, iface, 351 400 sizeof(psk.psk_ifname)) >= sizeof(psk.psk_ifname)) 352 - errx(1, "invalid interface: %s", iface); 401 + pfctl_errx(opts, 1, "invalid interface: %s", iface); 353 402 354 403 if (ioctl(dev, DIOCCLRSTATES, &psk) == -1) 355 - err(1, "DIOCCLRSTATES"); 404 + pfctl_err(opts, 1, "DIOCCLRSTATES"); 356 405 if ((opts & PF_OPT_QUIET) == 0) 357 406 fprintf(stderr, "%d states cleared\n", psk.psk_killed); 358 407 } ··· 2109 2158 } 2110 2159 2111 2160 int 2112 - pfctl_show_anchors(int dev, int opts, char *anchorname) 2161 + pfctl_walk_show(int opts, struct pfioc_ruleset *pr, void *warg) 2162 + { 2163 + if (pr->path[0]) { 2164 + if (pr->path[0] != '_' || (opts & PF_OPT_VERBOSE)) 2165 + printf(" %s/%s\n", pr->path, pr->name); 2166 + } else if (pr->name[0] != '_' || (opts & PF_OPT_VERBOSE)) 2167 + printf(" %s\n", pr->name); 2168 + 2169 + return (0); 2170 + } 2171 + 2172 + int 2173 + pfctl_walk_get(int opts, struct pfioc_ruleset *pr, void *warg) 2174 + { 2175 + struct pfr_anchoritem *pfra; 2176 + struct pfr_anchors *anchors; 2177 + int e; 2178 + 2179 + anchors = (struct pfr_anchors *) warg; 2180 + 2181 + pfra = malloc(sizeof(*pfra)); 2182 + if (pfra == NULL) 2183 + err(1, "%s", __func__); 2184 + 2185 + if (pr->path[0]) 2186 + e = asprintf(&pfra->pfra_anchorname, "%s/%s", pr->path, 2187 + pr->name); 2188 + else 2189 + e = asprintf(&pfra->pfra_anchorname, "%s", pr->name); 2190 + 2191 + if (e == -1) 2192 + err(1, "%s", __func__); 2193 + 2194 + 2195 + SLIST_INSERT_HEAD(anchors, pfra, pfra_sle); 2196 + 2197 + return (0); 2198 + } 2199 + 2200 + int 2201 + pfctl_walk_anchors(int dev, int opts, const char *anchor, 2202 + int(walkf)(int, struct pfioc_ruleset *, void *), void *warg) 2113 2203 { 2114 2204 struct pfioc_ruleset pr; 2115 2205 u_int32_t mnr, nr; 2116 2206 2117 2207 memset(&pr, 0, sizeof(pr)); 2118 - memcpy(pr.path, anchorname, sizeof(pr.path)); 2208 + strlcpy(pr.path, anchor, sizeof(pr.path)); 2119 2209 if (ioctl(dev, DIOCGETRULESETS, &pr) == -1) { 2120 2210 if (errno == EINVAL) 2121 - fprintf(stderr, "Anchor '%s' not found.\n", 2122 - anchorname); 2211 + fprintf(stderr, "Anchor '%s' not found.\n", anchor); 2123 2212 else 2124 2213 err(1, "DIOCGETRULESETS"); 2125 2214 return (-1); ··· 2134 2223 if (!strcmp(pr.name, PF_RESERVED_ANCHOR)) 2135 2224 continue; 2136 2225 sub[0] = '\0'; 2137 - if (pr.path[0]) { 2138 - strlcat(sub, pr.path, sizeof(sub)); 2139 - strlcat(sub, "/", sizeof(sub)); 2140 - } 2141 - strlcat(sub, pr.name, sizeof(sub)); 2142 - if (sub[0] != '_' || (opts & PF_OPT_VERBOSE)) 2143 - printf(" %s\n", sub); 2144 - if ((opts & PF_OPT_VERBOSE) && pfctl_show_anchors(dev, opts, sub)) 2226 + 2227 + if (walkf(opts, &pr, warg)) 2228 + return (-1); 2229 + 2230 + if (pr.path[0]) 2231 + snprintf(sub, sizeof(sub), "%s/%s", 2232 + pr.path, pr.name); 2233 + else 2234 + snprintf(sub, sizeof(sub), "%s", 2235 + pr.name); 2236 + if (pfctl_walk_anchors(dev, opts, sub, walkf, warg)) 2145 2237 return (-1); 2146 2238 } 2147 2239 return (0); 2148 2240 } 2149 2241 2242 + int 2243 + pfctl_show_anchors(int dev, int opts, char *anchor) 2244 + { 2245 + return ( 2246 + pfctl_walk_anchors(dev, opts, anchor, pfctl_walk_show, NULL)); 2247 + } 2248 + 2249 + struct pfr_anchors * 2250 + pfctl_get_anchors(int dev, const char *anchor, int opts) 2251 + { 2252 + struct pfioc_ruleset pr; 2253 + static struct pfr_anchors anchors; 2254 + char *n; 2255 + 2256 + SLIST_INIT(&anchors); 2257 + 2258 + memset(&pr, 0, sizeof(pr)); 2259 + if (*anchor != '\0') { 2260 + n = dirname(anchor); 2261 + if (n[0] != '.' && n[1] != '\0') 2262 + strlcpy(pr.path, n, sizeof(pr.path)); 2263 + n = basename(anchor); 2264 + if (n != NULL) 2265 + strlcpy(pr.name, n, sizeof(pr.name)); 2266 + } 2267 + 2268 + /* insert a root anchor first. */ 2269 + pfctl_walk_get(opts, &pr, &anchors); 2270 + 2271 + if (pfctl_walk_anchors(dev, opts, anchor, pfctl_walk_get, &anchors)) 2272 + errx(1, 2273 + "%s failed to retrieve list of anchors, can't continue", 2274 + __func__); 2275 + 2276 + return (&anchors); 2277 + } 2278 + 2279 + int 2280 + pfctl_call_cleartables(int dev, int opts, struct pfr_anchoritem *pfra) 2281 + { 2282 + /* 2283 + * PF_OPT_QUIET makes pfctl_clear_tables() to stop printing number of 2284 + * tables cleared for given anchor. 2285 + */ 2286 + opts |= PF_OPT_QUIET; 2287 + return ((pfctl_clear_tables(pfra->pfra_anchorname, opts) == -1) ? 2288 + 1 : 0); 2289 + } 2290 + 2291 + int 2292 + pfctl_call_clearrules(int dev, int opts, struct pfr_anchoritem *pfra) 2293 + { 2294 + /* 2295 + * PF_OPT_QUIET makes pfctl_clear_rules() to stop printing a 'rules 2296 + * cleared' message for every anchor it deletes. 2297 + */ 2298 + opts |= PF_OPT_QUIET; 2299 + return (pfctl_clear_rules(dev, opts, pfra->pfra_anchorname)); 2300 + } 2301 + 2302 + int 2303 + pfctl_call_clearanchors(int dev, int opts, struct pfr_anchoritem *pfra) 2304 + { 2305 + int rv = 0; 2306 + 2307 + rv |= pfctl_call_cleartables(dev, opts, pfra); 2308 + rv |= pfctl_call_clearrules(dev, opts, pfra); 2309 + 2310 + return (rv); 2311 + } 2312 + 2313 + int 2314 + pfctl_recurse(int dev, int opts, const char *anchorname, 2315 + int(*walkf)(int, int, struct pfr_anchoritem *)) 2316 + { 2317 + int rv = 0; 2318 + struct pfr_anchors *anchors; 2319 + struct pfr_anchoritem *pfra, *pfra_save; 2320 + 2321 + anchors = pfctl_get_anchors(dev, anchorname, opts); 2322 + /* 2323 + * While traversing the list, pfctl_clear_*() must always return 2324 + * so that failures on one anchor do not prevent clearing others. 2325 + */ 2326 + opts |= PF_OPT_IGNFAIL; 2327 + printf("Removing:\n"); 2328 + SLIST_FOREACH_SAFE(pfra, anchors, pfra_sle, pfra_save) { 2329 + printf(" %s\n", (*pfra->pfra_anchorname == '\0') ? 2330 + "<root>" : pfra->pfra_anchorname); 2331 + rv |= walkf(dev, opts, pfra); 2332 + SLIST_REMOVE(anchors, pfra, pfr_anchoritem, pfra_sle); 2333 + free(pfra->pfra_anchorname); 2334 + free(pfra); 2335 + } 2336 + 2337 + return (rv); 2338 + } 2339 + 2150 2340 const char * 2151 2341 pfctl_lookup_option(char *cmd, const char **list) 2152 2342 { ··· 2276 2466 int 2277 2467 main(int argc, char *argv[]) 2278 2468 { 2279 - int error = 0; 2280 2469 int ch; 2281 2470 int mode = O_RDONLY; 2282 2471 int opts = 0; ··· 2497 2686 2498 2687 if (opts & PF_OPT_DISABLE) 2499 2688 if (pfctl_disable(dev, opts)) 2500 - error = 1; 2689 + exit_val = 1; 2501 2690 2502 2691 if ((path = calloc(1, PATH_MAX)) == NULL) 2503 2692 errx(1, "%s: calloc", __func__); ··· 2574 2763 if (clearopt != NULL) { 2575 2764 switch (*clearopt) { 2576 2765 case 'r': 2577 - pfctl_clear_rules(dev, opts, anchorname); 2766 + if (opts & PF_OPT_RECURSE) 2767 + pfctl_recurse(dev, opts, anchorname, 2768 + pfctl_call_clearrules); 2769 + else 2770 + pfctl_clear_rules(dev, opts, anchorname); 2578 2771 break; 2579 2772 case 's': 2580 2773 pfctl_clear_states(dev, ifaceopt, opts); ··· 2591 2784 usage(); 2592 2785 /* NOTREACHED */ 2593 2786 } 2594 - pfctl_clear_tables(anchorname, opts); 2595 - pfctl_clear_rules(dev, opts, anchorname); 2787 + if (opts & PF_OPT_RECURSE) 2788 + pfctl_recurse(dev, opts, anchorname, 2789 + pfctl_call_clearanchors); 2790 + else { 2791 + pfctl_clear_tables(anchorname, opts); 2792 + pfctl_clear_rules(dev, opts, anchorname); 2793 + } 2794 + 2596 2795 if (!*anchorname) { 2597 2796 pfctl_clear_states(dev, ifaceopt, opts); 2598 2797 pfctl_clear_src_nodes(dev, opts); ··· 2605 2804 pfctl_clear_fingerprints(dev, opts); 2606 2805 break; 2607 2806 case 'T': 2608 - pfctl_clear_tables(anchorname, opts); 2807 + if ((opts & PF_OPT_RECURSE) == 0) 2808 + pfctl_clear_tables(anchorname, opts); 2809 + else 2810 + pfctl_recurse(dev, opts, anchorname, 2811 + pfctl_call_cleartables); 2609 2812 break; 2610 2813 case 'R': 2611 2814 pfctl_reset(dev, opts); ··· 2627 2830 pfctl_kill_src_nodes(dev, opts); 2628 2831 2629 2832 if (tblcmdopt != NULL) { 2630 - error = pfctl_table(argc, argv, tableopt, 2833 + exit_val = pfctl_table(argc, argv, tableopt, 2631 2834 tblcmdopt, rulesopt, anchorname, opts); 2632 2835 rulesopt = NULL; 2633 2836 } ··· 2649 2852 if (rulesopt != NULL && !anchorname[0]) { 2650 2853 pfctl_clear_interface_flags(dev, opts | PF_OPT_QUIET); 2651 2854 if (pfctl_file_fingerprints(dev, opts, PF_OSFP_FILE)) 2652 - error = 1; 2855 + exit_val = 1; 2653 2856 } 2654 2857 2655 2858 if (rulesopt != NULL) { 2656 2859 if (pfctl_rules(dev, rulesopt, opts, optimize, 2657 2860 anchorname, NULL)) 2658 - error = 1; 2861 + exit_val = 1; 2659 2862 } 2660 2863 2661 2864 if (opts & PF_OPT_ENABLE) 2662 2865 if (pfctl_enable(dev, opts)) 2663 - error = 1; 2866 + exit_val = 1; 2664 2867 2665 2868 if (debugopt != NULL) { 2666 2869 if ((level = string_to_loglevel((char *)debugopt)) < 0) { ··· 2688 2891 if (lfile != NULL) 2689 2892 pfctl_state_load(dev, lfile); 2690 2893 2691 - exit(error); 2894 + exit(exit_val); 2692 2895 }
+13 -2
sbin/pfctl/pfctl.h
··· 1 - /* $OpenBSD: pfctl.h,v 1.60 2019/01/11 01:56:54 kn Exp $ */ 1 + /* $OpenBSD: pfctl.h,v 1.61 2020/01/15 11:52:50 sashan Exp $ */ 2 2 3 3 /* 4 4 * Copyright (c) 2001 Daniel Hartmeier ··· 48 48 (var) != NULL; \ 49 49 (var) = pfr_buf_next((buf), (var))) 50 50 51 + 52 + struct pfr_anchoritem { 53 + SLIST_ENTRY(pfr_anchoritem) pfra_sle; 54 + char *pfra_anchorname; 55 + }; 56 + 57 + SLIST_HEAD(pfr_anchors, pfr_anchoritem); 58 + 51 59 int pfr_get_fd(void); 52 60 int pfr_clr_tables(struct pfr_table *, int *, int); 53 61 int pfr_add_tables(struct pfr_table *, int, int *, int); ··· 75 83 int pfi_clr_istats(const char *, int *, int); 76 84 77 85 void pfctl_print_title(char *); 78 - void pfctl_clear_tables(const char *, int); 86 + int pfctl_clear_tables(const char *, int); 79 87 void pfctl_show_tables(const char *, int); 80 88 int pfctl_table(int, char *[], char *, const char *, char *, 81 89 const char *, int); ··· 96 104 int pfctl_trans(int, struct pfr_buffer *, u_long, int); 97 105 98 106 int pfctl_show_queues(int, const char *, int, int); 107 + 108 + void pfctl_err(int, int, const char *, ...); 109 + void pfctl_errx(int, int, const char *, ...); 99 110 100 111 #endif /* _PFCTL_H_ */
+2 -2
sbin/pfctl/pfctl_osfp.c
··· 1 - /* $OpenBSD: pfctl_osfp.c,v 1.26 2019/06/28 13:32:45 deraadt Exp $ */ 1 + /* $OpenBSD: pfctl_osfp.c,v 1.27 2020/01/15 11:52:50 sashan Exp $ */ 2 2 3 3 /* 4 4 * Copyright (c) 2003 Mike Frantzen <frantzen@openbsd.org> ··· 260 260 pfctl_clear_fingerprints(int dev, int opts) 261 261 { 262 262 if (ioctl(dev, DIOCOSFPFLUSH) == -1) 263 - err(1, "DIOCOSFPFLUSH"); 263 + pfctl_err(opts, 1, "DIOCOSFPFLUSH"); 264 264 } 265 265 266 266 /* flush pfctl's view of the fingerprints */
+17 -16
sbin/pfctl/pfctl_parser.h
··· 1 - /* $OpenBSD: pfctl_parser.h,v 1.115 2019/03/07 08:01:52 kn Exp $ */ 1 + /* $OpenBSD: pfctl_parser.h,v 1.116 2020/01/15 11:52:50 sashan Exp $ */ 2 2 3 3 /* 4 4 * Copyright (c) 2001 Daniel Hartmeier ··· 36 36 37 37 #define PF_OSFP_FILE "/etc/pf.os" 38 38 39 - #define PF_OPT_DISABLE 0x0001 40 - #define PF_OPT_ENABLE 0x0002 41 - #define PF_OPT_VERBOSE 0x0004 42 - #define PF_OPT_NOACTION 0x0008 43 - #define PF_OPT_QUIET 0x0010 44 - #define PF_OPT_CLRRULECTRS 0x0020 45 - #define PF_OPT_USEDNS 0x0040 46 - #define PF_OPT_VERBOSE2 0x0080 47 - #define PF_OPT_DUMMYACTION 0x0100 48 - #define PF_OPT_DEBUG 0x0200 49 - #define PF_OPT_SHOWALL 0x0400 50 - #define PF_OPT_OPTIMIZE 0x0800 51 - #define PF_OPT_NODNS 0x1000 52 - #define PF_OPT_RECURSE 0x4000 53 - #define PF_OPT_PORTNAMES 0x8000 39 + #define PF_OPT_DISABLE 0x00001 40 + #define PF_OPT_ENABLE 0x00002 41 + #define PF_OPT_VERBOSE 0x00004 42 + #define PF_OPT_NOACTION 0x00008 43 + #define PF_OPT_QUIET 0x00010 44 + #define PF_OPT_CLRRULECTRS 0x00020 45 + #define PF_OPT_USEDNS 0x00040 46 + #define PF_OPT_VERBOSE2 0x00080 47 + #define PF_OPT_DUMMYACTION 0x00100 48 + #define PF_OPT_DEBUG 0x00200 49 + #define PF_OPT_SHOWALL 0x00400 50 + #define PF_OPT_OPTIMIZE 0x00800 51 + #define PF_OPT_NODNS 0x01000 52 + #define PF_OPT_RECURSE 0x04000 53 + #define PF_OPT_PORTNAMES 0x08000 54 + #define PF_OPT_IGNFAIL 0x10000 54 55 55 56 #define PF_TH_ALL 0xFF 56 57
+12 -5
sbin/pfctl/pfctl_table.c
··· 1 - /* $OpenBSD: pfctl_table.c,v 1.81 2019/04/18 22:29:41 kn Exp $ */ 1 + /* $OpenBSD: pfctl_table.c,v 1.82 2020/01/15 11:52:50 sashan Exp $ */ 2 2 3 3 /* 4 4 * Copyright (c) 2002 Cedric Berger ··· 77 77 if ((!(opts & PF_OPT_NOACTION) || \ 78 78 (opts & PF_OPT_DUMMYACTION)) && \ 79 79 (fct)) { \ 80 - radix_perror(); \ 80 + if ((opts & PF_OPT_RECURSE) == 0)\ 81 + radix_perror(); \ 81 82 goto _error; \ 82 83 } \ 83 84 } while (0) ··· 101 102 table.pfrt_flags &= ~PFR_TFLAG_PERSIST; \ 102 103 } while(0) 103 104 104 - void 105 + int 105 106 pfctl_clear_tables(const char *anchor, int opts) 106 107 { 107 - if (pfctl_table(0, NULL, NULL, "-F", NULL, anchor, opts) == -1) 108 - exit(1); 108 + int rv; 109 + 110 + if ((rv = pfctl_table(0, NULL, NULL, "-F", NULL, anchor, opts)) == -1) { 111 + if ((opts & PF_OPT_IGNFAIL) == 0) 112 + exit(1); 113 + } 114 + 115 + return (rv); 109 116 } 110 117 111 118 void