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