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