1 /* $OpenBSD: pfctl.c,v 1.215 2004/05/05 23:16:03 frantzen Exp $ */
2
3 /*
4 * Copyright (c) 2001 Daniel Hartmeier
5 * Copyright (c) 2002,2003 Henning Brauer
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 *
12 * - Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * - Redistributions in binary form must reproduce the above
15 * copyright notice, this list of conditions and the following
16 * disclaimer in the documentation and/or other materials provided
17 * with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
22 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
23 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
24 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
25 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
26 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
27 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
29 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30 * POSSIBILITY OF SUCH DAMAGE.
31 *
32 */
33
34 #include <sys/types.h>
35 #include <sys/ioctl.h>
36 #include <sys/socket.h>
37 #include <sys/stat.h>
38
39 #include <net/if.h>
40 #include <netinet/in.h>
41 #include <net/pfvar.h>
42 #include <arpa/inet.h>
43 #include <altq/altq.h>
44
45 #include <err.h>
46 #include <errno.h>
47 #include <fcntl.h>
48 #include <limits.h>
49 #include <netdb.h>
50 #include <stdio.h>
51 #include <stdlib.h>
52 #include <string.h>
53 #include <unistd.h>
54
55 #include "pfctl_parser.h"
56 #include "pfctl.h"
57
58 void usage(void);
59 int pfctl_enable(int, int);
60 int pfctl_disable(int, int);
61 int pfctl_clear_stats(int, int);
62 int pfctl_clear_rules(int, int, char *, char *);
63 int pfctl_clear_nat(int, int, char *, char *);
64 int pfctl_clear_altq(int, int);
65 int pfctl_clear_src_nodes(int, int);
66 int pfctl_clear_states(int, const char *, int);
67 int pfctl_kill_states(int, const char *, int);
68 int pfctl_get_pool(int, struct pf_pool *, u_int32_t, u_int32_t, int,
69 char *, char *);
70 void pfctl_print_rule_counters(struct pf_rule *, int);
71 int pfctl_show_rules(int, int, int, char *, char *);
72 int pfctl_show_nat(int, int, char *, char *);
73 int pfctl_show_src_nodes(int, int);
74 int pfctl_show_states(int, const char *, int);
75 int pfctl_show_status(int, int);
76 int pfctl_show_timeouts(int, int);
77 int pfctl_show_limits(int, int);
78 int pfctl_debug(int, u_int32_t, int);
79 int pfctl_clear_rule_counters(int, int);
80 int pfctl_test_altqsupport(int, int);
81 int pfctl_show_anchors(int, int, char *);
82 const char *pfctl_lookup_option(char *, const char **);
83
84 const char *clearopt;
85 char *rulesopt;
86 const char *showopt;
87 const char *debugopt;
88 char *anchoropt;
89 char *pf_device = "/dev/pf";
90 char *ifaceopt;
91 char *tableopt;
92 const char *tblcmdopt;
93 int state_killers;
94 char *state_kill[2];
95 int loadopt;
96 int altqsupport;
97
98 int dev = -1;
99 int first_title = 1;
100 int labels = 0;
101
102 const char *infile;
103
104 static const struct {
105 const char *name;
106 int index;
107 } pf_limits[] = {
108 { "states", PF_LIMIT_STATES },
109 { "src-nodes", PF_LIMIT_SRC_NODES },
110 { "frags", PF_LIMIT_FRAGS },
111 { NULL, 0 }
112 };
113
114 struct pf_hint {
115 const char *name;
116 int timeout;
117 };
118 static const struct pf_hint pf_hint_normal[] = {
119 { "tcp.first", 2 * 60 },
120 { "tcp.opening", 30 },
121 { "tcp.established", 24 * 60 * 60 },
122 { "tcp.closing", 15 * 60 },
123 { "tcp.finwait", 45 },
124 { "tcp.closed", 90 },
125 { "tcp.tsdiff", 30 },
126 { NULL, 0 }
127 };
128 static const struct pf_hint pf_hint_satellite[] = {
129 { "tcp.first", 3 * 60 },
130 { "tcp.opening", 30 + 5 },
131 { "tcp.established", 24 * 60 * 60 },
132 { "tcp.closing", 15 * 60 + 5 },
133 { "tcp.finwait", 45 + 5 },
134 { "tcp.closed", 90 + 5 },
135 { "tcp.tsdiff", 60 },
136 { NULL, 0 }
137 };
138 static const struct pf_hint pf_hint_conservative[] = {
139 { "tcp.first", 60 * 60 },
140 { "tcp.opening", 15 * 60 },
141 { "tcp.established", 5 * 24 * 60 * 60 },
142 { "tcp.closing", 60 * 60 },
143 { "tcp.finwait", 10 * 60 },
144 { "tcp.closed", 3 * 60 },
145 { "tcp.tsdiff", 60 },
146 { NULL, 0 }
147 };
148 static const struct pf_hint pf_hint_aggressive[] = {
149 { "tcp.first", 30 },
150 { "tcp.opening", 5 },
151 { "tcp.established", 5 * 60 * 60 },
152 { "tcp.closing", 60 },
153 { "tcp.finwait", 30 },
154 { "tcp.closed", 30 },
155 { "tcp.tsdiff", 10 },
156 { NULL, 0 }
157 };
158
159 static const struct {
160 const char *name;
161 const struct pf_hint *hint;
162 } pf_hints[] = {
163 { "normal", pf_hint_normal },
164 { "satellite", pf_hint_satellite },
165 { "high-latency", pf_hint_satellite },
166 { "conservative", pf_hint_conservative },
167 { "aggressive", pf_hint_aggressive },
168 { NULL, NULL }
169 };
170
171 static const char *clearopt_list[] = {
172 "nat", "queue", "rules", "Sources",
173 "state", "info", "Tables", "osfp", "all", NULL
174 };
175
176 static const char *showopt_list[] = {
177 "nat", "queue", "rules", "Anchors", "Sources", "state", "info",
178 "Interfaces", "labels", "timeouts", "memory", "Tables", "osfp",
179 "all", NULL
180 };
181
182 static const char *tblcmdopt_list[] = {
183 "kill", "flush", "add", "delete", "load", "replace", "show",
184 "test", "zero", NULL
185 };
186
187 static const char *debugopt_list[] = {
188 "none", "urgent", "misc", "loud", NULL
189 };
190
191
192 void
usage(void)193 usage(void)
194 {
195 extern char *__progname;
196
197 fprintf(stderr, "usage: %s [-AdeghNnOqRrvz] ", __progname);
198 fprintf(stderr, "[-a anchor[:ruleset]] [-D macro=value]\n");
199 fprintf(stderr, " ");
200 fprintf(stderr, "[-F modifier] [-f file] [-i interface] ");
201 fprintf(stderr, "[-k host] [-p device]\n");
202 fprintf(stderr, " ");
203 fprintf(stderr, "[-s modifier] [-T command [address ...]] ");
204 fprintf(stderr, "[-t table] [-x level]\n");
205 exit(1);
206 }
207
208 int
pfctl_enable(int dev,int opts)209 pfctl_enable(int dev, int opts)
210 {
211 if (ioctl(dev, DIOCSTART)) {
212 if (errno == EEXIST)
213 errx(1, "pf already enabled");
214 else
215 err(1, "DIOCSTART");
216 }
217 if ((opts & PF_OPT_QUIET) == 0)
218 fprintf(stderr, "pf enabled\n");
219
220 if (altqsupport && ioctl(dev, DIOCSTARTALTQ))
221 if (errno != EEXIST)
222 err(1, "DIOCSTARTALTQ");
223
224 return (0);
225 }
226
227 int
pfctl_disable(int dev,int opts)228 pfctl_disable(int dev, int opts)
229 {
230 if (ioctl(dev, DIOCSTOP)) {
231 if (errno == ENOENT)
232 errx(1, "pf not enabled");
233 else
234 err(1, "DIOCSTOP");
235 }
236 if ((opts & PF_OPT_QUIET) == 0)
237 fprintf(stderr, "pf disabled\n");
238
239 if (altqsupport && ioctl(dev, DIOCSTOPALTQ))
240 if (errno != ENOENT)
241 err(1, "DIOCSTOPALTQ");
242
243 return (0);
244 }
245
246 int
pfctl_clear_stats(int dev,int opts)247 pfctl_clear_stats(int dev, int opts)
248 {
249 if (ioctl(dev, DIOCCLRSTATUS))
250 err(1, "DIOCCLRSTATUS");
251 if ((opts & PF_OPT_QUIET) == 0)
252 fprintf(stderr, "pf: statistics cleared\n");
253 return (0);
254 }
255
256 int
pfctl_clear_rules(int dev,int opts,char * anchorname,char * rulesetname)257 pfctl_clear_rules(int dev, int opts, char *anchorname, char *rulesetname)
258 {
259 struct pfr_buffer t;
260
261 if (*anchorname && !*rulesetname) {
262 struct pfioc_ruleset pr;
263 int mnr, nr, r;
264
265 memset(&pr, 0, sizeof(pr));
266 memcpy(pr.anchor, anchorname, sizeof(pr.anchor));
267 if (ioctl(dev, DIOCGETRULESETS, &pr)) {
268 if (errno == EINVAL)
269 fprintf(stderr, "No rulesets in anchor '%s'.\n",
270 anchorname);
271 else
272 err(1, "DIOCGETRULESETS");
273 return (-1);
274 }
275 mnr = pr.nr;
276 for (nr = mnr - 1; nr >= 0; --nr) {
277 pr.nr = nr;
278 if (ioctl(dev, DIOCGETRULESET, &pr))
279 err(1, "DIOCGETRULESET");
280 r = pfctl_clear_rules(dev, opts | PF_OPT_QUIET,
281 anchorname, pr.name);
282 if (r)
283 return (r);
284 }
285 if ((opts & PF_OPT_QUIET) == 0)
286 fprintf(stderr, "rules cleared\n");
287 return (0);
288 }
289 memset(&t, 0, sizeof(t));
290 t.pfrb_type = PFRB_TRANS;
291 if (pfctl_add_trans(&t, PF_RULESET_SCRUB, anchorname, rulesetname) ||
292 pfctl_add_trans(&t, PF_RULESET_FILTER, anchorname, rulesetname) ||
293 pfctl_trans(dev, &t, DIOCXBEGIN, 0) ||
294 pfctl_trans(dev, &t, DIOCXCOMMIT, 0))
295 err(1, "pfctl_clear_rules");
296 if ((opts & PF_OPT_QUIET) == 0)
297 fprintf(stderr, "rules cleared\n");
298 return (0);
299 }
300
301 int
pfctl_clear_nat(int dev,int opts,char * anchorname,char * rulesetname)302 pfctl_clear_nat(int dev, int opts, char *anchorname, char *rulesetname)
303 {
304 struct pfr_buffer t;
305
306 if (*anchorname && !*rulesetname) {
307 struct pfioc_ruleset pr;
308 int mnr, nr, r;
309
310 memset(&pr, 0, sizeof(pr));
311 memcpy(pr.anchor, anchorname, sizeof(pr.anchor));
312 if (ioctl(dev, DIOCGETRULESETS, &pr)) {
313 if (errno == EINVAL)
314 fprintf(stderr, "No rulesets in anchor '%s'.\n",
315 anchorname);
316 else
317 err(1, "DIOCGETRULESETS");
318 return (-1);
319 }
320 mnr = pr.nr;
321 for (nr = mnr - 1; nr >= 0; --nr) {
322 pr.nr = nr;
323 if (ioctl(dev, DIOCGETRULESET, &pr))
324 err(1, "DIOCGETRULESET");
325 r = pfctl_clear_nat(dev, opts | PF_OPT_QUIET,
326 anchorname, pr.name);
327 if (r)
328 return (r);
329 }
330 if ((opts & PF_OPT_QUIET) == 0)
331 fprintf(stderr, "nat cleared\n");
332 return (0);
333 }
334 memset(&t, 0, sizeof(t));
335 t.pfrb_type = PFRB_TRANS;
336 if (pfctl_add_trans(&t, PF_RULESET_NAT, anchorname, rulesetname) ||
337 pfctl_add_trans(&t, PF_RULESET_BINAT, anchorname, rulesetname) ||
338 pfctl_add_trans(&t, PF_RULESET_RDR, anchorname, rulesetname) ||
339 pfctl_trans(dev, &t, DIOCXBEGIN, 0) ||
340 pfctl_trans(dev, &t, DIOCXCOMMIT, 0))
341 err(1, "pfctl_clear_nat");
342 if ((opts & PF_OPT_QUIET) == 0)
343 fprintf(stderr, "nat cleared\n");
344 return (0);
345 }
346
347 int
pfctl_clear_altq(int dev,int opts)348 pfctl_clear_altq(int dev, int opts)
349 {
350 struct pfr_buffer t;
351
352 if (!altqsupport)
353 return (-1);
354 memset(&t, 0, sizeof(t));
355 t.pfrb_type = PFRB_TRANS;
356 if (pfctl_add_trans(&t, PF_RULESET_ALTQ, "", "") ||
357 pfctl_trans(dev, &t, DIOCXBEGIN, 0) ||
358 pfctl_trans(dev, &t, DIOCXCOMMIT, 0))
359 err(1, "pfctl_clear_altq");
360 if ((opts & PF_OPT_QUIET) == 0)
361 fprintf(stderr, "altq cleared\n");
362 return (0);
363 }
364
365 int
pfctl_clear_src_nodes(int dev,int opts)366 pfctl_clear_src_nodes(int dev, int opts)
367 {
368 if (ioctl(dev, DIOCCLRSRCNODES))
369 err(1, "DIOCCLRSRCNODES");
370 if ((opts & PF_OPT_QUIET) == 0)
371 fprintf(stderr, "source tracking entries cleared\n");
372 return (0);
373 }
374
375 int
pfctl_clear_states(int dev,const char * iface,int opts)376 pfctl_clear_states(int dev, const char *iface, int opts)
377 {
378 struct pfioc_state_kill psk;
379
380 memset(&psk, 0, sizeof(psk));
381 if (iface != NULL && strlcpy(psk.psk_ifname, iface,
382 sizeof(psk.psk_ifname)) >= sizeof(psk.psk_ifname))
383 errx(1, "invalid interface: %s", iface);
384
385 if (ioctl(dev, DIOCCLRSTATES, &psk))
386 err(1, "DIOCCLRSTATES");
387 if ((opts & PF_OPT_QUIET) == 0)
388 fprintf(stderr, "%d states cleared\n", psk.psk_af);
389 return (0);
390 }
391
392 int
pfctl_kill_states(int dev,const char * iface,int opts)393 pfctl_kill_states(int dev, const char *iface, int opts)
394 {
395 struct pfioc_state_kill psk;
396 struct addrinfo *res[2], *resp[2];
397 struct sockaddr last_src, last_dst;
398 int killed, sources, dests;
399 int ret_ga;
400
401 killed = sources = dests = 0;
402
403 memset(&psk, 0, sizeof(psk));
404 memset(&psk.psk_src.addr.v.a.mask, 0xff,
405 sizeof(psk.psk_src.addr.v.a.mask));
406 memset(&last_src, 0xff, sizeof(last_src));
407 memset(&last_dst, 0xff, sizeof(last_dst));
408 if (iface != NULL && strlcpy(psk.psk_ifname, iface,
409 sizeof(psk.psk_ifname)) >= sizeof(psk.psk_ifname))
410 errx(1, "invalid interface: %s", iface);
411
412 if ((ret_ga = getaddrinfo(state_kill[0], NULL, NULL, &res[0]))) {
413 errx(1, "getaddrinfo: %s", gai_strerror(ret_ga));
414 /* NOTREACHED */
415 }
416 for (resp[0] = res[0]; resp[0]; resp[0] = resp[0]->ai_next) {
417 if (resp[0]->ai_addr == NULL)
418 continue;
419 /* We get lots of duplicates. Catch the easy ones */
420 if (memcmp(&last_src, resp[0]->ai_addr, sizeof(last_src)) == 0)
421 continue;
422 last_src = *(struct sockaddr *)resp[0]->ai_addr;
423
424 psk.psk_af = resp[0]->ai_family;
425 sources++;
426
427 if (psk.psk_af == AF_INET)
428 psk.psk_src.addr.v.a.addr.v4 =
429 ((struct sockaddr_in *)resp[0]->ai_addr)->sin_addr;
430 else if (psk.psk_af == AF_INET6)
431 psk.psk_src.addr.v.a.addr.v6 =
432 ((struct sockaddr_in6 *)resp[0]->ai_addr)->
433 sin6_addr;
434 else
435 errx(1, "Unknown address family %d", psk.psk_af);
436
437 if (state_killers > 1) {
438 dests = 0;
439 memset(&psk.psk_dst.addr.v.a.mask, 0xff,
440 sizeof(psk.psk_dst.addr.v.a.mask));
441 memset(&last_dst, 0xff, sizeof(last_dst));
442 if ((ret_ga = getaddrinfo(state_kill[1], NULL, NULL,
443 &res[1]))) {
444 errx(1, "getaddrinfo: %s",
445 gai_strerror(ret_ga));
446 /* NOTREACHED */
447 }
448 for (resp[1] = res[1]; resp[1];
449 resp[1] = resp[1]->ai_next) {
450 if (resp[1]->ai_addr == NULL)
451 continue;
452 if (psk.psk_af != resp[1]->ai_family)
453 continue;
454
455 if (memcmp(&last_dst, resp[1]->ai_addr,
456 sizeof(last_dst)) == 0)
457 continue;
458 last_dst = *(struct sockaddr *)resp[1]->ai_addr;
459
460 dests++;
461
462 if (psk.psk_af == AF_INET)
463 psk.psk_dst.addr.v.a.addr.v4 =
464 ((struct sockaddr_in *)resp[1]->
465 ai_addr)->sin_addr;
466 else if (psk.psk_af == AF_INET6)
467 psk.psk_dst.addr.v.a.addr.v6 =
468 ((struct sockaddr_in6 *)resp[1]->
469 ai_addr)->sin6_addr;
470 else
471 errx(1, "Unknown address family %d",
472 psk.psk_af);
473
474 if (ioctl(dev, DIOCKILLSTATES, &psk))
475 err(1, "DIOCKILLSTATES");
476 killed += psk.psk_af;
477 /* fixup psk.psk_af */
478 psk.psk_af = resp[1]->ai_family;
479 }
480 freeaddrinfo(res[1]);
481 } else {
482 if (ioctl(dev, DIOCKILLSTATES, &psk))
483 err(1, "DIOCKILLSTATES");
484 killed += psk.psk_af;
485 /* fixup psk.psk_af */
486 psk.psk_af = res[0]->ai_family;
487 }
488 }
489
490 freeaddrinfo(res[0]);
491
492 if ((opts & PF_OPT_QUIET) == 0)
493 fprintf(stderr, "killed %d states from %d sources and %d "
494 "destinations\n", killed, sources, dests);
495 return (0);
496 }
497
498 int
pfctl_get_pool(int dev,struct pf_pool * pool,u_int32_t nr,u_int32_t ticket,int r_action,char * anchorname,char * rulesetname)499 pfctl_get_pool(int dev, struct pf_pool *pool, u_int32_t nr,
500 u_int32_t ticket, int r_action, char *anchorname, char *rulesetname)
501 {
502 struct pfioc_pooladdr pp;
503 struct pf_pooladdr *pa;
504 u_int32_t pnr, mpnr;
505
506 memset(&pp, 0, sizeof(pp));
507 memcpy(pp.anchor, anchorname, sizeof(pp.anchor));
508 memcpy(pp.ruleset, rulesetname, sizeof(pp.ruleset));
509 pp.r_action = r_action;
510 pp.r_num = nr;
511 pp.ticket = ticket;
512 if (ioctl(dev, DIOCGETADDRS, &pp)) {
513 warn("DIOCGETADDRS");
514 return (-1);
515 }
516 mpnr = pp.nr;
517 TAILQ_INIT(&pool->list);
518 for (pnr = 0; pnr < mpnr; ++pnr) {
519 pp.nr = pnr;
520 if (ioctl(dev, DIOCGETADDR, &pp)) {
521 warn("DIOCGETADDR");
522 return (-1);
523 }
524 pa = calloc(1, sizeof(struct pf_pooladdr));
525 if (pa == NULL)
526 err(1, "calloc");
527 memmove(pa, &pp.addr, sizeof(struct pf_pooladdr));
528 TAILQ_INSERT_TAIL(&pool->list, pa, entries);
529 }
530
531 return (0);
532 }
533
534 void
pfctl_clear_pool(struct pf_pool * pool)535 pfctl_clear_pool(struct pf_pool *pool)
536 {
537 struct pf_pooladdr *pa;
538
539 while ((pa = TAILQ_FIRST(&pool->list)) != NULL) {
540 TAILQ_REMOVE(&pool->list, pa, entries);
541 free(pa);
542 }
543 }
544
545 void
pfctl_print_rule_counters(struct pf_rule * rule,int opts)546 pfctl_print_rule_counters(struct pf_rule *rule, int opts)
547 {
548 if (opts & PF_OPT_DEBUG) {
549 const char *t[PF_SKIP_COUNT] = { "i", "d", "f",
550 "p", "sa", "sp", "da", "dp" };
551 int i;
552
553 printf(" [ Skip steps: ");
554 for (i = 0; i < PF_SKIP_COUNT; ++i) {
555 if (rule->skip[i].nr == rule->nr + 1)
556 continue;
557 printf("%s=", t[i]);
558 if (rule->skip[i].nr == -1)
559 printf("end ");
560 else
561 printf("%u ", rule->skip[i].nr);
562 }
563 printf("]\n");
564
565 printf(" [ queue: qname=%s qid=%u pqname=%s pqid=%u ]\n",
566 rule->qname, rule->qid, rule->pqname, rule->pqid);
567 }
568 if (opts & PF_OPT_VERBOSE)
569 printf(" [ Evaluations: %-8llu Packets: %-8llu "
570 "Bytes: %-10llu States: %-6u]\n",
571 (unsigned long long)rule->evaluations,
572 (unsigned long long)rule->packets,
573 (unsigned long long)rule->bytes, rule->states);
574 }
575
576 void
pfctl_print_title(char * title)577 pfctl_print_title(char *title)
578 {
579 if (!first_title)
580 printf("\n");
581 first_title = 0;
582 printf("%s\n", title);
583 }
584
585 int
pfctl_show_rules(int dev,int opts,int format,char * anchorname,char * rulesetname)586 pfctl_show_rules(int dev, int opts, int format, char *anchorname,
587 char *rulesetname)
588 {
589 struct pfioc_rule pr;
590 u_int32_t nr, mnr, header = 0;
591 int rule_numbers = opts & (PF_OPT_VERBOSE2 | PF_OPT_DEBUG);
592
593 if (*anchorname && !*rulesetname) {
594 struct pfioc_ruleset pr;
595 int r;
596
597 memset(&pr, 0, sizeof(pr));
598 memcpy(pr.anchor, anchorname, sizeof(pr.anchor));
599 if (ioctl(dev, DIOCGETRULESETS, &pr)) {
600 if (errno == EINVAL)
601 fprintf(stderr, "No rulesets in anchor '%s'.\n",
602 anchorname);
603 else
604 err(1, "DIOCGETRULESETS");
605 return (-1);
606 }
607 if (opts & PF_OPT_SHOWALL && pr.nr)
608 pfctl_print_title("FILTER RULES:");
609 mnr = pr.nr;
610 for (nr = 0; nr < mnr; ++nr) {
611 pr.nr = nr;
612 if (ioctl(dev, DIOCGETRULESET, &pr))
613 err(1, "DIOCGETRULESET");
614 r = pfctl_show_rules(dev, opts, format, anchorname,
615 pr.name);
616 if (r)
617 return (r);
618 }
619 return (0);
620 }
621
622 memset(&pr, 0, sizeof(pr));
623 memcpy(pr.anchor, anchorname, sizeof(pr.anchor));
624 memcpy(pr.ruleset, rulesetname, sizeof(pr.ruleset));
625 if (opts & PF_OPT_SHOWALL) {
626 pr.rule.action = PF_PASS;
627 if (ioctl(dev, DIOCGETRULES, &pr)) {
628 warn("DIOCGETRULES");
629 return (-1);
630 }
631 header++;
632 }
633 pr.rule.action = PF_SCRUB;
634 if (ioctl(dev, DIOCGETRULES, &pr)) {
635 warn("DIOCGETRULES");
636 return (-1);
637 }
638 if (opts & PF_OPT_SHOWALL) {
639 if (format == 0 && (pr.nr > 0 || header))
640 pfctl_print_title("FILTER RULES:");
641 else if (format == 1 && labels)
642 pfctl_print_title("LABEL COUNTERS:");
643 }
644 mnr = pr.nr;
645 for (nr = 0; nr < mnr; ++nr) {
646 pr.nr = nr;
647 if (ioctl(dev, DIOCGETRULE, &pr)) {
648 warn("DIOCGETRULE");
649 return (-1);
650 }
651
652 if (pfctl_get_pool(dev, &pr.rule.rpool,
653 nr, pr.ticket, PF_SCRUB, anchorname, rulesetname) != 0)
654 return (-1);
655
656 switch (format) {
657 case 1:
658 if (pr.rule.label[0]) {
659 printf("%s ", pr.rule.label);
660 printf("%llu %llu %llu\n",
661 (unsigned long long)pr.rule.evaluations,
662 (unsigned long long)pr.rule.packets,
663 (unsigned long long)pr.rule.bytes);
664 }
665 break;
666 default:
667 if (pr.rule.label[0] && (opts & PF_OPT_SHOWALL))
668 labels = 1;
669 print_rule(&pr.rule, rule_numbers);
670 pfctl_print_rule_counters(&pr.rule, opts);
671 }
672 pfctl_clear_pool(&pr.rule.rpool);
673 }
674 pr.rule.action = PF_PASS;
675 if (ioctl(dev, DIOCGETRULES, &pr)) {
676 warn("DIOCGETRULES");
677 return (-1);
678 }
679 mnr = pr.nr;
680 for (nr = 0; nr < mnr; ++nr) {
681 pr.nr = nr;
682 if (ioctl(dev, DIOCGETRULE, &pr)) {
683 warn("DIOCGETRULE");
684 return (-1);
685 }
686
687 if (pfctl_get_pool(dev, &pr.rule.rpool,
688 nr, pr.ticket, PF_PASS, anchorname, rulesetname) != 0)
689 return (-1);
690
691 switch (format) {
692 case 1:
693 if (pr.rule.label[0]) {
694 printf("%s ", pr.rule.label);
695 printf("%llu %llu %llu\n",
696 (unsigned long long)pr.rule.evaluations,
697 (unsigned long long)pr.rule.packets,
698 (unsigned long long)pr.rule.bytes);
699 }
700 break;
701 default:
702 if (pr.rule.label[0] && (opts & PF_OPT_SHOWALL))
703 labels = 1;
704 print_rule(&pr.rule, rule_numbers);
705 pfctl_print_rule_counters(&pr.rule, opts);
706 }
707 pfctl_clear_pool(&pr.rule.rpool);
708 }
709 return (0);
710 }
711
712 int
pfctl_show_nat(int dev,int opts,char * anchorname,char * rulesetname)713 pfctl_show_nat(int dev, int opts, char *anchorname, char *rulesetname)
714 {
715 struct pfioc_rule pr;
716 u_int32_t mnr, nr;
717 static int nattype[3] = { PF_NAT, PF_RDR, PF_BINAT };
718 int i, dotitle = opts & PF_OPT_SHOWALL;
719
720 if (*anchorname && !*rulesetname) {
721 struct pfioc_ruleset pr;
722 int r;
723
724 memset(&pr, 0, sizeof(pr));
725 memcpy(pr.anchor, anchorname, sizeof(pr.anchor));
726 if (ioctl(dev, DIOCGETRULESETS, &pr)) {
727 if (errno == EINVAL)
728 fprintf(stderr, "No rulesets in anchor '%s'.\n",
729 anchorname);
730 else
731 err(1, "DIOCGETRULESETS");
732 return (-1);
733 }
734 mnr = pr.nr;
735 for (nr = 0; nr < mnr; ++nr) {
736 pr.nr = nr;
737 if (ioctl(dev, DIOCGETRULESET, &pr))
738 err(1, "DIOCGETRULESET");
739 r = pfctl_show_nat(dev, opts, anchorname, pr.name);
740 if (r)
741 return (r);
742 }
743 return (0);
744 }
745
746 memset(&pr, 0, sizeof(pr));
747 memcpy(pr.anchor, anchorname, sizeof(pr.anchor));
748 memcpy(pr.ruleset, rulesetname, sizeof(pr.ruleset));
749 for (i = 0; i < 3; i++) {
750 pr.rule.action = nattype[i];
751 if (ioctl(dev, DIOCGETRULES, &pr)) {
752 warn("DIOCGETRULES");
753 return (-1);
754 }
755 mnr = pr.nr;
756 for (nr = 0; nr < mnr; ++nr) {
757 pr.nr = nr;
758 if (ioctl(dev, DIOCGETRULE, &pr)) {
759 warn("DIOCGETRULE");
760 return (-1);
761 }
762 if (pfctl_get_pool(dev, &pr.rule.rpool, nr,
763 pr.ticket, nattype[i], anchorname,
764 rulesetname) != 0)
765 return (-1);
766 if (dotitle) {
767 pfctl_print_title("TRANSLATION RULES:");
768 dotitle = 0;
769 }
770 print_rule(&pr.rule, opts & PF_OPT_VERBOSE2);
771 pfctl_print_rule_counters(&pr.rule, opts);
772 pfctl_clear_pool(&pr.rule.rpool);
773 }
774 }
775 return (0);
776 }
777
778 int
pfctl_show_src_nodes(int dev,int opts)779 pfctl_show_src_nodes(int dev, int opts)
780 {
781 struct pfioc_src_nodes psn;
782 struct pf_src_node *p;
783 char *inbuf = NULL, *newinbuf = NULL;
784 unsigned len = 0;
785 int i;
786
787 memset(&psn, 0, sizeof(psn));
788 for (;;) {
789 psn.psn_len = len;
790 if (len) {
791 newinbuf = realloc(inbuf, len);
792 if (newinbuf == NULL)
793 err(1, "realloc");
794 psn.psn_buf = inbuf = newinbuf;
795 }
796 if (ioctl(dev, DIOCGETSRCNODES, &psn) < 0) {
797 warn("DIOCGETSRCNODES");
798 return (-1);
799 }
800 if (psn.psn_len + sizeof(struct pfioc_src_nodes) < len)
801 break;
802 if (len == 0 && psn.psn_len == 0)
803 return (0);
804 if (len == 0 && psn.psn_len != 0)
805 len = psn.psn_len;
806 if (psn.psn_len == 0)
807 return (0); /* no src_nodes */
808 len *= 2;
809 }
810 p = psn.psn_src_nodes;
811 if (psn.psn_len > 0 && (opts & PF_OPT_SHOWALL))
812 pfctl_print_title("SOURCE TRACKING NODES:");
813 for (i = 0; i < psn.psn_len; i += sizeof(*p)) {
814 print_src_node(p, opts);
815 p++;
816 }
817 return (0);
818 }
819
820 int
pfctl_show_states(int dev,const char * iface,int opts)821 pfctl_show_states(int dev, const char *iface, int opts)
822 {
823 struct pfioc_states ps;
824 struct pf_state *p;
825 char *inbuf = NULL, *newinbuf = NULL;
826 unsigned len = 0;
827 int i, dotitle = (opts & PF_OPT_SHOWALL);
828
829 memset(&ps, 0, sizeof(ps));
830 for (;;) {
831 ps.ps_len = len;
832 if (len) {
833 newinbuf = realloc(inbuf, len);
834 if (newinbuf == NULL)
835 err(1, "realloc");
836 ps.ps_buf = inbuf = newinbuf;
837 }
838 if (ioctl(dev, DIOCGETSTATES, &ps) < 0) {
839 warn("DIOCGETSTATES");
840 return (-1);
841 }
842 if (ps.ps_len + sizeof(struct pfioc_states) < len)
843 break;
844 if (len == 0 && ps.ps_len == 0)
845 return (0);
846 if (len == 0 && ps.ps_len != 0)
847 len = ps.ps_len;
848 if (ps.ps_len == 0)
849 return (0); /* no states */
850 len *= 2;
851 }
852 p = ps.ps_states;
853 for (i = 0; i < ps.ps_len; i += sizeof(*p), p++) {
854 if (iface != NULL && strcmp(p->u.ifname, iface))
855 continue;
856 if (dotitle) {
857 pfctl_print_title("STATES:");
858 dotitle = 0;
859 }
860 print_state(p, opts);
861 }
862 return (0);
863 }
864
865 int
pfctl_show_status(int dev,int opts)866 pfctl_show_status(int dev, int opts)
867 {
868 struct pf_status status;
869
870 if (ioctl(dev, DIOCGETSTATUS, &status)) {
871 warn("DIOCGETSTATUS");
872 return (-1);
873 }
874 if (opts & PF_OPT_SHOWALL)
875 pfctl_print_title("INFO:");
876 print_status(&status, opts);
877 return (0);
878 }
879
880 int
pfctl_show_timeouts(int dev,int opts)881 pfctl_show_timeouts(int dev, int opts)
882 {
883 struct pfioc_tm pt;
884 int i;
885
886 if (opts & PF_OPT_SHOWALL)
887 pfctl_print_title("TIMEOUTS:");
888 memset(&pt, 0, sizeof(pt));
889 for (i = 0; pf_timeouts[i].name; i++) {
890 pt.timeout = pf_timeouts[i].timeout;
891 if (ioctl(dev, DIOCGETTIMEOUT, &pt))
892 err(1, "DIOCGETTIMEOUT");
893 printf("%-20s %10d", pf_timeouts[i].name, pt.seconds);
894 if (i >= PFTM_ADAPTIVE_START && i <= PFTM_ADAPTIVE_END)
895 printf(" states");
896 else
897 printf("s");
898 printf("\n");
899 }
900 return (0);
901
902 }
903
904 int
pfctl_show_limits(int dev,int opts)905 pfctl_show_limits(int dev, int opts)
906 {
907 struct pfioc_limit pl;
908 int i;
909
910 if (opts & PF_OPT_SHOWALL)
911 pfctl_print_title("LIMITS:");
912 memset(&pl, 0, sizeof(pl));
913 for (i = 0; pf_limits[i].name; i++) {
914 pl.index = pf_limits[i].index;
915 if (ioctl(dev, DIOCGETLIMIT, &pl))
916 err(1, "DIOCGETLIMIT");
917 printf("%-10s ", pf_limits[i].name);
918 if (pl.limit == UINT_MAX)
919 printf("unlimited\n");
920 else
921 printf("hard limit %6u\n", pl.limit);
922 }
923 return (0);
924 }
925
926 /* callbacks for rule/nat/rdr/addr */
927 int
pfctl_add_pool(struct pfctl * pf,struct pf_pool * p,sa_family_t af)928 pfctl_add_pool(struct pfctl *pf, struct pf_pool *p, sa_family_t af)
929 {
930 struct pf_pooladdr *pa;
931
932 if ((pf->opts & PF_OPT_NOACTION) == 0) {
933 if (ioctl(pf->dev, DIOCBEGINADDRS, &pf->paddr))
934 err(1, "DIOCBEGINADDRS");
935 }
936
937 pf->paddr.af = af;
938 TAILQ_FOREACH(pa, &p->list, entries) {
939 memcpy(&pf->paddr.addr, pa, sizeof(struct pf_pooladdr));
940 if ((pf->opts & PF_OPT_NOACTION) == 0) {
941 if (ioctl(pf->dev, DIOCADDADDR, &pf->paddr))
942 err(1, "DIOCADDADDR");
943 }
944 }
945 return (0);
946 }
947
948 int
pfctl_add_rule(struct pfctl * pf,struct pf_rule * r)949 pfctl_add_rule(struct pfctl *pf, struct pf_rule *r)
950 {
951 u_int8_t rs_num;
952 struct pfioc_rule pr;
953
954 switch (r->action) {
955 case PF_SCRUB:
956 if ((loadopt & PFCTL_FLAG_FILTER) == 0)
957 return (0);
958 rs_num = PF_RULESET_SCRUB;
959 break;
960 case PF_DROP:
961 case PF_PASS:
962 if ((loadopt & PFCTL_FLAG_FILTER) == 0)
963 return (0);
964 rs_num = PF_RULESET_FILTER;
965 break;
966 case PF_NAT:
967 case PF_NONAT:
968 if ((loadopt & PFCTL_FLAG_NAT) == 0)
969 return (0);
970 rs_num = PF_RULESET_NAT;
971 break;
972 case PF_RDR:
973 case PF_NORDR:
974 if ((loadopt & PFCTL_FLAG_NAT) == 0)
975 return (0);
976 rs_num = PF_RULESET_RDR;
977 break;
978 case PF_BINAT:
979 case PF_NOBINAT:
980 if ((loadopt & PFCTL_FLAG_NAT) == 0)
981 return (0);
982 rs_num = PF_RULESET_BINAT;
983 break;
984 default:
985 errx(1, "Invalid rule type");
986 break;
987 }
988
989 if ((pf->opts & PF_OPT_NOACTION) == 0) {
990 bzero(&pr, sizeof(pr));
991 if (strlcpy(pr.anchor, pf->anchor, sizeof(pr.anchor)) >=
992 sizeof(pr.anchor) ||
993 strlcpy(pr.ruleset, pf->ruleset, sizeof(pr.ruleset)) >=
994 sizeof(pr.ruleset))
995 errx(1, "pfctl_add_rule: strlcpy");
996 if (pfctl_add_pool(pf, &r->rpool, r->af))
997 return (1);
998 pr.ticket = pfctl_get_ticket(pf->trans, rs_num, pf->anchor,
999 pf->ruleset);
1000 pr.pool_ticket = pf->paddr.ticket;
1001 memcpy(&pr.rule, r, sizeof(pr.rule));
1002 if (ioctl(pf->dev, DIOCADDRULE, &pr))
1003 err(1, "DIOCADDRULE");
1004 }
1005 if (pf->opts & PF_OPT_VERBOSE)
1006 print_rule(r, pf->opts & PF_OPT_VERBOSE2);
1007 pfctl_clear_pool(&r->rpool);
1008 return (0);
1009 }
1010
1011 int
pfctl_add_altq(struct pfctl * pf,struct pf_altq * a)1012 pfctl_add_altq(struct pfctl *pf, struct pf_altq *a)
1013 {
1014 if (altqsupport &&
1015 (loadopt & PFCTL_FLAG_ALTQ) != 0) {
1016 memcpy(&pf->paltq->altq, a, sizeof(struct pf_altq));
1017 if ((pf->opts & PF_OPT_NOACTION) == 0) {
1018 if (ioctl(pf->dev, DIOCADDALTQ, pf->paltq)) {
1019 if (errno == ENXIO)
1020 errx(1, "qtype not configured");
1021 else if (errno == ENODEV)
1022 errx(1, "%s: driver does not support "
1023 "altq", a->ifname);
1024 else
1025 err(1, "DIOCADDALTQ");
1026 }
1027 }
1028 pfaltq_store(&pf->paltq->altq);
1029 }
1030 return (0);
1031 }
1032
1033 int
pfctl_rules(int dev,char * filename,int opts,char * anchorname,char * rulesetname,struct pfr_buffer * trans)1034 pfctl_rules(int dev, char *filename, int opts, char *anchorname,
1035 char *rulesetname, struct pfr_buffer *trans)
1036 {
1037 #define ERR(x) do { warn(x); goto _error; } while(0)
1038 #define ERRX(x) do { warnx(x); goto _error; } while(0)
1039
1040 FILE *fin;
1041 struct pfr_buffer *t, buf;
1042 struct pfioc_altq pa;
1043 struct pfctl pf;
1044 struct pfr_table trs;
1045 int osize;
1046
1047 if (trans == NULL) {
1048 bzero(&buf, sizeof(buf));
1049 buf.pfrb_type = PFRB_TRANS;
1050 t = &buf;
1051 osize = 0;
1052 } else {
1053 t = trans;
1054 osize = t->pfrb_size;
1055 }
1056
1057 memset(&pa, 0, sizeof(pa));
1058 memset(&pf, 0, sizeof(pf));
1059 memset(&trs, 0, sizeof(trs));
1060 if (strlcpy(trs.pfrt_anchor, anchorname,
1061 sizeof(trs.pfrt_anchor)) >= sizeof(trs.pfrt_anchor) ||
1062 strlcpy(trs.pfrt_ruleset, rulesetname,
1063 sizeof(trs.pfrt_ruleset)) >= sizeof(trs.pfrt_ruleset))
1064 ERRX("pfctl_rules: strlcpy");
1065 if (strcmp(filename, "-") == 0) {
1066 fin = stdin;
1067 infile = "stdin";
1068 } else {
1069 if ((fin = pfctl_fopen(filename, "r")) == NULL) {
1070 warn("%s", filename);
1071 return (1);
1072 }
1073 infile = filename;
1074 }
1075 pf.dev = dev;
1076 pf.opts = opts;
1077 pf.loadopt = loadopt;
1078 if (anchorname[0])
1079 pf.loadopt &= ~PFCTL_FLAG_ALTQ;
1080 pf.paltq = &pa;
1081 pf.trans = t;
1082 pf.rule_nr = 0;
1083 pf.anchor = anchorname;
1084 pf.ruleset = rulesetname;
1085
1086 if ((opts & PF_OPT_NOACTION) == 0) {
1087 if ((pf.loadopt & PFCTL_FLAG_NAT) != 0) {
1088 if (pfctl_add_trans(t, PF_RULESET_NAT, anchorname,
1089 rulesetname) ||
1090 pfctl_add_trans(t, PF_RULESET_BINAT, anchorname,
1091 rulesetname) ||
1092 pfctl_add_trans(t, PF_RULESET_RDR, anchorname,
1093 rulesetname))
1094 ERR("pfctl_rules");
1095 }
1096 if (((altqsupport && (pf.loadopt & PFCTL_FLAG_ALTQ) != 0))) {
1097 if (pfctl_add_trans(t, PF_RULESET_ALTQ, anchorname,
1098 rulesetname))
1099 ERR("pfctl_rules");
1100 }
1101 if ((pf.loadopt & PFCTL_FLAG_FILTER) != 0) {
1102 if (pfctl_add_trans(t, PF_RULESET_SCRUB, anchorname,
1103 rulesetname) ||
1104 pfctl_add_trans(t, PF_RULESET_FILTER, anchorname,
1105 rulesetname))
1106 ERR("pfctl_rules");
1107 }
1108 if (pf.loadopt & PFCTL_FLAG_TABLE) {
1109 if (pfctl_add_trans(t, PF_RULESET_TABLE, anchorname,
1110 rulesetname))
1111 ERR("pfctl_rules");
1112 }
1113 if (pfctl_trans(dev, t, DIOCXBEGIN, osize))
1114 ERR("DIOCXBEGIN");
1115 if (altqsupport && (pf.loadopt & PFCTL_FLAG_ALTQ))
1116 pa.ticket = pfctl_get_ticket(t, PF_RULESET_ALTQ,
1117 anchorname, rulesetname);
1118 if (pf.loadopt & PFCTL_FLAG_TABLE)
1119 pf.tticket = pfctl_get_ticket(t, PF_RULESET_TABLE,
1120 anchorname, rulesetname);
1121 }
1122 if (parse_rules(fin, &pf) < 0) {
1123 if ((opts & PF_OPT_NOACTION) == 0)
1124 ERRX("Syntax error in config file: "
1125 "pf rules not loaded");
1126 else
1127 goto _error;
1128 }
1129 if ((altqsupport && (pf.loadopt & PFCTL_FLAG_ALTQ) != 0))
1130 if (check_commit_altq(dev, opts) != 0)
1131 ERRX("errors in altq config");
1132 if (fin != stdin)
1133 fclose(fin);
1134
1135 /* process "load anchor" directives */
1136 if (!anchorname[0] && !rulesetname[0])
1137 if (pfctl_load_anchors(dev, opts, t) == -1)
1138 ERRX("load anchors");
1139
1140 if (trans == NULL && (opts & PF_OPT_NOACTION) == 0)
1141 if (pfctl_trans(dev, t, DIOCXCOMMIT, 0))
1142 ERR("DIOCXCOMMIT");
1143 return (0);
1144
1145 _error:
1146 if (trans == NULL) { /* main ruleset */
1147 if ((opts & PF_OPT_NOACTION) == 0)
1148 if (pfctl_trans(dev, t, DIOCXROLLBACK, 0))
1149 err(1, "DIOCXROLLBACK");
1150 exit(1);
1151 } else /* sub ruleset */
1152 return (-1);
1153
1154 #undef ERR
1155 #undef ERRX
1156 }
1157
1158 FILE *
pfctl_fopen(const char * name,const char * mode)1159 pfctl_fopen(const char *name, const char *mode)
1160 {
1161 struct stat st;
1162 FILE *fp;
1163
1164 fp = fopen(name, mode);
1165 if (fp == NULL)
1166 return (NULL);
1167 if (fstat(fileno(fp), &st)) {
1168 fclose(fp);
1169 return (NULL);
1170 }
1171 if (S_ISDIR(st.st_mode)) {
1172 fclose(fp);
1173 errno = EISDIR;
1174 return (NULL);
1175 }
1176 return (fp);
1177 }
1178
1179 int
pfctl_set_limit(struct pfctl * pf,const char * opt,unsigned int limit)1180 pfctl_set_limit(struct pfctl *pf, const char *opt, unsigned int limit)
1181 {
1182 struct pfioc_limit pl;
1183 int i;
1184
1185 if ((loadopt & PFCTL_FLAG_OPTION) == 0)
1186 return (0);
1187
1188 memset(&pl, 0, sizeof(pl));
1189 for (i = 0; pf_limits[i].name; i++) {
1190 if (strcasecmp(opt, pf_limits[i].name) == 0) {
1191 pl.index = pf_limits[i].index;
1192 pl.limit = limit;
1193 if ((pf->opts & PF_OPT_NOACTION) == 0) {
1194 if (ioctl(pf->dev, DIOCSETLIMIT, &pl)) {
1195 if (errno == EBUSY) {
1196 warnx("Current pool "
1197 "size exceeds requested "
1198 "hard limit");
1199 return (1);
1200 } else
1201 err(1, "DIOCSETLIMIT");
1202 }
1203 }
1204 break;
1205 }
1206 }
1207 if (pf_limits[i].name == NULL) {
1208 warnx("Bad pool name.");
1209 return (1);
1210 }
1211
1212 if (pf->opts & PF_OPT_VERBOSE)
1213 printf("set limit %s %d\n", opt, limit);
1214
1215 return (0);
1216 }
1217
1218 int
pfctl_set_timeout(struct pfctl * pf,const char * opt,int seconds,int quiet)1219 pfctl_set_timeout(struct pfctl *pf, const char *opt, int seconds, int quiet)
1220 {
1221 struct pfioc_tm pt;
1222 int i;
1223
1224 if ((loadopt & PFCTL_FLAG_OPTION) == 0)
1225 return (0);
1226
1227 memset(&pt, 0, sizeof(pt));
1228 for (i = 0; pf_timeouts[i].name; i++) {
1229 if (strcasecmp(opt, pf_timeouts[i].name) == 0) {
1230 pt.timeout = pf_timeouts[i].timeout;
1231 break;
1232 }
1233 }
1234
1235 if (pf_timeouts[i].name == NULL) {
1236 warnx("Bad timeout name.");
1237 return (1);
1238 }
1239
1240 pt.seconds = seconds;
1241 if ((pf->opts & PF_OPT_NOACTION) == 0) {
1242 if (ioctl(pf->dev, DIOCSETTIMEOUT, &pt))
1243 err(1, "DIOCSETTIMEOUT");
1244 }
1245
1246 if (pf->opts & PF_OPT_VERBOSE && ! quiet)
1247 printf("set timeout %s %d\n", opt, seconds);
1248
1249 return (0);
1250 }
1251
1252 int
pfctl_set_optimization(struct pfctl * pf,const char * opt)1253 pfctl_set_optimization(struct pfctl *pf, const char *opt)
1254 {
1255 const struct pf_hint *hint;
1256 int i, r;
1257
1258 if ((loadopt & PFCTL_FLAG_OPTION) == 0)
1259 return (0);
1260
1261 for (i = 0; pf_hints[i].name; i++)
1262 if (strcasecmp(opt, pf_hints[i].name) == 0)
1263 break;
1264
1265 hint = pf_hints[i].hint;
1266 if (hint == NULL) {
1267 warnx("Bad hint name.");
1268 return (1);
1269 }
1270
1271 for (i = 0; hint[i].name; i++)
1272 if ((r = pfctl_set_timeout(pf, hint[i].name,
1273 hint[i].timeout, 1)))
1274 return (r);
1275
1276 if (pf->opts & PF_OPT_VERBOSE)
1277 printf("set optimization %s\n", opt);
1278
1279 return (0);
1280 }
1281
1282 int
pfctl_set_logif(struct pfctl * pf,char * ifname)1283 pfctl_set_logif(struct pfctl *pf, char *ifname)
1284 {
1285 struct pfioc_if pi;
1286
1287 if ((loadopt & PFCTL_FLAG_OPTION) == 0)
1288 return (0);
1289
1290 memset(&pi, 0, sizeof(pi));
1291 if ((pf->opts & PF_OPT_NOACTION) == 0) {
1292 if (!strcmp(ifname, "none"))
1293 bzero(pi.ifname, sizeof(pi.ifname));
1294 else {
1295 if (strlcpy(pi.ifname, ifname,
1296 sizeof(pi.ifname)) >= sizeof(pi.ifname))
1297 errx(1, "pfctl_set_logif: strlcpy");
1298 }
1299 if (ioctl(pf->dev, DIOCSETSTATUSIF, &pi))
1300 err(1, "DIOCSETSTATUSIF");
1301 }
1302
1303 if (pf->opts & PF_OPT_VERBOSE)
1304 printf("set loginterface %s\n", ifname);
1305
1306 return (0);
1307 }
1308
1309 int
pfctl_set_hostid(struct pfctl * pf,u_int32_t hostid)1310 pfctl_set_hostid(struct pfctl *pf, u_int32_t hostid)
1311 {
1312 if ((loadopt & PFCTL_FLAG_OPTION) == 0)
1313 return (0);
1314
1315 HTONL(hostid);
1316
1317 if ((pf->opts & PF_OPT_NOACTION) == 0)
1318 if (ioctl(dev, DIOCSETHOSTID, &hostid))
1319 err(1, "DIOCSETHOSTID");
1320
1321 if (pf->opts & PF_OPT_VERBOSE)
1322 printf("set hostid 0x%08x\n", ntohl(hostid));
1323
1324 return (0);
1325 }
1326
1327 int
pfctl_set_debug(struct pfctl * pf,char * d)1328 pfctl_set_debug(struct pfctl *pf, char *d)
1329 {
1330 u_int32_t level;
1331
1332 if ((loadopt & PFCTL_FLAG_OPTION) == 0)
1333 return (0);
1334
1335 if (!strcmp(d, "none"))
1336 level = PF_DEBUG_NONE;
1337 else if (!strcmp(d, "urgent"))
1338 level = PF_DEBUG_URGENT;
1339 else if (!strcmp(d, "misc"))
1340 level = PF_DEBUG_MISC;
1341 else if (!strcmp(d, "loud"))
1342 level = PF_DEBUG_NOISY;
1343 else {
1344 warnx("unknown debug level \"%s\"", d);
1345 return (-1);
1346 }
1347
1348 if ((pf->opts & PF_OPT_NOACTION) == 0)
1349 if (ioctl(dev, DIOCSETDEBUG, &level))
1350 err(1, "DIOCSETDEBUG");
1351
1352 if (pf->opts & PF_OPT_VERBOSE)
1353 printf("set debug %s\n", d);
1354
1355 return (0);
1356 }
1357
1358 int
pfctl_debug(int dev,u_int32_t level,int opts)1359 pfctl_debug(int dev, u_int32_t level, int opts)
1360 {
1361 if (ioctl(dev, DIOCSETDEBUG, &level))
1362 err(1, "DIOCSETDEBUG");
1363 if ((opts & PF_OPT_QUIET) == 0) {
1364 fprintf(stderr, "debug level set to '");
1365 switch (level) {
1366 case PF_DEBUG_NONE:
1367 fprintf(stderr, "none");
1368 break;
1369 case PF_DEBUG_URGENT:
1370 fprintf(stderr, "urgent");
1371 break;
1372 case PF_DEBUG_MISC:
1373 fprintf(stderr, "misc");
1374 break;
1375 case PF_DEBUG_NOISY:
1376 fprintf(stderr, "loud");
1377 break;
1378 default:
1379 fprintf(stderr, "<invalid>");
1380 break;
1381 }
1382 fprintf(stderr, "'\n");
1383 }
1384 return (0);
1385 }
1386
1387 int
pfctl_clear_rule_counters(int dev,int opts)1388 pfctl_clear_rule_counters(int dev, int opts)
1389 {
1390 if (ioctl(dev, DIOCCLRRULECTRS))
1391 err(1, "DIOCCLRRULECTRS");
1392 if ((opts & PF_OPT_QUIET) == 0)
1393 fprintf(stderr, "pf: rule counters cleared\n");
1394 return (0);
1395 }
1396
1397 int
pfctl_test_altqsupport(int dev,int opts)1398 pfctl_test_altqsupport(int dev, int opts)
1399 {
1400 struct pfioc_altq pa;
1401
1402 if (ioctl(dev, DIOCGETALTQS, &pa)) {
1403 if (errno == ENODEV) {
1404 if (!(opts & PF_OPT_QUIET))
1405 fprintf(stderr, "No ALTQ support in kernel\n"
1406 "ALTQ related functions disabled\n");
1407 return (0);
1408 } else
1409 err(1, "DIOCGETALTQS");
1410 }
1411 return (1);
1412 }
1413
1414 int
pfctl_show_anchors(int dev,int opts,char * anchorname)1415 pfctl_show_anchors(int dev, int opts, char *anchorname)
1416 {
1417 u_int32_t nr, mnr;
1418
1419 if (!*anchorname) {
1420 struct pfioc_anchor pa;
1421
1422 memset(&pa, 0, sizeof(pa));
1423 if (ioctl(dev, DIOCGETANCHORS, &pa)) {
1424 warn("DIOCGETANCHORS");
1425 return (-1);
1426 }
1427 mnr = pa.nr;
1428 for (nr = 0; nr < mnr; ++nr) {
1429 pa.nr = nr;
1430 if (ioctl(dev, DIOCGETANCHOR, &pa)) {
1431 warn("DIOCGETANCHOR");
1432 return (-1);
1433 }
1434 if (!(opts & PF_OPT_VERBOSE) &&
1435 !strcmp(pa.name, PF_RESERVED_ANCHOR))
1436 continue;
1437 printf(" %s\n", pa.name);
1438 }
1439 } else {
1440 struct pfioc_ruleset pr;
1441
1442 memset(&pr, 0, sizeof(pr));
1443 memcpy(pr.anchor, anchorname, sizeof(pr.anchor));
1444 if (ioctl(dev, DIOCGETRULESETS, &pr)) {
1445 if (errno == EINVAL)
1446 fprintf(stderr, "No rulesets in anchor '%s'.\n",
1447 anchorname);
1448 else
1449 err(1, "DIOCGETRULESETS");
1450 return (-1);
1451 }
1452 mnr = pr.nr;
1453 for (nr = 0; nr < mnr; ++nr) {
1454 pr.nr = nr;
1455 if (ioctl(dev, DIOCGETRULESET, &pr))
1456 err(1, "DIOCGETRULESET");
1457 printf(" %s:%s\n", pr.anchor, pr.name);
1458 }
1459 }
1460 return (0);
1461 }
1462
1463 const char *
pfctl_lookup_option(char * cmd,const char ** list)1464 pfctl_lookup_option(char *cmd, const char **list)
1465 {
1466 if (cmd != NULL && *cmd)
1467 for (; *list; list++)
1468 if (!strncmp(cmd, *list, strlen(cmd)))
1469 return (*list);
1470 return (NULL);
1471 }
1472
1473 int
main(int argc,char * argv[])1474 main(int argc, char *argv[])
1475 {
1476 int error = 0;
1477 int ch;
1478 int mode = O_RDONLY;
1479 int opts = 0;
1480 char anchorname[PF_ANCHOR_NAME_SIZE];
1481 char rulesetname[PF_RULESET_NAME_SIZE];
1482
1483 if (argc < 2)
1484 usage();
1485
1486 while ((ch = getopt(argc, argv,
1487 "a:AdD:eqf:F:ghi:k:nNOp:rRs:t:T:vx:z")) != -1) {
1488 switch (ch) {
1489 case 'a':
1490 anchoropt = optarg;
1491 break;
1492 case 'd':
1493 opts |= PF_OPT_DISABLE;
1494 mode = O_RDWR;
1495 break;
1496 case 'D':
1497 if (pfctl_cmdline_symset(optarg) < 0)
1498 warnx("could not parse macro definition %s",
1499 optarg);
1500 break;
1501 case 'e':
1502 opts |= PF_OPT_ENABLE;
1503 mode = O_RDWR;
1504 break;
1505 case 'q':
1506 opts |= PF_OPT_QUIET;
1507 break;
1508 case 'F':
1509 clearopt = pfctl_lookup_option(optarg, clearopt_list);
1510 if (clearopt == NULL) {
1511 warnx("Unknown flush modifier '%s'", optarg);
1512 usage();
1513 }
1514 mode = O_RDWR;
1515 break;
1516 case 'i':
1517 ifaceopt = optarg;
1518 break;
1519 case 'k':
1520 if (state_killers >= 2) {
1521 warnx("can only specify -k twice");
1522 usage();
1523 /* NOTREACHED */
1524 }
1525 state_kill[state_killers++] = optarg;
1526 mode = O_RDWR;
1527 break;
1528 case 'n':
1529 opts |= PF_OPT_NOACTION;
1530 break;
1531 case 'N':
1532 loadopt |= PFCTL_FLAG_NAT;
1533 break;
1534 case 'r':
1535 opts |= PF_OPT_USEDNS;
1536 break;
1537 case 'f':
1538 rulesopt = optarg;
1539 mode = O_RDWR;
1540 break;
1541 case 'g':
1542 opts |= PF_OPT_DEBUG;
1543 break;
1544 case 'A':
1545 loadopt |= PFCTL_FLAG_ALTQ;
1546 break;
1547 case 'R':
1548 loadopt |= PFCTL_FLAG_FILTER;
1549 break;
1550 case 'O':
1551 loadopt |= PFCTL_FLAG_OPTION;
1552 break;
1553 case 'p':
1554 pf_device = optarg;
1555 break;
1556 case 's':
1557 showopt = pfctl_lookup_option(optarg, showopt_list);
1558 if (showopt == NULL) {
1559 warnx("Unknown show modifier '%s'", optarg);
1560 usage();
1561 }
1562 break;
1563 case 't':
1564 tableopt = optarg;
1565 break;
1566 case 'T':
1567 tblcmdopt = pfctl_lookup_option(optarg, tblcmdopt_list);
1568 if (tblcmdopt == NULL) {
1569 warnx("Unknown table command '%s'", optarg);
1570 usage();
1571 }
1572 break;
1573 case 'v':
1574 if (opts & PF_OPT_VERBOSE)
1575 opts |= PF_OPT_VERBOSE2;
1576 opts |= PF_OPT_VERBOSE;
1577 break;
1578 case 'x':
1579 debugopt = pfctl_lookup_option(optarg, debugopt_list);
1580 if (debugopt == NULL) {
1581 warnx("Unknown debug level '%s'", optarg);
1582 usage();
1583 }
1584 mode = O_RDWR;
1585 break;
1586 case 'z':
1587 opts |= PF_OPT_CLRRULECTRS;
1588 mode = O_RDWR;
1589 break;
1590 case 'h':
1591 /* FALLTHROUGH */
1592 default:
1593 usage();
1594 /* NOTREACHED */
1595 }
1596 }
1597
1598 if (tblcmdopt != NULL) {
1599 argc -= optind;
1600 argv += optind;
1601 ch = *tblcmdopt;
1602 if (ch == 'l') {
1603 loadopt |= PFCTL_FLAG_TABLE;
1604 tblcmdopt = NULL;
1605 } else
1606 mode = strchr("acdfkrz", ch) ? O_RDWR : O_RDONLY;
1607 } else if (argc != optind) {
1608 warnx("unknown command line argument: %s ...", argv[optind]);
1609 usage();
1610 /* NOTREACHED */
1611 }
1612 if (loadopt == 0)
1613 loadopt = ~0;
1614
1615 memset(anchorname, 0, sizeof(anchorname));
1616 memset(rulesetname, 0, sizeof(rulesetname));
1617 if (anchoropt != NULL) {
1618 char *t;
1619
1620 if ((t = strchr(anchoropt, ':')) == NULL) {
1621 if (strlcpy(anchorname, anchoropt,
1622 sizeof(anchorname)) >= sizeof(anchorname))
1623 errx(1, "anchor name '%s' too long",
1624 anchoropt);
1625 } else {
1626 char *p;
1627
1628 if ((p = strdup(anchoropt)) == NULL)
1629 err(1, "anchoropt: strdup");
1630 t = strsep(&p, ":");
1631 if (*t == '\0' || *p == '\0')
1632 errx(1, "anchor '%s' invalid", anchoropt);
1633 if (strlcpy(anchorname, t, sizeof(anchorname)) >=
1634 sizeof(anchorname))
1635 errx(1, "anchor name '%s' too long", t);
1636 if (strlcpy(rulesetname, p, sizeof(rulesetname)) >=
1637 sizeof(rulesetname))
1638 errx(1, "ruleset name '%s' too long", p);
1639 free(t); /* not p */
1640 }
1641 loadopt &= PFCTL_FLAG_FILTER|PFCTL_FLAG_NAT|PFCTL_FLAG_TABLE;
1642 }
1643
1644 if ((opts & PF_OPT_NOACTION) == 0) {
1645 dev = open(pf_device, mode);
1646 if (dev == -1)
1647 err(1, "%s", pf_device);
1648 altqsupport = pfctl_test_altqsupport(dev, opts);
1649 } else {
1650 dev = open(pf_device, O_RDONLY);
1651 if (dev >= 0)
1652 opts |= PF_OPT_DUMMYACTION;
1653 /* turn off options */
1654 opts &= ~ (PF_OPT_DISABLE | PF_OPT_ENABLE);
1655 clearopt = showopt = debugopt = NULL;
1656 altqsupport = 1;
1657 }
1658
1659 if (opts & PF_OPT_DISABLE)
1660 if (pfctl_disable(dev, opts))
1661 error = 1;
1662
1663 if (showopt != NULL) {
1664 switch (*showopt) {
1665 case 'A':
1666 pfctl_show_anchors(dev, opts, anchorname);
1667 break;
1668 case 'r':
1669 pfctl_load_fingerprints(dev, opts);
1670 pfctl_show_rules(dev, opts, 0, anchorname,
1671 rulesetname);
1672 break;
1673 case 'l':
1674 pfctl_load_fingerprints(dev, opts);
1675 pfctl_show_rules(dev, opts, 1, anchorname,
1676 rulesetname);
1677 break;
1678 case 'n':
1679 pfctl_load_fingerprints(dev, opts);
1680 pfctl_show_nat(dev, opts, anchorname, rulesetname);
1681 break;
1682 case 'q':
1683 pfctl_show_altq(dev, ifaceopt, opts,
1684 opts & PF_OPT_VERBOSE2);
1685 break;
1686 case 's':
1687 pfctl_show_states(dev, ifaceopt, opts);
1688 break;
1689 case 'S':
1690 pfctl_show_src_nodes(dev, opts);
1691 break;
1692 case 'i':
1693 pfctl_show_status(dev, opts);
1694 break;
1695 case 't':
1696 pfctl_show_timeouts(dev, opts);
1697 break;
1698 case 'm':
1699 pfctl_show_limits(dev, opts);
1700 break;
1701 case 'a':
1702 opts |= PF_OPT_SHOWALL;
1703 pfctl_load_fingerprints(dev, opts);
1704
1705 pfctl_show_nat(dev, opts, anchorname, rulesetname);
1706 pfctl_show_rules(dev, opts, 0, anchorname,
1707 rulesetname);
1708 pfctl_show_altq(dev, ifaceopt, opts, 0);
1709 pfctl_show_states(dev, ifaceopt, opts);
1710 pfctl_show_src_nodes(dev, opts);
1711 pfctl_show_status(dev, opts);
1712 pfctl_show_rules(dev, opts, 1, anchorname, rulesetname);
1713 pfctl_show_timeouts(dev, opts);
1714 pfctl_show_limits(dev, opts);
1715 pfctl_show_tables(anchorname, rulesetname, opts);
1716 pfctl_show_fingerprints(opts);
1717 break;
1718 case 'T':
1719 pfctl_show_tables(anchorname, rulesetname, opts);
1720 break;
1721 case 'o':
1722 pfctl_load_fingerprints(dev, opts);
1723 pfctl_show_fingerprints(opts);
1724 break;
1725 case 'I':
1726 pfctl_show_ifaces(ifaceopt, opts);
1727 break;
1728 }
1729 }
1730
1731 if (clearopt != NULL) {
1732 switch (*clearopt) {
1733 case 'r':
1734 pfctl_clear_rules(dev, opts, anchorname, rulesetname);
1735 break;
1736 case 'n':
1737 pfctl_clear_nat(dev, opts, anchorname, rulesetname);
1738 break;
1739 case 'q':
1740 pfctl_clear_altq(dev, opts);
1741 break;
1742 case 's':
1743 pfctl_clear_states(dev, ifaceopt, opts);
1744 break;
1745 case 'S':
1746 pfctl_clear_src_nodes(dev, opts);
1747 break;
1748 case 'i':
1749 pfctl_clear_stats(dev, opts);
1750 break;
1751 case 'a':
1752 pfctl_clear_rules(dev, opts, anchorname, rulesetname);
1753 pfctl_clear_nat(dev, opts, anchorname, rulesetname);
1754 pfctl_clear_tables(anchorname, rulesetname, opts);
1755 if (!*anchorname && !*rulesetname) {
1756 pfctl_clear_altq(dev, opts);
1757 pfctl_clear_states(dev, ifaceopt, opts);
1758 pfctl_clear_src_nodes(dev, opts);
1759 pfctl_clear_stats(dev, opts);
1760 pfctl_clear_fingerprints(dev, opts);
1761 }
1762 break;
1763 case 'o':
1764 pfctl_clear_fingerprints(dev, opts);
1765 break;
1766 case 'T':
1767 pfctl_clear_tables(anchorname, rulesetname, opts);
1768 break;
1769 }
1770 }
1771 if (state_killers)
1772 pfctl_kill_states(dev, ifaceopt, opts);
1773
1774 if (tblcmdopt != NULL) {
1775 error = pfctl_command_tables(argc, argv, tableopt,
1776 tblcmdopt, rulesopt, anchorname, rulesetname, opts);
1777 rulesopt = NULL;
1778 }
1779
1780 if (rulesopt != NULL)
1781 if (pfctl_file_fingerprints(dev, opts, PF_OSFP_FILE))
1782 error = 1;
1783
1784 if (rulesopt != NULL) {
1785 if (pfctl_rules(dev, rulesopt, opts, anchorname, rulesetname,
1786 NULL))
1787 error = 1;
1788 else if (!(opts & PF_OPT_NOACTION) &&
1789 (loadopt & PFCTL_FLAG_TABLE))
1790 warn_namespace_collision(NULL);
1791 }
1792
1793 if (opts & PF_OPT_ENABLE)
1794 if (pfctl_enable(dev, opts))
1795 error = 1;
1796
1797 if (debugopt != NULL) {
1798 switch (*debugopt) {
1799 case 'n':
1800 pfctl_debug(dev, PF_DEBUG_NONE, opts);
1801 break;
1802 case 'u':
1803 pfctl_debug(dev, PF_DEBUG_URGENT, opts);
1804 break;
1805 case 'm':
1806 pfctl_debug(dev, PF_DEBUG_MISC, opts);
1807 break;
1808 case 'l':
1809 pfctl_debug(dev, PF_DEBUG_NOISY, opts);
1810 break;
1811 }
1812 }
1813
1814 if (opts & PF_OPT_CLRRULECTRS) {
1815 if (pfctl_clear_rule_counters(dev, opts))
1816 error = 1;
1817 }
1818 exit(error);
1819 }
1820