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