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 { "ethernet", 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 (error = pfctl_optimize_ruleset(pf, rs)) != 0)
2518 goto error;
2519
2520 while ((r = TAILQ_FIRST(rs->rules[rs_num].active.ptr)) != NULL) {
2521 TAILQ_REMOVE(rs->rules[rs_num].active.ptr, r, entries);
2522
2523 for (int i = 0; i < PF_RULE_MAX_LABEL_COUNT; i++)
2524 expand_label(r->label[i], PF_RULE_LABEL_SIZE, r);
2525 expand_label(r->tagname, PF_TAG_NAME_SIZE, r);
2526 expand_label(r->match_tagname, PF_TAG_NAME_SIZE, r);
2527
2528 if ((error = pfctl_load_rule(pf, path, r, depth)))
2529 goto error;
2530 if (r->anchor) {
2531 if ((error = pfctl_load_ruleset(pf, path,
2532 &r->anchor->ruleset, rs_num, depth + 1)))
2533 goto error;
2534 if ((error = pfctl_load_tables(pf, path, r->anchor, rs_num)))
2535 goto error;
2536 } else if (pf->opts & PF_OPT_VERBOSE)
2537 printf("\n");
2538 free(r);
2539 }
2540 if (brace && pf->opts & PF_OPT_VERBOSE) {
2541 INDENT(depth - 1, (pf->opts & PF_OPT_VERBOSE));
2542 printf("}\n");
2543 }
2544 path[len] = '\0';
2545 return (0);
2546
2547 error:
2548 path[len] = '\0';
2549 return (error);
2550
2551 }
2552
2553 int
pfctl_load_rule(struct pfctl * pf,char * path,struct pfctl_rule * r,int depth)2554 pfctl_load_rule(struct pfctl *pf, char *path, struct pfctl_rule *r, int depth)
2555 {
2556 u_int8_t rs_num = pf_get_ruleset_number(r->action);
2557 char *name;
2558 uint32_t ticket;
2559 char anchor[PF_ANCHOR_NAME_SIZE];
2560 int len = strlen(path);
2561 int error;
2562 bool was_present;
2563
2564 /* set up anchor before adding to path for anchor_call */
2565 if ((pf->opts & PF_OPT_NOACTION) == 0) {
2566 if (pf->trans == NULL)
2567 errx(1, "pfctl_load_rule: no transaction");
2568 ticket = pfctl_get_ticket(pf->trans, rs_num, path);
2569 if (rs_num == PF_RULESET_FILTER)
2570 pf->anchor->ruleset.tticket = ticket;
2571 }
2572 if (strlcpy(anchor, path, sizeof(anchor)) >= sizeof(anchor))
2573 errx(1, "pfctl_load_rule: strlcpy");
2574
2575 if (r->anchor) {
2576 if (r->anchor->match) {
2577 if (path[0])
2578 snprintf(&path[len], MAXPATHLEN - len,
2579 "/%s", r->anchor->name);
2580 else
2581 snprintf(&path[len], MAXPATHLEN - len,
2582 "%s", r->anchor->name);
2583 name = r->anchor->name;
2584 } else
2585 name = r->anchor->path;
2586 } else
2587 name = "";
2588
2589 was_present = false;
2590 if ((pf->opts & PF_OPT_NOACTION) == 0) {
2591 if ((pf->opts & PF_OPT_NOACTION) == 0) {
2592 if ((error = pfctl_begin_addrs(pf->h,
2593 &pf->paddr.ticket)) != 0)
2594 errc(1, error, "DIOCBEGINADDRS");
2595 }
2596
2597 if (pfctl_add_pool(pf, &r->rdr, PF_RDR))
2598 return (1);
2599 if (pfctl_add_pool(pf, &r->nat, PF_NAT))
2600 return (1);
2601 if (pfctl_add_pool(pf, &r->route, PF_RT))
2602 return (1);
2603 error = pfctl_add_rule_h(pf->h, r, anchor, name, ticket,
2604 pf->paddr.ticket);
2605 switch (error) {
2606 case 0:
2607 /* things worked, do nothing */
2608 break;
2609 case EEXIST:
2610 /* an identical rule is already present */
2611 was_present = true;
2612 break;
2613 default:
2614 errc(1, error, "DIOCADDRULE");
2615 }
2616 }
2617
2618 if (pf->opts & PF_OPT_VERBOSE) {
2619 INDENT(depth, !(pf->opts & PF_OPT_VERBOSE2));
2620 print_rule(r, name,
2621 pf->opts & PF_OPT_VERBOSE2,
2622 pf->opts & PF_OPT_NUMERIC);
2623 if (was_present)
2624 printf(" -- rule was already present");
2625 }
2626 path[len] = '\0';
2627 pfctl_clear_pool(&r->rdr);
2628 pfctl_clear_pool(&r->nat);
2629 return (0);
2630 }
2631
2632 int
pfctl_add_altq(struct pfctl * pf,struct pf_altq * a)2633 pfctl_add_altq(struct pfctl *pf, struct pf_altq *a)
2634 {
2635 if (altqsupport &&
2636 (loadopt & PFCTL_FLAG_ALTQ) != 0) {
2637 memcpy(&pf->paltq->altq, a, sizeof(struct pf_altq));
2638 if ((pf->opts & PF_OPT_NOACTION) == 0) {
2639 if (ioctl(pf->dev, DIOCADDALTQ, pf->paltq)) {
2640 if (errno == ENXIO)
2641 errx(1, "qtype not configured");
2642 else if (errno == ENODEV)
2643 errx(1, "%s: driver does not support "
2644 "altq", a->ifname);
2645 else
2646 err(1, "DIOCADDALTQ");
2647 }
2648 }
2649 pfaltq_store(&pf->paltq->altq);
2650 }
2651 return (0);
2652 }
2653
2654 int
pfctl_rules(int dev,char * filename,int opts,int optimize,char * anchorname,struct pfr_buffer * trans)2655 pfctl_rules(int dev, char *filename, int opts, int optimize,
2656 char *anchorname, struct pfr_buffer *trans)
2657 {
2658 #define ERR(...) do { warn(__VA_ARGS__); goto _error; } while(0)
2659 #define ERRX(...) do { warnx(__VA_ARGS__); goto _error; } while(0)
2660
2661 struct pfr_buffer *t, buf;
2662 struct pfioc_altq pa;
2663 struct pfctl pf;
2664 struct pfctl_ruleset *rs;
2665 struct pfctl_eth_ruleset *ethrs;
2666 struct pfr_table trs;
2667 char *path = NULL;
2668 int osize;
2669
2670 RB_INIT(&pf_anchors);
2671 memset(&pf_main_anchor, 0, sizeof(pf_main_anchor));
2672 pf_init_ruleset(&pf_main_anchor.ruleset);
2673 memset(&pf, 0, sizeof(pf));
2674 memset(&trs, 0, sizeof(trs));
2675 pf_main_anchor.ruleset.anchor = &pf_main_anchor;
2676
2677 memset(&pf_eth_main_anchor, 0, sizeof(pf_eth_main_anchor));
2678 pf_init_eth_ruleset(&pf_eth_main_anchor.ruleset);
2679 pf_eth_main_anchor.ruleset.anchor = &pf_eth_main_anchor;
2680
2681 if (trans == NULL) {
2682 bzero(&buf, sizeof(buf));
2683 buf.pfrb_type = PFRB_TRANS;
2684 pf.trans = &buf;
2685 t = &buf;
2686 osize = 0;
2687 } else {
2688 t = trans;
2689 osize = t->pfrb_size;
2690 }
2691
2692 memset(&pa, 0, sizeof(pa));
2693 pa.version = PFIOC_ALTQ_VERSION;
2694 memset(&pf, 0, sizeof(pf));
2695 memset(&trs, 0, sizeof(trs));
2696 if ((path = calloc(1, MAXPATHLEN)) == NULL)
2697 ERRX("%s: calloc", __func__);
2698 if (strlcpy(trs.pfrt_anchor, anchorname,
2699 sizeof(trs.pfrt_anchor)) >= sizeof(trs.pfrt_anchor))
2700 ERRX("%s: strlcpy", __func__);
2701 pf.dev = dev;
2702 pf.h = pfh;
2703 pf.opts = opts;
2704 pf.optimize = optimize;
2705 pf.loadopt = loadopt;
2706
2707 RB_INIT(&pf.statelim_ids);
2708 RB_INIT(&pf.statelim_nms);
2709 RB_INIT(&pf.sourcelim_ids);
2710 RB_INIT(&pf.sourcelim_nms);
2711
2712 /* non-brace anchor, create without resolving the path */
2713 if ((pf.anchor = calloc(1, sizeof(*pf.anchor))) == NULL)
2714 ERRX("%s: calloc", __func__);
2715 rs = &pf.anchor->ruleset;
2716 pf_init_ruleset(rs);
2717 rs->anchor = pf.anchor;
2718 if (strlcpy(pf.anchor->path, anchorname,
2719 sizeof(pf.anchor->path)) >= sizeof(pf.anchor->path))
2720 errx(1, "%s: strlcpy", __func__);
2721 if (strlcpy(pf.anchor->name, anchorname,
2722 sizeof(pf.anchor->name)) >= sizeof(pf.anchor->name))
2723 errx(1, "%s: strlcpy", __func__);
2724
2725
2726 pf.astack[0] = pf.anchor;
2727 pf.asd = 0;
2728 if (anchorname[0])
2729 pf.loadopt &= ~PFCTL_FLAG_ALTQ;
2730 pf.paltq = &pa;
2731 pf.trans = t;
2732 pfctl_init_options(&pf);
2733
2734 /* Set up ethernet anchor */
2735 if ((pf.eanchor = calloc(1, sizeof(*pf.eanchor))) == NULL)
2736 ERRX("%s: calloc", __func__);
2737
2738 if (strlcpy(pf.eanchor->path, anchorname,
2739 sizeof(pf.eanchor->path)) >= sizeof(pf.eanchor->path))
2740 errx(1, "%s: strlcpy", __func__);
2741 if (strlcpy(pf.eanchor->name, anchorname,
2742 sizeof(pf.eanchor->name)) >= sizeof(pf.eanchor->name))
2743 errx(1, "%s: strlcpy", __func__);
2744
2745 ethrs = &pf.eanchor->ruleset;
2746 pf_init_eth_ruleset(ethrs);
2747 ethrs->anchor = pf.eanchor;
2748 pf.eastack[0] = pf.eanchor;
2749
2750 if ((opts & PF_OPT_NOACTION) == 0) {
2751 /*
2752 * XXX For the time being we need to open transactions for
2753 * the main ruleset before parsing, because tables are still
2754 * loaded at parse time.
2755 */
2756 if (pfctl_ruleset_trans(&pf, anchorname, pf.anchor, true))
2757 ERRX("%s", __func__);
2758 if (pf.loadopt & PFCTL_FLAG_ETH)
2759 pf.eth_ticket = pfctl_get_ticket(t, PF_RULESET_ETH, anchorname);
2760 if (altqsupport && (pf.loadopt & PFCTL_FLAG_ALTQ))
2761 pa.ticket =
2762 pfctl_get_ticket(t, PF_RULESET_ALTQ, anchorname);
2763 if (pf.loadopt & PFCTL_FLAG_TABLE)
2764 pf.astack[0]->ruleset.tticket =
2765 pfctl_get_ticket(t, PF_RULESET_TABLE, anchorname);
2766 }
2767
2768 if (parse_config(filename, &pf) < 0) {
2769 if ((opts & PF_OPT_NOACTION) == 0)
2770 ERRX("Syntax error in config file: "
2771 "pf rules not loaded");
2772 else
2773 goto _error;
2774 }
2775 if (loadopt & PFCTL_FLAG_OPTION)
2776 pfctl_adjust_skip_ifaces(&pf);
2777
2778 if (anchorname[0] == '\0' && pf.loadopt & PFCTL_FLAG_FILTER) {
2779 pfctl_load_statelims(&pf);
2780 pfctl_load_sourcelims(&pf);
2781 }
2782
2783 if ((pf.loadopt & PFCTL_FLAG_FILTER &&
2784 (pfctl_load_ruleset(&pf, path, rs, PF_RULESET_SCRUB, 0))) ||
2785 (pf.loadopt & PFCTL_FLAG_ETH &&
2786 (pfctl_load_eth_ruleset(&pf, path, ethrs, 0))) ||
2787 (pf.loadopt & PFCTL_FLAG_NAT &&
2788 (pfctl_load_ruleset(&pf, path, rs, PF_RULESET_NAT, 0) ||
2789 pfctl_load_ruleset(&pf, path, rs, PF_RULESET_RDR, 0) ||
2790 pfctl_load_ruleset(&pf, path, rs, PF_RULESET_BINAT, 0))) ||
2791 (pf.loadopt & PFCTL_FLAG_FILTER &&
2792 pfctl_load_ruleset(&pf, path, rs, PF_RULESET_FILTER, 0))) {
2793 if ((opts & PF_OPT_NOACTION) == 0)
2794 ERRX("Unable to load rules into kernel");
2795 else
2796 goto _error;
2797 }
2798
2799 if ((altqsupport && (pf.loadopt & PFCTL_FLAG_ALTQ) != 0))
2800 if (check_commit_altq(dev, opts) != 0)
2801 ERRX("errors in altq config");
2802
2803 if (trans == NULL) {
2804 /* process "load anchor" directives */
2805 if (pfctl_load_anchors(dev, &pf) == -1)
2806 ERRX("load anchors");
2807
2808 if ((opts & PF_OPT_NOACTION) == 0) {
2809 if (!anchorname[0] && pfctl_load_options(&pf))
2810 goto _error;
2811 if (pfctl_trans(dev, t, DIOCXCOMMIT, osize))
2812 ERR("DIOCXCOMMIT");
2813 }
2814 }
2815 free(path);
2816 return (0);
2817
2818 _error:
2819 if (trans == NULL) { /* main ruleset */
2820 if ((opts & PF_OPT_NOACTION) == 0)
2821 if (pfctl_trans(dev, t, DIOCXROLLBACK, osize))
2822 err(1, "DIOCXROLLBACK");
2823 exit(1);
2824 } else { /* sub ruleset */
2825 free(path);
2826 return (-1);
2827 }
2828
2829 #undef ERR
2830 #undef ERRX
2831 }
2832
2833 FILE *
pfctl_fopen(const char * name,const char * mode)2834 pfctl_fopen(const char *name, const char *mode)
2835 {
2836 struct stat st;
2837 FILE *fp;
2838
2839 fp = fopen(name, mode);
2840 if (fp == NULL)
2841 return (NULL);
2842 if (fstat(fileno(fp), &st)) {
2843 fclose(fp);
2844 return (NULL);
2845 }
2846 if (S_ISDIR(st.st_mode)) {
2847 fclose(fp);
2848 errno = EISDIR;
2849 return (NULL);
2850 }
2851 return (fp);
2852 }
2853
2854 void
pfctl_init_options(struct pfctl * pf)2855 pfctl_init_options(struct pfctl *pf)
2856 {
2857
2858 pf->timeout[PFTM_TCP_FIRST_PACKET] = PFTM_TCP_FIRST_PACKET_VAL;
2859 pf->timeout[PFTM_TCP_OPENING] = PFTM_TCP_OPENING_VAL;
2860 pf->timeout[PFTM_TCP_ESTABLISHED] = PFTM_TCP_ESTABLISHED_VAL;
2861 pf->timeout[PFTM_TCP_CLOSING] = PFTM_TCP_CLOSING_VAL;
2862 pf->timeout[PFTM_TCP_FIN_WAIT] = PFTM_TCP_FIN_WAIT_VAL;
2863 pf->timeout[PFTM_TCP_CLOSED] = PFTM_TCP_CLOSED_VAL;
2864 pf->timeout[PFTM_SCTP_FIRST_PACKET] = PFTM_TCP_FIRST_PACKET_VAL;
2865 pf->timeout[PFTM_SCTP_OPENING] = PFTM_TCP_OPENING_VAL;
2866 pf->timeout[PFTM_SCTP_ESTABLISHED] = PFTM_TCP_ESTABLISHED_VAL;
2867 pf->timeout[PFTM_SCTP_CLOSING] = PFTM_TCP_CLOSING_VAL;
2868 pf->timeout[PFTM_SCTP_CLOSED] = PFTM_TCP_CLOSED_VAL;
2869 pf->timeout[PFTM_UDP_FIRST_PACKET] = PFTM_UDP_FIRST_PACKET_VAL;
2870 pf->timeout[PFTM_UDP_SINGLE] = PFTM_UDP_SINGLE_VAL;
2871 pf->timeout[PFTM_UDP_MULTIPLE] = PFTM_UDP_MULTIPLE_VAL;
2872 pf->timeout[PFTM_ICMP_FIRST_PACKET] = PFTM_ICMP_FIRST_PACKET_VAL;
2873 pf->timeout[PFTM_ICMP_ERROR_REPLY] = PFTM_ICMP_ERROR_REPLY_VAL;
2874 pf->timeout[PFTM_OTHER_FIRST_PACKET] = PFTM_OTHER_FIRST_PACKET_VAL;
2875 pf->timeout[PFTM_OTHER_SINGLE] = PFTM_OTHER_SINGLE_VAL;
2876 pf->timeout[PFTM_OTHER_MULTIPLE] = PFTM_OTHER_MULTIPLE_VAL;
2877 pf->timeout[PFTM_FRAG] = PFTM_FRAG_VAL;
2878 pf->timeout[PFTM_INTERVAL] = PFTM_INTERVAL_VAL;
2879 pf->timeout[PFTM_SRC_NODE] = PFTM_SRC_NODE_VAL;
2880 pf->timeout[PFTM_TS_DIFF] = PFTM_TS_DIFF_VAL;
2881 pf->timeout[PFTM_ADAPTIVE_START] = PFSTATE_ADAPT_START;
2882 pf->timeout[PFTM_ADAPTIVE_END] = PFSTATE_ADAPT_END;
2883
2884 pf->limit[PF_LIMIT_STATES] = PFSTATE_HIWAT;
2885 pf->limit[PF_LIMIT_FRAGS] = PFFRAG_FRENT_HIWAT;
2886
2887 pf->limit[PF_LIMIT_SRC_NODES] = (limit_curr[PF_LIMIT_SRC_NODES] == 0) ?
2888 PFSNODE_HIWAT : limit_curr[PF_LIMIT_SRC_NODES];
2889 pf->limit[PF_LIMIT_TABLE_ENTRIES] =
2890 (limit_curr[PF_LIMIT_TABLE_ENTRIES] == 0) ?
2891 PFR_KENTRY_HIWAT : limit_curr[PF_LIMIT_TABLE_ENTRIES];
2892 pf->limit[PF_LIMIT_ANCHORS] = (limit_curr[PF_LIMIT_ANCHORS] == 0) ?
2893 PF_ANCHOR_HIWAT : limit_curr[PF_LIMIT_ANCHORS];
2894
2895 pf->debug = PF_DEBUG_URGENT;
2896 pf->reassemble = 0;
2897
2898 pf->syncookies = false;
2899 pf->syncookieswat[0] = PF_SYNCOOKIES_LOWATPCT;
2900 pf->syncookieswat[1] = PF_SYNCOOKIES_HIWATPCT;
2901 }
2902
2903 int
pfctl_load_options(struct pfctl * pf)2904 pfctl_load_options(struct pfctl *pf)
2905 {
2906 int i, error = 0;
2907
2908 if ((loadopt & PFCTL_FLAG_OPTION) == 0)
2909 return (0);
2910
2911 /* load limits */
2912 for (i = 0; i < PF_LIMIT_MAX; i++) {
2913 if ((pf->opts & PF_OPT_MERGE) && !pf->limit_set[i])
2914 continue;
2915 if (pfctl_load_limit(pf, i, pf->limit[i]))
2916 error = 1;
2917 }
2918
2919 /*
2920 * If we've set the states limit, but haven't explicitly set adaptive
2921 * timeouts, do it now with a start of 60% and end of 120%.
2922 */
2923 if (pf->limit_set[PF_LIMIT_STATES] &&
2924 !pf->timeout_set[PFTM_ADAPTIVE_START] &&
2925 !pf->timeout_set[PFTM_ADAPTIVE_END]) {
2926 pf->timeout[PFTM_ADAPTIVE_START] =
2927 (pf->limit[PF_LIMIT_STATES] / 10) * 6;
2928 pf->timeout_set[PFTM_ADAPTIVE_START] = 1;
2929 pf->timeout[PFTM_ADAPTIVE_END] =
2930 (pf->limit[PF_LIMIT_STATES] / 10) * 12;
2931 pf->timeout_set[PFTM_ADAPTIVE_END] = 1;
2932 }
2933
2934 /* load timeouts */
2935 for (i = 0; i < PFTM_MAX; i++) {
2936 if ((pf->opts & PF_OPT_MERGE) && !pf->timeout_set[i])
2937 continue;
2938 if (pfctl_load_timeout(pf, i, pf->timeout[i]))
2939 error = 1;
2940 }
2941
2942 /* load debug */
2943 if (!(pf->opts & PF_OPT_MERGE) || pf->debug_set)
2944 if (pfctl_load_debug(pf, pf->debug))
2945 error = 1;
2946
2947 /* load logif */
2948 if (!(pf->opts & PF_OPT_MERGE) || pf->ifname_set)
2949 if (pfctl_load_logif(pf, pf->ifname))
2950 error = 1;
2951
2952 /* load hostid */
2953 if (!(pf->opts & PF_OPT_MERGE) || pf->hostid_set)
2954 if (pfctl_load_hostid(pf, pf->hostid))
2955 error = 1;
2956
2957 /* load reassembly settings */
2958 if (!(pf->opts & PF_OPT_MERGE) || pf->reass_set)
2959 if (pfctl_load_reassembly(pf, pf->reassemble))
2960 error = 1;
2961
2962 /* load keepcounters */
2963 if (pfctl_set_keepcounters(pf->dev, pf->keep_counters))
2964 error = 1;
2965
2966 /* load syncookies settings */
2967 if (pfctl_load_syncookies(pf, pf->syncookies))
2968 error = 1;
2969
2970 return (error);
2971 }
2972
2973 int
pfctl_apply_limit(struct pfctl * pf,const char * opt,unsigned int limit)2974 pfctl_apply_limit(struct pfctl *pf, const char *opt, unsigned int limit)
2975 {
2976 int i;
2977
2978
2979 for (i = 0; pf_limits[i].name; i++) {
2980 if (strcasecmp(opt, pf_limits[i].name) == 0) {
2981 pf->limit[pf_limits[i].index] = limit;
2982 pf->limit_set[pf_limits[i].index] = 1;
2983 break;
2984 }
2985 }
2986 if (pf_limits[i].name == NULL) {
2987 warnx("Bad pool name.");
2988 return (1);
2989 }
2990
2991 if (pf->opts & PF_OPT_VERBOSE)
2992 printf("set limit %s %d\n", opt, limit);
2993
2994 if ((pf->opts & PF_OPT_NOACTION) == 0)
2995 pfctl_load_options(pf);
2996
2997 return (0);
2998 }
2999
3000 int
pfctl_load_limit(struct pfctl * pf,unsigned int index,unsigned int limit)3001 pfctl_load_limit(struct pfctl *pf, unsigned int index, unsigned int limit)
3002 {
3003 static int restore_limit_handler_armed = 0;
3004
3005 if (pfctl_set_limit(pf->h, index, limit)) {
3006 if (errno == EBUSY)
3007 warnx("Current pool size exceeds requested %s limit %u",
3008 pf_limits[index].name, limit);
3009 else
3010 warnx("Cannot set %s limit to %u",
3011 pf_limits[index].name, limit);
3012 return (1);
3013 } else if (restore_limit_handler_armed == 0) {
3014 atexit(pfctl_restore_limits);
3015 restore_limit_handler_armed = 1;
3016 }
3017 return (0);
3018 }
3019
3020 int
pfctl_apply_timeout(struct pfctl * pf,const char * opt,int seconds,int quiet)3021 pfctl_apply_timeout(struct pfctl *pf, const char *opt, int seconds, int quiet)
3022 {
3023 int i;
3024
3025 if ((loadopt & PFCTL_FLAG_OPTION) == 0)
3026 return (0);
3027
3028 for (i = 0; pf_timeouts[i].name; i++) {
3029 if (strcasecmp(opt, pf_timeouts[i].name) == 0) {
3030 pf->timeout[pf_timeouts[i].timeout] = seconds;
3031 pf->timeout_set[pf_timeouts[i].timeout] = 1;
3032 break;
3033 }
3034 }
3035
3036 if (pf_timeouts[i].name == NULL) {
3037 warnx("Bad timeout name.");
3038 return (1);
3039 }
3040
3041
3042 if (pf->opts & PF_OPT_VERBOSE && ! quiet)
3043 printf("set timeout %s %d\n", opt, seconds);
3044
3045 return (0);
3046 }
3047
3048 int
pfctl_load_timeout(struct pfctl * pf,unsigned int timeout,unsigned int seconds)3049 pfctl_load_timeout(struct pfctl *pf, unsigned int timeout, unsigned int seconds)
3050 {
3051 if (pfctl_set_timeout(pf->h, timeout, seconds)) {
3052 warnx("DIOCSETTIMEOUT");
3053 return (1);
3054 }
3055 return (0);
3056 }
3057
3058 int
pfctl_set_reassembly(struct pfctl * pf,int on,int nodf)3059 pfctl_set_reassembly(struct pfctl *pf, int on, int nodf)
3060 {
3061 if ((loadopt & PFCTL_FLAG_OPTION) == 0)
3062 return (0);
3063
3064 pf->reass_set = 1;
3065 if (on) {
3066 pf->reassemble = PF_REASS_ENABLED;
3067 if (nodf)
3068 pf->reassemble |= PF_REASS_NODF;
3069 } else {
3070 pf->reassemble = 0;
3071 }
3072
3073 if (pf->opts & PF_OPT_VERBOSE)
3074 printf("set reassemble %s %s\n", on ? "yes" : "no",
3075 nodf ? "no-df" : "");
3076
3077 return (0);
3078 }
3079
3080 int
pfctl_set_optimization(struct pfctl * pf,const char * opt)3081 pfctl_set_optimization(struct pfctl *pf, const char *opt)
3082 {
3083 const struct pf_hint *hint;
3084 int i, r;
3085
3086 if ((loadopt & PFCTL_FLAG_OPTION) == 0)
3087 return (0);
3088
3089 for (i = 0; pf_hints[i].name; i++)
3090 if (strcasecmp(opt, pf_hints[i].name) == 0)
3091 break;
3092
3093 hint = pf_hints[i].hint;
3094 if (hint == NULL) {
3095 warnx("invalid state timeouts optimization");
3096 return (1);
3097 }
3098
3099 for (i = 0; hint[i].name; i++)
3100 if ((r = pfctl_apply_timeout(pf, hint[i].name,
3101 hint[i].timeout, 1)))
3102 return (r);
3103
3104 if (pf->opts & PF_OPT_VERBOSE)
3105 printf("set optimization %s\n", opt);
3106
3107 return (0);
3108 }
3109
3110 int
pfctl_set_logif(struct pfctl * pf,char * ifname)3111 pfctl_set_logif(struct pfctl *pf, char *ifname)
3112 {
3113
3114 if ((loadopt & PFCTL_FLAG_OPTION) == 0)
3115 return (0);
3116
3117 if (!strcmp(ifname, "none")) {
3118 free(pf->ifname);
3119 pf->ifname = NULL;
3120 } else {
3121 pf->ifname = strdup(ifname);
3122 if (!pf->ifname)
3123 errx(1, "pfctl_set_logif: strdup");
3124 }
3125 pf->ifname_set = 1;
3126
3127 if (pf->opts & PF_OPT_VERBOSE)
3128 printf("set loginterface %s\n", ifname);
3129
3130 return (0);
3131 }
3132
3133 int
pfctl_load_logif(struct pfctl * pf,char * ifname)3134 pfctl_load_logif(struct pfctl *pf, char *ifname)
3135 {
3136 if (ifname != NULL && strlen(ifname) >= IFNAMSIZ) {
3137 warnx("pfctl_load_logif: strlcpy");
3138 return (1);
3139 }
3140 return (pfctl_set_statusif(pfh, ifname ? ifname : ""));
3141 }
3142
3143 void
pfctl_set_hostid(struct pfctl * pf,u_int32_t hostid)3144 pfctl_set_hostid(struct pfctl *pf, u_int32_t hostid)
3145 {
3146 if ((loadopt & PFCTL_FLAG_OPTION) == 0)
3147 return;
3148
3149 HTONL(hostid);
3150
3151 pf->hostid = hostid;
3152 pf->hostid_set = 1;
3153
3154 if (pf->opts & PF_OPT_VERBOSE)
3155 printf("set hostid 0x%08x\n", ntohl(hostid));
3156 }
3157
3158 int
pfctl_load_hostid(struct pfctl * pf,u_int32_t hostid)3159 pfctl_load_hostid(struct pfctl *pf, u_int32_t hostid)
3160 {
3161 if (ioctl(dev, DIOCSETHOSTID, &hostid)) {
3162 warnx("DIOCSETHOSTID");
3163 return (1);
3164 }
3165 return (0);
3166 }
3167
3168 int
pfctl_load_reassembly(struct pfctl * pf,u_int32_t reassembly)3169 pfctl_load_reassembly(struct pfctl *pf, u_int32_t reassembly)
3170 {
3171 if (ioctl(dev, DIOCSETREASS, &reassembly)) {
3172 warnx("DIOCSETREASS");
3173 return (1);
3174 }
3175 return (0);
3176 }
3177
3178 int
pfctl_load_syncookies(struct pfctl * pf,u_int8_t val)3179 pfctl_load_syncookies(struct pfctl *pf, u_int8_t val)
3180 {
3181 struct pfctl_syncookies cookies;
3182
3183 bzero(&cookies, sizeof(cookies));
3184
3185 cookies.mode = val;
3186 cookies.lowwater = pf->syncookieswat[0];
3187 cookies.highwater = pf->syncookieswat[1];
3188
3189 if (pfctl_set_syncookies(dev, &cookies)) {
3190 warnx("DIOCSETSYNCOOKIES");
3191 return (1);
3192 }
3193 return (0);
3194 }
3195
3196 int
pfctl_cfg_syncookies(struct pfctl * pf,uint8_t val,struct pfctl_watermarks * w)3197 pfctl_cfg_syncookies(struct pfctl *pf, uint8_t val, struct pfctl_watermarks *w)
3198 {
3199 if (val != PF_SYNCOOKIES_ADAPTIVE && w != NULL) {
3200 warnx("syncookies start/end only apply to adaptive");
3201 return (1);
3202 }
3203 if (val == PF_SYNCOOKIES_ADAPTIVE && w != NULL) {
3204 if (!w->hi)
3205 w->hi = PF_SYNCOOKIES_HIWATPCT;
3206 if (!w->lo)
3207 w->lo = w->hi / 2;
3208 if (w->lo >= w->hi) {
3209 warnx("start must be higher than end");
3210 return (1);
3211 }
3212 pf->syncookieswat[0] = w->lo;
3213 pf->syncookieswat[1] = w->hi;
3214 pf->syncookieswat_set = 1;
3215 }
3216
3217 if (pf->opts & PF_OPT_VERBOSE) {
3218 if (val == PF_SYNCOOKIES_NEVER)
3219 printf("set syncookies never\n");
3220 else if (val == PF_SYNCOOKIES_ALWAYS)
3221 printf("set syncookies always\n");
3222 else if (val == PF_SYNCOOKIES_ADAPTIVE) {
3223 if (pf->syncookieswat_set)
3224 printf("set syncookies adaptive (start %u%%, "
3225 "end %u%%)\n", pf->syncookieswat[1],
3226 pf->syncookieswat[0]);
3227 else
3228 printf("set syncookies adaptive\n");
3229 } else { /* cannot happen */
3230 warnx("king bula ate all syncookies");
3231 return (1);
3232 }
3233 }
3234
3235 pf->syncookies = val;
3236 return (0);
3237 }
3238
3239 int
pfctl_do_set_debug(struct pfctl * pf,char * d)3240 pfctl_do_set_debug(struct pfctl *pf, char *d)
3241 {
3242 u_int32_t level;
3243 int ret;
3244
3245 if ((loadopt & PFCTL_FLAG_OPTION) == 0)
3246 return (0);
3247
3248 if (!strcmp(d, "none"))
3249 pf->debug = PF_DEBUG_NONE;
3250 else if (!strcmp(d, "urgent"))
3251 pf->debug = PF_DEBUG_URGENT;
3252 else if (!strcmp(d, "misc"))
3253 pf->debug = PF_DEBUG_MISC;
3254 else if (!strcmp(d, "loud"))
3255 pf->debug = PF_DEBUG_NOISY;
3256 else {
3257 warnx("unknown debug level \"%s\"", d);
3258 return (-1);
3259 }
3260
3261 pf->debug_set = 1;
3262 level = pf->debug;
3263
3264 if ((pf->opts & PF_OPT_NOACTION) == 0)
3265 if ((ret = pfctl_set_debug(pfh, level)) != 0)
3266 errc(1, ret, "DIOCSETDEBUG");
3267
3268 if (pf->opts & PF_OPT_VERBOSE)
3269 printf("set debug %s\n", d);
3270
3271 return (0);
3272 }
3273
3274 int
pfctl_load_debug(struct pfctl * pf,unsigned int level)3275 pfctl_load_debug(struct pfctl *pf, unsigned int level)
3276 {
3277 if (pfctl_set_debug(pf->h, level)) {
3278 warnx("DIOCSETDEBUG");
3279 return (1);
3280 }
3281 return (0);
3282 }
3283
3284 int
pfctl_set_interface_flags(struct pfctl * pf,char * ifname,int flags,int how)3285 pfctl_set_interface_flags(struct pfctl *pf, char *ifname, int flags, int how)
3286 {
3287 struct pfioc_iface pi;
3288 struct node_host *h = NULL, *n = NULL;
3289
3290 if ((loadopt & PFCTL_FLAG_OPTION) == 0)
3291 return (0);
3292
3293 bzero(&pi, sizeof(pi));
3294
3295 pi.pfiio_flags = flags;
3296
3297 /* Make sure our cache matches the kernel. If we set or clear the flag
3298 * for a group this applies to all members. */
3299 h = ifa_grouplookup(ifname, 0);
3300 for (n = h; n != NULL; n = n->next)
3301 pfctl_set_interface_flags(pf, n->ifname, flags, how);
3302
3303 if (strlcpy(pi.pfiio_name, ifname, sizeof(pi.pfiio_name)) >=
3304 sizeof(pi.pfiio_name))
3305 errx(1, "pfctl_set_interface_flags: strlcpy");
3306
3307 if ((pf->opts & PF_OPT_NOACTION) == 0) {
3308 if (how == 0) {
3309 if (ioctl(pf->dev, DIOCCLRIFFLAG, &pi))
3310 pfctl_err(pf->opts, 1, "DIOCCLRIFFLAG");
3311 } else {
3312 if (ioctl(pf->dev, DIOCSETIFFLAG, &pi))
3313 err(1, "DIOCSETIFFLAG");
3314 pfctl_check_skip_ifaces(ifname);
3315 }
3316 }
3317 return (0);
3318 }
3319
3320 void
pfctl_debug(int dev,u_int32_t level,int opts)3321 pfctl_debug(int dev, u_int32_t level, int opts)
3322 {
3323 int ret;
3324
3325 if ((ret = pfctl_set_debug(pfh, level)) != 0)
3326 errc(1, ret, "DIOCSETDEBUG");
3327 if ((opts & PF_OPT_QUIET) == 0) {
3328 fprintf(stderr, "debug level set to '");
3329 switch (level) {
3330 case PF_DEBUG_NONE:
3331 fprintf(stderr, "none");
3332 break;
3333 case PF_DEBUG_URGENT:
3334 fprintf(stderr, "urgent");
3335 break;
3336 case PF_DEBUG_MISC:
3337 fprintf(stderr, "misc");
3338 break;
3339 case PF_DEBUG_NOISY:
3340 fprintf(stderr, "loud");
3341 break;
3342 default:
3343 fprintf(stderr, "<invalid>");
3344 break;
3345 }
3346 fprintf(stderr, "'\n");
3347 }
3348 }
3349
3350 int
pfctl_test_altqsupport(int dev,int opts)3351 pfctl_test_altqsupport(int dev, int opts)
3352 {
3353 struct pfioc_altq pa;
3354
3355 pa.version = PFIOC_ALTQ_VERSION;
3356 if (ioctl(dev, DIOCGETALTQS, &pa)) {
3357 if (errno == ENODEV) {
3358 if (opts & PF_OPT_VERBOSE)
3359 fprintf(stderr, "No ALTQ support in kernel\n"
3360 "ALTQ related functions disabled\n");
3361 return (0);
3362 } else
3363 err(1, "DIOCGETALTQS");
3364 }
3365 return (1);
3366 }
3367
3368 int
pfctl_walk_show(int opts,struct pfioc_ruleset * pr,void * warg)3369 pfctl_walk_show(int opts, struct pfioc_ruleset *pr, void *warg)
3370 {
3371 if (pr->path[0]) {
3372 if (pr->path[0] != '_' || (opts & PF_OPT_VERBOSE))
3373 printf(" %s/%s\n", pr->path, pr->name);
3374 } else if (pr->name[0] != '_' || (opts & PF_OPT_VERBOSE))
3375 printf(" %s\n", pr->name);
3376
3377 return (0);
3378 }
3379
3380 int
pfctl_walk_get(int opts,struct pfioc_ruleset * pr,void * warg)3381 pfctl_walk_get(int opts, struct pfioc_ruleset *pr, void *warg)
3382 {
3383 struct pfr_anchoritem *pfra;
3384 struct pfr_anchors *anchors;
3385 int e;
3386
3387 anchors = (struct pfr_anchors *)warg;
3388
3389 pfra = malloc(sizeof(*pfra));
3390 if (pfra == NULL)
3391 err(1, "%s", __func__);
3392
3393 if (pr->path[0])
3394 e = asprintf(&pfra->pfra_anchorname, "%s/%s", pr->path,
3395 pr->name);
3396 else
3397 e = asprintf(&pfra->pfra_anchorname, "%s", pr->name);
3398
3399 if (e == -1)
3400 err(1, "%s", __func__);
3401
3402 SLIST_INSERT_HEAD(anchors, pfra, pfra_sle);
3403
3404 return (0);
3405 }
3406
3407 int
pfctl_walk_anchors(int dev,int opts,const char * anchor,int (walkf)(int,struct pfioc_ruleset *,void *),void * warg)3408 pfctl_walk_anchors(int dev, int opts, const char *anchor,
3409 int(walkf)(int, struct pfioc_ruleset *, void *), void *warg)
3410 {
3411 struct pfioc_ruleset pr;
3412 u_int32_t mnr, nr;
3413 int ret;
3414
3415 memset(&pr, 0, sizeof(pr));
3416 if ((ret = pfctl_get_rulesets(pfh, anchor, &mnr)) != 0)
3417 errx(1, "%s", pf_strerror(ret));
3418 for (nr = 0; nr < mnr; ++nr) {
3419 char sub[MAXPATHLEN];
3420
3421 if ((ret = pfctl_get_ruleset(pfh, anchor, nr, &pr)) != 0)
3422 errc(1, ret, "DIOCGETRULESET");
3423 if (!strcmp(pr.name, PF_RESERVED_ANCHOR))
3424 continue;
3425 sub[0] = '\0';
3426 if (walkf(opts, &pr, warg))
3427 return (-1);
3428
3429 if (pr.path[0])
3430 snprintf(sub, sizeof(sub), "%s/%s", pr.path, pr.name);
3431 else
3432 snprintf(sub, sizeof(sub), "%s", pr.name);
3433 if (pfctl_walk_anchors(dev, opts, sub, walkf, warg))
3434 return (-1);
3435 }
3436 return (0);
3437 }
3438
3439 int
pfctl_show_anchors(int dev,int opts,char * anchor)3440 pfctl_show_anchors(int dev, int opts, char *anchor)
3441 {
3442 return (
3443 pfctl_walk_anchors(dev, opts, anchor, pfctl_walk_show, NULL));
3444 }
3445
3446 struct pfr_anchors *
pfctl_get_anchors(int dev,const char * anchor,int opts)3447 pfctl_get_anchors(int dev, const char *anchor, int opts)
3448 {
3449 struct pfioc_ruleset pr;
3450 static struct pfr_anchors anchors;
3451 char anchorbuf[PATH_MAX];
3452 char *n;
3453
3454 SLIST_INIT(&anchors);
3455
3456 memset(&pr, 0, sizeof(pr));
3457 if (*anchor != '\0') {
3458 strlcpy(anchorbuf, anchor, sizeof(anchorbuf));
3459 n = dirname(anchorbuf);
3460 if (n[0] != '.' && n[1] != '\0')
3461 strlcpy(pr.path, n, sizeof(pr.path));
3462 strlcpy(anchorbuf, anchor, sizeof(anchorbuf));
3463 n = basename(anchorbuf);
3464 if (n != NULL)
3465 strlcpy(pr.name, n, sizeof(pr.name));
3466 }
3467
3468 /* insert a root anchor first. */
3469 pfctl_walk_get(opts, &pr, &anchors);
3470
3471 if (pfctl_walk_anchors(dev, opts, anchor, pfctl_walk_get, &anchors))
3472 errx(1, "%s failed to retrieve list of anchors, can't continue",
3473 __func__);
3474
3475 return (&anchors);
3476 }
3477
3478 int
pfctl_call_cleartables(int dev,int opts,struct pfr_anchoritem * pfra)3479 pfctl_call_cleartables(int dev, int opts, struct pfr_anchoritem *pfra)
3480 {
3481 /*
3482 * PF_OPT_QUIET makes pfctl_clear_tables() to stop printing number of
3483 * tables cleared for given anchor.
3484 */
3485 opts |= PF_OPT_QUIET;
3486 return ((pfctl_do_clear_tables(pfra->pfra_anchorname, opts) == -1) ?
3487 1 : 0);
3488 }
3489
3490 int
pfctl_call_clearrules(int dev,int opts,struct pfr_anchoritem * pfra)3491 pfctl_call_clearrules(int dev, int opts, struct pfr_anchoritem *pfra)
3492 {
3493 /*
3494 * PF_OPT_QUIET makes pfctl_clear_rules() to stop printing a 'rules
3495 * cleared' message for every anchor it deletes.
3496 */
3497 opts |= PF_OPT_QUIET;
3498 return (pfctl_flush_rules(dev, opts, pfra->pfra_anchorname));
3499 }
3500
3501 int
pfctl_call_clearanchors(int dev,int opts,struct pfr_anchoritem * pfra)3502 pfctl_call_clearanchors(int dev, int opts, struct pfr_anchoritem *pfra)
3503 {
3504 int rv = 0;
3505
3506 rv |= pfctl_call_cleartables(dev, opts, pfra);
3507 rv |= pfctl_call_clearrules(dev, opts, pfra);
3508
3509 return (rv);
3510 }
3511
3512 int
pfctl_call_showtables(int dev,int opts,struct pfr_anchoritem * pfra)3513 pfctl_call_showtables(int dev, int opts, struct pfr_anchoritem *pfra)
3514 {
3515 pfctl_show_tables(pfra->pfra_anchorname, opts);
3516 return (0);
3517 }
3518
3519 int
pfctl_recurse(int dev,int opts,const char * anchorname,int (* walkf)(int,int,struct pfr_anchoritem *))3520 pfctl_recurse(int dev, int opts, const char *anchorname,
3521 int(*walkf)(int, int, struct pfr_anchoritem *))
3522 {
3523 int rv = 0;
3524 struct pfr_anchors *anchors;
3525 struct pfr_anchoritem *pfra, *pfra_save;
3526
3527 anchors = pfctl_get_anchors(dev, anchorname, opts);
3528 /*
3529 * While traversing the list, pfctl_clear_*() must always return
3530 * so that failures on one anchor do not prevent clearing others.
3531 */
3532 opts |= PF_OPT_IGNFAIL;
3533 if ((opts & PF_OPT_CALLSHOW) == 0)
3534 printf("Removing:\n");
3535 SLIST_FOREACH_SAFE(pfra, anchors, pfra_sle, pfra_save) {
3536 if ((opts & PF_OPT_CALLSHOW) == 0)
3537 printf(" %s\n",
3538 (*pfra->pfra_anchorname == '\0') ? "/" :
3539 pfra->pfra_anchorname);
3540 rv |= walkf(dev, opts, pfra);
3541 SLIST_REMOVE(anchors, pfra, pfr_anchoritem, pfra_sle);
3542 free(pfra->pfra_anchorname);
3543 free(pfra);
3544 }
3545
3546 return (rv);
3547 }
3548
3549 int
pfctl_show_eth_anchors(int dev,int opts,char * anchorname)3550 pfctl_show_eth_anchors(int dev, int opts, char *anchorname)
3551 {
3552 struct pfctl_eth_rulesets_info ri;
3553 struct pfctl_eth_ruleset_info rs;
3554 int ret;
3555
3556 if ((ret = pfctl_get_eth_rulesets_info(dev, &ri, anchorname)) != 0) {
3557 if (ret != ENOENT)
3558 errc(1, ret, "DIOCGETETHRULESETS");
3559 return (-1);
3560 }
3561
3562 for (int nr = 0; nr < ri.nr; nr++) {
3563 char sub[MAXPATHLEN];
3564
3565 if ((ret = pfctl_get_eth_ruleset(dev, anchorname, nr, &rs)) != 0)
3566 errc(1, ret, "DIOCGETETHRULESET");
3567
3568 if (!strcmp(rs.name, PF_RESERVED_ANCHOR))
3569 continue;
3570 sub[0] = 0;
3571 if (rs.path[0]) {
3572 strlcat(sub, rs.path, sizeof(sub));
3573 strlcat(sub, "/", sizeof(sub));
3574 }
3575 strlcat(sub, rs.name, sizeof(sub));
3576 if (sub[0] != '_' || (opts & PF_OPT_VERBOSE))
3577 printf(" %s\n", sub);
3578 if ((opts & PF_OPT_VERBOSE) && pfctl_show_eth_anchors(dev, opts, sub))
3579 return (-1);
3580 }
3581 return (0);
3582 }
3583
3584 const char *
pfctl_lookup_option(char * cmd,const char * const * list)3585 pfctl_lookup_option(char *cmd, const char * const *list)
3586 {
3587 if (cmd != NULL && *cmd)
3588 for (; *list; list++)
3589 if (!strncmp(cmd, *list, strlen(cmd)))
3590 return (*list);
3591 return (NULL);
3592 }
3593
3594 int
pfctl_lookup_id(const char * cmd,const struct pfctl_opt_id * opt_ids)3595 pfctl_lookup_id(const char *cmd, const struct pfctl_opt_id *opt_ids)
3596 {
3597 const struct pfctl_opt_id *opt_id;
3598 int id = 0;
3599 size_t cmdlen = strlen(cmd);
3600
3601 for (opt_id = opt_ids; opt_id->id != 0; opt_id++) {
3602 if (strncmp(cmd, opt_id->name, cmdlen) == 0) {
3603 if (id != 0)
3604 errx(1, "%s is ambiguous", cmd);
3605
3606 id = opt_id->id;
3607 }
3608 }
3609
3610 return (id);
3611 }
3612
3613 void
pfctl_reset(int dev,int opts)3614 pfctl_reset(int dev, int opts)
3615 {
3616 struct pfctl pf;
3617 struct pfr_buffer t;
3618 int i;
3619
3620 memset(&pf, 0, sizeof(pf));
3621 pf.dev = dev;
3622 pf.h = pfh;
3623 pfctl_init_options(&pf);
3624
3625 /* Force reset upon pfctl_load_options() */
3626 pf.debug_set = 1;
3627 pf.reass_set = 1;
3628 pf.syncookieswat_set = 1;
3629 pf.ifname = strdup("none");
3630 if (pf.ifname == NULL)
3631 err(1, "%s: strdup", __func__);
3632 pf.ifname_set = 1;
3633
3634 memset(&t, 0, sizeof(t));
3635 t.pfrb_type = PFRB_TRANS;
3636 if (pfctl_trans(dev, &t, DIOCXBEGIN, 0))
3637 err(1, "%s: DIOCXBEGIN", __func__);
3638
3639 for (i = 0; pf_limits[i].name; i++)
3640 pf.limit_set[pf_limits[i].index] = 1;
3641
3642 for (i = 0; pf_timeouts[i].name; i++)
3643 pf.timeout_set[pf_timeouts[i].timeout] = 1;
3644
3645 pfctl_load_options(&pf);
3646
3647 if (pfctl_trans(dev, &t, DIOCXCOMMIT, 0))
3648 err(1, "%s: DIOCXCOMMIT", __func__);
3649
3650 pfctl_clear_interface_flags(dev, opts);
3651 }
3652
3653 int
main(int argc,char * argv[])3654 main(int argc, char *argv[])
3655 {
3656 int ch;
3657 int mode = O_RDONLY;
3658 int opts = 0;
3659 int optimize = PF_OPTIMIZE_BASIC;
3660 char anchorname[MAXPATHLEN];
3661 char *path;
3662 const char *idopt = NULL;
3663
3664 if (argc < 2)
3665 usage();
3666
3667 while ((ch = getopt(argc, argv,
3668 "a:AdD:eqf:F:ghi:I:k:K:mMnNOo:Pp:rRs:St:T:vx:z")) != -1) {
3669 switch (ch) {
3670 case 'a':
3671 anchoropt = optarg;
3672 break;
3673 case 'd':
3674 opts |= PF_OPT_DISABLE;
3675 mode = O_RDWR;
3676 break;
3677 case 'D':
3678 if (pfctl_cmdline_symset(optarg) < 0)
3679 warnx("could not parse macro definition %s",
3680 optarg);
3681 break;
3682 case 'e':
3683 opts |= PF_OPT_ENABLE;
3684 mode = O_RDWR;
3685 break;
3686 case 'q':
3687 opts |= PF_OPT_QUIET;
3688 break;
3689 case 'F':
3690 clearopt = pfctl_lookup_option(optarg, clearopt_list);
3691 if (clearopt == NULL) {
3692 warnx("Unknown flush modifier '%s'", optarg);
3693 usage();
3694 }
3695 mode = O_RDWR;
3696 break;
3697 case 'i':
3698 ifaceopt = optarg;
3699 break;
3700 case 'I':
3701 idopt = optarg;
3702 break;
3703 case 'k':
3704 if (state_killers >= 2) {
3705 warnx("can only specify -k twice");
3706 usage();
3707 /* NOTREACHED */
3708 }
3709 state_kill[state_killers++] = optarg;
3710 mode = O_RDWR;
3711 break;
3712 case 'K':
3713 if (src_node_killers >= 2) {
3714 warnx("can only specify -K twice");
3715 usage();
3716 /* NOTREACHED */
3717 }
3718 src_node_kill[src_node_killers++] = optarg;
3719 mode = O_RDWR;
3720 break;
3721 case 'm':
3722 opts |= PF_OPT_MERGE;
3723 break;
3724 case 'M':
3725 opts |= PF_OPT_KILLMATCH;
3726 break;
3727 case 'n':
3728 opts |= PF_OPT_NOACTION;
3729 break;
3730 case 'N':
3731 loadopt |= PFCTL_FLAG_NAT;
3732 break;
3733 case 'r':
3734 opts |= PF_OPT_USEDNS;
3735 break;
3736 case 'f':
3737 rulesopt = optarg;
3738 mode = O_RDWR;
3739 break;
3740 case 'g':
3741 opts |= PF_OPT_DEBUG;
3742 break;
3743 case 'A':
3744 loadopt |= PFCTL_FLAG_ALTQ;
3745 break;
3746 case 'R':
3747 loadopt |= PFCTL_FLAG_FILTER;
3748 break;
3749 case 'o':
3750 optiopt = pfctl_lookup_option(optarg, optiopt_list);
3751 if (optiopt == NULL) {
3752 warnx("Unknown optimization '%s'", optarg);
3753 usage();
3754 }
3755 opts |= PF_OPT_OPTIMIZE;
3756 break;
3757 case 'O':
3758 loadopt |= PFCTL_FLAG_OPTION;
3759 break;
3760 case 'p':
3761 pf_device = optarg;
3762 break;
3763 case 'P':
3764 opts |= PF_OPT_NUMERIC;
3765 break;
3766 case 's':
3767 showopt = pfctl_lookup_id(optarg, showopt_list);
3768 if (showopt == 0) {
3769 warnx("Unknown show modifier '%s'", optarg);
3770 usage();
3771 }
3772 break;
3773 case 'S':
3774 opts |= PF_OPT_NODNS;
3775 break;
3776 case 't':
3777 tableopt = optarg;
3778 break;
3779 case 'T':
3780 tblcmdopt = pfctl_lookup_option(optarg, tblcmdopt_list);
3781 if (tblcmdopt == NULL) {
3782 warnx("Unknown table command '%s'", optarg);
3783 usage();
3784 }
3785 break;
3786 case 'v':
3787 if (opts & PF_OPT_VERBOSE)
3788 opts |= PF_OPT_VERBOSE2;
3789 opts |= PF_OPT_VERBOSE;
3790 break;
3791 case 'x':
3792 debugopt = pfctl_lookup_option(optarg, debugopt_list);
3793 if (debugopt == NULL) {
3794 warnx("Unknown debug level '%s'", optarg);
3795 usage();
3796 }
3797 mode = O_RDWR;
3798 break;
3799 case 'z':
3800 opts |= PF_OPT_CLRRULECTRS;
3801 mode = O_RDWR;
3802 break;
3803 case 'h':
3804 /* FALLTHROUGH */
3805 default:
3806 usage();
3807 /* NOTREACHED */
3808 }
3809 }
3810
3811 if ((opts & PF_OPT_NODNS) && (opts & PF_OPT_USEDNS))
3812 errx(1, "-N and -r are mutually exclusive");
3813
3814 if ((tblcmdopt == NULL) ^ (tableopt == NULL) &&
3815 (tblcmdopt == NULL || *tblcmdopt != 'l'))
3816 usage();
3817
3818 if (tblcmdopt != NULL) {
3819 argc -= optind;
3820 argv += optind;
3821 ch = *tblcmdopt;
3822 if (ch == 'l') {
3823 loadopt |= PFCTL_FLAG_TABLE;
3824 tblcmdopt = NULL;
3825 } else
3826 mode = strchr("st", ch) ? O_RDONLY : O_RDWR;
3827 } else if (argc != optind) {
3828 warnx("unknown command line argument: %s ...", argv[optind]);
3829 usage();
3830 /* NOTREACHED */
3831 }
3832 if (loadopt == 0)
3833 loadopt = ~0;
3834
3835 memset(anchorname, 0, sizeof(anchorname));
3836 if (anchoropt != NULL) {
3837 int len = strlen(anchoropt);
3838
3839 if (anchoropt[0] == '\0')
3840 errx(1, "anchor name must not be empty");
3841 if (mode == O_RDONLY && showopt == 0 && tblcmdopt == NULL) {
3842 warnx("anchors apply to -f, -F, -s, and -T only");
3843 usage();
3844 }
3845 if (mode == O_RDWR && tblcmdopt == NULL &&
3846 (anchoropt[0] == '_' || strstr(anchoropt, "/_") != NULL))
3847 errx(1, "anchor names beginning with '_' cannot "
3848 "be modified from the command line");
3849
3850 if (len >= 1 && anchoropt[len - 1] == '*') {
3851 if (len >= 2 && anchoropt[len - 2] == '/')
3852 anchoropt[len - 2] = '\0';
3853 else
3854 anchoropt[len - 1] = '\0';
3855 opts |= PF_OPT_RECURSE;
3856 }
3857 if (strlcpy(anchorname, anchoropt,
3858 sizeof(anchorname)) >= sizeof(anchorname))
3859 errx(1, "anchor name '%s' too long",
3860 anchoropt);
3861 loadopt &= PFCTL_FLAG_FILTER|PFCTL_FLAG_NAT|PFCTL_FLAG_TABLE|PFCTL_FLAG_ETH;
3862 }
3863
3864 if ((opts & PF_OPT_NOACTION) == 0) {
3865 dev = open(pf_device, mode);
3866 if (dev == -1)
3867 err(1, "%s", pf_device);
3868 altqsupport = pfctl_test_altqsupport(dev, opts);
3869 } else {
3870 dev = open(pf_device, O_RDONLY);
3871 if (dev >= 0)
3872 opts |= PF_OPT_DUMMYACTION;
3873 /* turn off options */
3874 opts &= ~ (PF_OPT_DISABLE | PF_OPT_ENABLE);
3875 clearopt = debugopt = NULL;
3876 showopt = 0;
3877 #if !defined(ENABLE_ALTQ)
3878 altqsupport = 0;
3879 #else
3880 altqsupport = 1;
3881 #endif
3882 }
3883 pfh = pfctl_open(pf_device);
3884 if (pfh == NULL)
3885 err(1, "Failed to open netlink");
3886
3887 if ((opts & PF_OPT_NOACTION) == 0) {
3888 pfctl_read_limits(pfh);
3889 }
3890
3891 if (opts & PF_OPT_DISABLE)
3892 if (pfctl_disable(dev, opts))
3893 exit_val = 1;
3894
3895 if ((path = calloc(1, MAXPATHLEN)) == NULL)
3896 errx(1, "%s: calloc", __func__);
3897
3898 switch (showopt) {
3899 case SHOWOPT_NONE:
3900 break;
3901 case SHOWOPT_ANCHORS:
3902 pfctl_show_anchors(dev, opts, anchorname);
3903 if (opts & PF_OPT_VERBOSE2)
3904 printf("Ethernet:\n");
3905 pfctl_show_eth_anchors(dev, opts, anchorname);
3906 break;
3907 case SHOWOPT_RULES:
3908 pfctl_load_fingerprints(dev, opts);
3909 pfctl_show_rules(dev, path, opts, PFCTL_SHOW_RULES, anchorname,
3910 0, 0);
3911 break;
3912 case SHOWOPT_LABELS:
3913 pfctl_load_fingerprints(dev, opts);
3914 pfctl_show_rules(dev, path, opts, PFCTL_SHOW_LABELS, anchorname,
3915 0, 0);
3916 break;
3917 case SHOWOPT_NAT:
3918 pfctl_load_fingerprints(dev, opts);
3919 pfctl_show_nat(dev, path, opts, anchorname, 0, 0);
3920 break;
3921 case SHOWOPT_QUEUE:
3922 pfctl_show_altq(dev, ifaceopt, opts, opts & PF_OPT_VERBOSE2);
3923 break;
3924 case SHOWOPT_STATES:
3925 pfctl_show_states(dev, ifaceopt, opts);
3926 break;
3927 case SHOWOPT_SOURCES:
3928 pfctl_show_src_nodes(dev, opts);
3929 break;
3930 case SHOWOPT_INFO:
3931 pfctl_show_status(dev, opts);
3932 break;
3933 case SHOWOPT_RUNNING:
3934 exit_val = pfctl_show_running(dev);
3935 break;
3936 case SHOWOPT_TIMEOUTS:
3937 pfctl_show_timeouts(dev, opts);
3938 break;
3939 case SHOWOPT_MEMORY:
3940 pfctl_show_limits(dev, opts);
3941 break;
3942 case SHOWOPT_ETHER:
3943 pfctl_show_eth_rules(dev, path, opts, 0, anchorname, 0, 0);
3944 break;
3945 case SHOWOPT_ALL:
3946 opts |= PF_OPT_SHOWALL;
3947 pfctl_load_fingerprints(dev, opts);
3948
3949 pfctl_show_eth_rules(dev, path, opts, 0, anchorname, 0, 0);
3950
3951 pfctl_show_nat(dev, path, opts, anchorname, 0, 0);
3952 pfctl_show_rules(dev, path, opts, PFCTL_SHOW_RULES, anchorname,
3953 0, 0);
3954 pfctl_show_altq(dev, ifaceopt, opts, 0);
3955 pfctl_show_states(dev, ifaceopt, opts);
3956 pfctl_show_src_nodes(dev, opts);
3957 pfctl_show_status(dev, opts);
3958 pfctl_show_rules(dev, path, opts, PFCTL_SHOW_LABELS, anchorname,
3959 0, 0);
3960 pfctl_show_timeouts(dev, opts);
3961 pfctl_show_limits(dev, opts);
3962 pfctl_show_statelims(dev, PFCTL_SHOW_LABELS, opts);
3963 pfctl_show_sourcelims(dev, PFCTL_SHOW_LABELS, opts, idopt);
3964 pfctl_show_tables(anchorname, opts);
3965 pfctl_show_fingerprints(opts);
3966 break;
3967 case SHOWOPT_TABLES:
3968 if (opts & PF_OPT_RECURSE) {
3969 opts |= PF_OPT_CALLSHOW;
3970 pfctl_recurse(dev, opts, anchorname,
3971 pfctl_call_showtables);
3972 } else
3973 pfctl_show_tables(anchorname, opts);
3974 break;
3975 case SHOWOPT_OSFP:
3976 pfctl_load_fingerprints(dev, opts);
3977 pfctl_show_fingerprints(opts);
3978 break;
3979 case SHOWOPT_IFACES:
3980 pfctl_show_ifaces(ifaceopt, opts);
3981 break;
3982 case SHOWOPT_CREATORIDS:
3983 pfctl_show_creators(opts);
3984 break;
3985 case SHOWOPT_STATELIMS:
3986 pfctl_show_statelims(dev, PFCTL_SHOW_LABELS, opts);
3987 break;
3988 case SHOWOPT_SOURCELIMS:
3989 pfctl_show_sourcelims(dev, PFCTL_SHOW_LABELS, opts, idopt);
3990 break;
3991 }
3992
3993 if ((opts & PF_OPT_CLRRULECTRS) && showopt == 0) {
3994 pfctl_show_eth_rules(dev, path, opts, PFCTL_SHOW_NOTHING,
3995 anchorname, 0, 0);
3996 pfctl_show_rules(dev, path, opts, PFCTL_SHOW_NOTHING,
3997 anchorname, 0, 0);
3998 }
3999
4000 if (clearopt != NULL) {
4001 int mnr;
4002
4003 /* Check if anchor exists. */
4004 if ((pfctl_get_rulesets(pfh, anchorname, &mnr)) == ENOENT)
4005 errx(1, "No such anchor %s", anchorname);
4006
4007 switch (*clearopt) {
4008 case 'e':
4009 pfctl_flush_eth_rules(dev, opts, anchorname);
4010 break;
4011 case 'r':
4012 if (opts & PF_OPT_RECURSE)
4013 pfctl_recurse(dev, opts, anchorname,
4014 pfctl_call_clearrules);
4015 else
4016 pfctl_flush_rules(dev, opts, anchorname);
4017 break;
4018 case 'n':
4019 pfctl_flush_nat(dev, opts, anchorname);
4020 break;
4021 case 'q':
4022 pfctl_clear_altq(dev, opts);
4023 break;
4024 case 's':
4025 pfctl_clear_iface_states(dev, ifaceopt, opts);
4026 break;
4027 case 'S':
4028 pfctl_clear_src_nodes(dev, opts);
4029 break;
4030 case 'i':
4031 pfctl_clear_stats(pfh, opts);
4032 break;
4033 case 'a':
4034 if (ifaceopt) {
4035 warnx("don't specify an interface with -Fall");
4036 usage();
4037 /* NOTREACHED */
4038 }
4039 pfctl_flush_eth_rules(dev, opts, anchorname);
4040 pfctl_flush_rules(dev, opts, anchorname);
4041 pfctl_flush_nat(dev, opts, anchorname);
4042 if (opts & PF_OPT_RECURSE)
4043 pfctl_recurse(dev, opts, anchorname,
4044 pfctl_call_clearanchors);
4045 else {
4046 pfctl_do_clear_tables(anchorname, opts);
4047 pfctl_flush_rules(dev, opts, anchorname);
4048 }
4049 if (!*anchorname) {
4050 pfctl_clear_altq(dev, opts);
4051 pfctl_clear_iface_states(dev, ifaceopt, opts);
4052 pfctl_clear_src_nodes(dev, opts);
4053 pfctl_clear_stats(pfh, opts);
4054 pfctl_clear_fingerprints(dev, opts);
4055 pfctl_reset(dev, opts);
4056 }
4057 break;
4058 case 'o':
4059 pfctl_clear_fingerprints(dev, opts);
4060 break;
4061 case 'T':
4062 if ((opts & PF_OPT_RECURSE) == 0)
4063 pfctl_do_clear_tables(anchorname, opts);
4064 else
4065 pfctl_recurse(dev, opts, anchorname,
4066 pfctl_call_cleartables);
4067 break;
4068 case 'R':
4069 pfctl_reset(dev, opts);
4070 break;
4071 }
4072 }
4073 if (state_killers) {
4074 if (!strcmp(state_kill[0], "label"))
4075 pfctl_label_kill_states(dev, ifaceopt, opts);
4076 else if (!strcmp(state_kill[0], "id"))
4077 pfctl_id_kill_states(dev, ifaceopt, opts);
4078 else if (!strcmp(state_kill[0], "gateway"))
4079 pfctl_gateway_kill_states(dev, ifaceopt, opts);
4080 else if (!strcmp(state_kill[0], "key"))
4081 pfctl_key_kill_states(dev, ifaceopt, opts);
4082 else if (!strcmp(state_kill[0], "source"))
4083 pfctl_kill_source(dev, idopt, state_kill[1], opts);
4084 else
4085 pfctl_net_kill_states(dev, ifaceopt, opts);
4086 }
4087
4088 if (src_node_killers)
4089 pfctl_kill_src_nodes(dev, opts);
4090
4091 if (tblcmdopt != NULL) {
4092 exit_val = pfctl_table(argc, argv, tableopt,
4093 tblcmdopt, rulesopt, anchorname, opts);
4094 rulesopt = NULL;
4095 }
4096 if (optiopt != NULL) {
4097 switch (*optiopt) {
4098 case 'n':
4099 optimize = 0;
4100 break;
4101 case 'b':
4102 optimize |= PF_OPTIMIZE_BASIC;
4103 break;
4104 case 'o':
4105 case 'p':
4106 optimize |= PF_OPTIMIZE_PROFILE;
4107 break;
4108 }
4109 }
4110
4111 if ((rulesopt != NULL) && (loadopt & PFCTL_FLAG_OPTION) &&
4112 !anchorname[0] && !(opts & PF_OPT_NOACTION))
4113 pfctl_get_skip_ifaces();
4114
4115 if (rulesopt != NULL && !(opts & PF_OPT_MERGE) &&
4116 !anchorname[0] && (loadopt & PFCTL_FLAG_OPTION))
4117 if (pfctl_file_fingerprints(dev, opts, PF_OSFP_FILE))
4118 exit_val = 1;
4119
4120 if (rulesopt != NULL) {
4121 if (pfctl_rules(dev, rulesopt, opts, optimize,
4122 anchorname, NULL))
4123 exit_val = 1;
4124 }
4125
4126 if (opts & PF_OPT_ENABLE)
4127 if (pfctl_enable(dev, opts))
4128 exit_val = 1;
4129
4130 if (debugopt != NULL) {
4131 switch (*debugopt) {
4132 case 'n':
4133 pfctl_debug(dev, PF_DEBUG_NONE, opts);
4134 break;
4135 case 'u':
4136 pfctl_debug(dev, PF_DEBUG_URGENT, opts);
4137 break;
4138 case 'm':
4139 pfctl_debug(dev, PF_DEBUG_MISC, opts);
4140 break;
4141 case 'l':
4142 pfctl_debug(dev, PF_DEBUG_NOISY, opts);
4143 break;
4144 }
4145 }
4146
4147 /*
4148 * prevent pfctl_restore_limits() exit handler from restoring
4149 * pf(4) options settings on successful exit.
4150 */
4151 if (exit_val == 0) {
4152 close(dev);
4153 dev = -1;
4154 pfctl_close(pfh);
4155 pfh = NULL;
4156 }
4157
4158 return (exit_val);
4159 }
4160
4161 char *
pf_strerror(int errnum)4162 pf_strerror(int errnum)
4163 {
4164 switch (errnum) {
4165 case ESRCH:
4166 return "Table does not exist.";
4167 case EINVAL:
4168 case ENOENT:
4169 return "Anchor does not exist.";
4170 default:
4171 return strerror(errnum);
4172 }
4173 }
4174
4175 static inline int
pfctl_statelim_id_cmp(const struct pfctl_statelim * a,const struct pfctl_statelim * b)4176 pfctl_statelim_id_cmp(const struct pfctl_statelim *a,
4177 const struct pfctl_statelim *b)
4178 {
4179 uint32_t ida = a->ioc.id;
4180 uint32_t idb = b->ioc.id;
4181
4182 if (ida > idb)
4183 return (1);
4184 if (ida < idb)
4185 return (-1);
4186
4187 return (0);
4188 }
4189
4190 RB_GENERATE(pfctl_statelim_ids, pfctl_statelim, entry, pfctl_statelim_id_cmp);
4191
4192 static inline int
pfctl_statelim_nm_cmp(const struct pfctl_statelim * a,const struct pfctl_statelim * b)4193 pfctl_statelim_nm_cmp(const struct pfctl_statelim *a,
4194 const struct pfctl_statelim *b)
4195 {
4196 return (strcmp(a->ioc.name, b->ioc.name));
4197 }
4198
4199 RB_GENERATE(pfctl_statelim_nms, pfctl_statelim, entry, pfctl_statelim_nm_cmp);
4200
4201 int
pfctl_add_statelim(struct pfctl * pf,struct pfctl_statelim * stlim)4202 pfctl_add_statelim(struct pfctl *pf, struct pfctl_statelim *stlim)
4203 {
4204 struct pfctl_statelim *ostlim;
4205
4206 ostlim = RB_INSERT(pfctl_statelim_ids, &pf->statelim_ids, stlim);
4207 if (ostlim != NULL)
4208 return (-1);
4209
4210 ostlim = RB_INSERT(pfctl_statelim_nms, &pf->statelim_nms, stlim);
4211 if (ostlim != NULL) {
4212 RB_REMOVE(pfctl_statelim_ids, &pf->statelim_ids, stlim);
4213 return (-1);
4214 }
4215
4216 return (0);
4217 }
4218
4219 struct pfctl_statelim *
pfctl_get_statelim_id(struct pfctl * pf,uint32_t id)4220 pfctl_get_statelim_id(struct pfctl *pf, uint32_t id)
4221 {
4222 struct pfctl_statelim key;
4223
4224 key.ioc.id = id;
4225
4226 return (RB_FIND(pfctl_statelim_nms, &pf->statelim_nms, &key));
4227 }
4228
4229 struct pfctl_statelim *
pfctl_get_statelim_nm(struct pfctl * pf,const char * name)4230 pfctl_get_statelim_nm(struct pfctl *pf, const char *name)
4231 {
4232 struct pfctl_statelim key;
4233
4234 if (strlcpy(key.ioc.name, name, sizeof(key.ioc.name)) >=
4235 sizeof(key.ioc.name))
4236 return (NULL);
4237
4238 return (RB_FIND(pfctl_statelim_nms, &pf->statelim_nms, &key));
4239 }
4240
4241 static inline int
pfctl_sourcelim_id_cmp(const struct pfctl_sourcelim * a,const struct pfctl_sourcelim * b)4242 pfctl_sourcelim_id_cmp(const struct pfctl_sourcelim *a,
4243 const struct pfctl_sourcelim *b)
4244 {
4245 uint32_t ida = a->ioc.id;
4246 uint32_t idb = b->ioc.id;
4247
4248 if (ida > idb)
4249 return (1);
4250 if (ida < idb)
4251 return (-1);
4252
4253 return (0);
4254 }
4255
4256 RB_GENERATE(pfctl_sourcelim_ids, pfctl_sourcelim, entry,
4257 pfctl_sourcelim_id_cmp);
4258
4259 static inline int
pfctl_sourcelim_nm_cmp(const struct pfctl_sourcelim * a,const struct pfctl_sourcelim * b)4260 pfctl_sourcelim_nm_cmp(const struct pfctl_sourcelim *a,
4261 const struct pfctl_sourcelim *b)
4262 {
4263 return (strcmp(a->ioc.name, b->ioc.name));
4264 }
4265
4266 RB_GENERATE(pfctl_sourcelim_nms, pfctl_sourcelim, entry,
4267 pfctl_sourcelim_nm_cmp);
4268
4269 int
pfctl_add_sourcelim(struct pfctl * pf,struct pfctl_sourcelim * srlim)4270 pfctl_add_sourcelim(struct pfctl *pf, struct pfctl_sourcelim *srlim)
4271 {
4272 struct pfctl_sourcelim *osrlim;
4273
4274 osrlim = RB_INSERT(pfctl_sourcelim_ids, &pf->sourcelim_ids, srlim);
4275 if (osrlim != NULL)
4276 return (-1);
4277
4278 osrlim = RB_INSERT(pfctl_sourcelim_nms, &pf->sourcelim_nms, srlim);
4279 if (osrlim != NULL) {
4280 RB_REMOVE(pfctl_sourcelim_ids, &pf->sourcelim_ids, srlim);
4281 return (-1);
4282 }
4283
4284 return (0);
4285 }
4286
4287 struct pfctl_sourcelim *
pfctl_get_sourcelim_id(struct pfctl * pf,uint32_t id)4288 pfctl_get_sourcelim_id(struct pfctl *pf, uint32_t id)
4289 {
4290 struct pfctl_sourcelim key;
4291
4292 key.ioc.id = id;
4293
4294 return (RB_FIND(pfctl_sourcelim_nms, &pf->sourcelim_nms, &key));
4295 }
4296
4297 struct pfctl_sourcelim *
pfctl_get_sourcelim_nm(struct pfctl * pf,const char * name)4298 pfctl_get_sourcelim_nm(struct pfctl *pf, const char *name)
4299 {
4300 struct pfctl_sourcelim key;
4301
4302 if (strlcpy(key.ioc.name, name, sizeof(key.ioc.name)) >=
4303 sizeof(key.ioc.name))
4304 return (NULL);
4305
4306 return (RB_FIND(pfctl_sourcelim_nms, &pf->sourcelim_nms, &key));
4307 }
4308