pfctl.c revision 126353
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/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
56void	 usage(void);
57int	 pfctl_enable(int, int);
58int	 pfctl_disable(int, int);
59int	 pfctl_clear_stats(int, int);
60int	 pfctl_clear_rules(int, int, char *, char *);
61int	 pfctl_clear_nat(int, int, char *, char *);
62int	 pfctl_clear_altq(int, int);
63int	 pfctl_clear_states(int, int);
64int	 pfctl_kill_states(int, int);
65int	 pfctl_get_pool(int, struct pf_pool *, u_int32_t, u_int32_t, int,
66	     char *, char *);
67void	 pfctl_print_rule_counters(struct pf_rule *, int);
68int	 pfctl_show_rules(int, int, int, char *, char *);
69int	 pfctl_show_nat(int, int, char *, char *);
70int	 pfctl_show_states(int, u_int8_t, int);
71int	 pfctl_show_status(int);
72int	 pfctl_show_timeouts(int);
73int	 pfctl_show_limits(int);
74int	 pfctl_debug(int, u_int32_t, int);
75int	 pfctl_clear_rule_counters(int, int);
76int	 pfctl_test_altqsupport(int, int);
77int	 pfctl_show_anchors(int, int, char *);
78const char	*pfctl_lookup_option(char *, const char **);
79
80const char	*clearopt;
81char		*rulesopt;
82const char	*showopt;
83const char	*debugopt;
84char		*anchoropt;
85char		*tableopt;
86const char	*tblcmdopt;
87int		 state_killers;
88char		*state_kill[2];
89int		 loadopt;
90int		 altqsupport;
91
92int		 dev = -1;
93
94const char	*infile;
95
96static 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
105struct pf_hint {
106	const char	*name;
107	int		timeout;
108};
109static 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};
118static 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};
127static 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};
136static 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
146static 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
158static const char *clearopt_list[] = {
159	"nat", "queue", "rules", "state", "info", "Tables", "osfp", "all", NULL
160};
161
162static const char *showopt_list[] = {
163	"nat", "queue", "rules", "Anchors", "state", "info", "labels",
164	"timeouts", "memory", "Tables", "osfp", "all", NULL
165};
166
167static const char *tblcmdopt_list[] = {
168	"kill", "flush", "add", "delete", "load", "replace", "show",
169	"test", "zero", NULL
170};
171
172static const char *debugopt_list[] = {
173	"none", "urgent", "misc", "loud", NULL
174};
175
176
177void
178usage(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
191int
192pfctl_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
210int
211pfctl_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
229int
230pfctl_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
239int
240pfctl_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
290int
291pfctl_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
346int
347pfctl_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
363int
364pfctl_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
373int
374pfctl_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
475int
476pfctl_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
511void
512pfctl_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
522void
523pfctl_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
552int
553pfctl_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
657int
658pfctl_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
719int
720pfctl_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
759int
760pfctl_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
772int
773pfctl_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
794int
795pfctl_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 */
815int
816pfctl_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
836int
837pfctl_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
891int
892pfctl_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
913int
914pfctl_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	struct pfr_table	trs;
925	int			i;
926
927	memset(&pa, 0, sizeof(pa));
928	memset(&pf, 0, sizeof(pf));
929	memset(&trs, 0, sizeof(trs));
930	for (i = 0; i < PF_RULESET_MAX; i++) {
931		memset(&pr[i], 0, sizeof(pr[i]));
932		memcpy(pr[i].anchor, anchorname, sizeof(pr[i].anchor));
933		memcpy(pr[i].ruleset, rulesetname, sizeof(pr[i].ruleset));
934	}
935	if (strlcpy(trs.pfrt_anchor, anchorname,
936	    sizeof(trs.pfrt_anchor)) >= sizeof(trs.pfrt_anchor) ||
937	    strlcpy(trs.pfrt_ruleset, rulesetname,
938	    sizeof(trs.pfrt_ruleset)) >= sizeof(trs.pfrt_ruleset))
939		ERRX("pfctl_rules: strlcpy");
940	if (strcmp(filename, "-") == 0) {
941		fin = stdin;
942		infile = "stdin";
943	} else {
944		if ((fin = fopen(filename, "r")) == NULL) {
945			warn("%s", filename);
946			return (1);
947		}
948		infile = filename;
949	}
950	if ((opts & PF_OPT_NOACTION) == 0) {
951		if ((loadopt & PFCTL_FLAG_NAT) != 0) {
952			pr[PF_RULESET_NAT].rule.action = PF_NAT;
953			if (ioctl(dev, DIOCBEGINRULES, &pr[PF_RULESET_NAT]))
954				ERR("DIOCBEGINRULES");
955			pr[PF_RULESET_RDR].rule.action = PF_RDR;
956			if (ioctl(dev, DIOCBEGINRULES, &pr[PF_RULESET_RDR]))
957				ERR("DIOCBEGINRULES");
958			pr[PF_RULESET_BINAT].rule.action = PF_BINAT;
959			if (ioctl(dev, DIOCBEGINRULES, &pr[PF_RULESET_BINAT]))
960				ERR("DIOCBEGINRULES");
961		}
962		if (((altqsupport && (loadopt & PFCTL_FLAG_ALTQ) != 0)) &&
963		    ioctl(dev, DIOCBEGINALTQS, &pa.ticket)) {
964			ERR("DIOCBEGINALTQS");
965		}
966		if ((loadopt & PFCTL_FLAG_FILTER) != 0) {
967			pr[PF_RULESET_SCRUB].rule.action = PF_SCRUB;
968			if (ioctl(dev, DIOCBEGINRULES, &pr[PF_RULESET_SCRUB]))
969				ERR("DIOCBEGINRULES");
970			pr[PF_RULESET_FILTER].rule.action = PF_PASS;
971			if (ioctl(dev, DIOCBEGINRULES, &pr[PF_RULESET_FILTER]))
972				ERR("DIOCBEGINRULES");
973		}
974		if (loadopt & PFCTL_FLAG_TABLE) {
975			if (pfr_ina_begin(&trs, &pf.tticket, NULL, 0) != 0)
976				ERR("begin table");
977		}
978	}
979	/* fill in callback data */
980	pf.dev = dev;
981	pf.opts = opts;
982	pf.loadopt = loadopt;
983	pf.paltq = &pa;
984	for (i = 0; i < PF_RULESET_MAX; i++) {
985		pf.prule[i] = &pr[i];
986	}
987	pf.rule_nr = 0;
988	pf.anchor = anchorname;
989	pf.ruleset = rulesetname;
990	if (parse_rules(fin, &pf) < 0) {
991		if ((opts & PF_OPT_NOACTION) == 0)
992			ERRX("Syntax error in config file: "
993			    "pf rules not loaded");
994		else
995			goto _error;
996	}
997	if ((altqsupport && (loadopt & PFCTL_FLAG_ALTQ) != 0))
998		if (check_commit_altq(dev, opts) != 0)
999			ERRX("errors in altq config");
1000	if ((opts & PF_OPT_NOACTION) == 0) {
1001		if ((loadopt & PFCTL_FLAG_NAT) != 0) {
1002			pr[PF_RULESET_NAT].rule.action = PF_NAT;
1003			if (ioctl(dev, DIOCCOMMITRULES, &pr[PF_RULESET_NAT]) &&
1004			    (errno != EINVAL || pf.rule_nr))
1005				ERR("DIOCCOMMITRULES NAT");
1006			pr[PF_RULESET_RDR].rule.action = PF_RDR;
1007			if (ioctl(dev, DIOCCOMMITRULES, &pr[PF_RULESET_RDR]) &&
1008			    (errno != EINVAL || pf.rule_nr))
1009				ERR("DIOCCOMMITRULES RDR");
1010			pr[PF_RULESET_BINAT].rule.action = PF_BINAT;
1011			if (ioctl(dev, DIOCCOMMITRULES, &pr[PF_RULESET_BINAT]) &&
1012			    (errno != EINVAL || pf.rule_nr))
1013				ERR("DIOCCOMMITRULES BINAT");
1014		}
1015		if (((altqsupport && (loadopt & PFCTL_FLAG_ALTQ) != 0)) &&
1016		    ioctl(dev, DIOCCOMMITALTQS, &pa.ticket))
1017			ERR("DIOCCOMMITALTQS");
1018		if ((loadopt & PFCTL_FLAG_FILTER) != 0) {
1019			pr[PF_RULESET_SCRUB].rule.action = PF_SCRUB;
1020			if (ioctl(dev, DIOCCOMMITRULES, &pr[PF_RULESET_SCRUB]) &&
1021			    (errno != EINVAL || pf.rule_nr))
1022				ERR("DIOCCOMMITRULES SCRUB");
1023			pr[PF_RULESET_FILTER].rule.action = PF_PASS;
1024			if (ioctl(dev, DIOCCOMMITRULES, &pr[PF_RULESET_FILTER]) &&
1025			    (errno != EINVAL || pf.rule_nr))
1026				ERR("DIOCCOMMITRULES FILTER");
1027		}
1028		if (loadopt & PFCTL_FLAG_TABLE) {
1029			if (pfr_ina_commit(&trs, pf.tticket, NULL, NULL, 0))
1030				ERR("commit table");
1031			pf.tdirty = 0;
1032		}
1033	}
1034	if (fin != stdin)
1035		fclose(fin);
1036
1037	/* process "load anchor" directives */
1038	if (!anchorname[0] && !rulesetname[0])
1039		if (pfctl_load_anchors(dev, opts) == -1)
1040			ERRX("load anchors");
1041
1042	return (0);
1043
1044_error:
1045	if (pf.tdirty) /* cleanup kernel leftover */
1046		pfr_ina_begin(&trs, NULL, NULL, 0);
1047	exit(1);
1048
1049#undef ERR
1050#undef ERRX
1051}
1052
1053int
1054pfctl_set_limit(struct pfctl *pf, const char *opt, unsigned int limit)
1055{
1056	struct pfioc_limit pl;
1057	int i;
1058
1059	if ((loadopt & PFCTL_FLAG_OPTION) == 0)
1060		return (0);
1061
1062	memset(&pl, 0, sizeof(pl));
1063	for (i = 0; pf_limits[i].name; i++) {
1064		if (strcasecmp(opt, pf_limits[i].name) == 0) {
1065			pl.index = i;
1066			pl.limit = limit;
1067			if ((pf->opts & PF_OPT_NOACTION) == 0) {
1068				if (ioctl(pf->dev, DIOCSETLIMIT, &pl)) {
1069					if (errno == EBUSY) {
1070						warnx("Current pool "
1071						    "size exceeds requested "
1072						    "hard limit");
1073						return (1);
1074					} else
1075						err(1, "DIOCSETLIMIT");
1076				}
1077			}
1078			break;
1079		}
1080	}
1081	if (pf_limits[i].name == NULL) {
1082		warnx("Bad pool name.");
1083		return (1);
1084	}
1085
1086	if (pf->opts & PF_OPT_VERBOSE)
1087		printf("set limit %s %d\n", opt, limit);
1088
1089	return (0);
1090}
1091
1092int
1093pfctl_set_timeout(struct pfctl *pf, const char *opt, int seconds, int quiet)
1094{
1095	struct pfioc_tm pt;
1096	int i;
1097
1098	if ((loadopt & PFCTL_FLAG_OPTION) == 0)
1099		return (0);
1100
1101	memset(&pt, 0, sizeof(pt));
1102	for (i = 0; pf_timeouts[i].name; i++) {
1103		if (strcasecmp(opt, pf_timeouts[i].name) == 0) {
1104			pt.timeout = pf_timeouts[i].timeout;
1105			break;
1106		}
1107	}
1108
1109	if (pf_timeouts[i].name == NULL) {
1110		warnx("Bad timeout name.");
1111		return (1);
1112	}
1113
1114	pt.seconds = seconds;
1115	if ((pf->opts & PF_OPT_NOACTION) == 0) {
1116		if (ioctl(pf->dev, DIOCSETTIMEOUT, &pt))
1117			err(1, "DIOCSETTIMEOUT");
1118	}
1119
1120	if (pf->opts & PF_OPT_VERBOSE && ! quiet)
1121		printf("set timeout %s %d\n", opt, seconds);
1122
1123	return (0);
1124}
1125
1126int
1127pfctl_set_optimization(struct pfctl *pf, const char *opt)
1128{
1129	const struct pf_hint *hint;
1130	int i, r;
1131
1132	if ((loadopt & PFCTL_FLAG_OPTION) == 0)
1133		return (0);
1134
1135	for (i = 0; pf_hints[i].name; i++)
1136		if (strcasecmp(opt, pf_hints[i].name) == 0)
1137			break;
1138
1139	hint = pf_hints[i].hint;
1140	if (hint == NULL) {
1141		warnx("Bad hint name.");
1142		return (1);
1143	}
1144
1145	for (i = 0; hint[i].name; i++)
1146		if ((r = pfctl_set_timeout(pf, hint[i].name,
1147		    hint[i].timeout, 1)))
1148			return (r);
1149
1150	if (pf->opts & PF_OPT_VERBOSE)
1151		printf("set optimization %s\n", opt);
1152
1153	return (0);
1154}
1155
1156int
1157pfctl_set_logif(struct pfctl *pf, char *ifname)
1158{
1159	struct pfioc_if pi;
1160
1161	if ((loadopt & PFCTL_FLAG_OPTION) == 0)
1162		return (0);
1163
1164	memset(&pi, 0, sizeof(pi));
1165	if ((pf->opts & PF_OPT_NOACTION) == 0) {
1166		if (!strcmp(ifname, "none"))
1167			bzero(pi.ifname, sizeof(pi.ifname));
1168		else {
1169			if (strlcpy(pi.ifname, ifname,
1170			    sizeof(pi.ifname)) >= sizeof(pi.ifname))
1171				errx(1, "pfctl_set_logif: strlcpy");
1172		}
1173		if (ioctl(pf->dev, DIOCSETSTATUSIF, &pi))
1174			err(1, "DIOCSETSTATUSIF");
1175	}
1176
1177	if (pf->opts & PF_OPT_VERBOSE)
1178		printf("set loginterface %s\n", ifname);
1179
1180	return (0);
1181}
1182
1183int
1184pfctl_debug(int dev, u_int32_t level, int opts)
1185{
1186	if (ioctl(dev, DIOCSETDEBUG, &level))
1187		err(1, "DIOCSETDEBUG");
1188	if ((opts & PF_OPT_QUIET) == 0) {
1189		fprintf(stderr, "debug level set to '");
1190		switch (level) {
1191		case PF_DEBUG_NONE:
1192			fprintf(stderr, "none");
1193			break;
1194		case PF_DEBUG_URGENT:
1195			fprintf(stderr, "urgent");
1196			break;
1197		case PF_DEBUG_MISC:
1198			fprintf(stderr, "misc");
1199			break;
1200		case PF_DEBUG_NOISY:
1201			fprintf(stderr, "loud");
1202			break;
1203		default:
1204			fprintf(stderr, "<invalid>");
1205			break;
1206		}
1207		fprintf(stderr, "'\n");
1208	}
1209	return (0);
1210}
1211
1212int
1213pfctl_clear_rule_counters(int dev, int opts)
1214{
1215	if (ioctl(dev, DIOCCLRRULECTRS))
1216		err(1, "DIOCCLRRULECTRS");
1217	if ((opts & PF_OPT_QUIET) == 0)
1218		fprintf(stderr, "pf: rule counters cleared\n");
1219	return (0);
1220}
1221
1222int
1223pfctl_test_altqsupport(int dev, int opts)
1224{
1225	struct pfioc_altq pa;
1226
1227	if (ioctl(dev, DIOCGETALTQS, &pa)) {
1228		if (errno == ENODEV) {
1229			if (!(opts & PF_OPT_QUIET))
1230				fprintf(stderr, "No ALTQ support in kernel\n"
1231				    "ALTQ related functions disabled\n");
1232			return (0);
1233		} else
1234			err(1, "DIOCGETALTQS");
1235	}
1236	return (1);
1237}
1238
1239int
1240pfctl_show_anchors(int dev, int opts, char *anchorname)
1241{
1242	u_int32_t nr, mnr;
1243
1244	if (!*anchorname) {
1245		struct pfioc_anchor pa;
1246
1247		memset(&pa, 0, sizeof(pa));
1248		if (ioctl(dev, DIOCGETANCHORS, &pa)) {
1249			warn("DIOCGETANCHORS");
1250			return (-1);
1251		}
1252		mnr = pa.nr;
1253		if (!(opts & PF_OPT_QUIET))
1254			printf("%u anchors:\n", mnr);
1255		for (nr = 0; nr < mnr; ++nr) {
1256			pa.nr = nr;
1257			if (ioctl(dev, DIOCGETANCHOR, &pa)) {
1258				warn("DIOCGETANCHOR");
1259				return (-1);
1260			}
1261			printf("  %s\n", pa.name);
1262		}
1263	} else {
1264		struct pfioc_ruleset pr;
1265
1266		memset(&pr, 0, sizeof(pr));
1267		memcpy(pr.anchor, anchorname, sizeof(pr.anchor));
1268		if (ioctl(dev, DIOCGETRULESETS, &pr)) {
1269			if (errno == EINVAL)
1270				fprintf(stderr, "No rulesets in anchor '%s'.\n",
1271				    anchorname);
1272			else
1273				err(1, "DIOCGETRULESETS");
1274			return (-1);
1275		}
1276		mnr = pr.nr;
1277		if (!(opts & PF_OPT_QUIET))
1278			printf("%u rulesets in anchor %s:\n", mnr, anchorname);
1279		for (nr = 0; nr < mnr; ++nr) {
1280			pr.nr = nr;
1281			if (ioctl(dev, DIOCGETRULESET, &pr))
1282				err(1, "DIOCGETRULESET");
1283			printf("  %s:%s\n", pr.anchor, pr.name);
1284		}
1285	}
1286	return (0);
1287}
1288
1289const char *
1290pfctl_lookup_option(char *cmd, const char **list)
1291{
1292	if (cmd != NULL && *cmd)
1293		for (; *list; list++)
1294			if (!strncmp(cmd, *list, strlen(cmd)))
1295				return (*list);
1296	return (NULL);
1297}
1298
1299int
1300main(int argc, char *argv[])
1301{
1302	int	error = 0;
1303	int	ch;
1304	int	mode = O_RDONLY;
1305	int	opts = 0;
1306	char	anchorname[PF_ANCHOR_NAME_SIZE];
1307	char	rulesetname[PF_RULESET_NAME_SIZE];
1308
1309	if (argc < 2)
1310		usage();
1311
1312	while ((ch = getopt(argc, argv, "a:AdD:eqf:F:ghk:nNOrRs:t:T:vx:z")) !=
1313		-1) {
1314		switch (ch) {
1315		case 'a':
1316			anchoropt = optarg;
1317			break;
1318		case 'd':
1319			opts |= PF_OPT_DISABLE;
1320			mode = O_RDWR;
1321			break;
1322		case 'D':
1323			if (pfctl_cmdline_symset(optarg) < 0)
1324				warnx("could not parse macro definition %s",
1325				    optarg);
1326			break;
1327		case 'e':
1328			opts |= PF_OPT_ENABLE;
1329			mode = O_RDWR;
1330			break;
1331		case 'q':
1332			opts |= PF_OPT_QUIET;
1333			break;
1334		case 'F':
1335			clearopt = pfctl_lookup_option(optarg, clearopt_list);
1336			if (clearopt == NULL) {
1337				warnx("Unknown flush modifier '%s'", optarg);
1338				usage();
1339			}
1340			mode = O_RDWR;
1341			break;
1342		case 'k':
1343			if (state_killers >= 2) {
1344				warnx("can only specify -k twice");
1345				usage();
1346				/* NOTREACHED */
1347			}
1348			state_kill[state_killers++] = optarg;
1349			mode = O_RDWR;
1350			break;
1351		case 'n':
1352			opts |= PF_OPT_NOACTION;
1353			break;
1354		case 'N':
1355			loadopt |= PFCTL_FLAG_NAT;
1356			break;
1357		case 'r':
1358			opts |= PF_OPT_USEDNS;
1359			break;
1360		case 'f':
1361			rulesopt = optarg;
1362			mode = O_RDWR;
1363			break;
1364		case 'g':
1365			opts |= PF_OPT_DEBUG;
1366			break;
1367		case 'A':
1368			loadopt |= PFCTL_FLAG_ALTQ;
1369			break;
1370		case 'R':
1371			loadopt |= PFCTL_FLAG_FILTER;
1372			break;
1373		case 'O':
1374			loadopt |= PFCTL_FLAG_OPTION;
1375			break;
1376		case 's':
1377			showopt = pfctl_lookup_option(optarg, showopt_list);
1378			if (showopt == NULL) {
1379				warnx("Unknown show modifier '%s'", optarg);
1380				usage();
1381			}
1382			break;
1383		case 't':
1384			tableopt = optarg;
1385			break;
1386		case 'T':
1387			tblcmdopt = pfctl_lookup_option(optarg, tblcmdopt_list);
1388			if (tblcmdopt == NULL) {
1389				warnx("Unknown table command '%s'", optarg);
1390				usage();
1391			}
1392			break;
1393		case 'v':
1394			if (opts & PF_OPT_VERBOSE)
1395				opts |= PF_OPT_VERBOSE2;
1396			opts |= PF_OPT_VERBOSE;
1397			break;
1398		case 'x':
1399			debugopt = pfctl_lookup_option(optarg, debugopt_list);
1400			if (debugopt == NULL) {
1401				warnx("Unknown debug level '%s'", optarg);
1402				usage();
1403			}
1404			mode = O_RDWR;
1405			break;
1406		case 'z':
1407			opts |= PF_OPT_CLRRULECTRS;
1408			mode = O_RDWR;
1409			break;
1410		case 'h':
1411			/* FALLTHROUGH */
1412		default:
1413			usage();
1414			/* NOTREACHED */
1415		}
1416	}
1417
1418	if (tblcmdopt != NULL) {
1419		argc -= optind;
1420		argv += optind;
1421		ch = *tblcmdopt;
1422		if (ch == 'l') {
1423			loadopt |= PFCTL_FLAG_TABLE;
1424			tblcmdopt = NULL;
1425		} else {
1426			mode = strchr("acdfkrz", ch) ? O_RDWR : O_RDONLY;
1427			if (opts & PF_OPT_NOACTION) {
1428				dev = open("/dev/pf", mode);
1429				if (dev >= 0)
1430					opts |= PF_OPT_DUMMYACTION;
1431			}
1432		}
1433	} else if (argc != optind) {
1434		warnx("unknown command line argument: %s ...", argv[optind]);
1435		usage();
1436		/* NOTREACHED */
1437	}
1438	if (loadopt == 0)
1439		loadopt = ~0;
1440
1441	memset(anchorname, 0, sizeof(anchorname));
1442	memset(rulesetname, 0, sizeof(rulesetname));
1443	if (anchoropt != NULL) {
1444		char *t;
1445
1446		if ((t = strchr(anchoropt, ':')) == NULL) {
1447			if (strlcpy(anchorname, anchoropt,
1448			    sizeof(anchorname)) >= sizeof(anchorname))
1449				errx(1, "anchor name '%s' too long",
1450				    anchoropt);
1451		} else {
1452			char *p;
1453
1454			if ((p = strdup(anchoropt)) == NULL)
1455				err(1, "anchoropt: strdup");
1456			t = strsep(&p, ":");
1457			if (*t == '\0' || *p == '\0')
1458				errx(1, "anchor '%s' invalid", anchoropt);
1459			if (strlcpy(anchorname, t, sizeof(anchorname)) >=
1460			    sizeof(anchorname))
1461				errx(1, "anchor name '%s' too long", t);
1462			if (strlcpy(rulesetname, p, sizeof(rulesetname)) >=
1463			    sizeof(rulesetname))
1464				errx(1, "ruleset name '%s' too long", p);
1465			free(t); /* not p */
1466		}
1467		loadopt &= PFCTL_FLAG_FILTER|PFCTL_FLAG_NAT|PFCTL_FLAG_TABLE;
1468	}
1469
1470	if ((opts & PF_OPT_NOACTION) == 0) {
1471		dev = open("/dev/pf", mode);
1472		if (dev == -1)
1473			err(1, "/dev/pf");
1474		altqsupport = pfctl_test_altqsupport(dev, opts);
1475	} else {
1476		/* turn off options */
1477		opts &= ~ (PF_OPT_DISABLE | PF_OPT_ENABLE);
1478		clearopt = showopt = debugopt = NULL;
1479		altqsupport = 1;
1480	}
1481
1482	if (opts & PF_OPT_DISABLE)
1483		if (pfctl_disable(dev, opts))
1484			error = 1;
1485
1486	if (showopt != NULL) {
1487		switch (*showopt) {
1488		case 'A':
1489			pfctl_show_anchors(dev, opts, anchorname);
1490			break;
1491		case 'r':
1492			pfctl_load_fingerprints(dev, opts);
1493			pfctl_show_rules(dev, opts, 0, anchorname,
1494			    rulesetname);
1495			break;
1496		case 'l':
1497			pfctl_load_fingerprints(dev, opts);
1498			pfctl_show_rules(dev, opts, 1, anchorname,
1499			    rulesetname);
1500			break;
1501		case 'n':
1502			pfctl_load_fingerprints(dev, opts);
1503			pfctl_show_nat(dev, opts, anchorname, rulesetname);
1504			break;
1505		case 'q':
1506			pfctl_show_altq(dev, opts, opts & PF_OPT_VERBOSE2);
1507			break;
1508		case 's':
1509			pfctl_show_states(dev, 0, opts);
1510			break;
1511		case 'i':
1512			pfctl_show_status(dev);
1513			break;
1514		case 't':
1515			pfctl_show_timeouts(dev);
1516			break;
1517		case 'm':
1518			pfctl_show_limits(dev);
1519			break;
1520		case 'a':
1521			pfctl_load_fingerprints(dev, opts);
1522
1523			pfctl_show_rules(dev, opts, 0, anchorname,
1524			    rulesetname);
1525			pfctl_show_nat(dev, opts, anchorname, rulesetname);
1526			pfctl_show_altq(dev, opts, 0);
1527			pfctl_show_states(dev, 0, opts);
1528			pfctl_show_status(dev);
1529			pfctl_show_rules(dev, opts, 1, anchorname, rulesetname);
1530			pfctl_show_timeouts(dev);
1531			pfctl_show_limits(dev);
1532			pfctl_show_tables(anchorname, rulesetname, opts);
1533			pfctl_show_fingerprints(opts);
1534			break;
1535		case 'T':
1536			pfctl_show_tables(anchorname, rulesetname, opts);
1537			break;
1538		case 'o':
1539			pfctl_load_fingerprints(dev, opts);
1540			pfctl_show_fingerprints(opts);
1541			break;
1542		}
1543	}
1544
1545	if (clearopt != NULL) {
1546		switch (*clearopt) {
1547		case 'r':
1548			pfctl_clear_rules(dev, opts, anchorname, rulesetname);
1549			break;
1550		case 'n':
1551			pfctl_clear_nat(dev, opts, anchorname, rulesetname);
1552			break;
1553		case 'q':
1554			pfctl_clear_altq(dev, opts);
1555			break;
1556		case 's':
1557			pfctl_clear_states(dev, opts);
1558			break;
1559		case 'i':
1560			pfctl_clear_stats(dev, opts);
1561			break;
1562		case 'a':
1563			pfctl_clear_rules(dev, opts, anchorname, rulesetname);
1564			pfctl_clear_nat(dev, opts, anchorname, rulesetname);
1565			pfctl_clear_altq(dev, opts);
1566			pfctl_clear_states(dev, opts);
1567			pfctl_clear_stats(dev, opts);
1568			pfctl_clear_tables(anchorname, rulesetname, opts);
1569			pfctl_clear_fingerprints(dev, opts);
1570			break;
1571		case 'o':
1572			pfctl_clear_fingerprints(dev, opts);
1573			break;
1574		case 'T':
1575			pfctl_clear_tables(anchorname, rulesetname, opts);
1576			break;
1577		}
1578	}
1579	if (state_killers)
1580		pfctl_kill_states(dev, opts);
1581
1582	if (tblcmdopt != NULL) {
1583		error = pfctl_command_tables(argc, argv, tableopt,
1584		    tblcmdopt, rulesopt, anchorname, rulesetname, opts);
1585		rulesopt = NULL;
1586	}
1587
1588	if (rulesopt != NULL)
1589		if (pfctl_file_fingerprints(dev, opts, PF_OSFP_FILE))
1590			error = 1;
1591
1592	if (rulesopt != NULL) {
1593		if (pfctl_rules(dev, rulesopt, opts, anchorname, rulesetname))
1594			error = 1;
1595		else if (!(opts & PF_OPT_NOACTION) &&
1596		    (loadopt & PFCTL_FLAG_TABLE))
1597			warn_namespace_collision(NULL);
1598	}
1599
1600	if (opts & PF_OPT_ENABLE)
1601		if (pfctl_enable(dev, opts))
1602			error = 1;
1603
1604	if (debugopt != NULL) {
1605		switch (*debugopt) {
1606		case 'n':
1607			pfctl_debug(dev, PF_DEBUG_NONE, opts);
1608			break;
1609		case 'u':
1610			pfctl_debug(dev, PF_DEBUG_URGENT, opts);
1611			break;
1612		case 'm':
1613			pfctl_debug(dev, PF_DEBUG_MISC, opts);
1614			break;
1615		case 'l':
1616			pfctl_debug(dev, PF_DEBUG_NOISY, opts);
1617			break;
1618		}
1619	}
1620
1621	if (opts & PF_OPT_CLRRULECTRS) {
1622		if (pfctl_clear_rule_counters(dev, opts))
1623			error = 1;
1624	}
1625	exit(error);
1626}
1627