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