1 /* $OpenBSD: pfctl.c,v 1.278 2008/08/31 20:18:17 jmc Exp $ */
2
3 /*-
4 * SPDX-License-Identifier: BSD-2-Clause
5 *
6 * Copyright (c) 2001 Daniel Hartmeier
7 * Copyright (c) 2002,2003 Henning Brauer
8 * All rights reserved.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 *
14 * - Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * - Redistributions in binary form must reproduce the above
17 * copyright notice, this list of conditions and the following
18 * disclaimer in the documentation and/or other materials provided
19 * with the distribution.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
29 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32 * POSSIBILITY OF SUCH DAMAGE.
33 *
34 */
35
36 #include <sys/cdefs.h>
37 #define PFIOC_USE_LATEST
38
39 #include <sys/types.h>
40 #include <sys/ioctl.h>
41 #include <sys/socket.h>
42 #include <sys/stat.h>
43 #include <sys/endian.h>
44
45 #include <net/if.h>
46 #include <netinet/in.h>
47 #include <net/pfvar.h>
48 #include <arpa/inet.h>
49 #include <net/altq/altq.h>
50
51 #include <err.h>
52 #include <errno.h>
53 #include <fcntl.h>
54 #include <libpfctl.h>
55 #include <limits.h>
56 #include <netdb.h>
57 #include <stdint.h>
58 #include <stdio.h>
59 #include <stdlib.h>
60 #include <string.h>
61 #include <unistd.h>
62 #include <stdarg.h>
63 #include <stddef.h>
64 #include <libgen.h>
65
66 #include "pfctl_parser.h"
67 #include "pfctl.h"
68
69 struct pfctl_opt_id;
70
71 void usage(void);
72 int pfctl_enable(int, int);
73 int pfctl_disable(int, int);
74 void pfctl_clear_stats(struct pfctl_handle *, int);
75 void pfctl_get_skip_ifaces(void);
76 void pfctl_check_skip_ifaces(char *);
77 void pfctl_adjust_skip_ifaces(struct pfctl *);
78 void pfctl_clear_interface_flags(int, int);
79 void pfctl_flush_eth_rules(int, int, char *);
80 int pfctl_flush_rules(int, int, char *);
81 void pfctl_flush_nat(int, int, char *);
82 int pfctl_clear_altq(int, int);
83 void pfctl_clear_src_nodes(int, int);
84 void pfctl_clear_iface_states(int, const char *, int);
85 struct addrinfo *
86 pfctl_addrprefix(char *, struct pf_addr *, int);
87 void pfctl_kill_src_nodes(int, int);
88 void pfctl_net_kill_states(int, const char *, int);
89 void pfctl_gateway_kill_states(int, const char *, int);
90 void pfctl_label_kill_states(int, const char *, int);
91 void pfctl_id_kill_states(int, const char *, int);
92 void pfctl_key_kill_states(int, const char *, int);
93 void pfctl_kill_source(int, const char *, const char *, int);
94 int pfctl_parse_host(char *, struct pf_rule_addr *);
95 void pfctl_init_options(struct pfctl *);
96 int pfctl_load_options(struct pfctl *);
97 int pfctl_load_limit(struct pfctl *, unsigned int, unsigned int);
98 int pfctl_load_timeout(struct pfctl *, unsigned int, unsigned int);
99 int pfctl_load_debug(struct pfctl *, unsigned int);
100 int pfctl_load_logif(struct pfctl *, char *);
101 int pfctl_load_hostid(struct pfctl *, u_int32_t);
102 int pfctl_load_reassembly(struct pfctl *, u_int32_t);
103 int pfctl_load_syncookies(struct pfctl *, u_int8_t);
104 int pfctl_get_pool(int, struct pfctl_pool *, u_int32_t, u_int32_t, int,
105 const char *, int);
106 void pfctl_print_eth_rule_counters(struct pfctl_eth_rule *, int);
107 void pfctl_print_rule_counters(struct pfctl_rule *, int);
108 int pfctl_show_statelims(int, enum pfctl_show, int);
109 int pfctl_show_sourcelims(int, enum pfctl_show, int, const char *);
110 int pfctl_show_eth_rules(int, char *, int, enum pfctl_show, char *, int, int);
111 int pfctl_show_rules(int, char *, int, enum pfctl_show, char *, int, int);
112 int pfctl_show_nat(int, const char *, int, char *, int, int);
113 int pfctl_show_src_nodes(int, int);
114 int pfctl_show_states(int, const char *, int);
115 int pfctl_show_status(int, int);
116 int pfctl_show_running(int);
117 int pfctl_show_timeouts(int, int);
118 int pfctl_show_limits(int, int);
119 void pfctl_read_limits(struct pfctl_handle *);
120 void pfctl_restore_limits(void);
121 void pfctl_debug(int, u_int32_t, int);
122 int pfctl_test_altqsupport(int, int);
123 int pfctl_show_anchors(int, int, char *);
124 int pfctl_show_eth_anchors(int, int, char *);
125 int pfctl_ruleset_trans(struct pfctl *, char *, struct pfctl_anchor *, bool);
126 void pfctl_load_statelims(struct pfctl *);
127 void pfctl_load_statelim(struct pfctl *, struct pfctl_statelim *);
128 void pfctl_load_sourcelims(struct pfctl *);
129 void pfctl_load_sourcelim(struct pfctl *, struct pfctl_source_lim *);
130 int pfctl_eth_ruleset_trans(struct pfctl *, char *,
131 struct pfctl_eth_anchor *);
132 int pfctl_load_eth_ruleset(struct pfctl *, char *,
133 struct pfctl_eth_ruleset *, int);
134 int pfctl_load_eth_rule(struct pfctl *, char *, struct pfctl_eth_rule *,
135 int);
136 int pfctl_load_ruleset(struct pfctl *, char *,
137 struct pfctl_ruleset *, int, int);
138 int pfctl_load_rule(struct pfctl *, char *, struct pfctl_rule *, int);
139 const char *pfctl_lookup_option(char *, const char * const *);
140 int pfctl_lookup_id(const char *, const struct pfctl_opt_id *);
141 void pfctl_reset(int, int);
142 int pfctl_walk_show(int, struct pfioc_ruleset *, void *);
143 int pfctl_walk_get(int, struct pfioc_ruleset *, void *);
144 int pfctl_walk_anchors(int, int, const char *,
145 int(*)(int, struct pfioc_ruleset *, void *), void *);
146 struct pfr_anchors *
147 pfctl_get_anchors(int, const char *, int);
148 int pfctl_recurse(int, int, const char *,
149 int(*)(int, int, struct pfr_anchoritem *));
150 int pfctl_call_clearrules(int, int, struct pfr_anchoritem *);
151 int pfctl_call_cleartables(int, int, struct pfr_anchoritem *);
152 int pfctl_call_clearanchors(int, int, struct pfr_anchoritem *);
153 int pfctl_call_showtables(int, int, struct pfr_anchoritem *);
154
155 RB_PROTOTYPE(pfctl_statelim_ids, pfctl_statelim, entry,
156 pfctl_statelim_id_cmp);
157 RB_PROTOTYPE(pfctl_statelim_nms, pfctl_statelim, entry,
158 pfctl_statelim_nm_cmp);
159 RB_PROTOTYPE(pfctl_sourcelim_ids, pfctl_sourcelim, entry,
160 pfctl_sourcelim_id_cmp);
161 RB_PROTOTYPE(pfctl_sourcelim_nms, pfctl_sourcelim, entry,
162 pfctl_sourcelim_nm_cmp);
163
164 enum showopt_id {
165 SHOWOPT_NONE = 0,
166 SHOWOPT_ETHER,
167 SHOWOPT_NAT,
168 SHOWOPT_QUEUE,
169 SHOWOPT_RULES,
170 SHOWOPT_ANCHORS,
171 SHOWOPT_SOURCES,
172 SHOWOPT_STATES,
173 SHOWOPT_INFO,
174 SHOWOPT_IFACES,
175 SHOWOPT_LABELS,
176 SHOWOPT_TIMEOUTS,
177 SHOWOPT_MEMORY,
178 SHOWOPT_TABLES,
179 SHOWOPT_OSFP,
180 SHOWOPT_RUNNING,
181 SHOWOPT_STATELIMS,
182 SHOWOPT_SOURCELIMS,
183 SHOWOPT_CREATORIDS,
184 SHOWOPT_ALL,
185 };
186
187 static struct pfctl_anchor_global pf_anchors;
188 struct pfctl_anchor pf_main_anchor;
189 struct pfctl_eth_anchor pf_eth_main_anchor;
190 static struct pfr_buffer skip_b;
191
192 static const char *clearopt;
193 static char *rulesopt;
194 static int showopt;
195 static const char *debugopt;
196 static char *anchoropt;
197 static const char *optiopt = NULL;
198 static const char *pf_device = PF_DEVICE;
199 static char *ifaceopt;
200 static char *tableopt;
201 static const char *tblcmdopt;
202 static int src_node_killers;
203 static char *src_node_kill[2];
204 static int state_killers;
205 static char *state_kill[2];
206 int loadopt;
207 int altqsupport;
208
209 int dev = -1;
210 struct pfctl_handle *pfh = NULL;
211 static int first_title = 1;
212 static int labels = 0;
213 static int exit_val = 0;
214
215 #define INDENT(d, o) do { \
216 if (o) { \
217 int i; \
218 for (i=0; i < d; i++) \
219 printf(" "); \
220 } \
221 } while (0); \
222
223
224 static const struct {
225 const char *name;
226 int index;
227 } pf_limits[] = {
228 { "states", PF_LIMIT_STATES },
229 { "src-nodes", PF_LIMIT_SRC_NODES },
230 { "frags", PF_LIMIT_FRAGS },
231 { "table-entries", PF_LIMIT_TABLE_ENTRIES },
232 { "anchors", PF_LIMIT_ANCHORS },
233 { "eth-anchors", PF_LIMIT_ETH_ANCHORS },
234 { NULL, 0 }
235 };
236
237 static unsigned int limit_curr[PF_LIMIT_MAX];
238
239 struct pf_hint {
240 const char *name;
241 int timeout;
242 };
243 static const struct pf_hint pf_hint_normal[] = {
244 { "tcp.first", 2 * 60 },
245 { "tcp.opening", 30 },
246 { "tcp.established", 24 * 60 * 60 },
247 { "tcp.closing", 15 * 60 },
248 { "tcp.finwait", 45 },
249 { "tcp.closed", 90 },
250 { "tcp.tsdiff", 30 },
251 { NULL, 0 }
252 };
253 static const struct pf_hint pf_hint_satellite[] = {
254 { "tcp.first", 3 * 60 },
255 { "tcp.opening", 30 + 5 },
256 { "tcp.established", 24 * 60 * 60 },
257 { "tcp.closing", 15 * 60 + 5 },
258 { "tcp.finwait", 45 + 5 },
259 { "tcp.closed", 90 + 5 },
260 { "tcp.tsdiff", 60 },
261 { NULL, 0 }
262 };
263 static const struct pf_hint pf_hint_conservative[] = {
264 { "tcp.first", 60 * 60 },
265 { "tcp.opening", 15 * 60 },
266 { "tcp.established", 5 * 24 * 60 * 60 },
267 { "tcp.closing", 60 * 60 },
268 { "tcp.finwait", 10 * 60 },
269 { "tcp.closed", 3 * 60 },
270 { "tcp.tsdiff", 60 },
271 { NULL, 0 }
272 };
273 static const struct pf_hint pf_hint_aggressive[] = {
274 { "tcp.first", 30 },
275 { "tcp.opening", 5 },
276 { "tcp.established", 5 * 60 * 60 },
277 { "tcp.closing", 60 },
278 { "tcp.finwait", 30 },
279 { "tcp.closed", 30 },
280 { "tcp.tsdiff", 10 },
281 { NULL, 0 }
282 };
283
284 static const struct {
285 const char *name;
286 const struct pf_hint *hint;
287 } pf_hints[] = {
288 { "normal", pf_hint_normal },
289 { "satellite", pf_hint_satellite },
290 { "high-latency", pf_hint_satellite },
291 { "conservative", pf_hint_conservative },
292 { "aggressive", pf_hint_aggressive },
293 { NULL, NULL }
294 };
295
296 static const char * const clearopt_list[] = {
297 "nat", "queue", "rules", "Sources",
298 "states", "info", "Tables", "osfp", "all",
299 "ethernet", "Reset", NULL
300 };
301
302 struct pfctl_opt_id {
303 const char *name;
304 int id;
305 };
306
307 static const struct pfctl_opt_id showopt_list[] = {
308 { "ether", SHOWOPT_ETHER },
309 { "nat", SHOWOPT_NAT },
310 { "queue", SHOWOPT_QUEUE },
311 { "rules", SHOWOPT_RULES },
312 { "Anchors", SHOWOPT_ANCHORS },
313 { "Sources", SHOWOPT_SOURCES },
314 { "states", SHOWOPT_STATES },
315 { "info", SHOWOPT_INFO },
316 { "Interfaces", SHOWOPT_IFACES },
317 { "labels", SHOWOPT_LABELS },
318 { "timeouts", SHOWOPT_TIMEOUTS },
319 { "memory", SHOWOPT_MEMORY },
320 { "Tables", SHOWOPT_TABLES },
321 { "osfp", SHOWOPT_OSFP },
322 { "Running", SHOWOPT_RUNNING },
323 { "LimiterStates", SHOWOPT_STATELIMS },
324 { "LimiterSrcs", SHOWOPT_SOURCELIMS },
325 { "creatorids", SHOWOPT_CREATORIDS },
326 { "all", SHOWOPT_ALL },
327
328 { NULL, SHOWOPT_NONE },
329 };
330
331 static const char * const tblcmdopt_list[] = {
332 "kill", "flush", "add", "delete", "load", "replace", "show",
333 "test", "zero", "expire", "reset", NULL
334 };
335
336 static const char * const debugopt_list[] = {
337 "none", "urgent", "misc", "loud", NULL
338 };
339
340 static const char * const optiopt_list[] = {
341 "none", "basic", "profile", NULL
342 };
343
344 void
usage(void)345 usage(void)
346 {
347 extern char *__progname;
348
349 fprintf(stderr,
350 "usage: %s [-AdeghMmNnOPqRSrvz] [-a anchor] [-D macro=value] [-F modifier]\n"
351 "\t[-f file] [-i interface] [-K host | network]\n"
352 "\t[-k host | network | gateway | label | id] [-o level] [-p device]\n"
353 "\t[-s modifier] [-t table -T command [address ...]] [-x level]\n",
354 __progname);
355
356 exit(1);
357 }
358
359 void
pfctl_err(int opts,int eval,const char * fmt,...)360 pfctl_err(int opts, int eval, const char *fmt, ...)
361 {
362 va_list ap;
363
364 va_start(ap, fmt);
365
366 if ((opts & PF_OPT_IGNFAIL) == 0)
367 verr(eval, fmt, ap);
368 else
369 vwarn(fmt, ap);
370
371 va_end(ap);
372
373 exit_val = eval;
374 }
375
376 void
pfctl_errx(int opts,int eval,const char * fmt,...)377 pfctl_errx(int opts, int eval, const char *fmt, ...)
378 {
379 va_list ap;
380
381 va_start(ap, fmt);
382
383 if ((opts & PF_OPT_IGNFAIL) == 0)
384 verrx(eval, fmt, ap);
385 else
386 vwarnx(fmt, ap);
387
388 va_end(ap);
389
390 exit_val = eval;
391 }
392
393 /*
394 * Cache protocol number to name translations.
395 *
396 * Translation is performed a lot e.g., when dumping states and
397 * getprotobynumber is incredibly expensive.
398 *
399 * Note from the getprotobynumber(3) manpage:
400 * <quote>
401 * These functions use a thread-specific data space; if the data is needed
402 * for future use, it should be copied before any subsequent calls overwrite
403 * it. Only the Internet protocols are currently understood.
404 * </quote>
405 *
406 * Consequently we only cache the name and strdup it for safety.
407 *
408 * At the time of writing this comment the last entry in /etc/protocols is:
409 * divert 258 DIVERT # Divert pseudo-protocol [non IANA]
410 */
411 const char *
pfctl_proto2name(int proto)412 pfctl_proto2name(int proto)
413 {
414 static const char *pfctl_proto_cache[259];
415 struct protoent *p;
416
417 if (proto >= nitems(pfctl_proto_cache)) {
418 p = getprotobynumber(proto);
419 if (p == NULL) {
420 return (NULL);
421 }
422 return (p->p_name);
423 }
424
425 if (pfctl_proto_cache[proto] == NULL) {
426 p = getprotobynumber(proto);
427 if (p == NULL) {
428 return (NULL);
429 }
430 pfctl_proto_cache[proto] = strdup(p->p_name);
431 }
432
433 return (pfctl_proto_cache[proto]);
434 }
435
436 int
pfctl_enable(int dev,int opts)437 pfctl_enable(int dev, int opts)
438 {
439 int ret;
440
441 if ((ret = pfctl_startstop(pfh, 1)) != 0) {
442 if (ret == EEXIST)
443 errx(1, "pf already enabled");
444 else if (ret == ESRCH)
445 errx(1, "pfil registration failed");
446 else
447 errc(1, ret, "DIOCSTART");
448 }
449 if ((opts & PF_OPT_QUIET) == 0)
450 fprintf(stderr, "pf enabled\n");
451
452 if (altqsupport && ioctl(dev, DIOCSTARTALTQ))
453 if (errno != EEXIST)
454 err(1, "DIOCSTARTALTQ");
455
456 return (0);
457 }
458
459 int
pfctl_disable(int dev,int opts)460 pfctl_disable(int dev, int opts)
461 {
462 int ret;
463
464 if ((ret = pfctl_startstop(pfh, 0)) != 0) {
465 if (ret == ENOENT)
466 errx(1, "pf not enabled");
467 else
468 errc(1, ret, "DIOCSTOP");
469 }
470 if ((opts & PF_OPT_QUIET) == 0)
471 fprintf(stderr, "pf disabled\n");
472
473 if (altqsupport && ioctl(dev, DIOCSTOPALTQ))
474 if (errno != ENOENT)
475 err(1, "DIOCSTOPALTQ");
476
477 return (0);
478 }
479
480 void
pfctl_clear_stats(struct pfctl_handle * h,int opts)481 pfctl_clear_stats(struct pfctl_handle *h, int opts)
482 {
483 int ret;
484 if ((ret = pfctl_clear_status(h)) != 0)
485 pfctl_err(opts, 1, "DIOCCLRSTATUS");
486 if ((opts & PF_OPT_QUIET) == 0)
487 fprintf(stderr, "pf: statistics cleared\n");
488 }
489
490 void
pfctl_get_skip_ifaces(void)491 pfctl_get_skip_ifaces(void)
492 {
493 bzero(&skip_b, sizeof(skip_b));
494 skip_b.pfrb_type = PFRB_IFACES;
495 for (;;) {
496 pfr_buf_grow(&skip_b, skip_b.pfrb_size);
497 skip_b.pfrb_size = skip_b.pfrb_msize;
498 if (pfi_get_ifaces(NULL, skip_b.pfrb_caddr, &skip_b.pfrb_size))
499 err(1, "pfi_get_ifaces");
500 if (skip_b.pfrb_size <= skip_b.pfrb_msize)
501 break;
502 }
503 }
504
505 void
pfctl_check_skip_ifaces(char * ifname)506 pfctl_check_skip_ifaces(char *ifname)
507 {
508 struct pfi_kif *p;
509 struct node_host *h = NULL, *n = NULL;
510
511 PFRB_FOREACH(p, &skip_b) {
512 if (!strcmp(ifname, p->pfik_name) &&
513 (p->pfik_flags & PFI_IFLAG_SKIP))
514 p->pfik_flags &= ~PFI_IFLAG_SKIP;
515 if (!strcmp(ifname, p->pfik_name) && p->pfik_group != NULL) {
516 if ((h = ifa_grouplookup(p->pfik_name, 0)) == NULL)
517 continue;
518
519 for (n = h; n != NULL; n = n->next) {
520 if (strncmp(p->pfik_name, ifname, IFNAMSIZ))
521 continue;
522
523 p->pfik_flags &= ~PFI_IFLAG_SKIP;
524 }
525 }
526 }
527 }
528
529 void
pfctl_adjust_skip_ifaces(struct pfctl * pf)530 pfctl_adjust_skip_ifaces(struct pfctl *pf)
531 {
532 struct pfi_kif *p, *pp;
533 struct node_host *h = NULL, *n = NULL;
534
535 PFRB_FOREACH(p, &skip_b) {
536 if (p->pfik_group == NULL || !(p->pfik_flags & PFI_IFLAG_SKIP))
537 continue;
538
539 pfctl_set_interface_flags(pf, p->pfik_name, PFI_IFLAG_SKIP, 0);
540 if ((h = ifa_grouplookup(p->pfik_name, 0)) == NULL)
541 continue;
542
543 for (n = h; n != NULL; n = n->next)
544 PFRB_FOREACH(pp, &skip_b) {
545 if (strncmp(pp->pfik_name, n->ifname, IFNAMSIZ))
546 continue;
547
548 if (!(pp->pfik_flags & PFI_IFLAG_SKIP))
549 pfctl_set_interface_flags(pf,
550 pp->pfik_name, PFI_IFLAG_SKIP, 1);
551 if (pp->pfik_flags & PFI_IFLAG_SKIP)
552 pp->pfik_flags &= ~PFI_IFLAG_SKIP;
553 }
554 }
555
556 PFRB_FOREACH(p, &skip_b) {
557 if (! (p->pfik_flags & PFI_IFLAG_SKIP))
558 continue;
559
560 pfctl_set_interface_flags(pf, p->pfik_name, PFI_IFLAG_SKIP, 0);
561 }
562 }
563
564 void
pfctl_clear_interface_flags(int dev,int opts)565 pfctl_clear_interface_flags(int dev, int opts)
566 {
567 struct pfioc_iface pi;
568
569 if ((opts & PF_OPT_NOACTION) == 0) {
570 bzero(&pi, sizeof(pi));
571 pi.pfiio_flags = PFI_IFLAG_SKIP;
572
573 if (ioctl(dev, DIOCCLRIFFLAG, &pi))
574 err(1, "DIOCCLRIFFLAG");
575 if ((opts & PF_OPT_QUIET) == 0)
576 fprintf(stderr, "pf: interface flags reset\n");
577 }
578 }
579
580 void
pfctl_flush_eth_rules(int dev,int opts,char * anchorname)581 pfctl_flush_eth_rules(int dev, int opts, char *anchorname)
582 {
583 int ret;
584
585 ret = pfctl_clear_eth_rules(dev, anchorname);
586 if (ret != 0)
587 err(1, "pfctl_clear_eth_rules");
588
589 if ((opts & PF_OPT_QUIET) == 0)
590 fprintf(stderr, "Ethernet rules cleared\n");
591 }
592
593 int
pfctl_flush_rules(int dev,int opts,char * anchorname)594 pfctl_flush_rules(int dev, int opts, char *anchorname)
595 {
596 int ret;
597
598 ret = pfctl_clear_rules(dev, anchorname);
599 if (ret != 0) {
600 pfctl_err(opts, 1, "%s", __func__);
601 return (1);
602 } else if ((opts & PF_OPT_QUIET) == 0)
603 fprintf(stderr, "rules cleared\n");
604
605 return (0);
606 }
607
608 void
pfctl_flush_nat(int dev,int opts,char * anchorname)609 pfctl_flush_nat(int dev, int opts, char *anchorname)
610 {
611 int ret;
612
613 ret = pfctl_clear_nat(dev, anchorname);
614 if (ret != 0)
615 err(1, "pfctl_clear_nat");
616 if ((opts & PF_OPT_QUIET) == 0)
617 fprintf(stderr, "nat cleared\n");
618 }
619
620 int
pfctl_clear_altq(int dev,int opts)621 pfctl_clear_altq(int dev, int opts)
622 {
623 struct pfr_buffer t;
624
625 if (!altqsupport)
626 return (-1);
627 memset(&t, 0, sizeof(t));
628 t.pfrb_type = PFRB_TRANS;
629 if (pfctl_add_trans(&t, PF_RULESET_ALTQ, "") ||
630 pfctl_trans(dev, &t, DIOCXBEGIN, 0) ||
631 pfctl_trans(dev, &t, DIOCXCOMMIT, 0))
632 err(1, "pfctl_clear_altq");
633 if ((opts & PF_OPT_QUIET) == 0)
634 fprintf(stderr, "altq cleared\n");
635 return (0);
636 }
637
638 void
pfctl_clear_src_nodes(int dev,int opts)639 pfctl_clear_src_nodes(int dev, int opts)
640 {
641 if (ioctl(dev, DIOCCLRSRCNODES))
642 pfctl_err(opts, 1, "DIOCCLRSRCNODES");
643 if ((opts & PF_OPT_QUIET) == 0)
644 fprintf(stderr, "source tracking entries cleared\n");
645 }
646
647 void
pfctl_clear_iface_states(int dev,const char * iface,int opts)648 pfctl_clear_iface_states(int dev, const char *iface, int opts)
649 {
650 struct pfctl_kill kill;
651 unsigned int killed;
652 int ret;
653
654 memset(&kill, 0, sizeof(kill));
655 if (iface != NULL && strlcpy(kill.ifname, iface,
656 sizeof(kill.ifname)) >= sizeof(kill.ifname))
657 pfctl_errx(opts, 1, "invalid interface: %s", iface);
658
659 if (opts & PF_OPT_KILLMATCH)
660 kill.kill_match = true;
661
662 if ((ret = pfctl_clear_states_h(pfh, &kill, &killed)) != 0)
663 pfctl_err(opts, 1, "DIOCCLRSTATUS");
664 if ((opts & PF_OPT_QUIET) == 0)
665 fprintf(stderr, "%d states cleared\n", killed);
666 }
667
668 struct addrinfo *
pfctl_addrprefix(char * addr,struct pf_addr * mask,int numeric)669 pfctl_addrprefix(char *addr, struct pf_addr *mask, int numeric)
670 {
671 char *p;
672 const char *errstr;
673 int prefix, ret_ga, q, r;
674 struct addrinfo hints, *res;
675
676 bzero(&hints, sizeof(hints));
677 hints.ai_socktype = SOCK_DGRAM; /* dummy */
678 if (numeric)
679 hints.ai_flags = AI_NUMERICHOST;
680
681 if ((p = strchr(addr, '/')) != NULL) {
682 *p++ = '\0';
683 /* prefix only with numeric addresses */
684 hints.ai_flags |= AI_NUMERICHOST;
685 }
686
687 if ((ret_ga = getaddrinfo(addr, NULL, &hints, &res))) {
688 errx(1, "getaddrinfo: %s", gai_strerror(ret_ga));
689 /* NOTREACHED */
690 }
691
692 if (p == NULL)
693 return (res);
694
695 prefix = strtonum(p, 0, res->ai_family == AF_INET6 ? 128 : 32, &errstr);
696 if (errstr)
697 errx(1, "prefix is %s: %s", errstr, p);
698
699 q = prefix >> 3;
700 r = prefix & 7;
701 switch (res->ai_family) {
702 case AF_INET:
703 bzero(&mask->v4, sizeof(mask->v4));
704 mask->v4.s_addr = htonl((u_int32_t)
705 (0xffffffffffULL << (32 - prefix)));
706 break;
707 case AF_INET6:
708 bzero(&mask->v6, sizeof(mask->v6));
709 if (q > 0)
710 memset((void *)&mask->v6, 0xff, q);
711 if (r > 0)
712 *((u_char *)&mask->v6 + q) =
713 (0xff00 >> r) & 0xff;
714 break;
715 }
716
717 return (res);
718 }
719
720 void
pfctl_kill_src_nodes(int dev,int opts)721 pfctl_kill_src_nodes(int dev, int opts)
722 {
723 struct pfioc_src_node_kill psnk;
724 struct addrinfo *res[2], *resp[2];
725 struct sockaddr last_src, last_dst;
726 int killed, sources, dests;
727
728 killed = sources = dests = 0;
729
730 memset(&psnk, 0, sizeof(psnk));
731 memset(&psnk.psnk_src.addr.v.a.mask, 0xff,
732 sizeof(psnk.psnk_src.addr.v.a.mask));
733 memset(&last_src, 0xff, sizeof(last_src));
734 memset(&last_dst, 0xff, sizeof(last_dst));
735
736 res[0] = pfctl_addrprefix(src_node_kill[0],
737 &psnk.psnk_src.addr.v.a.mask, (opts & PF_OPT_NODNS));
738
739 for (resp[0] = res[0]; resp[0]; resp[0] = resp[0]->ai_next) {
740 if (resp[0]->ai_addr == NULL)
741 continue;
742 /* We get lots of duplicates. Catch the easy ones */
743 if (memcmp(&last_src, resp[0]->ai_addr, sizeof(last_src)) == 0)
744 continue;
745 last_src = *(struct sockaddr *)resp[0]->ai_addr;
746
747 psnk.psnk_af = resp[0]->ai_family;
748 sources++;
749
750 copy_satopfaddr(&psnk.psnk_src.addr.v.a.addr, resp[0]->ai_addr);
751
752 if (src_node_killers > 1) {
753 dests = 0;
754 memset(&psnk.psnk_dst.addr.v.a.mask, 0xff,
755 sizeof(psnk.psnk_dst.addr.v.a.mask));
756 memset(&last_dst, 0xff, sizeof(last_dst));
757 res[1] = pfctl_addrprefix(src_node_kill[1],
758 &psnk.psnk_dst.addr.v.a.mask,
759 (opts & PF_OPT_NODNS));
760 for (resp[1] = res[1]; resp[1];
761 resp[1] = resp[1]->ai_next) {
762 if (resp[1]->ai_addr == NULL)
763 continue;
764 if (psnk.psnk_af != resp[1]->ai_family)
765 continue;
766
767 if (memcmp(&last_dst, resp[1]->ai_addr,
768 sizeof(last_dst)) == 0)
769 continue;
770 last_dst = *(struct sockaddr *)resp[1]->ai_addr;
771
772 dests++;
773
774 copy_satopfaddr(&psnk.psnk_dst.addr.v.a.addr,
775 resp[1]->ai_addr);
776 if (ioctl(dev, DIOCKILLSRCNODES, &psnk))
777 err(1, "DIOCKILLSRCNODES");
778 killed += psnk.psnk_killed;
779 }
780 freeaddrinfo(res[1]);
781 } else {
782 if (ioctl(dev, DIOCKILLSRCNODES, &psnk))
783 err(1, "DIOCKILLSRCNODES");
784 killed += psnk.psnk_killed;
785 }
786 }
787
788 freeaddrinfo(res[0]);
789
790 if ((opts & PF_OPT_QUIET) == 0)
791 fprintf(stderr, "killed %d src nodes from %d sources and %d "
792 "destinations\n", killed, sources, dests);
793 }
794
795 void
pfctl_net_kill_states(int dev,const char * iface,int opts)796 pfctl_net_kill_states(int dev, const char *iface, int opts)
797 {
798 struct pfctl_kill kill;
799 struct addrinfo *res[2], *resp[2];
800 struct sockaddr last_src, last_dst;
801 unsigned int newkilled;
802 int killed, sources, dests;
803 int ret;
804
805 killed = sources = dests = 0;
806
807 memset(&kill, 0, sizeof(kill));
808 memset(&kill.src.addr.v.a.mask, 0xff,
809 sizeof(kill.src.addr.v.a.mask));
810 memset(&last_src, 0xff, sizeof(last_src));
811 memset(&last_dst, 0xff, sizeof(last_dst));
812 if (iface != NULL && strlcpy(kill.ifname, iface,
813 sizeof(kill.ifname)) >= sizeof(kill.ifname))
814 pfctl_errx(opts, 1, "invalid interface: %s", iface);
815
816 if (state_killers == 2 && (strcmp(state_kill[0], "nat") == 0)) {
817 kill.nat = true;
818 state_kill[0] = state_kill[1];
819 state_killers = 1;
820 }
821
822 res[0] = pfctl_addrprefix(state_kill[0],
823 &kill.src.addr.v.a.mask, (opts & PF_OPT_NODNS));
824
825 if (opts & PF_OPT_KILLMATCH)
826 kill.kill_match = true;
827
828 for (resp[0] = res[0]; resp[0]; resp[0] = resp[0]->ai_next) {
829 if (resp[0]->ai_addr == NULL)
830 continue;
831 /* We get lots of duplicates. Catch the easy ones */
832 if (memcmp(&last_src, resp[0]->ai_addr, sizeof(last_src)) == 0)
833 continue;
834 last_src = *(struct sockaddr *)resp[0]->ai_addr;
835
836 kill.af = resp[0]->ai_family;
837 sources++;
838
839 copy_satopfaddr(&kill.src.addr.v.a.addr, resp[0]->ai_addr);
840
841 if (state_killers > 1) {
842 dests = 0;
843 memset(&kill.dst.addr.v.a.mask, 0xff,
844 sizeof(kill.dst.addr.v.a.mask));
845 memset(&last_dst, 0xff, sizeof(last_dst));
846 res[1] = pfctl_addrprefix(state_kill[1],
847 &kill.dst.addr.v.a.mask,
848 (opts & PF_OPT_NODNS));
849 for (resp[1] = res[1]; resp[1];
850 resp[1] = resp[1]->ai_next) {
851 if (resp[1]->ai_addr == NULL)
852 continue;
853 if (kill.af != resp[1]->ai_family)
854 continue;
855
856 if (memcmp(&last_dst, resp[1]->ai_addr,
857 sizeof(last_dst)) == 0)
858 continue;
859 last_dst = *(struct sockaddr *)resp[1]->ai_addr;
860
861 dests++;
862
863 copy_satopfaddr(&kill.dst.addr.v.a.addr,
864 resp[1]->ai_addr);
865
866 if ((ret = pfctl_kill_states_h(pfh, &kill, &newkilled)) != 0)
867 pfctl_errx(opts, 1, "DIOCKILLSTATES");
868 killed += newkilled;
869 }
870 freeaddrinfo(res[1]);
871 } else {
872 if ((ret = pfctl_kill_states_h(pfh, &kill, &newkilled)) != 0)
873 pfctl_errx(opts, 1, "DIOCKILLSTATES");
874 killed += newkilled;
875 }
876 }
877
878 freeaddrinfo(res[0]);
879
880 if ((opts & PF_OPT_QUIET) == 0)
881 fprintf(stderr, "killed %d states from %d sources and %d "
882 "destinations\n", killed, sources, dests);
883 }
884
885 void
pfctl_gateway_kill_states(int dev,const char * iface,int opts)886 pfctl_gateway_kill_states(int dev, const char *iface, int opts)
887 {
888 struct pfctl_kill kill;
889 struct addrinfo *res, *resp;
890 struct sockaddr last_src;
891 unsigned int newkilled;
892 int killed = 0;
893
894 if (state_killers != 2 || (strlen(state_kill[1]) == 0)) {
895 warnx("no gateway specified");
896 usage();
897 }
898
899 memset(&kill, 0, sizeof(kill));
900 memset(&kill.rt_addr.addr.v.a.mask, 0xff,
901 sizeof(kill.rt_addr.addr.v.a.mask));
902 memset(&last_src, 0xff, sizeof(last_src));
903 if (iface != NULL && strlcpy(kill.ifname, iface,
904 sizeof(kill.ifname)) >= sizeof(kill.ifname))
905 pfctl_errx(opts, 1, "invalid interface: %s", iface);
906
907 if (opts & PF_OPT_KILLMATCH)
908 kill.kill_match = true;
909
910 res = pfctl_addrprefix(state_kill[1], &kill.rt_addr.addr.v.a.mask,
911 (opts & PF_OPT_NODNS));
912
913 for (resp = res; resp; resp = resp->ai_next) {
914 if (resp->ai_addr == NULL)
915 continue;
916 /* We get lots of duplicates. Catch the easy ones */
917 if (memcmp(&last_src, resp->ai_addr, sizeof(last_src)) == 0)
918 continue;
919 last_src = *(struct sockaddr *)resp->ai_addr;
920
921 kill.af = resp->ai_family;
922
923 copy_satopfaddr(&kill.rt_addr.addr.v.a.addr,
924 resp->ai_addr);
925 if (pfctl_kill_states_h(pfh, &kill, &newkilled))
926 pfctl_errx(opts, 1, "DIOCKILLSTATES");
927 killed += newkilled;
928 }
929
930 freeaddrinfo(res);
931
932 if ((opts & PF_OPT_QUIET) == 0)
933 fprintf(stderr, "killed %d states\n", killed);
934 }
935
936 void
pfctl_label_kill_states(int dev,const char * iface,int opts)937 pfctl_label_kill_states(int dev, const char *iface, int opts)
938 {
939 struct pfctl_kill kill;
940 unsigned int killed;
941 int ret;
942
943 if (state_killers != 2 || (strlen(state_kill[1]) == 0)) {
944 warnx("no label specified");
945 usage();
946 }
947 memset(&kill, 0, sizeof(kill));
948 if (iface != NULL && strlcpy(kill.ifname, iface,
949 sizeof(kill.ifname)) >= sizeof(kill.ifname))
950 pfctl_errx(opts, 1, "invalid interface: %s", iface);
951
952 if (opts & PF_OPT_KILLMATCH)
953 kill.kill_match = true;
954
955 if (strlcpy(kill.label, state_kill[1], sizeof(kill.label)) >=
956 sizeof(kill.label))
957 errx(1, "label too long: %s", state_kill[1]);
958
959 if ((ret = pfctl_kill_states_h(pfh, &kill, &killed)) != 0)
960 pfctl_errx(opts, 1, "DIOCKILLSTATES");
961
962 if ((opts & PF_OPT_QUIET) == 0)
963 fprintf(stderr, "killed %d states\n", killed);
964 }
965
966 void
pfctl_id_kill_states(int dev,const char * iface,int opts)967 pfctl_id_kill_states(int dev, const char *iface, int opts)
968 {
969 struct pfctl_kill kill;
970 unsigned int killed;
971 int ret;
972
973 if (state_killers != 2 || (strlen(state_kill[1]) == 0)) {
974 warnx("no id specified");
975 usage();
976 }
977
978 memset(&kill, 0, sizeof(kill));
979
980 if (opts & PF_OPT_KILLMATCH)
981 kill.kill_match = true;
982
983 if ((sscanf(state_kill[1], "%jx/%x",
984 &kill.cmp.id, &kill.cmp.creatorid)) == 2) {
985 }
986 else if ((sscanf(state_kill[1], "%jx", &kill.cmp.id)) == 1) {
987 kill.cmp.creatorid = 0;
988 } else {
989 warnx("wrong id format specified");
990 usage();
991 }
992 if (kill.cmp.id == 0) {
993 warnx("cannot kill id 0");
994 usage();
995 }
996
997 if ((ret = pfctl_kill_states_h(pfh, &kill, &killed)) != 0)
998 pfctl_errx(opts, 1, "DIOCKILLSTATES");
999
1000 if ((opts & PF_OPT_QUIET) == 0)
1001 fprintf(stderr, "killed %d states\n", killed);
1002 }
1003
1004 void
pfctl_key_kill_states(int dev,const char * iface,int opts)1005 pfctl_key_kill_states(int dev, const char *iface, int opts)
1006 {
1007 struct pfctl_kill kill;
1008 char *s, *token, *tokens[4];
1009 struct protoent *p;
1010 u_int i, sidx, didx;
1011 int ret, killed;
1012
1013 if (state_killers != 2 || (strlen(state_kill[1]) == 0)) {
1014 warnx("no key specified");
1015 usage();
1016 }
1017 memset(&kill, 0, sizeof(kill));
1018
1019 if (iface != NULL &&
1020 strlcpy(kill.ifname, iface, sizeof(kill.ifname)) >=
1021 sizeof(kill.ifname))
1022 pfctl_errx(opts, 1, "invalid interface: %s", iface);
1023
1024 s = strdup(state_kill[1]);
1025 if (!s)
1026 errx(1, "%s: strdup", __func__);
1027 i = 0;
1028 while ((token = strsep(&s, " \t")) != NULL)
1029 if (*token != '\0') {
1030 if (i < 4)
1031 tokens[i] = token;
1032 i++;
1033 }
1034 if (i != 4)
1035 errx(1, "%s: key must be "
1036 "\"protocol host1:port1 direction host2:port2\" format",
1037 __func__);
1038
1039 if ((p = getprotobyname(tokens[0])) == NULL)
1040 errx(1, "invalid protocol: %s", tokens[0]);
1041 kill.proto = p->p_proto;
1042
1043 if (strcmp(tokens[2], "->") == 0) {
1044 sidx = 1;
1045 didx = 3;
1046 } else if (strcmp(tokens[2], "<-") == 0) {
1047 sidx = 3;
1048 didx = 1;
1049 } else
1050 errx(1, "invalid direction: %s", tokens[2]);
1051
1052 if (pfctl_parse_host(tokens[sidx], &kill.src) == -1)
1053 errx(1, "invalid host: %s", tokens[sidx]);
1054 if (pfctl_parse_host(tokens[didx], &kill.dst) == -1)
1055 errx(1, "invalid host: %s", tokens[didx]);
1056
1057 if ((ret = pfctl_kill_states_h(pfh, &kill, &killed)) != 0)
1058 pfctl_errx(opts, 1, "DIOCKILLSTATES");
1059
1060 if ((opts & PF_OPT_QUIET) == 0)
1061 fprintf(stderr, "killed %d states\n", killed);
1062 }
1063
1064 int
pfctl_parse_host(char * str,struct pf_rule_addr * addr)1065 pfctl_parse_host(char *str, struct pf_rule_addr *addr)
1066 {
1067 char *s = NULL, *sbs, *sbe;
1068 struct addrinfo hints, *ai;
1069
1070 s = strdup(str);
1071 if (!s)
1072 errx(1, "pfctl_parse_host: strdup");
1073
1074 memset(&hints, 0, sizeof(hints));
1075 hints.ai_socktype = SOCK_DGRAM; /* dummy */
1076 hints.ai_flags = AI_NUMERICHOST;
1077
1078 if ((sbs = strchr(s, '[')) != NULL && (sbe = strrchr(s, ']')) != NULL) {
1079 hints.ai_family = AF_INET6;
1080 *(sbs++) = *sbe = '\0';
1081 } else if ((sbs = strchr(s, ':')) != NULL) {
1082 hints.ai_family = AF_INET;
1083 *(sbs++) = '\0';
1084 } else {
1085 /* Assume that no ':<number>' means port 0 */
1086 }
1087
1088 if (getaddrinfo(s, sbs, &hints, &ai) != 0)
1089 goto error;
1090
1091 copy_satopfaddr(&addr->addr.v.a.addr, ai->ai_addr);
1092 addr->port[0] = ai->ai_family == AF_INET6 ?
1093 ((struct sockaddr_in6 *)ai->ai_addr)->sin6_port :
1094 ((struct sockaddr_in *)ai->ai_addr)->sin_port;
1095 freeaddrinfo(ai);
1096 free(s);
1097
1098 memset(&addr->addr.v.a.mask, 0xff, sizeof(struct pf_addr));
1099 addr->port_op = PF_OP_EQ;
1100 addr->addr.type = PF_ADDR_ADDRMASK;
1101
1102 return (0);
1103
1104 error:
1105 free(s);
1106 return (-1);
1107 }
1108
1109 int
pfctl_get_pool(int dev,struct pfctl_pool * pool,u_int32_t nr,u_int32_t ticket,int r_action,const char * anchorname,int which)1110 pfctl_get_pool(int dev, struct pfctl_pool *pool, u_int32_t nr,
1111 u_int32_t ticket, int r_action, const char *anchorname, int which)
1112 {
1113 struct pfioc_pooladdr pp;
1114 struct pfctl_pooladdr *pa;
1115 u_int32_t pnr, mpnr;
1116 int ret;
1117
1118 memset(&pp, 0, sizeof(pp));
1119 if ((ret = pfctl_get_addrs(pfh, ticket, nr, r_action, anchorname, &mpnr, which)) != 0) {
1120 warnc(ret, "DIOCGETADDRS");
1121 return (-1);
1122 }
1123
1124 TAILQ_INIT(&pool->list);
1125 for (pnr = 0; pnr < mpnr; ++pnr) {
1126 if ((ret = pfctl_get_addr(pfh, ticket, nr, r_action, anchorname, pnr, &pp, which)) != 0) {
1127 warnc(ret, "DIOCGETADDR");
1128 return (-1);
1129 }
1130 pa = calloc(1, sizeof(struct pfctl_pooladdr));
1131 if (pa == NULL)
1132 err(1, "calloc");
1133 bcopy(&pp.addr, pa, sizeof(struct pfctl_pooladdr));
1134 pa->af = pp.af;
1135 TAILQ_INSERT_TAIL(&pool->list, pa, entries);
1136 }
1137
1138 return (0);
1139 }
1140
1141 void
pfctl_move_pool(struct pfctl_pool * src,struct pfctl_pool * dst)1142 pfctl_move_pool(struct pfctl_pool *src, struct pfctl_pool *dst)
1143 {
1144 struct pfctl_pooladdr *pa;
1145
1146 while ((pa = TAILQ_FIRST(&src->list)) != NULL) {
1147 TAILQ_REMOVE(&src->list, pa, entries);
1148 TAILQ_INSERT_TAIL(&dst->list, pa, entries);
1149 }
1150 }
1151
1152 void
pfctl_clear_pool(struct pfctl_pool * pool)1153 pfctl_clear_pool(struct pfctl_pool *pool)
1154 {
1155 struct pfctl_pooladdr *pa;
1156
1157 while ((pa = TAILQ_FIRST(&pool->list)) != NULL) {
1158 TAILQ_REMOVE(&pool->list, pa, entries);
1159 free(pa);
1160 }
1161 }
1162
1163 void
pfctl_print_eth_rule_counters(struct pfctl_eth_rule * rule,int opts)1164 pfctl_print_eth_rule_counters(struct pfctl_eth_rule *rule, int opts)
1165 {
1166 if (opts & PF_OPT_VERBOSE) {
1167 printf(" [ Evaluations: %-8llu Packets: %-8llu "
1168 "Bytes: %-10llu]\n",
1169 (unsigned long long)rule->evaluations,
1170 (unsigned long long)(rule->packets[0] +
1171 rule->packets[1]),
1172 (unsigned long long)(rule->bytes[0] +
1173 rule->bytes[1]));
1174 }
1175 if (opts & PF_OPT_VERBOSE2) {
1176 char timestr[30];
1177
1178 if (rule->last_active_timestamp != 0) {
1179 bcopy(ctime(&rule->last_active_timestamp), timestr,
1180 sizeof(timestr));
1181 *strchr(timestr, '\n') = '\0';
1182 } else {
1183 snprintf(timestr, sizeof(timestr), "N/A");
1184 }
1185 printf(" [ Last Active Time: %s ]\n", timestr);
1186 }
1187 }
1188
1189 void
pfctl_print_rule_counters(struct pfctl_rule * rule,int opts)1190 pfctl_print_rule_counters(struct pfctl_rule *rule, int opts)
1191 {
1192 if (opts & PF_OPT_DEBUG) {
1193 const char *t[PF_SKIP_COUNT] = { "i", "d", "f",
1194 "p", "sa", "da", "sp", "dp" };
1195 int i;
1196
1197 printf(" [ Skip steps: ");
1198 for (i = 0; i < PF_SKIP_COUNT; ++i) {
1199 if (rule->skip[i].nr == rule->nr + 1)
1200 continue;
1201 printf("%s=", t[i]);
1202 if (rule->skip[i].nr == -1)
1203 printf("end ");
1204 else
1205 printf("%u ", rule->skip[i].nr);
1206 }
1207 printf("]\n");
1208
1209 printf(" [ queue: qname=%s qid=%u pqname=%s pqid=%u ]\n",
1210 rule->qname, rule->qid, rule->pqname, rule->pqid);
1211 if (rule->rule_flag & PFRULE_EXPIRED)
1212 printf(" [ Expired: %lld secs ago ]\n",
1213 (long long)(time(NULL) - rule->exptime));
1214 }
1215 if (opts & PF_OPT_VERBOSE) {
1216 printf(" [ Evaluations: %-8llu Packets: %-8llu "
1217 "Bytes: %-10llu States: %-6ju]\n",
1218 (unsigned long long)rule->evaluations,
1219 (unsigned long long)(rule->packets[0] +
1220 rule->packets[1]),
1221 (unsigned long long)(rule->bytes[0] +
1222 rule->bytes[1]), (uintmax_t)rule->states_cur);
1223 printf(" [ Source Nodes: %-6ju "
1224 "Limit: %-6ju "
1225 "NAT/RDR: %-6ju "
1226 "Route: %-6ju "
1227 "]\n",
1228 (uintmax_t)rule->src_nodes,
1229 (uintmax_t)rule->src_nodes_type[PF_SN_LIMIT],
1230 (uintmax_t)rule->src_nodes_type[PF_SN_NAT],
1231 (uintmax_t)rule->src_nodes_type[PF_SN_ROUTE]);
1232 if (!(opts & PF_OPT_DEBUG))
1233 printf(" [ Inserted: uid %u pid %u "
1234 "State Creations: %-6ju]\n",
1235 (unsigned)rule->cuid, (unsigned)rule->cpid,
1236 (uintmax_t)rule->states_tot);
1237 }
1238 if (opts & PF_OPT_VERBOSE2) {
1239 char timestr[30];
1240 if (rule->last_active_timestamp != 0) {
1241 bcopy(ctime(&rule->last_active_timestamp), timestr,
1242 sizeof(timestr));
1243 *strchr(timestr, '\n') = '\0';
1244 } else {
1245 snprintf(timestr, sizeof(timestr), "N/A");
1246 }
1247 printf(" [ Last Active Time: %s ]\n", timestr);
1248 }
1249 }
1250
1251 void
pfctl_print_title(char * title)1252 pfctl_print_title(char *title)
1253 {
1254 if (!first_title)
1255 printf("\n");
1256 first_title = 0;
1257 printf("%s\n", title);
1258 }
1259
1260 int
pfctl_show_statelims(int dev,enum pfctl_show format,int opts)1261 pfctl_show_statelims(int dev, enum pfctl_show format, int opts)
1262 {
1263 struct pfctl_state_lim stlim;
1264 uint32_t id = PF_STATELIM_ID_MIN;
1265 int error;
1266
1267 if (opts & PF_OPT_SHOWALL)
1268 pfctl_print_title("STATE LIMITERS:");
1269
1270 if (format == PFCTL_SHOW_LABELS) {
1271 printf("%3s %8s/%-8s %5s/%-5s %8s %8s %8s\n", "ID", "USE",
1272 "LIMIT", "RATE", "SECS", "ADMIT", "HARDLIM", "RATELIM");
1273 }
1274
1275 for (;;) {
1276 memset(&stlim, 0, sizeof(stlim));
1277 stlim.id = id;
1278
1279 error = pfctl_state_limiter_nget(pfh, &stlim);
1280 if (error != 0) {
1281 if (error == ENOENT) {
1282 /* we're done */
1283 return (0);
1284 }
1285 warnc(error, "DIOCGETNSTATELIM %u", stlim.id);
1286 return (-1);
1287 }
1288
1289 switch (format) {
1290 case PFCTL_SHOW_RULES:
1291 print_statelim(&stlim);
1292 break;
1293 case PFCTL_SHOW_LABELS:
1294 printf("%3u %8u/%-8u ", stlim.id, stlim.inuse,
1295 stlim.limit);
1296 if (stlim.rate.limit != 0) {
1297 printf("%5u/%-5u ", stlim.rate.limit,
1298 stlim.rate.seconds);
1299 } else
1300 printf("%5s/%-5s ", "nil", "nil");
1301 printf("%8ju %8ju %8ju\n", stlim.admitted,
1302 stlim.hardlimited, stlim.ratelimited);
1303 break;
1304 default:
1305 errx(1, "%s: unexpected format %d", __func__, format);
1306 /* NOTREACHED */
1307 }
1308
1309 id = stlim.id + 1;
1310 }
1311 }
1312
1313 static inline int
pf_addr_inc(struct pf_addr * addr)1314 pf_addr_inc(struct pf_addr *addr)
1315 {
1316 int i;
1317 uint32_t val, inc;
1318
1319 for (i = 3; i >= 0; i--) {
1320 val = ntohl(addr->addr32[i]);
1321 inc = val + 1;
1322 addr->addr32[i] = htonl(inc);
1323 if (inc > val)
1324 return (0);
1325 }
1326
1327 return (1);
1328 }
1329
1330 static int
pfctl_print_source(struct pfctl_source * e,void * arg)1331 pfctl_print_source(struct pfctl_source *e, void *arg)
1332 {
1333 print_addr_str(e->af, &e->addr);
1334 switch (e->af) {
1335 case AF_INET:
1336 printf("/%u ", e->inet_prefix);
1337 break;
1338 case AF_INET6:
1339 printf("/%u ", e->inet6_prefix);
1340 break;
1341 default:
1342 printf("/af? ");
1343 break;
1344 }
1345 printf("rdomain %u ", e->rdomain);
1346
1347 printf("inuse %u/%u ", e->inuse, e->limit);
1348 printf("admit %ju hardlim %ju ratelim %ju\n",
1349 e->admitted, e->hardlimited, e->ratelimited);
1350
1351 return (0);
1352 }
1353
1354 static int
pfctl_show_sources(int dev,const struct pfctl_source_lim * srlim,enum pfctl_show format,int opts)1355 pfctl_show_sources(int dev, const struct pfctl_source_lim *srlim,
1356 enum pfctl_show format, int opts)
1357 {
1358 int error;
1359
1360 if (format != PFCTL_SHOW_LABELS)
1361 errx(1, "%s format is not PFCTL_SHOW_LABELS", __func__);
1362
1363 error = pfctl_source_get(pfh, srlim->id, pfctl_print_source, NULL);
1364 if (error != 0)
1365 warnc(error, "DIOCGETNSOURCE %u", srlim->id);
1366 return (error);
1367 }
1368
1369 int
pfctl_show_sourcelims(int dev,enum pfctl_show format,int opts,const char * idopt)1370 pfctl_show_sourcelims(int dev, enum pfctl_show format, int opts,
1371 const char *idopt)
1372 {
1373 struct pfctl_source_lim srlim;
1374 uint32_t id = PF_SOURCELIM_ID_MIN;
1375 int error;
1376
1377 if (idopt != NULL) {
1378 const char *errstr;
1379
1380 id = strtonum(idopt, PF_SOURCELIM_ID_MIN, PF_SOURCELIM_ID_MAX,
1381 &errstr);
1382 if (errstr != NULL)
1383 errx(1, "source limiter id: %s", errstr);
1384 }
1385
1386 if (opts & PF_OPT_SHOWALL)
1387 pfctl_print_title("SOURCE LIMITERS:");
1388
1389 if (format == PFCTL_SHOW_LABELS) {
1390 printf("%3s %8s/%-8s %5s %5s/%-5s %8s %8s %8s %8s\n", "ID",
1391 "USE", "ADDRS", "LIMIT", "RATE", "SECS", "ADMIT", "ADDRLIM",
1392 "HARDLIM", "RATELIM");
1393 }
1394
1395 for (;;) {
1396 memset(&srlim, 0, sizeof(srlim));
1397 srlim.id = id;
1398
1399 if (idopt != NULL) {
1400 error = pfctl_source_limiter_get(pfh, &srlim);
1401 } else {
1402 error = pfctl_source_limiter_nget(pfh, &srlim);
1403 }
1404
1405 if (error != 0) {
1406 if (error == ESRCH) {
1407 /* we're done */
1408 return (0);
1409 }
1410 warnc(error, "DIOCGETNSOURCELIM %u", srlim.id);
1411 return (-1);
1412 }
1413
1414 switch (format) {
1415 case PFCTL_SHOW_RULES:
1416 print_sourcelim(&srlim);
1417 break;
1418
1419 case PFCTL_SHOW_LABELS:
1420 printf("%3u %8u/%-8u %5u ", srlim.id, srlim.nentries,
1421 srlim.entries, srlim.limit);
1422 if (srlim.rate.limit != 0) {
1423 printf("%5u/%-5u ", srlim.rate.limit,
1424 srlim.rate.seconds);
1425 } else
1426 printf("%5s/%-5s ", "nil", "nil");
1427 printf("%8ju %8ju %8ju %8ju\n",
1428 srlim.admitted, srlim.addrlimited, srlim.hardlimited,
1429 srlim.ratelimited);
1430
1431 if (opts & PF_OPT_VERBOSE)
1432 if (pfctl_show_sources(dev, &srlim, format,
1433 opts) != 0)
1434 return (-1);
1435 break;
1436
1437 default:
1438 errx(1, "%s: unexpected format %d", __func__, format);
1439 /* NOTREACHED */
1440 }
1441
1442 id = srlim.id + 1;
1443 }
1444
1445 return (0);
1446 }
1447
1448 void
pfctl_kill_source(int dev,const char * idopt,const char * source,int opts)1449 pfctl_kill_source(int dev, const char *idopt, const char *source, int opts)
1450 {
1451 struct pfctl_source_clear clear = { 0 };
1452 unsigned int id;
1453 const char *errstr;
1454 struct addrinfo hints, *res;
1455 int error;
1456
1457 if (idopt == NULL)
1458 errx(1, "source limiter id unspecified");
1459 if (source == NULL)
1460 errx(1, "source limiter address unspecified");
1461
1462 id = strtonum(idopt, PF_SOURCELIM_ID_MIN, PF_SOURCELIM_ID_MAX, &errstr);
1463 if (errstr != NULL)
1464 errx(1, "source limiter id: %s", errstr);
1465
1466 memset(&hints, 0, sizeof(hints));
1467 hints.ai_socktype = SOCK_DGRAM; /* dummy */
1468 hints.ai_flags = AI_NUMERICHOST;
1469
1470 error = getaddrinfo(source, NULL, &hints, &res);
1471 if (error != 0)
1472 errx(1, "source limiter address: %s", gai_strerror(error));
1473
1474 clear.id = id;
1475 clear.af = res->ai_family;
1476 copy_satopfaddr(&clear.addr, res->ai_addr);
1477
1478 freeaddrinfo(res);
1479
1480 error = pfctl_source_clear(pfh, &clear);
1481 switch (error) {
1482 case 0:
1483 break;
1484 case ESRCH:
1485 errx(1, "source limiter %u not found", id);
1486 case ENOENT:
1487 errx(1, "source limiter %u: %s not found", id, source);
1488 default:
1489 err(1, "kill source limiter %u entry %s", id, source);
1490 }
1491 }
1492
1493 int
pfctl_show_eth_rules(int dev,char * path,int opts,enum pfctl_show format,char * anchorname,int depth,int wildcard)1494 pfctl_show_eth_rules(int dev, char *path, int opts, enum pfctl_show format,
1495 char *anchorname, int depth, int wildcard)
1496 {
1497 char anchor_call[MAXPATHLEN];
1498 struct pfctl_eth_rules_info info;
1499 struct pfctl_eth_rule rule;
1500 int brace;
1501 int dotitle = opts & PF_OPT_SHOWALL;
1502 int len = strlen(path);
1503 int ret;
1504 char *npath, *p;
1505
1506 /*
1507 * Truncate a trailing / and * on an anchorname before searching for
1508 * the ruleset, this is syntactic sugar that doesn't actually make it
1509 * to the kernel.
1510 */
1511 if ((p = strrchr(anchorname, '/')) != NULL &&
1512 p[1] == '*' && p[2] == '\0') {
1513 p[0] = '\0';
1514 }
1515
1516 if (anchorname[0] == '/') {
1517 if ((npath = calloc(1, MAXPATHLEN)) == NULL)
1518 errx(1, "calloc");
1519 snprintf(npath, MAXPATHLEN, "%s", anchorname);
1520 } else {
1521 if (path[0])
1522 snprintf(&path[len], MAXPATHLEN - len, "/%s", anchorname);
1523 else
1524 snprintf(&path[len], MAXPATHLEN - len, "%s", anchorname);
1525 npath = path;
1526 }
1527
1528 /*
1529 * If this anchor was called with a wildcard path, go through
1530 * the rulesets in the anchor rather than the rules.
1531 */
1532 if (wildcard && (opts & PF_OPT_RECURSE)) {
1533 struct pfctl_eth_rulesets_info ri;
1534 u_int32_t mnr, nr;
1535
1536 if ((ret = pfctl_get_eth_rulesets_info(dev, &ri, npath)) != 0) {
1537 if (ret == EINVAL) {
1538 fprintf(stderr, "Anchor '%s' "
1539 "not found.\n", anchorname);
1540 } else {
1541 warnc(ret, "DIOCGETETHRULESETS");
1542 return (-1);
1543 }
1544 }
1545 mnr = ri.nr;
1546
1547 pfctl_print_eth_rule_counters(&rule, opts);
1548 for (nr = 0; nr < mnr; ++nr) {
1549 struct pfctl_eth_ruleset_info rs;
1550
1551 if ((ret = pfctl_get_eth_ruleset(dev, npath, nr, &rs)) != 0)
1552 errc(1, ret, "DIOCGETETHRULESET");
1553 INDENT(depth, !(opts & PF_OPT_VERBOSE));
1554 printf("anchor \"%s\" all {\n", rs.name);
1555 pfctl_show_eth_rules(dev, npath, opts,
1556 format, rs.name, depth + 1, 0);
1557 INDENT(depth, !(opts & PF_OPT_VERBOSE));
1558 printf("}\n");
1559 }
1560 path[len] = '\0';
1561 return (0);
1562 }
1563
1564 if ((ret = pfctl_get_eth_rules_info(dev, &info, path)) != 0) {
1565 warnc(ret, "DIOCGETETHRULES");
1566 return (-1);
1567 }
1568 for (int nr = 0; nr < info.nr; nr++) {
1569 brace = 0;
1570 INDENT(depth, !(opts & PF_OPT_VERBOSE));
1571 if ((ret = pfctl_get_eth_rule(dev, nr, info.ticket, path, &rule,
1572 opts & PF_OPT_CLRRULECTRS, anchor_call)) != 0) {
1573 warnc(ret, "DIOCGETETHRULE");
1574 return (-1);
1575 }
1576 if (anchor_call[0] &&
1577 ((((p = strrchr(anchor_call, '_')) != NULL) &&
1578 (p == anchor_call ||
1579 *(--p) == '/')) || (opts & PF_OPT_RECURSE))) {
1580 brace++;
1581 int aclen = strlen(anchor_call);
1582 if (anchor_call[aclen - 1] == '*')
1583 anchor_call[aclen - 2] = '\0';
1584 }
1585 p = &anchor_call[0];
1586 if (dotitle) {
1587 pfctl_print_title("ETH RULES:");
1588 dotitle = 0;
1589 }
1590 print_eth_rule(&rule, anchor_call,
1591 opts & (PF_OPT_VERBOSE2 | PF_OPT_DEBUG));
1592 if (brace)
1593 printf(" {\n");
1594 else
1595 printf("\n");
1596 pfctl_print_eth_rule_counters(&rule, opts);
1597 if (brace) {
1598 pfctl_show_eth_rules(dev, path, opts, format,
1599 p, depth + 1, rule.anchor_wildcard);
1600 INDENT(depth, !(opts & PF_OPT_VERBOSE));
1601 printf("}\n");
1602 }
1603 }
1604
1605 path[len] = '\0';
1606 return (0);
1607 }
1608
1609 int
pfctl_show_rules(int dev,char * path,int opts,enum pfctl_show format,char * anchorname,int depth,int wildcard)1610 pfctl_show_rules(int dev, char *path, int opts, enum pfctl_show format,
1611 char *anchorname, int depth, int wildcard)
1612 {
1613 struct pfctl_rules_info ri;
1614 struct pfctl_rule rule;
1615 char anchor_call[MAXPATHLEN];
1616 u_int32_t nr, header = 0;
1617 int numeric = opts & PF_OPT_NUMERIC;
1618 int len = strlen(path), ret = 0;
1619 char *npath, *p;
1620
1621 /*
1622 * Truncate a trailing / and * on an anchorname before searching for
1623 * the ruleset, this is syntactic sugar that doesn't actually make it
1624 * to the kernel.
1625 */
1626 if ((p = strrchr(anchorname, '/')) != NULL &&
1627 p[1] == '*' && p[2] == '\0') {
1628 p[0] = '\0';
1629 }
1630
1631 if (anchorname[0] == '/') {
1632 if ((npath = calloc(1, MAXPATHLEN)) == NULL)
1633 errx(1, "calloc");
1634 strlcpy(npath, anchorname, MAXPATHLEN);
1635 } else {
1636 if (path[0])
1637 snprintf(&path[len], MAXPATHLEN - len, "/%s", anchorname);
1638 else
1639 snprintf(&path[len], MAXPATHLEN - len, "%s", anchorname);
1640 npath = path;
1641 }
1642
1643 /*
1644 * If this anchor was called with a wildcard path, go through
1645 * the rulesets in the anchor rather than the rules.
1646 */
1647 if (wildcard && (opts & PF_OPT_RECURSE)) {
1648 struct pfioc_ruleset prs;
1649 u_int32_t mnr, nr;
1650
1651 memset(&prs, 0, sizeof(prs));
1652 if ((ret = pfctl_get_rulesets(pfh, npath, &mnr)) != 0)
1653 errx(1, "%s", pf_strerror(ret));
1654
1655 for (nr = 0; nr < mnr; ++nr) {
1656 if ((ret = pfctl_get_ruleset(pfh, npath, nr, &prs)) != 0)
1657 errx(1, "%s", pf_strerror(ret));
1658 INDENT(depth, !(opts & PF_OPT_VERBOSE));
1659 printf("anchor \"%s\" all {\n", prs.name);
1660 pfctl_show_rules(dev, npath, opts,
1661 format, prs.name, depth + 1, 0);
1662 INDENT(depth, !(opts & PF_OPT_VERBOSE));
1663 printf("}\n");
1664 }
1665 path[len] = '\0';
1666 return (0);
1667 }
1668
1669 if (opts & PF_OPT_SHOWALL) {
1670 ret = pfctl_get_rules_info_h(pfh, &ri, PF_PASS, path);
1671 if (ret != 0) {
1672 warnx("%s", pf_strerror(ret));
1673 goto error;
1674 }
1675 header++;
1676 }
1677 ret = pfctl_get_rules_info_h(pfh, &ri, PF_SCRUB, path);
1678 if (ret != 0) {
1679 warnx("%s", pf_strerror(ret));
1680 goto error;
1681 }
1682 if (opts & PF_OPT_SHOWALL) {
1683 if (format == PFCTL_SHOW_RULES && (ri.nr > 0 || header))
1684 pfctl_print_title("FILTER RULES:");
1685 else if (format == PFCTL_SHOW_LABELS && labels)
1686 pfctl_print_title("LABEL COUNTERS:");
1687 }
1688
1689 for (nr = 0; nr < ri.nr; ++nr) {
1690 if ((ret = pfctl_get_clear_rule_h(pfh, nr, ri.ticket, path, PF_SCRUB,
1691 &rule, anchor_call, opts & PF_OPT_CLRRULECTRS)) != 0) {
1692 warnc(ret, "DIOCGETRULENV");
1693 goto error;
1694 }
1695
1696 if (pfctl_get_pool(dev, &rule.rdr,
1697 nr, ri.ticket, PF_SCRUB, path, PF_RDR) != 0)
1698 goto error;
1699
1700 if (pfctl_get_pool(dev, &rule.nat,
1701 nr, ri.ticket, PF_SCRUB, path, PF_NAT) != 0)
1702 goto error;
1703
1704 if (pfctl_get_pool(dev, &rule.route,
1705 nr, ri.ticket, PF_SCRUB, path, PF_RT) != 0)
1706 goto error;
1707
1708 switch (format) {
1709 case PFCTL_SHOW_LABELS:
1710 break;
1711 case PFCTL_SHOW_RULES:
1712 if (rule.label[0][0] && (opts & PF_OPT_SHOWALL))
1713 labels = 1;
1714 print_rule(&rule, anchor_call, opts, numeric);
1715 /*
1716 * Do not print newline, when we have not
1717 * printed expired rule.
1718 */
1719 if (!(rule.rule_flag & PFRULE_EXPIRED) ||
1720 (opts & (PF_OPT_VERBOSE2|PF_OPT_DEBUG)))
1721 printf("\n");
1722 pfctl_print_rule_counters(&rule, opts);
1723 break;
1724 case PFCTL_SHOW_NOTHING:
1725 break;
1726 }
1727 pfctl_clear_pool(&rule.rdr);
1728 pfctl_clear_pool(&rule.nat);
1729 pfctl_clear_pool(&rule.route);
1730 }
1731 ret = pfctl_get_rules_info_h(pfh, &ri, PF_PASS, path);
1732 if (ret != 0) {
1733 warnc(ret, "DIOCGETRULES");
1734 goto error;
1735 }
1736 for (nr = 0; nr < ri.nr; ++nr) {
1737 if ((ret = pfctl_get_clear_rule_h(pfh, nr, ri.ticket, path, PF_PASS,
1738 &rule, anchor_call, opts & PF_OPT_CLRRULECTRS)) != 0) {
1739 warnc(ret, "DIOCGETRULE");
1740 goto error;
1741 }
1742
1743 if (pfctl_get_pool(dev, &rule.rdr,
1744 nr, ri.ticket, PF_PASS, path, PF_RDR) != 0)
1745 goto error;
1746
1747 if (pfctl_get_pool(dev, &rule.nat,
1748 nr, ri.ticket, PF_PASS, path, PF_NAT) != 0)
1749 goto error;
1750
1751 if (pfctl_get_pool(dev, &rule.route,
1752 nr, ri.ticket, PF_PASS, path, PF_RT) != 0)
1753 goto error;
1754
1755 switch (format) {
1756 case PFCTL_SHOW_LABELS: {
1757 bool show = false;
1758 int i = 0;
1759
1760 while (rule.label[i][0]) {
1761 printf("%s ", rule.label[i++]);
1762 show = true;
1763 }
1764
1765 if (show) {
1766 printf("%llu %llu %llu %llu"
1767 " %llu %llu %llu %ju\n",
1768 (unsigned long long)rule.evaluations,
1769 (unsigned long long)(rule.packets[0] +
1770 rule.packets[1]),
1771 (unsigned long long)(rule.bytes[0] +
1772 rule.bytes[1]),
1773 (unsigned long long)rule.packets[0],
1774 (unsigned long long)rule.bytes[0],
1775 (unsigned long long)rule.packets[1],
1776 (unsigned long long)rule.bytes[1],
1777 (uintmax_t)rule.states_tot);
1778 }
1779
1780 if (anchor_call[0] &&
1781 (((p = strrchr(anchor_call, '/')) ?
1782 p[1] == '_' : anchor_call[0] == '_') ||
1783 opts & PF_OPT_RECURSE)) {
1784 pfctl_show_rules(dev, npath, opts, format,
1785 anchor_call, depth, rule.anchor_wildcard);
1786 }
1787 break;
1788 }
1789 case PFCTL_SHOW_RULES:
1790 if (rule.label[0][0] && (opts & PF_OPT_SHOWALL))
1791 labels = 1;
1792 INDENT(depth, !(opts & PF_OPT_VERBOSE));
1793 print_rule(&rule, anchor_call, opts, numeric);
1794
1795 /*
1796 * If this is a 'unnamed' brace notation
1797 * anchor, OR the user has explicitly requested
1798 * recursion, print it recursively.
1799 */
1800 if (anchor_call[0] &&
1801 (((p = strrchr(anchor_call, '/')) ?
1802 p[1] == '_' : anchor_call[0] == '_') ||
1803 opts & PF_OPT_RECURSE)) {
1804 printf(" {\n");
1805 pfctl_print_rule_counters(&rule, opts);
1806 pfctl_show_rules(dev, npath, opts, format,
1807 anchor_call, depth + 1,
1808 rule.anchor_wildcard);
1809 INDENT(depth, !(opts & PF_OPT_VERBOSE));
1810 printf("}\n");
1811 } else {
1812 printf("\n");
1813 pfctl_print_rule_counters(&rule, opts);
1814 }
1815 break;
1816 case PFCTL_SHOW_NOTHING:
1817 break;
1818 }
1819 pfctl_clear_pool(&rule.rdr);
1820 pfctl_clear_pool(&rule.nat);
1821 }
1822
1823 error:
1824 path[len] = '\0';
1825 return (ret);
1826 }
1827
1828 int
pfctl_show_nat(int dev,const char * path,int opts,char * anchorname,int depth,int wildcard)1829 pfctl_show_nat(int dev, const char *path, int opts, char *anchorname, int depth,
1830 int wildcard)
1831 {
1832 struct pfctl_rules_info ri;
1833 struct pfctl_rule rule;
1834 char anchor_call[MAXPATHLEN];
1835 u_int32_t nr;
1836 static int nattype[3] = { PF_NAT, PF_RDR, PF_BINAT };
1837 int i, dotitle = opts & PF_OPT_SHOWALL;
1838 int ret;
1839 int len = strlen(path);
1840 char *npath, *p;
1841
1842 /*
1843 * Truncate a trailing / and * on an anchorname before searching for
1844 * the ruleset, this is syntactic sugar that doesn't actually make it
1845 * to the kernel.
1846 */
1847 if ((p = strrchr(anchorname, '/')) != NULL &&
1848 p[1] == '*' && p[2] == '\0') {
1849 p[0] = '\0';
1850 }
1851
1852 if ((npath = calloc(1, MAXPATHLEN)) == NULL)
1853 errx(1, "calloc");
1854
1855 if (anchorname[0] == '/') {
1856 snprintf(npath, MAXPATHLEN, "%s", anchorname);
1857 } else {
1858 snprintf(npath, MAXPATHLEN, "%s", path);
1859 if (npath[0])
1860 snprintf(&npath[len], MAXPATHLEN - len, "/%s", anchorname);
1861 else
1862 snprintf(&npath[len], MAXPATHLEN - len, "%s", anchorname);
1863 }
1864
1865 /*
1866 * If this anchor was called with a wildcard path, go through
1867 * the rulesets in the anchor rather than the rules.
1868 */
1869 if (wildcard && (opts & PF_OPT_RECURSE)) {
1870 struct pfioc_ruleset prs;
1871 u_int32_t mnr, nr;
1872 memset(&prs, 0, sizeof(prs));
1873 if ((ret = pfctl_get_rulesets(pfh, npath, &mnr)) != 0) {
1874 if (ret == EINVAL)
1875 fprintf(stderr, "NAT anchor '%s' "
1876 "not found.\n", anchorname);
1877 else
1878 errx(1, "%s", pf_strerror(ret));
1879 }
1880
1881 for (nr = 0; nr < mnr; ++nr) {
1882 if ((ret = pfctl_get_ruleset(pfh, npath, nr, &prs)) != 0)
1883 errx(1, "%s", pf_strerror(ret));
1884 INDENT(depth, !(opts & PF_OPT_VERBOSE));
1885 printf("nat-anchor \"%s\" all {\n", prs.name);
1886 pfctl_show_nat(dev, npath, opts,
1887 prs.name, depth + 1, 0);
1888 INDENT(depth, !(opts & PF_OPT_VERBOSE));
1889 printf("}\n");
1890 }
1891 npath[len] = '\0';
1892 return (0);
1893 }
1894
1895 for (i = 0; i < 3; i++) {
1896 ret = pfctl_get_rules_info_h(pfh, &ri, nattype[i], npath);
1897 if (ret != 0) {
1898 warnc(ret, "DIOCGETRULES");
1899 return (-1);
1900 }
1901 for (nr = 0; nr < ri.nr; ++nr) {
1902 INDENT(depth, !(opts & PF_OPT_VERBOSE));
1903
1904 if ((ret = pfctl_get_rule_h(pfh, nr, ri.ticket, npath,
1905 nattype[i], &rule, anchor_call)) != 0) {
1906 warnc(ret, "DIOCGETRULE");
1907 return (-1);
1908 }
1909 if (pfctl_get_pool(dev, &rule.rdr, nr,
1910 ri.ticket, nattype[i], npath, PF_RDR) != 0)
1911 return (-1);
1912 if (pfctl_get_pool(dev, &rule.nat, nr,
1913 ri.ticket, nattype[i], npath, PF_NAT) != 0)
1914 return (-1);
1915 if (pfctl_get_pool(dev, &rule.route, nr,
1916 ri.ticket, nattype[i], npath, PF_RT) != 0)
1917 return (-1);
1918
1919 if (dotitle) {
1920 pfctl_print_title("TRANSLATION RULES:");
1921 dotitle = 0;
1922 }
1923 print_rule(&rule, anchor_call,
1924 opts & PF_OPT_VERBOSE2, opts & PF_OPT_NUMERIC);
1925 if (anchor_call[0] &&
1926 (((p = strrchr(anchor_call, '/')) ?
1927 p[1] == '_' : anchor_call[0] == '_') ||
1928 opts & PF_OPT_RECURSE)) {
1929 printf(" {\n");
1930 pfctl_print_rule_counters(&rule, opts);
1931 pfctl_show_nat(dev, npath, opts, anchor_call,
1932 depth + 1, rule.anchor_wildcard);
1933 INDENT(depth, !(opts & PF_OPT_VERBOSE));
1934 printf("}\n");
1935 } else {
1936 printf("\n");
1937 pfctl_print_rule_counters(&rule, opts);
1938 }
1939 }
1940 }
1941 return (0);
1942 }
1943
1944 static int
pfctl_print_src_node(struct pfctl_src_node * sn,void * arg)1945 pfctl_print_src_node(struct pfctl_src_node *sn, void *arg)
1946 {
1947 int *opts = (int *)arg;
1948
1949 if (*opts & PF_OPT_SHOWALL) {
1950 pfctl_print_title("SOURCE TRACKING NODES:");
1951 *opts &= ~PF_OPT_SHOWALL;
1952 }
1953
1954 print_src_node(sn, *opts);
1955
1956 return (0);
1957 }
1958
1959 int
pfctl_show_src_nodes(int dev,int opts)1960 pfctl_show_src_nodes(int dev, int opts)
1961 {
1962 int error;
1963
1964 error = pfctl_get_srcnodes(pfh, pfctl_print_src_node, &opts);
1965
1966 return (error);
1967 }
1968
1969 struct pfctl_show_state_arg {
1970 int opts;
1971 int dotitle;
1972 const char *iface;
1973 };
1974
1975 static int
pfctl_show_state(struct pfctl_state * s,void * arg)1976 pfctl_show_state(struct pfctl_state *s, void *arg)
1977 {
1978 struct pfctl_show_state_arg *a = (struct pfctl_show_state_arg *)arg;
1979
1980 if (a->dotitle) {
1981 pfctl_print_title("STATES:");
1982 a->dotitle = 0;
1983 }
1984 print_state(s, a->opts);
1985
1986 return (0);
1987 }
1988
1989 int
pfctl_show_states(int dev,const char * iface,int opts)1990 pfctl_show_states(int dev, const char *iface, int opts)
1991 {
1992 struct pfctl_show_state_arg arg;
1993 struct pfctl_state_filter filter = {};
1994
1995 if (iface != NULL)
1996 strlcpy(filter.ifname, iface, IFNAMSIZ);
1997
1998 arg.opts = opts;
1999 arg.dotitle = opts & PF_OPT_SHOWALL;
2000 arg.iface = iface;
2001
2002 if (pfctl_get_states_h(pfh, &filter, pfctl_show_state, &arg))
2003 return (-1);
2004
2005 return (0);
2006 }
2007
2008 int
pfctl_show_status(int dev,int opts)2009 pfctl_show_status(int dev, int opts)
2010 {
2011 struct pfctl_status *status;
2012 struct pfctl_syncookies cookies;
2013 int ret;
2014
2015 if ((status = pfctl_get_status_h(pfh)) == NULL) {
2016 warn("DIOCGETSTATUS");
2017 return (-1);
2018 }
2019 if ((ret = pfctl_get_syncookies(dev, &cookies)) != 0) {
2020 pfctl_free_status(status);
2021 warnc(ret, "DIOCGETSYNCOOKIES");
2022 return (-1);
2023 }
2024 if (opts & PF_OPT_SHOWALL)
2025 pfctl_print_title("INFO:");
2026 print_status(status, &cookies, opts);
2027 pfctl_free_status(status);
2028 return (0);
2029 }
2030
2031 int
pfctl_show_running(int dev)2032 pfctl_show_running(int dev)
2033 {
2034 struct pfctl_status *status;
2035 int running;
2036
2037 if ((status = pfctl_get_status_h(pfh)) == NULL) {
2038 warn("DIOCGETSTATUS");
2039 return (-1);
2040 }
2041
2042 running = status->running;
2043
2044 print_running(status);
2045 pfctl_free_status(status);
2046 return (!running);
2047 }
2048
2049 int
pfctl_show_timeouts(int dev,int opts)2050 pfctl_show_timeouts(int dev, int opts)
2051 {
2052 uint32_t seconds;
2053 int i;
2054 int ret;
2055
2056 if (opts & PF_OPT_SHOWALL)
2057 pfctl_print_title("TIMEOUTS:");
2058 for (i = 0; pf_timeouts[i].name; i++) {
2059 if ((ret = pfctl_get_timeout(pfh, pf_timeouts[i].timeout, &seconds)) != 0)
2060 errc(1, ret, "DIOCGETTIMEOUT");
2061 printf("%-20s %10d", pf_timeouts[i].name, seconds);
2062 if (pf_timeouts[i].timeout >= PFTM_ADAPTIVE_START &&
2063 pf_timeouts[i].timeout <= PFTM_ADAPTIVE_END)
2064 printf(" states");
2065 else
2066 printf("s");
2067 printf("\n");
2068 }
2069 return (0);
2070
2071 }
2072
2073 int
pfctl_show_limits(int dev,int opts)2074 pfctl_show_limits(int dev, int opts)
2075 {
2076 unsigned int limit;
2077 int i;
2078 int ret;
2079
2080 if (opts & PF_OPT_SHOWALL)
2081 pfctl_print_title("LIMITS:");
2082 for (i = 0; pf_limits[i].name; i++) {
2083 if ((ret = pfctl_get_limit(pfh, pf_limits[i].index, &limit)) != 0)
2084 errc(1, ret, "DIOCGETLIMIT");
2085 printf("%-13s ", pf_limits[i].name);
2086 if (limit == UINT_MAX)
2087 printf("unlimited\n");
2088 else
2089 printf("hard limit %8u\n", limit);
2090 }
2091 return (0);
2092 }
2093
2094 void
pfctl_read_limits(struct pfctl_handle * h)2095 pfctl_read_limits(struct pfctl_handle *h)
2096 {
2097 int i;
2098
2099 for (i = 0; pf_limits[i].name; i++) {
2100 if (pfctl_get_limit(h, i, &limit_curr[i]))
2101 err(1, "DIOCGETLIMIT");
2102 }
2103 }
2104
2105 void
pfctl_restore_limits(void)2106 pfctl_restore_limits(void)
2107 {
2108 int i;
2109
2110 if (pfh == NULL)
2111 return;
2112
2113 for (i = 0; pf_limits[i].name; i++) {
2114 if (pfctl_set_limit(pfh, i, limit_curr[i]))
2115 warn("DIOCSETLIMIT (%s)", pf_limits[i].name);
2116 }
2117 }
2118
2119 void
pfctl_show_creators(int opts)2120 pfctl_show_creators(int opts)
2121 {
2122 int ret;
2123 uint32_t creators[16];
2124 size_t count = nitems(creators);
2125
2126 ret = pfctl_get_creatorids(pfh, creators, &count);
2127 if (ret != 0)
2128 errx(ret, "Failed to retrieve creators");
2129
2130 printf("Creator IDs:\n");
2131 for (size_t i = 0; i < count; i++)
2132 printf("%08x\n", creators[i]);
2133 }
2134
2135 /* callbacks for rule/nat/rdr/addr */
2136 int
pfctl_add_pool(struct pfctl * pf,struct pfctl_pool * p,int which)2137 pfctl_add_pool(struct pfctl *pf, struct pfctl_pool *p, int which)
2138 {
2139 struct pfctl_pooladdr *pa;
2140 int ret;
2141
2142 TAILQ_FOREACH(pa, &p->list, entries) {
2143 memcpy(&pf->paddr.addr, pa, sizeof(struct pfctl_pooladdr));
2144 pf->paddr.af = pa->af;
2145 if ((pf->opts & PF_OPT_NOACTION) == 0) {
2146 if ((ret = pfctl_add_addr(pf->h, &pf->paddr, which)) != 0)
2147 errc(1, ret, "DIOCADDADDR");
2148 }
2149 }
2150 return (0);
2151 }
2152
2153 void
pfctl_init_rule(struct pfctl_rule * r)2154 pfctl_init_rule(struct pfctl_rule *r)
2155 {
2156 memset(r, 0, sizeof(struct pfctl_rule));
2157 TAILQ_INIT(&(r->rdr.list));
2158 TAILQ_INIT(&(r->nat.list));
2159 TAILQ_INIT(&(r->route.list));
2160 }
2161
2162 void
pfctl_append_rule(struct pfctl * pf,struct pfctl_rule * r)2163 pfctl_append_rule(struct pfctl *pf, struct pfctl_rule *r)
2164 {
2165 u_int8_t rs_num;
2166 struct pfctl_rule *rule;
2167 struct pfctl_ruleset *rs;
2168
2169 rs_num = pf_get_ruleset_number(r->action);
2170 if (rs_num == PF_RULESET_MAX)
2171 errx(1, "Invalid rule type %d", r->action);
2172
2173 rs = &pf->anchor->ruleset;
2174
2175 if ((rule = calloc(1, sizeof(*rule))) == NULL)
2176 err(1, "calloc");
2177 bcopy(r, rule, sizeof(*rule));
2178 TAILQ_INIT(&rule->rdr.list);
2179 pfctl_move_pool(&r->rdr, &rule->rdr);
2180 TAILQ_INIT(&rule->nat.list);
2181 pfctl_move_pool(&r->nat, &rule->nat);
2182 TAILQ_INIT(&rule->route.list);
2183 pfctl_move_pool(&r->route, &rule->route);
2184
2185 TAILQ_INSERT_TAIL(rs->rules[rs_num].active.ptr, rule, entries);
2186 }
2187
2188 int
pfctl_append_eth_rule(struct pfctl * pf,struct pfctl_eth_rule * r,const char * anchor_call)2189 pfctl_append_eth_rule(struct pfctl *pf, struct pfctl_eth_rule *r,
2190 const char *anchor_call)
2191 {
2192 struct pfctl_eth_rule *rule;
2193 struct pfctl_eth_ruleset *rs;
2194 char *p;
2195
2196 rs = &pf->eanchor->ruleset;
2197
2198 if (anchor_call[0] && r->anchor == NULL) {
2199 /*
2200 * Don't make non-brace anchors part of the main anchor pool.
2201 */
2202 if ((r->anchor = calloc(1, sizeof(*r->anchor))) == NULL)
2203 err(1, "pfctl_append_rule: calloc");
2204
2205 pf_init_eth_ruleset(&r->anchor->ruleset);
2206 r->anchor->ruleset.anchor = r->anchor;
2207 if (strlcpy(r->anchor->path, anchor_call,
2208 sizeof(rule->anchor->path)) >= sizeof(rule->anchor->path))
2209 errx(1, "pfctl_append_rule: strlcpy");
2210 if ((p = strrchr(anchor_call, '/')) != NULL) {
2211 if (!strlen(p))
2212 err(1, "pfctl_append_eth_rule: bad anchor name %s",
2213 anchor_call);
2214 } else
2215 p = (char *)anchor_call;
2216 if (strlcpy(r->anchor->name, p,
2217 sizeof(rule->anchor->name)) >= sizeof(rule->anchor->name))
2218 errx(1, "pfctl_append_eth_rule: strlcpy");
2219 }
2220
2221 if ((rule = calloc(1, sizeof(*rule))) == NULL)
2222 err(1, "calloc");
2223 bcopy(r, rule, sizeof(*rule));
2224
2225 TAILQ_INSERT_TAIL(&rs->rules, rule, entries);
2226 return (0);
2227 }
2228
2229 int
pfctl_eth_ruleset_trans(struct pfctl * pf,char * path,struct pfctl_eth_anchor * a)2230 pfctl_eth_ruleset_trans(struct pfctl *pf, char *path,
2231 struct pfctl_eth_anchor *a)
2232 {
2233 int osize = pf->trans->pfrb_size;
2234
2235 if ((pf->loadopt & PFCTL_FLAG_ETH) != 0) {
2236 if (pfctl_add_trans(pf->trans, PF_RULESET_ETH, path))
2237 return (1);
2238 }
2239 if (pfctl_trans(pf->dev, pf->trans, DIOCXBEGIN, osize))
2240 return (5);
2241
2242 return (0);
2243 }
2244
2245 int
pfctl_ruleset_trans(struct pfctl * pf,char * path,struct pfctl_anchor * a,bool do_eth)2246 pfctl_ruleset_trans(struct pfctl *pf, char *path, struct pfctl_anchor *a, bool do_eth)
2247 {
2248 int osize = pf->trans->pfrb_size;
2249
2250 if ((pf->loadopt & PFCTL_FLAG_ETH) != 0 && do_eth) {
2251 if (pfctl_add_trans(pf->trans, PF_RULESET_ETH, path))
2252 return (1);
2253 }
2254 if ((pf->loadopt & PFCTL_FLAG_NAT) != 0) {
2255 if (pfctl_add_trans(pf->trans, PF_RULESET_NAT, path) ||
2256 pfctl_add_trans(pf->trans, PF_RULESET_BINAT, path) ||
2257 pfctl_add_trans(pf->trans, PF_RULESET_RDR, path))
2258 return (1);
2259 }
2260 if (a == pf->astack[0] && ((altqsupport &&
2261 (pf->loadopt & PFCTL_FLAG_ALTQ) != 0))) {
2262 if (pfctl_add_trans(pf->trans, PF_RULESET_ALTQ, path))
2263 return (2);
2264 }
2265 if ((pf->loadopt & PFCTL_FLAG_FILTER) != 0) {
2266 if (pfctl_add_trans(pf->trans, PF_RULESET_SCRUB, path) ||
2267 pfctl_add_trans(pf->trans, PF_RULESET_FILTER, path))
2268 return (3);
2269 }
2270 if (pf->loadopt & PFCTL_FLAG_TABLE)
2271 if (pfctl_add_trans(pf->trans, PF_RULESET_TABLE, path))
2272 return (4);
2273 if (pfctl_trans(pf->dev, pf->trans, DIOCXBEGIN, osize))
2274 return (5);
2275
2276 return (0);
2277 }
2278
2279 void
pfctl_load_statelim(struct pfctl * pf,struct pfctl_statelim * stlim)2280 pfctl_load_statelim(struct pfctl *pf, struct pfctl_statelim *stlim)
2281 {
2282 int error;
2283
2284 if (pf->opts & PF_OPT_VERBOSE)
2285 print_statelim(&stlim->ioc);
2286
2287 if (pf->opts & PF_OPT_NOACTION)
2288 return;
2289
2290 error = pfctl_state_limiter_add(pf->h, &stlim->ioc);
2291 if (error) {
2292 errc(1, error, "DIOCADDSTATELIM %s id %u", stlim->ioc.name,
2293 stlim->ioc.id);
2294 }
2295 }
2296
2297 void
pfctl_load_statelims(struct pfctl * pf)2298 pfctl_load_statelims(struct pfctl *pf)
2299 {
2300 struct pfctl_statelim *stlim;
2301 u_int32_t ticket = 0;
2302
2303 if ((pf->opts & PF_OPT_NOACTION) == 0)
2304 ticket = pfctl_get_ticket(pf->trans, PF_RULESET_FILTER, "");
2305
2306 RB_FOREACH(stlim, pfctl_statelim_ids, &pf->statelim_ids)
2307 {
2308 stlim->ioc.ticket = ticket;
2309 pfctl_load_statelim(pf, stlim);
2310 }
2311
2312 /* Don't free the statelims because we're about to exit anyway. */
2313 }
2314
2315 void
pfctl_load_sourcelim(struct pfctl * pf,struct pfctl_source_lim * srlim)2316 pfctl_load_sourcelim(struct pfctl *pf, struct pfctl_source_lim *srlim)
2317 {
2318 int error;
2319
2320 if (pf->opts & PF_OPT_VERBOSE)
2321 print_sourcelim(srlim);
2322
2323 if (pf->opts & PF_OPT_NOACTION)
2324 return;
2325
2326 error = pfctl_source_limiter_add(pf->h, srlim);
2327 if (error != 0) {
2328 errc(1, error, "DIOCADDSOURCELIM %s id %u", srlim->name,
2329 srlim->id);
2330 }
2331 }
2332
2333 void
pfctl_load_sourcelims(struct pfctl * pf)2334 pfctl_load_sourcelims(struct pfctl *pf)
2335 {
2336 struct pfctl_sourcelim *srlim;
2337 uint32_t ticket = 0;
2338
2339 if ((pf->opts & PF_OPT_NOACTION) == 0)
2340 ticket = pfctl_get_ticket(pf->trans, PF_RULESET_FILTER, "");
2341
2342 RB_FOREACH(srlim, pfctl_sourcelim_ids, &pf->sourcelim_ids)
2343 {
2344 srlim->ioc.ticket = ticket;
2345 pfctl_load_sourcelim(pf, &srlim->ioc);
2346 }
2347
2348 /* Don't free the sourcelims because we're about to exit anyway. */
2349 }
2350
2351 int
pfctl_load_eth_ruleset(struct pfctl * pf,char * path,struct pfctl_eth_ruleset * rs,int depth)2352 pfctl_load_eth_ruleset(struct pfctl *pf, char *path,
2353 struct pfctl_eth_ruleset *rs, int depth)
2354 {
2355 struct pfctl_eth_rule *r;
2356 int error, len = strlen(path);
2357 int brace = 0;
2358
2359 pf->eanchor = rs->anchor;
2360 if (path[0])
2361 snprintf(&path[len], MAXPATHLEN - len, "/%s", pf->eanchor->name);
2362 else
2363 snprintf(&path[len], MAXPATHLEN - len, "%s", pf->eanchor->name);
2364
2365 if (depth) {
2366 if (TAILQ_FIRST(&rs->rules) != NULL) {
2367 brace++;
2368 if (pf->opts & PF_OPT_VERBOSE)
2369 printf(" {\n");
2370 if ((pf->opts & PF_OPT_NOACTION) == 0 &&
2371 (error = pfctl_eth_ruleset_trans(pf,
2372 path, rs->anchor))) {
2373 printf("pfctl_load_eth_rulesets: "
2374 "pfctl_eth_ruleset_trans %d\n", error);
2375 goto error;
2376 }
2377 } else if (pf->opts & PF_OPT_VERBOSE)
2378 printf("\n");
2379 }
2380
2381 while ((r = TAILQ_FIRST(&rs->rules)) != NULL) {
2382 TAILQ_REMOVE(&rs->rules, r, entries);
2383
2384 error = pfctl_load_eth_rule(pf, path, r, depth);
2385 if (error)
2386 return (error);
2387
2388 if (r->anchor) {
2389 if ((error = pfctl_load_eth_ruleset(pf, path,
2390 &r->anchor->ruleset, depth + 1)))
2391 return (error);
2392 } else if (pf->opts & PF_OPT_VERBOSE)
2393 printf("\n");
2394 free(r);
2395 }
2396 if (brace && pf->opts & PF_OPT_VERBOSE) {
2397 INDENT(depth - 1, (pf->opts & PF_OPT_VERBOSE));
2398 printf("}\n");
2399 }
2400 path[len] = '\0';
2401
2402 return (0);
2403 error:
2404 path[len] = '\0';
2405 return (error);
2406 }
2407
2408 int
pfctl_load_eth_rule(struct pfctl * pf,char * path,struct pfctl_eth_rule * r,int depth)2409 pfctl_load_eth_rule(struct pfctl *pf, char *path, struct pfctl_eth_rule *r,
2410 int depth)
2411 {
2412 char *name;
2413 char anchor[PF_ANCHOR_NAME_SIZE];
2414 int len = strlen(path);
2415 int ret;
2416
2417 if (strlcpy(anchor, path, sizeof(anchor)) >= sizeof(anchor))
2418 errx(1, "pfctl_load_eth_rule: strlcpy");
2419
2420 if (r->anchor) {
2421 if (r->anchor->match) {
2422 if (path[0])
2423 snprintf(&path[len], MAXPATHLEN - len,
2424 "/%s", r->anchor->name);
2425 else
2426 snprintf(&path[len], MAXPATHLEN - len,
2427 "%s", r->anchor->name);
2428 name = r->anchor->name;
2429 } else
2430 name = r->anchor->path;
2431 } else
2432 name = "";
2433
2434 if ((pf->opts & PF_OPT_NOACTION) == 0)
2435 if ((ret = pfctl_add_eth_rule(pf->dev, r, anchor, name,
2436 pf->eth_ticket)) != 0)
2437 errc(1, ret, "DIOCADDETHRULENV");
2438
2439 if (pf->opts & PF_OPT_VERBOSE) {
2440 INDENT(depth, !(pf->opts & PF_OPT_VERBOSE2));
2441 print_eth_rule(r, r->anchor ? r->anchor->name : "",
2442 pf->opts & (PF_OPT_VERBOSE2 | PF_OPT_DEBUG));
2443 }
2444
2445 path[len] = '\0';
2446
2447 return (0);
2448 }
2449
2450 static int
pfctl_load_tables(struct pfctl * pf,char * path,struct pfctl_anchor * a,int rs_num)2451 pfctl_load_tables(struct pfctl *pf, char *path, struct pfctl_anchor *a,
2452 int rs_num)
2453 {
2454 struct pfr_ktable *kt, *ktw;
2455 struct pfr_uktable *ukt;
2456 char anchor_path[PF_ANCHOR_MAXPATH];
2457 int e;
2458
2459 RB_FOREACH_SAFE(kt, pfr_ktablehead, &pfr_ktables, ktw) {
2460 if (strcmp(kt->pfrkt_anchor, a->path) != 0)
2461 continue;
2462
2463 if (path != NULL && *path) {
2464 strlcpy(anchor_path, kt->pfrkt_anchor,
2465 sizeof(anchor_path));
2466 snprintf(kt->pfrkt_anchor, PF_ANCHOR_MAXPATH, "%s/%s",
2467 path, anchor_path);
2468 }
2469 ukt = (struct pfr_uktable *)kt;
2470 e = pfr_ina_define(&ukt->pfrukt_t, ukt->pfrukt_addrs.pfrb_caddr,
2471 ukt->pfrukt_addrs.pfrb_size, NULL, NULL,
2472 pf->anchor->ruleset.tticket,
2473 ukt->pfrukt_init_addr ? PFR_FLAG_ADDRSTOO : 0);
2474 if (e != 0)
2475 err(1, "%s pfr_ina_define() %s@%s", __func__,
2476 kt->pfrkt_name, kt->pfrkt_anchor);
2477 RB_REMOVE(pfr_ktablehead, &pfr_ktables, kt);
2478 pfr_buf_clear(&ukt->pfrukt_addrs);
2479 free(ukt);
2480 }
2481
2482 return (0);
2483 }
2484
2485 int
pfctl_load_ruleset(struct pfctl * pf,char * path,struct pfctl_ruleset * rs,int rs_num,int depth)2486 pfctl_load_ruleset(struct pfctl *pf, char *path, struct pfctl_ruleset *rs,
2487 int rs_num, int depth)
2488 {
2489 struct pfctl_rule *r;
2490 int error, len = strlen(path);
2491 int brace = 0;
2492
2493 pf->anchor = rs->anchor;
2494
2495 if (path[0])
2496 snprintf(&path[len], MAXPATHLEN - len, "/%s", pf->anchor->name);
2497 else
2498 snprintf(&path[len], MAXPATHLEN - len, "%s", pf->anchor->name);
2499
2500 if (depth) {
2501 if (TAILQ_FIRST(rs->rules[rs_num].active.ptr) != NULL) {
2502 brace++;
2503 if (pf->opts & PF_OPT_VERBOSE)
2504 printf(" {\n");
2505 if ((pf->opts & PF_OPT_NOACTION) == 0 &&
2506 (error = pfctl_ruleset_trans(pf,
2507 path, rs->anchor, false))) {
2508 printf("%s: "
2509 "pfctl_ruleset_trans %d\n", __func__, error);
2510 goto error;
2511 }
2512 } else if (pf->opts & PF_OPT_VERBOSE)
2513 printf("\n");
2514 }
2515
2516 if (pf->optimize && rs_num == PF_RULESET_FILTER)
2517 pfctl_optimize_ruleset(pf, rs);
2518
2519 while ((r = TAILQ_FIRST(rs->rules[rs_num].active.ptr)) != NULL) {
2520 TAILQ_REMOVE(rs->rules[rs_num].active.ptr, r, entries);
2521
2522 for (int i = 0; i < PF_RULE_MAX_LABEL_COUNT; i++)
2523 expand_label(r->label[i], PF_RULE_LABEL_SIZE, r);
2524 expand_label(r->tagname, PF_TAG_NAME_SIZE, r);
2525 expand_label(r->match_tagname, PF_TAG_NAME_SIZE, r);
2526
2527 if ((error = pfctl_load_rule(pf, path, r, depth)))
2528 goto error;
2529 if (r->anchor) {
2530 if ((error = pfctl_load_ruleset(pf, path,
2531 &r->anchor->ruleset, rs_num, depth + 1)))
2532 goto error;
2533 if ((error = pfctl_load_tables(pf, path, r->anchor, rs_num)))
2534 goto error;
2535 } else if (pf->opts & PF_OPT_VERBOSE)
2536 printf("\n");
2537 free(r);
2538 }
2539 if (brace && pf->opts & PF_OPT_VERBOSE) {
2540 INDENT(depth - 1, (pf->opts & PF_OPT_VERBOSE));
2541 printf("}\n");
2542 }
2543 path[len] = '\0';
2544 return (0);
2545
2546 error:
2547 path[len] = '\0';
2548 return (error);
2549
2550 }
2551
2552 int
pfctl_load_rule(struct pfctl * pf,char * path,struct pfctl_rule * r,int depth)2553 pfctl_load_rule(struct pfctl *pf, char *path, struct pfctl_rule *r, int depth)
2554 {
2555 u_int8_t rs_num = pf_get_ruleset_number(r->action);
2556 char *name;
2557 uint32_t ticket;
2558 char anchor[PF_ANCHOR_NAME_SIZE];
2559 int len = strlen(path);
2560 int error;
2561 bool was_present;
2562
2563 /* set up anchor before adding to path for anchor_call */
2564 if ((pf->opts & PF_OPT_NOACTION) == 0) {
2565 if (pf->trans == NULL)
2566 errx(1, "pfctl_load_rule: no transaction");
2567 ticket = pfctl_get_ticket(pf->trans, rs_num, path);
2568 if (rs_num == PF_RULESET_FILTER)
2569 pf->anchor->ruleset.tticket = ticket;
2570 }
2571 if (strlcpy(anchor, path, sizeof(anchor)) >= sizeof(anchor))
2572 errx(1, "pfctl_load_rule: strlcpy");
2573
2574 if (r->anchor) {
2575 if (r->anchor->match) {
2576 if (path[0])
2577 snprintf(&path[len], MAXPATHLEN - len,
2578 "/%s", r->anchor->name);
2579 else
2580 snprintf(&path[len], MAXPATHLEN - len,
2581 "%s", r->anchor->name);
2582 name = r->anchor->name;
2583 } else
2584 name = r->anchor->path;
2585 } else
2586 name = "";
2587
2588 was_present = false;
2589 if ((pf->opts & PF_OPT_NOACTION) == 0) {
2590 if ((pf->opts & PF_OPT_NOACTION) == 0) {
2591 if ((error = pfctl_begin_addrs(pf->h,
2592 &pf->paddr.ticket)) != 0)
2593 errc(1, error, "DIOCBEGINADDRS");
2594 }
2595
2596 if (pfctl_add_pool(pf, &r->rdr, PF_RDR))
2597 return (1);
2598 if (pfctl_add_pool(pf, &r->nat, PF_NAT))
2599 return (1);
2600 if (pfctl_add_pool(pf, &r->route, PF_RT))
2601 return (1);
2602 error = pfctl_add_rule_h(pf->h, r, anchor, name, ticket,
2603 pf->paddr.ticket);
2604 switch (error) {
2605 case 0:
2606 /* things worked, do nothing */
2607 break;
2608 case EEXIST:
2609 /* an identical rule is already present */
2610 was_present = true;
2611 break;
2612 default:
2613 errc(1, error, "DIOCADDRULE");
2614 }
2615 }
2616
2617 if (pf->opts & PF_OPT_VERBOSE) {
2618 INDENT(depth, !(pf->opts & PF_OPT_VERBOSE2));
2619 print_rule(r, name,
2620 pf->opts & PF_OPT_VERBOSE2,
2621 pf->opts & PF_OPT_NUMERIC);
2622 if (was_present)
2623 printf(" -- rule was already present");
2624 }
2625 path[len] = '\0';
2626 pfctl_clear_pool(&r->rdr);
2627 pfctl_clear_pool(&r->nat);
2628 return (0);
2629 }
2630
2631 int
pfctl_add_altq(struct pfctl * pf,struct pf_altq * a)2632 pfctl_add_altq(struct pfctl *pf, struct pf_altq *a)
2633 {
2634 if (altqsupport &&
2635 (loadopt & PFCTL_FLAG_ALTQ) != 0) {
2636 memcpy(&pf->paltq->altq, a, sizeof(struct pf_altq));
2637 if ((pf->opts & PF_OPT_NOACTION) == 0) {
2638 if (ioctl(pf->dev, DIOCADDALTQ, pf->paltq)) {
2639 if (errno == ENXIO)
2640 errx(1, "qtype not configured");
2641 else if (errno == ENODEV)
2642 errx(1, "%s: driver does not support "
2643 "altq", a->ifname);
2644 else
2645 err(1, "DIOCADDALTQ");
2646 }
2647 }
2648 pfaltq_store(&pf->paltq->altq);
2649 }
2650 return (0);
2651 }
2652
2653 int
pfctl_rules(int dev,char * filename,int opts,int optimize,char * anchorname,struct pfr_buffer * trans)2654 pfctl_rules(int dev, char *filename, int opts, int optimize,
2655 char *anchorname, struct pfr_buffer *trans)
2656 {
2657 #define ERR(...) do { warn(__VA_ARGS__); goto _error; } while(0)
2658 #define ERRX(...) do { warnx(__VA_ARGS__); goto _error; } while(0)
2659
2660 struct pfr_buffer *t, buf;
2661 struct pfioc_altq pa;
2662 struct pfctl pf;
2663 struct pfctl_ruleset *rs;
2664 struct pfctl_eth_ruleset *ethrs;
2665 struct pfr_table trs;
2666 char *path = NULL;
2667 int osize;
2668
2669 RB_INIT(&pf_anchors);
2670 memset(&pf_main_anchor, 0, sizeof(pf_main_anchor));
2671 pf_init_ruleset(&pf_main_anchor.ruleset);
2672 memset(&pf, 0, sizeof(pf));
2673 memset(&trs, 0, sizeof(trs));
2674 pf_main_anchor.ruleset.anchor = &pf_main_anchor;
2675
2676 memset(&pf_eth_main_anchor, 0, sizeof(pf_eth_main_anchor));
2677 pf_init_eth_ruleset(&pf_eth_main_anchor.ruleset);
2678 pf_eth_main_anchor.ruleset.anchor = &pf_eth_main_anchor;
2679
2680 if (trans == NULL) {
2681 bzero(&buf, sizeof(buf));
2682 buf.pfrb_type = PFRB_TRANS;
2683 pf.trans = &buf;
2684 t = &buf;
2685 osize = 0;
2686 } else {
2687 t = trans;
2688 osize = t->pfrb_size;
2689 }
2690
2691 memset(&pa, 0, sizeof(pa));
2692 pa.version = PFIOC_ALTQ_VERSION;
2693 memset(&pf, 0, sizeof(pf));
2694 memset(&trs, 0, sizeof(trs));
2695 if ((path = calloc(1, MAXPATHLEN)) == NULL)
2696 ERRX("%s: calloc", __func__);
2697 if (strlcpy(trs.pfrt_anchor, anchorname,
2698 sizeof(trs.pfrt_anchor)) >= sizeof(trs.pfrt_anchor))
2699 ERRX("%s: strlcpy", __func__);
2700 pf.dev = dev;
2701 pf.h = pfh;
2702 pf.opts = opts;
2703 pf.optimize = optimize;
2704 pf.loadopt = loadopt;
2705
2706 RB_INIT(&pf.statelim_ids);
2707 RB_INIT(&pf.statelim_nms);
2708 RB_INIT(&pf.sourcelim_ids);
2709 RB_INIT(&pf.sourcelim_nms);
2710
2711 /* non-brace anchor, create without resolving the path */
2712 if ((pf.anchor = calloc(1, sizeof(*pf.anchor))) == NULL)
2713 ERRX("%s: calloc", __func__);
2714 rs = &pf.anchor->ruleset;
2715 pf_init_ruleset(rs);
2716 rs->anchor = pf.anchor;
2717 if (strlcpy(pf.anchor->path, anchorname,
2718 sizeof(pf.anchor->path)) >= sizeof(pf.anchor->path))
2719 errx(1, "%s: strlcpy", __func__);
2720 if (strlcpy(pf.anchor->name, anchorname,
2721 sizeof(pf.anchor->name)) >= sizeof(pf.anchor->name))
2722 errx(1, "%s: strlcpy", __func__);
2723
2724
2725 pf.astack[0] = pf.anchor;
2726 pf.asd = 0;
2727 if (anchorname[0])
2728 pf.loadopt &= ~PFCTL_FLAG_ALTQ;
2729 pf.paltq = &pa;
2730 pf.trans = t;
2731 pfctl_init_options(&pf);
2732
2733 /* Set up ethernet anchor */
2734 if ((pf.eanchor = calloc(1, sizeof(*pf.eanchor))) == NULL)
2735 ERRX("%s: calloc", __func__);
2736
2737 if (strlcpy(pf.eanchor->path, anchorname,
2738 sizeof(pf.eanchor->path)) >= sizeof(pf.eanchor->path))
2739 errx(1, "%s: strlcpy", __func__);
2740 if (strlcpy(pf.eanchor->name, anchorname,
2741 sizeof(pf.eanchor->name)) >= sizeof(pf.eanchor->name))
2742 errx(1, "%s: strlcpy", __func__);
2743
2744 ethrs = &pf.eanchor->ruleset;
2745 pf_init_eth_ruleset(ethrs);
2746 ethrs->anchor = pf.eanchor;
2747 pf.eastack[0] = pf.eanchor;
2748
2749 if ((opts & PF_OPT_NOACTION) == 0) {
2750 /*
2751 * XXX For the time being we need to open transactions for
2752 * the main ruleset before parsing, because tables are still
2753 * loaded at parse time.
2754 */
2755 if (pfctl_ruleset_trans(&pf, anchorname, pf.anchor, true))
2756 ERRX("%s", __func__);
2757 if (pf.loadopt & PFCTL_FLAG_ETH)
2758 pf.eth_ticket = pfctl_get_ticket(t, PF_RULESET_ETH, anchorname);
2759 if (altqsupport && (pf.loadopt & PFCTL_FLAG_ALTQ))
2760 pa.ticket =
2761 pfctl_get_ticket(t, PF_RULESET_ALTQ, anchorname);
2762 if (pf.loadopt & PFCTL_FLAG_TABLE)
2763 pf.astack[0]->ruleset.tticket =
2764 pfctl_get_ticket(t, PF_RULESET_TABLE, anchorname);
2765 }
2766
2767 if (parse_config(filename, &pf) < 0) {
2768 if ((opts & PF_OPT_NOACTION) == 0)
2769 ERRX("Syntax error in config file: "
2770 "pf rules not loaded");
2771 else
2772 goto _error;
2773 }
2774 if (loadopt & PFCTL_FLAG_OPTION)
2775 pfctl_adjust_skip_ifaces(&pf);
2776
2777 if (anchorname[0] == '\0' && pf.loadopt & PFCTL_FLAG_FILTER) {
2778 pfctl_load_statelims(&pf);
2779 pfctl_load_sourcelims(&pf);
2780 }
2781
2782 if ((pf.loadopt & PFCTL_FLAG_FILTER &&
2783 (pfctl_load_ruleset(&pf, path, rs, PF_RULESET_SCRUB, 0))) ||
2784 (pf.loadopt & PFCTL_FLAG_ETH &&
2785 (pfctl_load_eth_ruleset(&pf, path, ethrs, 0))) ||
2786 (pf.loadopt & PFCTL_FLAG_NAT &&
2787 (pfctl_load_ruleset(&pf, path, rs, PF_RULESET_NAT, 0) ||
2788 pfctl_load_ruleset(&pf, path, rs, PF_RULESET_RDR, 0) ||
2789 pfctl_load_ruleset(&pf, path, rs, PF_RULESET_BINAT, 0))) ||
2790 (pf.loadopt & PFCTL_FLAG_FILTER &&
2791 pfctl_load_ruleset(&pf, path, rs, PF_RULESET_FILTER, 0))) {
2792 if ((opts & PF_OPT_NOACTION) == 0)
2793 ERRX("Unable to load rules into kernel");
2794 else
2795 goto _error;
2796 }
2797
2798 if ((altqsupport && (pf.loadopt & PFCTL_FLAG_ALTQ) != 0))
2799 if (check_commit_altq(dev, opts) != 0)
2800 ERRX("errors in altq config");
2801
2802 if (trans == NULL) {
2803 /* process "load anchor" directives */
2804 if (pfctl_load_anchors(dev, &pf) == -1)
2805 ERRX("load anchors");
2806
2807 if ((opts & PF_OPT_NOACTION) == 0) {
2808 if (!anchorname[0] && pfctl_load_options(&pf))
2809 goto _error;
2810 if (pfctl_trans(dev, t, DIOCXCOMMIT, osize))
2811 ERR("DIOCXCOMMIT");
2812 }
2813 }
2814 free(path);
2815 return (0);
2816
2817 _error:
2818 if (trans == NULL) { /* main ruleset */
2819 if ((opts & PF_OPT_NOACTION) == 0)
2820 if (pfctl_trans(dev, t, DIOCXROLLBACK, osize))
2821 err(1, "DIOCXROLLBACK");
2822 exit(1);
2823 } else { /* sub ruleset */
2824 free(path);
2825 return (-1);
2826 }
2827
2828 #undef ERR
2829 #undef ERRX
2830 }
2831
2832 FILE *
pfctl_fopen(const char * name,const char * mode)2833 pfctl_fopen(const char *name, const char *mode)
2834 {
2835 struct stat st;
2836 FILE *fp;
2837
2838 fp = fopen(name, mode);
2839 if (fp == NULL)
2840 return (NULL);
2841 if (fstat(fileno(fp), &st)) {
2842 fclose(fp);
2843 return (NULL);
2844 }
2845 if (S_ISDIR(st.st_mode)) {
2846 fclose(fp);
2847 errno = EISDIR;
2848 return (NULL);
2849 }
2850 return (fp);
2851 }
2852
2853 void
pfctl_init_options(struct pfctl * pf)2854 pfctl_init_options(struct pfctl *pf)
2855 {
2856
2857 pf->timeout[PFTM_TCP_FIRST_PACKET] = PFTM_TCP_FIRST_PACKET_VAL;
2858 pf->timeout[PFTM_TCP_OPENING] = PFTM_TCP_OPENING_VAL;
2859 pf->timeout[PFTM_TCP_ESTABLISHED] = PFTM_TCP_ESTABLISHED_VAL;
2860 pf->timeout[PFTM_TCP_CLOSING] = PFTM_TCP_CLOSING_VAL;
2861 pf->timeout[PFTM_TCP_FIN_WAIT] = PFTM_TCP_FIN_WAIT_VAL;
2862 pf->timeout[PFTM_TCP_CLOSED] = PFTM_TCP_CLOSED_VAL;
2863 pf->timeout[PFTM_SCTP_FIRST_PACKET] = PFTM_TCP_FIRST_PACKET_VAL;
2864 pf->timeout[PFTM_SCTP_OPENING] = PFTM_TCP_OPENING_VAL;
2865 pf->timeout[PFTM_SCTP_ESTABLISHED] = PFTM_TCP_ESTABLISHED_VAL;
2866 pf->timeout[PFTM_SCTP_CLOSING] = PFTM_TCP_CLOSING_VAL;
2867 pf->timeout[PFTM_SCTP_CLOSED] = PFTM_TCP_CLOSED_VAL;
2868 pf->timeout[PFTM_UDP_FIRST_PACKET] = PFTM_UDP_FIRST_PACKET_VAL;
2869 pf->timeout[PFTM_UDP_SINGLE] = PFTM_UDP_SINGLE_VAL;
2870 pf->timeout[PFTM_UDP_MULTIPLE] = PFTM_UDP_MULTIPLE_VAL;
2871 pf->timeout[PFTM_ICMP_FIRST_PACKET] = PFTM_ICMP_FIRST_PACKET_VAL;
2872 pf->timeout[PFTM_ICMP_ERROR_REPLY] = PFTM_ICMP_ERROR_REPLY_VAL;
2873 pf->timeout[PFTM_OTHER_FIRST_PACKET] = PFTM_OTHER_FIRST_PACKET_VAL;
2874 pf->timeout[PFTM_OTHER_SINGLE] = PFTM_OTHER_SINGLE_VAL;
2875 pf->timeout[PFTM_OTHER_MULTIPLE] = PFTM_OTHER_MULTIPLE_VAL;
2876 pf->timeout[PFTM_FRAG] = PFTM_FRAG_VAL;
2877 pf->timeout[PFTM_INTERVAL] = PFTM_INTERVAL_VAL;
2878 pf->timeout[PFTM_SRC_NODE] = PFTM_SRC_NODE_VAL;
2879 pf->timeout[PFTM_TS_DIFF] = PFTM_TS_DIFF_VAL;
2880 pf->timeout[PFTM_ADAPTIVE_START] = PFSTATE_ADAPT_START;
2881 pf->timeout[PFTM_ADAPTIVE_END] = PFSTATE_ADAPT_END;
2882
2883 pf->limit[PF_LIMIT_STATES] = PFSTATE_HIWAT;
2884 pf->limit[PF_LIMIT_FRAGS] = PFFRAG_FRENT_HIWAT;
2885
2886 pf->limit[PF_LIMIT_SRC_NODES] = (limit_curr[PF_LIMIT_SRC_NODES] == 0) ?
2887 PFSNODE_HIWAT : limit_curr[PF_LIMIT_SRC_NODES];
2888 pf->limit[PF_LIMIT_TABLE_ENTRIES] =
2889 (limit_curr[PF_LIMIT_TABLE_ENTRIES] == 0) ?
2890 PFR_KENTRY_HIWAT : limit_curr[PF_LIMIT_TABLE_ENTRIES];
2891 pf->limit[PF_LIMIT_ANCHORS] = (limit_curr[PF_LIMIT_ANCHORS] == 0) ?
2892 PF_ANCHOR_HIWAT : limit_curr[PF_LIMIT_ANCHORS];
2893
2894 pf->debug = PF_DEBUG_URGENT;
2895 pf->reassemble = 0;
2896
2897 pf->syncookies = false;
2898 pf->syncookieswat[0] = PF_SYNCOOKIES_LOWATPCT;
2899 pf->syncookieswat[1] = PF_SYNCOOKIES_HIWATPCT;
2900 }
2901
2902 int
pfctl_load_options(struct pfctl * pf)2903 pfctl_load_options(struct pfctl *pf)
2904 {
2905 int i, error = 0;
2906
2907 if ((loadopt & PFCTL_FLAG_OPTION) == 0)
2908 return (0);
2909
2910 /* load limits */
2911 for (i = 0; i < PF_LIMIT_MAX; i++) {
2912 if ((pf->opts & PF_OPT_MERGE) && !pf->limit_set[i])
2913 continue;
2914 if (pfctl_load_limit(pf, i, pf->limit[i]))
2915 error = 1;
2916 }
2917
2918 /*
2919 * If we've set the states limit, but haven't explicitly set adaptive
2920 * timeouts, do it now with a start of 60% and end of 120%.
2921 */
2922 if (pf->limit_set[PF_LIMIT_STATES] &&
2923 !pf->timeout_set[PFTM_ADAPTIVE_START] &&
2924 !pf->timeout_set[PFTM_ADAPTIVE_END]) {
2925 pf->timeout[PFTM_ADAPTIVE_START] =
2926 (pf->limit[PF_LIMIT_STATES] / 10) * 6;
2927 pf->timeout_set[PFTM_ADAPTIVE_START] = 1;
2928 pf->timeout[PFTM_ADAPTIVE_END] =
2929 (pf->limit[PF_LIMIT_STATES] / 10) * 12;
2930 pf->timeout_set[PFTM_ADAPTIVE_END] = 1;
2931 }
2932
2933 /* load timeouts */
2934 for (i = 0; i < PFTM_MAX; i++) {
2935 if ((pf->opts & PF_OPT_MERGE) && !pf->timeout_set[i])
2936 continue;
2937 if (pfctl_load_timeout(pf, i, pf->timeout[i]))
2938 error = 1;
2939 }
2940
2941 /* load debug */
2942 if (!(pf->opts & PF_OPT_MERGE) || pf->debug_set)
2943 if (pfctl_load_debug(pf, pf->debug))
2944 error = 1;
2945
2946 /* load logif */
2947 if (!(pf->opts & PF_OPT_MERGE) || pf->ifname_set)
2948 if (pfctl_load_logif(pf, pf->ifname))
2949 error = 1;
2950
2951 /* load hostid */
2952 if (!(pf->opts & PF_OPT_MERGE) || pf->hostid_set)
2953 if (pfctl_load_hostid(pf, pf->hostid))
2954 error = 1;
2955
2956 /* load reassembly settings */
2957 if (!(pf->opts & PF_OPT_MERGE) || pf->reass_set)
2958 if (pfctl_load_reassembly(pf, pf->reassemble))
2959 error = 1;
2960
2961 /* load keepcounters */
2962 if (pfctl_set_keepcounters(pf->dev, pf->keep_counters))
2963 error = 1;
2964
2965 /* load syncookies settings */
2966 if (pfctl_load_syncookies(pf, pf->syncookies))
2967 error = 1;
2968
2969 return (error);
2970 }
2971
2972 int
pfctl_apply_limit(struct pfctl * pf,const char * opt,unsigned int limit)2973 pfctl_apply_limit(struct pfctl *pf, const char *opt, unsigned int limit)
2974 {
2975 int i;
2976
2977
2978 for (i = 0; pf_limits[i].name; i++) {
2979 if (strcasecmp(opt, pf_limits[i].name) == 0) {
2980 pf->limit[pf_limits[i].index] = limit;
2981 pf->limit_set[pf_limits[i].index] = 1;
2982 break;
2983 }
2984 }
2985 if (pf_limits[i].name == NULL) {
2986 warnx("Bad pool name.");
2987 return (1);
2988 }
2989
2990 if (pf->opts & PF_OPT_VERBOSE)
2991 printf("set limit %s %d\n", opt, limit);
2992
2993 if ((pf->opts & PF_OPT_NOACTION) == 0)
2994 pfctl_load_options(pf);
2995
2996 return (0);
2997 }
2998
2999 int
pfctl_load_limit(struct pfctl * pf,unsigned int index,unsigned int limit)3000 pfctl_load_limit(struct pfctl *pf, unsigned int index, unsigned int limit)
3001 {
3002 static int restore_limit_handler_armed = 0;
3003
3004 if (pfctl_set_limit(pf->h, index, limit)) {
3005 if (errno == EBUSY)
3006 warnx("Current pool size exceeds requested %s limit %u",
3007 pf_limits[index].name, limit);
3008 else
3009 warnx("Cannot set %s limit to %u",
3010 pf_limits[index].name, limit);
3011 return (1);
3012 } else if (restore_limit_handler_armed == 0) {
3013 atexit(pfctl_restore_limits);
3014 restore_limit_handler_armed = 1;
3015 }
3016 return (0);
3017 }
3018
3019 int
pfctl_apply_timeout(struct pfctl * pf,const char * opt,int seconds,int quiet)3020 pfctl_apply_timeout(struct pfctl *pf, const char *opt, int seconds, int quiet)
3021 {
3022 int i;
3023
3024 if ((loadopt & PFCTL_FLAG_OPTION) == 0)
3025 return (0);
3026
3027 for (i = 0; pf_timeouts[i].name; i++) {
3028 if (strcasecmp(opt, pf_timeouts[i].name) == 0) {
3029 pf->timeout[pf_timeouts[i].timeout] = seconds;
3030 pf->timeout_set[pf_timeouts[i].timeout] = 1;
3031 break;
3032 }
3033 }
3034
3035 if (pf_timeouts[i].name == NULL) {
3036 warnx("Bad timeout name.");
3037 return (1);
3038 }
3039
3040
3041 if (pf->opts & PF_OPT_VERBOSE && ! quiet)
3042 printf("set timeout %s %d\n", opt, seconds);
3043
3044 return (0);
3045 }
3046
3047 int
pfctl_load_timeout(struct pfctl * pf,unsigned int timeout,unsigned int seconds)3048 pfctl_load_timeout(struct pfctl *pf, unsigned int timeout, unsigned int seconds)
3049 {
3050 if (pfctl_set_timeout(pf->h, timeout, seconds)) {
3051 warnx("DIOCSETTIMEOUT");
3052 return (1);
3053 }
3054 return (0);
3055 }
3056
3057 int
pfctl_set_reassembly(struct pfctl * pf,int on,int nodf)3058 pfctl_set_reassembly(struct pfctl *pf, int on, int nodf)
3059 {
3060 if ((loadopt & PFCTL_FLAG_OPTION) == 0)
3061 return (0);
3062
3063 pf->reass_set = 1;
3064 if (on) {
3065 pf->reassemble = PF_REASS_ENABLED;
3066 if (nodf)
3067 pf->reassemble |= PF_REASS_NODF;
3068 } else {
3069 pf->reassemble = 0;
3070 }
3071
3072 if (pf->opts & PF_OPT_VERBOSE)
3073 printf("set reassemble %s %s\n", on ? "yes" : "no",
3074 nodf ? "no-df" : "");
3075
3076 return (0);
3077 }
3078
3079 int
pfctl_set_optimization(struct pfctl * pf,const char * opt)3080 pfctl_set_optimization(struct pfctl *pf, const char *opt)
3081 {
3082 const struct pf_hint *hint;
3083 int i, r;
3084
3085 if ((loadopt & PFCTL_FLAG_OPTION) == 0)
3086 return (0);
3087
3088 for (i = 0; pf_hints[i].name; i++)
3089 if (strcasecmp(opt, pf_hints[i].name) == 0)
3090 break;
3091
3092 hint = pf_hints[i].hint;
3093 if (hint == NULL) {
3094 warnx("invalid state timeouts optimization");
3095 return (1);
3096 }
3097
3098 for (i = 0; hint[i].name; i++)
3099 if ((r = pfctl_apply_timeout(pf, hint[i].name,
3100 hint[i].timeout, 1)))
3101 return (r);
3102
3103 if (pf->opts & PF_OPT_VERBOSE)
3104 printf("set optimization %s\n", opt);
3105
3106 return (0);
3107 }
3108
3109 int
pfctl_set_logif(struct pfctl * pf,char * ifname)3110 pfctl_set_logif(struct pfctl *pf, char *ifname)
3111 {
3112
3113 if ((loadopt & PFCTL_FLAG_OPTION) == 0)
3114 return (0);
3115
3116 if (!strcmp(ifname, "none")) {
3117 free(pf->ifname);
3118 pf->ifname = NULL;
3119 } else {
3120 pf->ifname = strdup(ifname);
3121 if (!pf->ifname)
3122 errx(1, "pfctl_set_logif: strdup");
3123 }
3124 pf->ifname_set = 1;
3125
3126 if (pf->opts & PF_OPT_VERBOSE)
3127 printf("set loginterface %s\n", ifname);
3128
3129 return (0);
3130 }
3131
3132 int
pfctl_load_logif(struct pfctl * pf,char * ifname)3133 pfctl_load_logif(struct pfctl *pf, char *ifname)
3134 {
3135 if (ifname != NULL && strlen(ifname) >= IFNAMSIZ) {
3136 warnx("pfctl_load_logif: strlcpy");
3137 return (1);
3138 }
3139 return (pfctl_set_statusif(pfh, ifname ? ifname : ""));
3140 }
3141
3142 void
pfctl_set_hostid(struct pfctl * pf,u_int32_t hostid)3143 pfctl_set_hostid(struct pfctl *pf, u_int32_t hostid)
3144 {
3145 if ((loadopt & PFCTL_FLAG_OPTION) == 0)
3146 return;
3147
3148 HTONL(hostid);
3149
3150 pf->hostid = hostid;
3151 pf->hostid_set = 1;
3152
3153 if (pf->opts & PF_OPT_VERBOSE)
3154 printf("set hostid 0x%08x\n", ntohl(hostid));
3155 }
3156
3157 int
pfctl_load_hostid(struct pfctl * pf,u_int32_t hostid)3158 pfctl_load_hostid(struct pfctl *pf, u_int32_t hostid)
3159 {
3160 if (ioctl(dev, DIOCSETHOSTID, &hostid)) {
3161 warnx("DIOCSETHOSTID");
3162 return (1);
3163 }
3164 return (0);
3165 }
3166
3167 int
pfctl_load_reassembly(struct pfctl * pf,u_int32_t reassembly)3168 pfctl_load_reassembly(struct pfctl *pf, u_int32_t reassembly)
3169 {
3170 if (ioctl(dev, DIOCSETREASS, &reassembly)) {
3171 warnx("DIOCSETREASS");
3172 return (1);
3173 }
3174 return (0);
3175 }
3176
3177 int
pfctl_load_syncookies(struct pfctl * pf,u_int8_t val)3178 pfctl_load_syncookies(struct pfctl *pf, u_int8_t val)
3179 {
3180 struct pfctl_syncookies cookies;
3181
3182 bzero(&cookies, sizeof(cookies));
3183
3184 cookies.mode = val;
3185 cookies.lowwater = pf->syncookieswat[0];
3186 cookies.highwater = pf->syncookieswat[1];
3187
3188 if (pfctl_set_syncookies(dev, &cookies)) {
3189 warnx("DIOCSETSYNCOOKIES");
3190 return (1);
3191 }
3192 return (0);
3193 }
3194
3195 int
pfctl_cfg_syncookies(struct pfctl * pf,uint8_t val,struct pfctl_watermarks * w)3196 pfctl_cfg_syncookies(struct pfctl *pf, uint8_t val, struct pfctl_watermarks *w)
3197 {
3198 if (val != PF_SYNCOOKIES_ADAPTIVE && w != NULL) {
3199 warnx("syncookies start/end only apply to adaptive");
3200 return (1);
3201 }
3202 if (val == PF_SYNCOOKIES_ADAPTIVE && w != NULL) {
3203 if (!w->hi)
3204 w->hi = PF_SYNCOOKIES_HIWATPCT;
3205 if (!w->lo)
3206 w->lo = w->hi / 2;
3207 if (w->lo >= w->hi) {
3208 warnx("start must be higher than end");
3209 return (1);
3210 }
3211 pf->syncookieswat[0] = w->lo;
3212 pf->syncookieswat[1] = w->hi;
3213 pf->syncookieswat_set = 1;
3214 }
3215
3216 if (pf->opts & PF_OPT_VERBOSE) {
3217 if (val == PF_SYNCOOKIES_NEVER)
3218 printf("set syncookies never\n");
3219 else if (val == PF_SYNCOOKIES_ALWAYS)
3220 printf("set syncookies always\n");
3221 else if (val == PF_SYNCOOKIES_ADAPTIVE) {
3222 if (pf->syncookieswat_set)
3223 printf("set syncookies adaptive (start %u%%, "
3224 "end %u%%)\n", pf->syncookieswat[1],
3225 pf->syncookieswat[0]);
3226 else
3227 printf("set syncookies adaptive\n");
3228 } else { /* cannot happen */
3229 warnx("king bula ate all syncookies");
3230 return (1);
3231 }
3232 }
3233
3234 pf->syncookies = val;
3235 return (0);
3236 }
3237
3238 int
pfctl_do_set_debug(struct pfctl * pf,char * d)3239 pfctl_do_set_debug(struct pfctl *pf, char *d)
3240 {
3241 u_int32_t level;
3242 int ret;
3243
3244 if ((loadopt & PFCTL_FLAG_OPTION) == 0)
3245 return (0);
3246
3247 if (!strcmp(d, "none"))
3248 pf->debug = PF_DEBUG_NONE;
3249 else if (!strcmp(d, "urgent"))
3250 pf->debug = PF_DEBUG_URGENT;
3251 else if (!strcmp(d, "misc"))
3252 pf->debug = PF_DEBUG_MISC;
3253 else if (!strcmp(d, "loud"))
3254 pf->debug = PF_DEBUG_NOISY;
3255 else {
3256 warnx("unknown debug level \"%s\"", d);
3257 return (-1);
3258 }
3259
3260 pf->debug_set = 1;
3261 level = pf->debug;
3262
3263 if ((pf->opts & PF_OPT_NOACTION) == 0)
3264 if ((ret = pfctl_set_debug(pfh, level)) != 0)
3265 errc(1, ret, "DIOCSETDEBUG");
3266
3267 if (pf->opts & PF_OPT_VERBOSE)
3268 printf("set debug %s\n", d);
3269
3270 return (0);
3271 }
3272
3273 int
pfctl_load_debug(struct pfctl * pf,unsigned int level)3274 pfctl_load_debug(struct pfctl *pf, unsigned int level)
3275 {
3276 if (pfctl_set_debug(pf->h, level)) {
3277 warnx("DIOCSETDEBUG");
3278 return (1);
3279 }
3280 return (0);
3281 }
3282
3283 int
pfctl_set_interface_flags(struct pfctl * pf,char * ifname,int flags,int how)3284 pfctl_set_interface_flags(struct pfctl *pf, char *ifname, int flags, int how)
3285 {
3286 struct pfioc_iface pi;
3287 struct node_host *h = NULL, *n = NULL;
3288
3289 if ((loadopt & PFCTL_FLAG_OPTION) == 0)
3290 return (0);
3291
3292 bzero(&pi, sizeof(pi));
3293
3294 pi.pfiio_flags = flags;
3295
3296 /* Make sure our cache matches the kernel. If we set or clear the flag
3297 * for a group this applies to all members. */
3298 h = ifa_grouplookup(ifname, 0);
3299 for (n = h; n != NULL; n = n->next)
3300 pfctl_set_interface_flags(pf, n->ifname, flags, how);
3301
3302 if (strlcpy(pi.pfiio_name, ifname, sizeof(pi.pfiio_name)) >=
3303 sizeof(pi.pfiio_name))
3304 errx(1, "pfctl_set_interface_flags: strlcpy");
3305
3306 if ((pf->opts & PF_OPT_NOACTION) == 0) {
3307 if (how == 0) {
3308 if (ioctl(pf->dev, DIOCCLRIFFLAG, &pi))
3309 pfctl_err(pf->opts, 1, "DIOCCLRIFFLAG");
3310 } else {
3311 if (ioctl(pf->dev, DIOCSETIFFLAG, &pi))
3312 err(1, "DIOCSETIFFLAG");
3313 pfctl_check_skip_ifaces(ifname);
3314 }
3315 }
3316 return (0);
3317 }
3318
3319 void
pfctl_debug(int dev,u_int32_t level,int opts)3320 pfctl_debug(int dev, u_int32_t level, int opts)
3321 {
3322 int ret;
3323
3324 if ((ret = pfctl_set_debug(pfh, level)) != 0)
3325 errc(1, ret, "DIOCSETDEBUG");
3326 if ((opts & PF_OPT_QUIET) == 0) {
3327 fprintf(stderr, "debug level set to '");
3328 switch (level) {
3329 case PF_DEBUG_NONE:
3330 fprintf(stderr, "none");
3331 break;
3332 case PF_DEBUG_URGENT:
3333 fprintf(stderr, "urgent");
3334 break;
3335 case PF_DEBUG_MISC:
3336 fprintf(stderr, "misc");
3337 break;
3338 case PF_DEBUG_NOISY:
3339 fprintf(stderr, "loud");
3340 break;
3341 default:
3342 fprintf(stderr, "<invalid>");
3343 break;
3344 }
3345 fprintf(stderr, "'\n");
3346 }
3347 }
3348
3349 int
pfctl_test_altqsupport(int dev,int opts)3350 pfctl_test_altqsupport(int dev, int opts)
3351 {
3352 struct pfioc_altq pa;
3353
3354 pa.version = PFIOC_ALTQ_VERSION;
3355 if (ioctl(dev, DIOCGETALTQS, &pa)) {
3356 if (errno == ENODEV) {
3357 if (opts & PF_OPT_VERBOSE)
3358 fprintf(stderr, "No ALTQ support in kernel\n"
3359 "ALTQ related functions disabled\n");
3360 return (0);
3361 } else
3362 err(1, "DIOCGETALTQS");
3363 }
3364 return (1);
3365 }
3366
3367 int
pfctl_walk_show(int opts,struct pfioc_ruleset * pr,void * warg)3368 pfctl_walk_show(int opts, struct pfioc_ruleset *pr, void *warg)
3369 {
3370 if (pr->path[0]) {
3371 if (pr->path[0] != '_' || (opts & PF_OPT_VERBOSE))
3372 printf(" %s/%s\n", pr->path, pr->name);
3373 } else if (pr->name[0] != '_' || (opts & PF_OPT_VERBOSE))
3374 printf(" %s\n", pr->name);
3375
3376 return (0);
3377 }
3378
3379 int
pfctl_walk_get(int opts,struct pfioc_ruleset * pr,void * warg)3380 pfctl_walk_get(int opts, struct pfioc_ruleset *pr, void *warg)
3381 {
3382 struct pfr_anchoritem *pfra;
3383 struct pfr_anchors *anchors;
3384 int e;
3385
3386 anchors = (struct pfr_anchors *)warg;
3387
3388 pfra = malloc(sizeof(*pfra));
3389 if (pfra == NULL)
3390 err(1, "%s", __func__);
3391
3392 if (pr->path[0])
3393 e = asprintf(&pfra->pfra_anchorname, "%s/%s", pr->path,
3394 pr->name);
3395 else
3396 e = asprintf(&pfra->pfra_anchorname, "%s", pr->name);
3397
3398 if (e == -1)
3399 err(1, "%s", __func__);
3400
3401 SLIST_INSERT_HEAD(anchors, pfra, pfra_sle);
3402
3403 return (0);
3404 }
3405
3406 int
pfctl_walk_anchors(int dev,int opts,const char * anchor,int (walkf)(int,struct pfioc_ruleset *,void *),void * warg)3407 pfctl_walk_anchors(int dev, int opts, const char *anchor,
3408 int(walkf)(int, struct pfioc_ruleset *, void *), void *warg)
3409 {
3410 struct pfioc_ruleset pr;
3411 u_int32_t mnr, nr;
3412 int ret;
3413
3414 memset(&pr, 0, sizeof(pr));
3415 if ((ret = pfctl_get_rulesets(pfh, anchor, &mnr)) != 0)
3416 errx(1, "%s", pf_strerror(ret));
3417 for (nr = 0; nr < mnr; ++nr) {
3418 char sub[MAXPATHLEN];
3419
3420 if ((ret = pfctl_get_ruleset(pfh, anchor, nr, &pr)) != 0)
3421 errc(1, ret, "DIOCGETRULESET");
3422 if (!strcmp(pr.name, PF_RESERVED_ANCHOR))
3423 continue;
3424 sub[0] = '\0';
3425 if (walkf(opts, &pr, warg))
3426 return (-1);
3427
3428 if (pr.path[0])
3429 snprintf(sub, sizeof(sub), "%s/%s", pr.path, pr.name);
3430 else
3431 snprintf(sub, sizeof(sub), "%s", pr.name);
3432 if (pfctl_walk_anchors(dev, opts, sub, walkf, warg))
3433 return (-1);
3434 }
3435 return (0);
3436 }
3437
3438 int
pfctl_show_anchors(int dev,int opts,char * anchor)3439 pfctl_show_anchors(int dev, int opts, char *anchor)
3440 {
3441 return (
3442 pfctl_walk_anchors(dev, opts, anchor, pfctl_walk_show, NULL));
3443 }
3444
3445 struct pfr_anchors *
pfctl_get_anchors(int dev,const char * anchor,int opts)3446 pfctl_get_anchors(int dev, const char *anchor, int opts)
3447 {
3448 struct pfioc_ruleset pr;
3449 static struct pfr_anchors anchors;
3450 char anchorbuf[PATH_MAX];
3451 char *n;
3452
3453 SLIST_INIT(&anchors);
3454
3455 memset(&pr, 0, sizeof(pr));
3456 if (*anchor != '\0') {
3457 strlcpy(anchorbuf, anchor, sizeof(anchorbuf));
3458 n = dirname(anchorbuf);
3459 if (n[0] != '.' && n[1] != '\0')
3460 strlcpy(pr.path, n, sizeof(pr.path));
3461 strlcpy(anchorbuf, anchor, sizeof(anchorbuf));
3462 n = basename(anchorbuf);
3463 if (n != NULL)
3464 strlcpy(pr.name, n, sizeof(pr.name));
3465 }
3466
3467 /* insert a root anchor first. */
3468 pfctl_walk_get(opts, &pr, &anchors);
3469
3470 if (pfctl_walk_anchors(dev, opts, anchor, pfctl_walk_get, &anchors))
3471 errx(1, "%s failed to retrieve list of anchors, can't continue",
3472 __func__);
3473
3474 return (&anchors);
3475 }
3476
3477 int
pfctl_call_cleartables(int dev,int opts,struct pfr_anchoritem * pfra)3478 pfctl_call_cleartables(int dev, int opts, struct pfr_anchoritem *pfra)
3479 {
3480 /*
3481 * PF_OPT_QUIET makes pfctl_clear_tables() to stop printing number of
3482 * tables cleared for given anchor.
3483 */
3484 opts |= PF_OPT_QUIET;
3485 return ((pfctl_do_clear_tables(pfra->pfra_anchorname, opts) == -1) ?
3486 1 : 0);
3487 }
3488
3489 int
pfctl_call_clearrules(int dev,int opts,struct pfr_anchoritem * pfra)3490 pfctl_call_clearrules(int dev, int opts, struct pfr_anchoritem *pfra)
3491 {
3492 /*
3493 * PF_OPT_QUIET makes pfctl_clear_rules() to stop printing a 'rules
3494 * cleared' message for every anchor it deletes.
3495 */
3496 opts |= PF_OPT_QUIET;
3497 return (pfctl_flush_rules(dev, opts, pfra->pfra_anchorname));
3498 }
3499
3500 int
pfctl_call_clearanchors(int dev,int opts,struct pfr_anchoritem * pfra)3501 pfctl_call_clearanchors(int dev, int opts, struct pfr_anchoritem *pfra)
3502 {
3503 int rv = 0;
3504
3505 rv |= pfctl_call_cleartables(dev, opts, pfra);
3506 rv |= pfctl_call_clearrules(dev, opts, pfra);
3507
3508 return (rv);
3509 }
3510
3511 int
pfctl_call_showtables(int dev,int opts,struct pfr_anchoritem * pfra)3512 pfctl_call_showtables(int dev, int opts, struct pfr_anchoritem *pfra)
3513 {
3514 pfctl_show_tables(pfra->pfra_anchorname, opts);
3515 return (0);
3516 }
3517
3518 int
pfctl_recurse(int dev,int opts,const char * anchorname,int (* walkf)(int,int,struct pfr_anchoritem *))3519 pfctl_recurse(int dev, int opts, const char *anchorname,
3520 int(*walkf)(int, int, struct pfr_anchoritem *))
3521 {
3522 int rv = 0;
3523 struct pfr_anchors *anchors;
3524 struct pfr_anchoritem *pfra, *pfra_save;
3525
3526 anchors = pfctl_get_anchors(dev, anchorname, opts);
3527 /*
3528 * While traversing the list, pfctl_clear_*() must always return
3529 * so that failures on one anchor do not prevent clearing others.
3530 */
3531 opts |= PF_OPT_IGNFAIL;
3532 if ((opts & PF_OPT_CALLSHOW) == 0)
3533 printf("Removing:\n");
3534 SLIST_FOREACH_SAFE(pfra, anchors, pfra_sle, pfra_save) {
3535 if ((opts & PF_OPT_CALLSHOW) == 0)
3536 printf(" %s\n",
3537 (*pfra->pfra_anchorname == '\0') ? "/" :
3538 pfra->pfra_anchorname);
3539 rv |= walkf(dev, opts, pfra);
3540 SLIST_REMOVE(anchors, pfra, pfr_anchoritem, pfra_sle);
3541 free(pfra->pfra_anchorname);
3542 free(pfra);
3543 }
3544
3545 return (rv);
3546 }
3547
3548 int
pfctl_show_eth_anchors(int dev,int opts,char * anchorname)3549 pfctl_show_eth_anchors(int dev, int opts, char *anchorname)
3550 {
3551 struct pfctl_eth_rulesets_info ri;
3552 struct pfctl_eth_ruleset_info rs;
3553 int ret;
3554
3555 if ((ret = pfctl_get_eth_rulesets_info(dev, &ri, anchorname)) != 0) {
3556 if (ret != ENOENT)
3557 errc(1, ret, "DIOCGETETHRULESETS");
3558 return (-1);
3559 }
3560
3561 for (int nr = 0; nr < ri.nr; nr++) {
3562 char sub[MAXPATHLEN];
3563
3564 if ((ret = pfctl_get_eth_ruleset(dev, anchorname, nr, &rs)) != 0)
3565 errc(1, ret, "DIOCGETETHRULESET");
3566
3567 if (!strcmp(rs.name, PF_RESERVED_ANCHOR))
3568 continue;
3569 sub[0] = 0;
3570 if (rs.path[0]) {
3571 strlcat(sub, rs.path, sizeof(sub));
3572 strlcat(sub, "/", sizeof(sub));
3573 }
3574 strlcat(sub, rs.name, sizeof(sub));
3575 if (sub[0] != '_' || (opts & PF_OPT_VERBOSE))
3576 printf(" %s\n", sub);
3577 if ((opts & PF_OPT_VERBOSE) && pfctl_show_eth_anchors(dev, opts, sub))
3578 return (-1);
3579 }
3580 return (0);
3581 }
3582
3583 const char *
pfctl_lookup_option(char * cmd,const char * const * list)3584 pfctl_lookup_option(char *cmd, const char * const *list)
3585 {
3586 if (cmd != NULL && *cmd)
3587 for (; *list; list++)
3588 if (!strncmp(cmd, *list, strlen(cmd)))
3589 return (*list);
3590 return (NULL);
3591 }
3592
3593 int
pfctl_lookup_id(const char * cmd,const struct pfctl_opt_id * opt_ids)3594 pfctl_lookup_id(const char *cmd, const struct pfctl_opt_id *opt_ids)
3595 {
3596 const struct pfctl_opt_id *opt_id;
3597 int id = 0;
3598 size_t cmdlen = strlen(cmd);
3599
3600 for (opt_id = opt_ids; opt_id->id != 0; opt_id++) {
3601 if (strncmp(cmd, opt_id->name, cmdlen) == 0) {
3602 if (id != 0)
3603 errx(1, "%s is ambiguous", cmd);
3604
3605 id = opt_id->id;
3606 }
3607 }
3608
3609 return (id);
3610 }
3611
3612 void
pfctl_reset(int dev,int opts)3613 pfctl_reset(int dev, int opts)
3614 {
3615 struct pfctl pf;
3616 struct pfr_buffer t;
3617 int i;
3618
3619 memset(&pf, 0, sizeof(pf));
3620 pf.dev = dev;
3621 pf.h = pfh;
3622 pfctl_init_options(&pf);
3623
3624 /* Force reset upon pfctl_load_options() */
3625 pf.debug_set = 1;
3626 pf.reass_set = 1;
3627 pf.syncookieswat_set = 1;
3628 pf.ifname = strdup("none");
3629 if (pf.ifname == NULL)
3630 err(1, "%s: strdup", __func__);
3631 pf.ifname_set = 1;
3632
3633 memset(&t, 0, sizeof(t));
3634 t.pfrb_type = PFRB_TRANS;
3635 if (pfctl_trans(dev, &t, DIOCXBEGIN, 0))
3636 err(1, "%s: DIOCXBEGIN", __func__);
3637
3638 for (i = 0; pf_limits[i].name; i++)
3639 pf.limit_set[pf_limits[i].index] = 1;
3640
3641 for (i = 0; pf_timeouts[i].name; i++)
3642 pf.timeout_set[pf_timeouts[i].timeout] = 1;
3643
3644 pfctl_load_options(&pf);
3645
3646 if (pfctl_trans(dev, &t, DIOCXCOMMIT, 0))
3647 err(1, "%s: DIOCXCOMMIT", __func__);
3648
3649 pfctl_clear_interface_flags(dev, opts);
3650 }
3651
3652 int
main(int argc,char * argv[])3653 main(int argc, char *argv[])
3654 {
3655 int ch;
3656 int mode = O_RDONLY;
3657 int opts = 0;
3658 int optimize = PF_OPTIMIZE_BASIC;
3659 char anchorname[MAXPATHLEN];
3660 char *path;
3661 const char *idopt = NULL;
3662
3663 if (argc < 2)
3664 usage();
3665
3666 while ((ch = getopt(argc, argv,
3667 "a:AdD:eqf:F:ghi:I:k:K:mMnNOo:Pp:rRs:St:T:vx:z")) != -1) {
3668 switch (ch) {
3669 case 'a':
3670 anchoropt = optarg;
3671 break;
3672 case 'd':
3673 opts |= PF_OPT_DISABLE;
3674 mode = O_RDWR;
3675 break;
3676 case 'D':
3677 if (pfctl_cmdline_symset(optarg) < 0)
3678 warnx("could not parse macro definition %s",
3679 optarg);
3680 break;
3681 case 'e':
3682 opts |= PF_OPT_ENABLE;
3683 mode = O_RDWR;
3684 break;
3685 case 'q':
3686 opts |= PF_OPT_QUIET;
3687 break;
3688 case 'F':
3689 clearopt = pfctl_lookup_option(optarg, clearopt_list);
3690 if (clearopt == NULL) {
3691 warnx("Unknown flush modifier '%s'", optarg);
3692 usage();
3693 }
3694 mode = O_RDWR;
3695 break;
3696 case 'i':
3697 ifaceopt = optarg;
3698 break;
3699 case 'I':
3700 idopt = optarg;
3701 break;
3702 case 'k':
3703 if (state_killers >= 2) {
3704 warnx("can only specify -k twice");
3705 usage();
3706 /* NOTREACHED */
3707 }
3708 state_kill[state_killers++] = optarg;
3709 mode = O_RDWR;
3710 break;
3711 case 'K':
3712 if (src_node_killers >= 2) {
3713 warnx("can only specify -K twice");
3714 usage();
3715 /* NOTREACHED */
3716 }
3717 src_node_kill[src_node_killers++] = optarg;
3718 mode = O_RDWR;
3719 break;
3720 case 'm':
3721 opts |= PF_OPT_MERGE;
3722 break;
3723 case 'M':
3724 opts |= PF_OPT_KILLMATCH;
3725 break;
3726 case 'n':
3727 opts |= PF_OPT_NOACTION;
3728 break;
3729 case 'N':
3730 loadopt |= PFCTL_FLAG_NAT;
3731 break;
3732 case 'r':
3733 opts |= PF_OPT_USEDNS;
3734 break;
3735 case 'f':
3736 rulesopt = optarg;
3737 mode = O_RDWR;
3738 break;
3739 case 'g':
3740 opts |= PF_OPT_DEBUG;
3741 break;
3742 case 'A':
3743 loadopt |= PFCTL_FLAG_ALTQ;
3744 break;
3745 case 'R':
3746 loadopt |= PFCTL_FLAG_FILTER;
3747 break;
3748 case 'o':
3749 optiopt = pfctl_lookup_option(optarg, optiopt_list);
3750 if (optiopt == NULL) {
3751 warnx("Unknown optimization '%s'", optarg);
3752 usage();
3753 }
3754 opts |= PF_OPT_OPTIMIZE;
3755 break;
3756 case 'O':
3757 loadopt |= PFCTL_FLAG_OPTION;
3758 break;
3759 case 'p':
3760 pf_device = optarg;
3761 break;
3762 case 'P':
3763 opts |= PF_OPT_NUMERIC;
3764 break;
3765 case 's':
3766 showopt = pfctl_lookup_id(optarg, showopt_list);
3767 if (showopt == 0) {
3768 warnx("Unknown show modifier '%s'", optarg);
3769 usage();
3770 }
3771 break;
3772 case 'S':
3773 opts |= PF_OPT_NODNS;
3774 break;
3775 case 't':
3776 tableopt = optarg;
3777 break;
3778 case 'T':
3779 tblcmdopt = pfctl_lookup_option(optarg, tblcmdopt_list);
3780 if (tblcmdopt == NULL) {
3781 warnx("Unknown table command '%s'", optarg);
3782 usage();
3783 }
3784 break;
3785 case 'v':
3786 if (opts & PF_OPT_VERBOSE)
3787 opts |= PF_OPT_VERBOSE2;
3788 opts |= PF_OPT_VERBOSE;
3789 break;
3790 case 'x':
3791 debugopt = pfctl_lookup_option(optarg, debugopt_list);
3792 if (debugopt == NULL) {
3793 warnx("Unknown debug level '%s'", optarg);
3794 usage();
3795 }
3796 mode = O_RDWR;
3797 break;
3798 case 'z':
3799 opts |= PF_OPT_CLRRULECTRS;
3800 mode = O_RDWR;
3801 break;
3802 case 'h':
3803 /* FALLTHROUGH */
3804 default:
3805 usage();
3806 /* NOTREACHED */
3807 }
3808 }
3809
3810 if ((opts & PF_OPT_NODNS) && (opts & PF_OPT_USEDNS))
3811 errx(1, "-N and -r are mutually exclusive");
3812
3813 if ((tblcmdopt == NULL) ^ (tableopt == NULL) &&
3814 (tblcmdopt == NULL || *tblcmdopt != 'l'))
3815 usage();
3816
3817 if (tblcmdopt != NULL) {
3818 argc -= optind;
3819 argv += optind;
3820 ch = *tblcmdopt;
3821 if (ch == 'l') {
3822 loadopt |= PFCTL_FLAG_TABLE;
3823 tblcmdopt = NULL;
3824 } else
3825 mode = strchr("st", ch) ? O_RDONLY : O_RDWR;
3826 } else if (argc != optind) {
3827 warnx("unknown command line argument: %s ...", argv[optind]);
3828 usage();
3829 /* NOTREACHED */
3830 }
3831 if (loadopt == 0)
3832 loadopt = ~0;
3833
3834 memset(anchorname, 0, sizeof(anchorname));
3835 if (anchoropt != NULL) {
3836 int len = strlen(anchoropt);
3837
3838 if (anchoropt[0] == '\0')
3839 errx(1, "anchor name must not be empty");
3840 if (mode == O_RDONLY && showopt == 0 && tblcmdopt == NULL) {
3841 warnx("anchors apply to -f, -F, -s, and -T only");
3842 usage();
3843 }
3844 if (mode == O_RDWR && tblcmdopt == NULL &&
3845 (anchoropt[0] == '_' || strstr(anchoropt, "/_") != NULL))
3846 errx(1, "anchor names beginning with '_' cannot "
3847 "be modified from the command line");
3848
3849 if (len >= 1 && anchoropt[len - 1] == '*') {
3850 if (len >= 2 && anchoropt[len - 2] == '/')
3851 anchoropt[len - 2] = '\0';
3852 else
3853 anchoropt[len - 1] = '\0';
3854 opts |= PF_OPT_RECURSE;
3855 }
3856 if (strlcpy(anchorname, anchoropt,
3857 sizeof(anchorname)) >= sizeof(anchorname))
3858 errx(1, "anchor name '%s' too long",
3859 anchoropt);
3860 loadopt &= PFCTL_FLAG_FILTER|PFCTL_FLAG_NAT|PFCTL_FLAG_TABLE|PFCTL_FLAG_ETH;
3861 }
3862
3863 if ((opts & PF_OPT_NOACTION) == 0) {
3864 dev = open(pf_device, mode);
3865 if (dev == -1)
3866 err(1, "%s", pf_device);
3867 altqsupport = pfctl_test_altqsupport(dev, opts);
3868 } else {
3869 dev = open(pf_device, O_RDONLY);
3870 if (dev >= 0)
3871 opts |= PF_OPT_DUMMYACTION;
3872 /* turn off options */
3873 opts &= ~ (PF_OPT_DISABLE | PF_OPT_ENABLE);
3874 clearopt = debugopt = NULL;
3875 showopt = 0;
3876 #if !defined(ENABLE_ALTQ)
3877 altqsupport = 0;
3878 #else
3879 altqsupport = 1;
3880 #endif
3881 }
3882 pfh = pfctl_open(pf_device);
3883 if (pfh == NULL)
3884 err(1, "Failed to open netlink");
3885
3886 if ((opts & PF_OPT_NOACTION) == 0) {
3887 pfctl_read_limits(pfh);
3888 }
3889
3890 if (opts & PF_OPT_DISABLE)
3891 if (pfctl_disable(dev, opts))
3892 exit_val = 1;
3893
3894 if ((path = calloc(1, MAXPATHLEN)) == NULL)
3895 errx(1, "%s: calloc", __func__);
3896
3897 switch (showopt) {
3898 case SHOWOPT_NONE:
3899 break;
3900 case SHOWOPT_ANCHORS:
3901 pfctl_show_anchors(dev, opts, anchorname);
3902 if (opts & PF_OPT_VERBOSE2)
3903 printf("Ethernet:\n");
3904 pfctl_show_eth_anchors(dev, opts, anchorname);
3905 break;
3906 case SHOWOPT_RULES:
3907 pfctl_load_fingerprints(dev, opts);
3908 pfctl_show_rules(dev, path, opts, PFCTL_SHOW_RULES, anchorname,
3909 0, 0);
3910 break;
3911 case SHOWOPT_LABELS:
3912 pfctl_load_fingerprints(dev, opts);
3913 pfctl_show_rules(dev, path, opts, PFCTL_SHOW_LABELS, anchorname,
3914 0, 0);
3915 break;
3916 case SHOWOPT_NAT:
3917 pfctl_load_fingerprints(dev, opts);
3918 pfctl_show_nat(dev, path, opts, anchorname, 0, 0);
3919 break;
3920 case SHOWOPT_QUEUE:
3921 pfctl_show_altq(dev, ifaceopt, opts, opts & PF_OPT_VERBOSE2);
3922 break;
3923 case SHOWOPT_STATES:
3924 pfctl_show_states(dev, ifaceopt, opts);
3925 break;
3926 case SHOWOPT_SOURCES:
3927 pfctl_show_src_nodes(dev, opts);
3928 break;
3929 case SHOWOPT_INFO:
3930 pfctl_show_status(dev, opts);
3931 break;
3932 case SHOWOPT_RUNNING:
3933 exit_val = pfctl_show_running(dev);
3934 break;
3935 case SHOWOPT_TIMEOUTS:
3936 pfctl_show_timeouts(dev, opts);
3937 break;
3938 case SHOWOPT_MEMORY:
3939 pfctl_show_limits(dev, opts);
3940 break;
3941 case SHOWOPT_ETHER:
3942 pfctl_show_eth_rules(dev, path, opts, 0, anchorname, 0, 0);
3943 break;
3944 case SHOWOPT_ALL:
3945 opts |= PF_OPT_SHOWALL;
3946 pfctl_load_fingerprints(dev, opts);
3947
3948 pfctl_show_eth_rules(dev, path, opts, 0, anchorname, 0, 0);
3949
3950 pfctl_show_nat(dev, path, opts, anchorname, 0, 0);
3951 pfctl_show_rules(dev, path, opts, PFCTL_SHOW_RULES, anchorname,
3952 0, 0);
3953 pfctl_show_altq(dev, ifaceopt, opts, 0);
3954 pfctl_show_states(dev, ifaceopt, opts);
3955 pfctl_show_src_nodes(dev, opts);
3956 pfctl_show_status(dev, opts);
3957 pfctl_show_rules(dev, path, opts, PFCTL_SHOW_LABELS, anchorname,
3958 0, 0);
3959 pfctl_show_timeouts(dev, opts);
3960 pfctl_show_limits(dev, opts);
3961 pfctl_show_statelims(dev, PFCTL_SHOW_LABELS, opts);
3962 pfctl_show_sourcelims(dev, PFCTL_SHOW_LABELS, opts, idopt);
3963 pfctl_show_tables(anchorname, opts);
3964 pfctl_show_fingerprints(opts);
3965 break;
3966 case SHOWOPT_TABLES:
3967 if (opts & PF_OPT_RECURSE) {
3968 opts |= PF_OPT_CALLSHOW;
3969 pfctl_recurse(dev, opts, anchorname,
3970 pfctl_call_showtables);
3971 } else
3972 pfctl_show_tables(anchorname, opts);
3973 break;
3974 case SHOWOPT_OSFP:
3975 pfctl_load_fingerprints(dev, opts);
3976 pfctl_show_fingerprints(opts);
3977 break;
3978 case SHOWOPT_IFACES:
3979 pfctl_show_ifaces(ifaceopt, opts);
3980 break;
3981 case SHOWOPT_CREATORIDS:
3982 pfctl_show_creators(opts);
3983 break;
3984 case SHOWOPT_STATELIMS:
3985 pfctl_show_statelims(dev, PFCTL_SHOW_LABELS, opts);
3986 break;
3987 case SHOWOPT_SOURCELIMS:
3988 pfctl_show_sourcelims(dev, PFCTL_SHOW_LABELS, opts, idopt);
3989 break;
3990 }
3991
3992 if ((opts & PF_OPT_CLRRULECTRS) && showopt == 0) {
3993 pfctl_show_eth_rules(dev, path, opts, PFCTL_SHOW_NOTHING,
3994 anchorname, 0, 0);
3995 pfctl_show_rules(dev, path, opts, PFCTL_SHOW_NOTHING,
3996 anchorname, 0, 0);
3997 }
3998
3999 if (clearopt != NULL) {
4000 int mnr;
4001
4002 /* Check if anchor exists. */
4003 if ((pfctl_get_rulesets(pfh, anchorname, &mnr)) == ENOENT)
4004 errx(1, "No such anchor %s", anchorname);
4005
4006 switch (*clearopt) {
4007 case 'e':
4008 pfctl_flush_eth_rules(dev, opts, anchorname);
4009 break;
4010 case 'r':
4011 if (opts & PF_OPT_RECURSE)
4012 pfctl_recurse(dev, opts, anchorname,
4013 pfctl_call_clearrules);
4014 else
4015 pfctl_flush_rules(dev, opts, anchorname);
4016 break;
4017 case 'n':
4018 pfctl_flush_nat(dev, opts, anchorname);
4019 break;
4020 case 'q':
4021 pfctl_clear_altq(dev, opts);
4022 break;
4023 case 's':
4024 pfctl_clear_iface_states(dev, ifaceopt, opts);
4025 break;
4026 case 'S':
4027 pfctl_clear_src_nodes(dev, opts);
4028 break;
4029 case 'i':
4030 pfctl_clear_stats(pfh, opts);
4031 break;
4032 case 'a':
4033 if (ifaceopt) {
4034 warnx("don't specify an interface with -Fall");
4035 usage();
4036 /* NOTREACHED */
4037 }
4038 pfctl_flush_eth_rules(dev, opts, anchorname);
4039 pfctl_flush_rules(dev, opts, anchorname);
4040 pfctl_flush_nat(dev, opts, anchorname);
4041 if (opts & PF_OPT_RECURSE)
4042 pfctl_recurse(dev, opts, anchorname,
4043 pfctl_call_clearanchors);
4044 else {
4045 pfctl_do_clear_tables(anchorname, opts);
4046 pfctl_flush_rules(dev, opts, anchorname);
4047 }
4048 if (!*anchorname) {
4049 pfctl_clear_altq(dev, opts);
4050 pfctl_clear_iface_states(dev, ifaceopt, opts);
4051 pfctl_clear_src_nodes(dev, opts);
4052 pfctl_clear_stats(pfh, opts);
4053 pfctl_clear_fingerprints(dev, opts);
4054 pfctl_reset(dev, opts);
4055 }
4056 break;
4057 case 'o':
4058 pfctl_clear_fingerprints(dev, opts);
4059 break;
4060 case 'T':
4061 if ((opts & PF_OPT_RECURSE) == 0)
4062 pfctl_do_clear_tables(anchorname, opts);
4063 else
4064 pfctl_recurse(dev, opts, anchorname,
4065 pfctl_call_cleartables);
4066 break;
4067 case 'R':
4068 pfctl_reset(dev, opts);
4069 break;
4070 }
4071 }
4072 if (state_killers) {
4073 if (!strcmp(state_kill[0], "label"))
4074 pfctl_label_kill_states(dev, ifaceopt, opts);
4075 else if (!strcmp(state_kill[0], "id"))
4076 pfctl_id_kill_states(dev, ifaceopt, opts);
4077 else if (!strcmp(state_kill[0], "gateway"))
4078 pfctl_gateway_kill_states(dev, ifaceopt, opts);
4079 else if (!strcmp(state_kill[0], "key"))
4080 pfctl_key_kill_states(dev, ifaceopt, opts);
4081 else if (!strcmp(state_kill[0], "source"))
4082 pfctl_kill_source(dev, idopt, state_kill[1], opts);
4083 else
4084 pfctl_net_kill_states(dev, ifaceopt, opts);
4085 }
4086
4087 if (src_node_killers)
4088 pfctl_kill_src_nodes(dev, opts);
4089
4090 if (tblcmdopt != NULL) {
4091 exit_val = pfctl_table(argc, argv, tableopt,
4092 tblcmdopt, rulesopt, anchorname, opts);
4093 rulesopt = NULL;
4094 }
4095 if (optiopt != NULL) {
4096 switch (*optiopt) {
4097 case 'n':
4098 optimize = 0;
4099 break;
4100 case 'b':
4101 optimize |= PF_OPTIMIZE_BASIC;
4102 break;
4103 case 'o':
4104 case 'p':
4105 optimize |= PF_OPTIMIZE_PROFILE;
4106 break;
4107 }
4108 }
4109
4110 if ((rulesopt != NULL) && (loadopt & PFCTL_FLAG_OPTION) &&
4111 !anchorname[0] && !(opts & PF_OPT_NOACTION))
4112 pfctl_get_skip_ifaces();
4113
4114 if (rulesopt != NULL && !(opts & PF_OPT_MERGE) &&
4115 !anchorname[0] && (loadopt & PFCTL_FLAG_OPTION))
4116 if (pfctl_file_fingerprints(dev, opts, PF_OSFP_FILE))
4117 exit_val = 1;
4118
4119 if (rulesopt != NULL) {
4120 if (pfctl_rules(dev, rulesopt, opts, optimize,
4121 anchorname, NULL))
4122 exit_val = 1;
4123 }
4124
4125 if (opts & PF_OPT_ENABLE)
4126 if (pfctl_enable(dev, opts))
4127 exit_val = 1;
4128
4129 if (debugopt != NULL) {
4130 switch (*debugopt) {
4131 case 'n':
4132 pfctl_debug(dev, PF_DEBUG_NONE, opts);
4133 break;
4134 case 'u':
4135 pfctl_debug(dev, PF_DEBUG_URGENT, opts);
4136 break;
4137 case 'm':
4138 pfctl_debug(dev, PF_DEBUG_MISC, opts);
4139 break;
4140 case 'l':
4141 pfctl_debug(dev, PF_DEBUG_NOISY, opts);
4142 break;
4143 }
4144 }
4145
4146 /*
4147 * prevent pfctl_restore_limits() exit handler from restoring
4148 * pf(4) options settings on successful exit.
4149 */
4150 if (exit_val == 0) {
4151 close(dev);
4152 dev = -1;
4153 pfctl_close(pfh);
4154 pfh = NULL;
4155 }
4156
4157 return (exit_val);
4158 }
4159
4160 char *
pf_strerror(int errnum)4161 pf_strerror(int errnum)
4162 {
4163 switch (errnum) {
4164 case ESRCH:
4165 return "Table does not exist.";
4166 case EINVAL:
4167 case ENOENT:
4168 return "Anchor does not exist.";
4169 default:
4170 return strerror(errnum);
4171 }
4172 }
4173
4174 static inline int
pfctl_statelim_id_cmp(const struct pfctl_statelim * a,const struct pfctl_statelim * b)4175 pfctl_statelim_id_cmp(const struct pfctl_statelim *a,
4176 const struct pfctl_statelim *b)
4177 {
4178 uint32_t ida = a->ioc.id;
4179 uint32_t idb = b->ioc.id;
4180
4181 if (ida > idb)
4182 return (1);
4183 if (ida < idb)
4184 return (-1);
4185
4186 return (0);
4187 }
4188
4189 RB_GENERATE(pfctl_statelim_ids, pfctl_statelim, entry, pfctl_statelim_id_cmp);
4190
4191 static inline int
pfctl_statelim_nm_cmp(const struct pfctl_statelim * a,const struct pfctl_statelim * b)4192 pfctl_statelim_nm_cmp(const struct pfctl_statelim *a,
4193 const struct pfctl_statelim *b)
4194 {
4195 return (strcmp(a->ioc.name, b->ioc.name));
4196 }
4197
4198 RB_GENERATE(pfctl_statelim_nms, pfctl_statelim, entry, pfctl_statelim_nm_cmp);
4199
4200 int
pfctl_add_statelim(struct pfctl * pf,struct pfctl_statelim * stlim)4201 pfctl_add_statelim(struct pfctl *pf, struct pfctl_statelim *stlim)
4202 {
4203 struct pfctl_statelim *ostlim;
4204
4205 ostlim = RB_INSERT(pfctl_statelim_ids, &pf->statelim_ids, stlim);
4206 if (ostlim != NULL)
4207 return (-1);
4208
4209 ostlim = RB_INSERT(pfctl_statelim_nms, &pf->statelim_nms, stlim);
4210 if (ostlim != NULL) {
4211 RB_REMOVE(pfctl_statelim_ids, &pf->statelim_ids, stlim);
4212 return (-1);
4213 }
4214
4215 return (0);
4216 }
4217
4218 struct pfctl_statelim *
pfctl_get_statelim_id(struct pfctl * pf,uint32_t id)4219 pfctl_get_statelim_id(struct pfctl *pf, uint32_t id)
4220 {
4221 struct pfctl_statelim key;
4222
4223 key.ioc.id = id;
4224
4225 return (RB_FIND(pfctl_statelim_nms, &pf->statelim_nms, &key));
4226 }
4227
4228 struct pfctl_statelim *
pfctl_get_statelim_nm(struct pfctl * pf,const char * name)4229 pfctl_get_statelim_nm(struct pfctl *pf, const char *name)
4230 {
4231 struct pfctl_statelim key;
4232
4233 if (strlcpy(key.ioc.name, name, sizeof(key.ioc.name)) >=
4234 sizeof(key.ioc.name))
4235 return (NULL);
4236
4237 return (RB_FIND(pfctl_statelim_nms, &pf->statelim_nms, &key));
4238 }
4239
4240 static inline int
pfctl_sourcelim_id_cmp(const struct pfctl_sourcelim * a,const struct pfctl_sourcelim * b)4241 pfctl_sourcelim_id_cmp(const struct pfctl_sourcelim *a,
4242 const struct pfctl_sourcelim *b)
4243 {
4244 uint32_t ida = a->ioc.id;
4245 uint32_t idb = b->ioc.id;
4246
4247 if (ida > idb)
4248 return (1);
4249 if (ida < idb)
4250 return (-1);
4251
4252 return (0);
4253 }
4254
4255 RB_GENERATE(pfctl_sourcelim_ids, pfctl_sourcelim, entry,
4256 pfctl_sourcelim_id_cmp);
4257
4258 static inline int
pfctl_sourcelim_nm_cmp(const struct pfctl_sourcelim * a,const struct pfctl_sourcelim * b)4259 pfctl_sourcelim_nm_cmp(const struct pfctl_sourcelim *a,
4260 const struct pfctl_sourcelim *b)
4261 {
4262 return (strcmp(a->ioc.name, b->ioc.name));
4263 }
4264
4265 RB_GENERATE(pfctl_sourcelim_nms, pfctl_sourcelim, entry,
4266 pfctl_sourcelim_nm_cmp);
4267
4268 int
pfctl_add_sourcelim(struct pfctl * pf,struct pfctl_sourcelim * srlim)4269 pfctl_add_sourcelim(struct pfctl *pf, struct pfctl_sourcelim *srlim)
4270 {
4271 struct pfctl_sourcelim *osrlim;
4272
4273 osrlim = RB_INSERT(pfctl_sourcelim_ids, &pf->sourcelim_ids, srlim);
4274 if (osrlim != NULL)
4275 return (-1);
4276
4277 osrlim = RB_INSERT(pfctl_sourcelim_nms, &pf->sourcelim_nms, srlim);
4278 if (osrlim != NULL) {
4279 RB_REMOVE(pfctl_sourcelim_ids, &pf->sourcelim_ids, srlim);
4280 return (-1);
4281 }
4282
4283 return (0);
4284 }
4285
4286 struct pfctl_sourcelim *
pfctl_get_sourcelim_id(struct pfctl * pf,uint32_t id)4287 pfctl_get_sourcelim_id(struct pfctl *pf, uint32_t id)
4288 {
4289 struct pfctl_sourcelim key;
4290
4291 key.ioc.id = id;
4292
4293 return (RB_FIND(pfctl_sourcelim_nms, &pf->sourcelim_nms, &key));
4294 }
4295
4296 struct pfctl_sourcelim *
pfctl_get_sourcelim_nm(struct pfctl * pf,const char * name)4297 pfctl_get_sourcelim_nm(struct pfctl *pf, const char *name)
4298 {
4299 struct pfctl_sourcelim key;
4300
4301 if (strlcpy(key.ioc.name, name, sizeof(key.ioc.name)) >=
4302 sizeof(key.ioc.name))
4303 return (NULL);
4304
4305 return (RB_FIND(pfctl_sourcelim_nms, &pf->sourcelim_nms, &key));
4306 }
4307