xref: /openbsd-src/sbin/pfctl/pfctl_table.c (revision e496dff3a7dbaf23b2ee7a45993a39db823238e4)
1*e496dff3Skirill /*	$OpenBSD: pfctl_table.c,v 1.91 2024/11/20 13:57:29 kirill Exp $ */
2a987d4fdScedric 
3a987d4fdScedric /*
4a987d4fdScedric  * Copyright (c) 2002 Cedric Berger
5a987d4fdScedric  * All rights reserved.
6a987d4fdScedric  *
7a987d4fdScedric  * Redistribution and use in source and binary forms, with or without
8a987d4fdScedric  * modification, are permitted provided that the following conditions
9a987d4fdScedric  * are met:
10a987d4fdScedric  *
11a987d4fdScedric  *    - Redistributions of source code must retain the above copyright
12a987d4fdScedric  *      notice, this list of conditions and the following disclaimer.
13a987d4fdScedric  *    - Redistributions in binary form must reproduce the above
14a987d4fdScedric  *      copyright notice, this list of conditions and the following
15a987d4fdScedric  *      disclaimer in the documentation and/or other materials provided
16a987d4fdScedric  *      with the distribution.
17a987d4fdScedric  *
18a987d4fdScedric  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19a987d4fdScedric  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20a987d4fdScedric  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
21a987d4fdScedric  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
22a987d4fdScedric  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
23a987d4fdScedric  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
24a987d4fdScedric  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25a987d4fdScedric  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26a987d4fdScedric  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27a987d4fdScedric  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
28a987d4fdScedric  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29a987d4fdScedric  * POSSIBILITY OF SUCH DAMAGE.
30a987d4fdScedric  *
31a987d4fdScedric  */
32a987d4fdScedric 
33a987d4fdScedric #include <sys/types.h>
34a987d4fdScedric #include <sys/ioctl.h>
35a987d4fdScedric #include <sys/socket.h>
36a987d4fdScedric 
378717211fSderaadt #include <netinet/in.h>
388717211fSderaadt #include <arpa/inet.h>
39a987d4fdScedric #include <net/if.h>
40a987d4fdScedric #include <net/pfvar.h>
41a987d4fdScedric 
425eb10b9fScedric #include <ctype.h>
43a987d4fdScedric #include <err.h>
44a987d4fdScedric #include <errno.h>
45a987d4fdScedric #include <netdb.h>
465eb10b9fScedric #include <stdarg.h>
47a987d4fdScedric #include <stdio.h>
48a987d4fdScedric #include <stdlib.h>
49a987d4fdScedric #include <string.h>
505eb10b9fScedric #include <time.h>
51b9fc9a72Sderaadt #include <limits.h>
52a987d4fdScedric 
53a987d4fdScedric #include "pfctl_parser.h"
545eb10b9fScedric #include "pfctl.h"
55a987d4fdScedric 
56a987d4fdScedric extern void	usage(void);
5796e89413Scedric static void	print_table(struct pfr_table *, int, int);
58bb75ae08Sdhartmei static void	print_tstats(struct pfr_tstats *, int);
597c8726d4Sbenno static int	load_addr(struct pfr_buffer *, int, char *[], char *, int, int);
6018aff148Scedric static void	print_addrx(struct pfr_addr *, struct pfr_addr *, int);
6118aff148Scedric static void	print_astats(struct pfr_astats *, int);
62132c30ccShenning static void	xprintf(int, const char *, ...);
63c1f13696Shenning static void	print_iface(struct pfi_kif *, int);
64a987d4fdScedric 
65132c30ccShenning static const char	*stats_text[PFR_DIR_MAX][PFR_OP_TABLE_MAX] = {
66401a01c8Sblambert 	{ "In/Block:",	"In/Match:",	"In/Pass:",	"In/XPass:" },
67401a01c8Sblambert 	{ "Out/Block:",	"Out/Match:",	"Out/Pass:",	"Out/XPass:" }
68a987d4fdScedric };
69a987d4fdScedric 
70ec359bd5Scedric static const char	*istats_text[2][2][2] = {
71ec359bd5Scedric 	{ { "In4/Pass:", "In4/Block:" }, { "Out4/Pass:", "Out4/Block:" } },
72ec359bd5Scedric 	{ { "In6/Pass:", "In6/Block:" }, { "Out6/Pass:", "Out6/Block:" } }
73ec359bd5Scedric };
74ec359bd5Scedric 
7512430db7Sderaadt #define RVTEST(fct) do {						\
7602cc3c1dScedric 		if ((!(opts & PF_OPT_NOACTION) ||			\
7702cc3c1dScedric 		    (opts & PF_OPT_DUMMYACTION)) &&			\
7802cc3c1dScedric 		    (fct)) {						\
79ae711728Ssashan 			if ((opts & PF_OPT_RECURSE) == 0)		\
80c802a0d9Skn 				warnx("%s", pf_strerror(errno));	\
8142e05679Scedric 			goto _error;					\
8212430db7Sderaadt 		}							\
83a987d4fdScedric 	} while (0)
84a987d4fdScedric 
85e78acc1eScedric #define CREATE_TABLE do {						\
86898866c2Skn 		warn_duplicate_tables(table.pfrt_name,			\
87898866c2Skn 		    table.pfrt_anchor);					\
88e78acc1eScedric 		table.pfrt_flags |= PFR_TFLAG_PERSIST;			\
89ba1c4096Scedric 		if ((!(opts & PF_OPT_NOACTION) ||			\
90ba1c4096Scedric 		    (opts & PF_OPT_DUMMYACTION)) &&			\
91ba1c4096Scedric 		    (pfr_add_tables(&table, 1, &nadd, flags)) &&	\
92ba1c4096Scedric 		    (errno != EPERM)) {					\
93c802a0d9Skn 			warnx("%s", pf_strerror(errno));		\
94ba1c4096Scedric 			goto _error;					\
95ba1c4096Scedric 		}							\
96e78acc1eScedric 		if (nadd) {						\
97e78acc1eScedric 			xprintf(opts, "%d table created", nadd);	\
98e78acc1eScedric 			if (opts & PF_OPT_NOACTION)			\
99e78acc1eScedric 				return (0);				\
100e78acc1eScedric 		}							\
101e78acc1eScedric 		table.pfrt_flags &= ~PFR_TFLAG_PERSIST;			\
102e78acc1eScedric 	} while(0)
103e78acc1eScedric 
104ae711728Ssashan int
105d9ad7941Sdhartmei pfctl_clear_tables(const char *anchor, int opts)
106a987d4fdScedric {
107ae711728Ssashan 	int	rv;
108ae711728Ssashan 
109ae711728Ssashan 	if ((rv = pfctl_table(0, NULL, NULL, "-F", NULL, anchor, opts)) == -1) {
110ae711728Ssashan 		if ((opts & PF_OPT_IGNFAIL) == 0)
111c5e9690eSawolk 			exit(1);
112a987d4fdScedric 	}
113a987d4fdScedric 
114ae711728Ssashan 	return (rv);
115ae711728Ssashan }
116ae711728Ssashan 
117c5e9690eSawolk void
118d9ad7941Sdhartmei pfctl_show_tables(const char *anchor, int opts)
119a987d4fdScedric {
120c5e9690eSawolk 	if (pfctl_table(0, NULL, NULL, "-s", NULL, anchor, opts) == -1)
121c5e9690eSawolk 		exit(1);
122a987d4fdScedric }
123a987d4fdScedric 
124a987d4fdScedric int
125132c30ccShenning pfctl_table(int argc, char *argv[], char *tname, const char *command,
126d9ad7941Sdhartmei     char *file, const char *anchor, int opts)
127a987d4fdScedric {
128a987d4fdScedric 	struct pfr_table	 table;
12942e05679Scedric 	struct pfr_buffer	 b, b2;
13042e05679Scedric 	struct pfr_addr		*a, *a2;
131a987d4fdScedric 	int			 nadd = 0, ndel = 0, nchange = 0, nzero = 0;
13242e05679Scedric 	int			 rv = 0, flags = 0, nmatch = 0;
13342e05679Scedric 	void			*p;
134a987d4fdScedric 
13523ebdef5Scedric 	if (command == NULL)
136a987d4fdScedric 		usage();
137a987d4fdScedric 	if (opts & PF_OPT_NOACTION)
138a987d4fdScedric 		flags |= PFR_FLAG_DUMMY;
13942e05679Scedric 
14042e05679Scedric 	bzero(&b, sizeof(b));
14142e05679Scedric 	bzero(&b2, sizeof(b2));
142a987d4fdScedric 	bzero(&table, sizeof(table));
143a987d4fdScedric 	if (tname != NULL) {
144a987d4fdScedric 		if (strlen(tname) >= PF_TABLE_NAME_SIZE)
145a987d4fdScedric 			usage();
14649bf4173Sdhartmei 		if (strlcpy(table.pfrt_name, tname,
14749bf4173Sdhartmei 		    sizeof(table.pfrt_name)) >= sizeof(table.pfrt_name))
14849bf4173Sdhartmei 			errx(1, "pfctl_table: strlcpy");
149a987d4fdScedric 	}
1503e963a2eScedric 	if (strlcpy(table.pfrt_anchor, anchor,
151d9ad7941Sdhartmei 	    sizeof(table.pfrt_anchor)) >= sizeof(table.pfrt_anchor))
1523e963a2eScedric 		errx(1, "pfctl_table: strlcpy");
15342e05679Scedric 
15423ebdef5Scedric 	if (!strcmp(command, "-F")) {
155a987d4fdScedric 		if (argc || file != NULL)
156a987d4fdScedric 			usage();
157a9b90787Scedric 		RVTEST(pfr_clr_tables(&table, &ndel, flags));
15802cc3c1dScedric 		xprintf(opts, "%d tables deleted", ndel);
15923ebdef5Scedric 	} else if (!strcmp(command, "-s")) {
16042e05679Scedric 		b.pfrb_type = (opts & PF_OPT_VERBOSE2) ?
16142e05679Scedric 		    PFRB_TSTATS : PFRB_TABLES;
162a987d4fdScedric 		if (argc || file != NULL)
163a987d4fdScedric 			usage();
164a987d4fdScedric 		for (;;) {
16542e05679Scedric 			pfr_buf_grow(&b, b.pfrb_size);
16642e05679Scedric 			b.pfrb_size = b.pfrb_msize;
16742e05679Scedric 			if (opts & PF_OPT_VERBOSE2)
16842e05679Scedric 				RVTEST(pfr_get_tstats(&table,
16942e05679Scedric 				    b.pfrb_caddr, &b.pfrb_size, flags));
17042e05679Scedric 			else
17142e05679Scedric 				RVTEST(pfr_get_tables(&table,
17242e05679Scedric 				    b.pfrb_caddr, &b.pfrb_size, flags));
17342e05679Scedric 			if (b.pfrb_size <= b.pfrb_msize)
174a987d4fdScedric 				break;
175a987d4fdScedric 		}
176c5b6504fSmcbride 
1779d4f5fa2Sderaadt 		if ((opts & PF_OPT_SHOWALL) && b.pfrb_size > 0)
178c5b6504fSmcbride 			pfctl_print_title("TABLES:");
179c5b6504fSmcbride 
18042e05679Scedric 		PFRB_FOREACH(p, &b)
181c06aa877Scedric 			if (opts & PF_OPT_VERBOSE2)
18242e05679Scedric 				print_tstats(p, opts & PF_OPT_DEBUG);
183a987d4fdScedric 			else
18442e05679Scedric 				print_table(p, opts & PF_OPT_VERBOSE,
18596e89413Scedric 				    opts & PF_OPT_DEBUG);
18623ebdef5Scedric 	} else if (!strcmp(command, "kill")) {
187a987d4fdScedric 		if (argc || file != NULL)
188a987d4fdScedric 			usage();
189a987d4fdScedric 		RVTEST(pfr_del_tables(&table, 1, &ndel, flags));
19002cc3c1dScedric 		xprintf(opts, "%d table deleted", ndel);
19123ebdef5Scedric 	} else if (!strcmp(command, "flush")) {
192a987d4fdScedric 		if (argc || file != NULL)
193a987d4fdScedric 			usage();
194a987d4fdScedric 		RVTEST(pfr_clr_addrs(&table, &ndel, flags));
19502cc3c1dScedric 		xprintf(opts, "%d addresses deleted", ndel);
19623ebdef5Scedric 	} else if (!strcmp(command, "add")) {
19742e05679Scedric 		b.pfrb_type = PFRB_ADDRS;
1987c8726d4Sbenno 		if (load_addr(&b, argc, argv, file, 0, opts))
19942e05679Scedric 			goto _error;
200e78acc1eScedric 		CREATE_TABLE;
201a987d4fdScedric 		if (opts & PF_OPT_VERBOSE)
202a987d4fdScedric 			flags |= PFR_FLAG_FEEDBACK;
20342e05679Scedric 		RVTEST(pfr_add_addrs(&table, b.pfrb_caddr, b.pfrb_size,
20442e05679Scedric 		    &nadd, flags));
20542e05679Scedric 		xprintf(opts, "%d/%d addresses added", nadd, b.pfrb_size);
206a987d4fdScedric 		if (opts & PF_OPT_VERBOSE)
20742e05679Scedric 			PFRB_FOREACH(a, &b)
208ab23e671Skn 				if (opts & PF_OPT_VERBOSE2 ||
209ab23e671Skn 				    a->pfra_fback != PFR_FB_NONE)
21042e05679Scedric 					print_addrx(a, NULL,
211a987d4fdScedric 					    opts & PF_OPT_USEDNS);
21223ebdef5Scedric 	} else if (!strcmp(command, "delete")) {
21342e05679Scedric 		b.pfrb_type = PFRB_ADDRS;
2147c8726d4Sbenno 		if (load_addr(&b, argc, argv, file, 0, opts))
21542e05679Scedric 			goto _error;
216a987d4fdScedric 		if (opts & PF_OPT_VERBOSE)
217a987d4fdScedric 			flags |= PFR_FLAG_FEEDBACK;
21842e05679Scedric 		RVTEST(pfr_del_addrs(&table, b.pfrb_caddr, b.pfrb_size,
21942e05679Scedric 		    &ndel, flags));
22042e05679Scedric 		xprintf(opts, "%d/%d addresses deleted", ndel, b.pfrb_size);
221a987d4fdScedric 		if (opts & PF_OPT_VERBOSE)
22242e05679Scedric 			PFRB_FOREACH(a, &b)
223ab23e671Skn 				if (opts & PF_OPT_VERBOSE2 ||
224ab23e671Skn 				    a->pfra_fback != PFR_FB_NONE)
22542e05679Scedric 					print_addrx(a, NULL,
226a987d4fdScedric 					    opts & PF_OPT_USEDNS);
22723ebdef5Scedric 	} else if (!strcmp(command, "replace")) {
22842e05679Scedric 		b.pfrb_type = PFRB_ADDRS;
2297c8726d4Sbenno 		if (load_addr(&b, argc, argv, file, 0, opts))
23042e05679Scedric 			goto _error;
231e78acc1eScedric 		CREATE_TABLE;
232a987d4fdScedric 		if (opts & PF_OPT_VERBOSE)
233a987d4fdScedric 			flags |= PFR_FLAG_FEEDBACK;
234a987d4fdScedric 		for (;;) {
23542e05679Scedric 			int sz2 = b.pfrb_msize;
236a987d4fdScedric 
23742e05679Scedric 			RVTEST(pfr_set_addrs(&table, b.pfrb_caddr, b.pfrb_size,
23842e05679Scedric 			    &sz2, &nadd, &ndel, &nchange, flags));
23942e05679Scedric 			if (sz2 <= b.pfrb_msize) {
24042e05679Scedric 				b.pfrb_size = sz2;
241a987d4fdScedric 				break;
242a987d4fdScedric 			} else
24342e05679Scedric 				pfr_buf_grow(&b, sz2);
244a987d4fdScedric 		}
245a987d4fdScedric 		if (nadd)
24602cc3c1dScedric 			xprintf(opts, "%d addresses added", nadd);
247a987d4fdScedric 		if (ndel)
24802cc3c1dScedric 			xprintf(opts, "%d addresses deleted", ndel);
249a987d4fdScedric 		if (nchange)
25002cc3c1dScedric 			xprintf(opts, "%d addresses changed", nchange);
251a987d4fdScedric 		if (!nadd && !ndel && !nchange)
25202cc3c1dScedric 			xprintf(opts, "no changes");
253a987d4fdScedric 		if (opts & PF_OPT_VERBOSE)
25442e05679Scedric 			PFRB_FOREACH(a, &b)
255ab23e671Skn 				if (opts & PF_OPT_VERBOSE2 ||
256ab23e671Skn 				    a->pfra_fback != PFR_FB_NONE)
25742e05679Scedric 					print_addrx(a, NULL,
258a987d4fdScedric 					    opts & PF_OPT_USEDNS);
259d11096e9Shenning 	} else if (!strcmp(command, "expire")) {
260d11096e9Shenning 		const char		*errstr;
261d11096e9Shenning 		u_int			 lifetime;
262d11096e9Shenning 
263d11096e9Shenning 		b.pfrb_type = PFRB_ASTATS;
264d11096e9Shenning 		b2.pfrb_type = PFRB_ADDRS;
265d11096e9Shenning 		if (argc != 1 || file != NULL)
266d11096e9Shenning 			usage();
267d11096e9Shenning 		lifetime = strtonum(*argv, 0, UINT_MAX, &errstr);
268d11096e9Shenning 		if (errstr)
269d11096e9Shenning 			errx(1, "expiry time: %s", errstr);
270d11096e9Shenning 		for (;;) {
271d11096e9Shenning 			pfr_buf_grow(&b, b.pfrb_size);
272d11096e9Shenning 			b.pfrb_size = b.pfrb_msize;
273d11096e9Shenning 			RVTEST(pfr_get_astats(&table, b.pfrb_caddr,
274d11096e9Shenning 			    &b.pfrb_size, flags));
275d11096e9Shenning 			if (b.pfrb_size <= b.pfrb_msize)
276d11096e9Shenning 				break;
277d11096e9Shenning 		}
278ed1ed052Smcbride 		PFRB_FOREACH(p, &b) {
279ab23e671Skn 			((struct pfr_astats *)p)->pfras_a.pfra_fback = PFR_FB_NONE;
280d11096e9Shenning 			if (time(NULL) - ((struct pfr_astats *)p)->pfras_tzero >
281d11096e9Shenning 			     lifetime)
282d11096e9Shenning 				if (pfr_buf_add(&b2,
283d11096e9Shenning 				    &((struct pfr_astats *)p)->pfras_a))
284d11096e9Shenning 					err(1, "duplicate buffer");
285ed1ed052Smcbride 		}
286d11096e9Shenning 
287d11096e9Shenning 		if (opts & PF_OPT_VERBOSE)
288d11096e9Shenning 			flags |= PFR_FLAG_FEEDBACK;
289d11096e9Shenning 		RVTEST(pfr_del_addrs(&table, b2.pfrb_caddr, b2.pfrb_size,
290d11096e9Shenning 		    &ndel, flags));
291d11096e9Shenning 		xprintf(opts, "%d/%d addresses expired", ndel, b2.pfrb_size);
292d11096e9Shenning 		if (opts & PF_OPT_VERBOSE)
293d11096e9Shenning 			PFRB_FOREACH(a, &b2)
294ab23e671Skn 				if (opts & PF_OPT_VERBOSE2 ||
295ab23e671Skn 				    a->pfra_fback != PFR_FB_NONE)
296d11096e9Shenning 					print_addrx(a, NULL,
297d11096e9Shenning 					    opts & PF_OPT_USEDNS);
29823ebdef5Scedric 	} else if (!strcmp(command, "show")) {
29942e05679Scedric 		b.pfrb_type = (opts & PF_OPT_VERBOSE) ?
30042e05679Scedric 			PFRB_ASTATS : PFRB_ADDRS;
301a987d4fdScedric 		if (argc || file != NULL)
302a987d4fdScedric 			usage();
303a987d4fdScedric 		for (;;) {
30442e05679Scedric 			pfr_buf_grow(&b, b.pfrb_size);
30542e05679Scedric 			b.pfrb_size = b.pfrb_msize;
30642e05679Scedric 			if (opts & PF_OPT_VERBOSE)
30742e05679Scedric 				RVTEST(pfr_get_astats(&table, b.pfrb_caddr,
30842e05679Scedric 				    &b.pfrb_size, flags));
30942e05679Scedric 			else
31042e05679Scedric 				RVTEST(pfr_get_addrs(&table, b.pfrb_caddr,
31142e05679Scedric 				    &b.pfrb_size, flags));
31242e05679Scedric 			if (b.pfrb_size <= b.pfrb_msize)
313a987d4fdScedric 				break;
314a987d4fdScedric 		}
31542e05679Scedric 		PFRB_FOREACH(p, &b)
31642e05679Scedric 			if (opts & PF_OPT_VERBOSE)
31742e05679Scedric 				print_astats(p, opts & PF_OPT_USEDNS);
31842e05679Scedric 			else
31942e05679Scedric 				print_addrx(p, NULL, opts & PF_OPT_USEDNS);
32023ebdef5Scedric 	} else if (!strcmp(command, "test")) {
32142e05679Scedric 		b.pfrb_type = PFRB_ADDRS;
32242e05679Scedric 		b2.pfrb_type = PFRB_ADDRS;
32342e05679Scedric 
3247c8726d4Sbenno 		if (load_addr(&b, argc, argv, file, 1, opts))
32542e05679Scedric 			goto _error;
326a987d4fdScedric 		if (opts & PF_OPT_VERBOSE2) {
327a987d4fdScedric 			flags |= PFR_FLAG_REPLACE;
32842e05679Scedric 			PFRB_FOREACH(a, &b)
32942e05679Scedric 				if (pfr_buf_add(&b2, a))
33042e05679Scedric 					err(1, "duplicate buffer");
331a987d4fdScedric 		}
33242e05679Scedric 		RVTEST(pfr_tst_addrs(&table, b.pfrb_caddr, b.pfrb_size,
33342e05679Scedric 		    &nmatch, flags));
33442e05679Scedric 		xprintf(opts, "%d/%d addresses match", nmatch, b.pfrb_size);
3359d4f5fa2Sderaadt 		if ((opts & PF_OPT_VERBOSE) && !(opts & PF_OPT_VERBOSE2))
33642e05679Scedric 			PFRB_FOREACH(a, &b)
33742e05679Scedric 				if (a->pfra_fback == PFR_FB_MATCH)
33842e05679Scedric 					print_addrx(a, NULL,
339a987d4fdScedric 					    opts & PF_OPT_USEDNS);
3409418048bScedric 		if (opts & PF_OPT_VERBOSE2) {
34142e05679Scedric 			a2 = NULL;
34242e05679Scedric 			PFRB_FOREACH(a, &b) {
34342e05679Scedric 				a2 = pfr_buf_next(&b2, a2);
34442e05679Scedric 				print_addrx(a2, a, opts & PF_OPT_USEDNS);
3459418048bScedric 			}
34642e05679Scedric 		}
34742e05679Scedric 		if (nmatch < b.pfrb_size)
34842e05679Scedric 			rv = 2;
349*e496dff3Skirill 	} else if (!strcmp(command, "zero") && (argc || file != NULL)) {
350*e496dff3Skirill 		b.pfrb_type = PFRB_ADDRS;
351*e496dff3Skirill 		if (load_addr(&b, argc, argv, file, 0, opts))
352*e496dff3Skirill 			goto _error;
353*e496dff3Skirill 		if (opts & PF_OPT_VERBOSE)
354*e496dff3Skirill 			flags |= PFR_FLAG_FEEDBACK;
355*e496dff3Skirill 		RVTEST(pfr_clr_astats(&table, b.pfrb_caddr, b.pfrb_size,
356*e496dff3Skirill 		    &nzero, flags));
357*e496dff3Skirill 		xprintf(opts, "%d/%d addresses cleared", nzero, b.pfrb_size);
358*e496dff3Skirill 		if (opts & PF_OPT_VERBOSE)
359*e496dff3Skirill 			PFRB_FOREACH(a, &b)
360*e496dff3Skirill 				if (opts & PF_OPT_VERBOSE2 ||
361*e496dff3Skirill 				    a->pfra_fback != PFR_FB_NONE)
362*e496dff3Skirill 					print_addrx(a, NULL,
363*e496dff3Skirill 					    opts & PF_OPT_USEDNS);
36423ebdef5Scedric 	} else if (!strcmp(command, "zero")) {
365c06aa877Scedric 		flags |= PFR_FLAG_ADDRSTOO;
366a987d4fdScedric 		RVTEST(pfr_clr_tstats(&table, 1, &nzero, flags));
36702cc3c1dScedric 		xprintf(opts, "%d table/stats cleared", nzero);
36823ebdef5Scedric 	} else
369db5ede9fSdhartmei 		warnx("pfctl_table: unknown command '%s'", command);
37042e05679Scedric 	goto _cleanup;
371a987d4fdScedric 
37242e05679Scedric _error:
37342e05679Scedric 	rv = -1;
37442e05679Scedric _cleanup:
37542e05679Scedric 	pfr_buf_clear(&b);
37642e05679Scedric 	pfr_buf_clear(&b2);
37742e05679Scedric 	return (rv);
378a987d4fdScedric }
379a987d4fdScedric 
380a987d4fdScedric void
38196e89413Scedric print_table(struct pfr_table *ta, int verbose, int debug)
382a987d4fdScedric {
38396e89413Scedric 	if (!debug && !(ta->pfrt_flags & PFR_TFLAG_ACTIVE))
384bb75ae08Sdhartmei 		return;
3853898e353Ssashan 	if (verbose)
3863898e353Ssashan 		printf("%c%c%c%c%c%c%c\t",
387c06aa877Scedric 		    (ta->pfrt_flags & PFR_TFLAG_CONST) ? 'c' : '-',
388c06aa877Scedric 		    (ta->pfrt_flags & PFR_TFLAG_PERSIST) ? 'p' : '-',
389c06aa877Scedric 		    (ta->pfrt_flags & PFR_TFLAG_ACTIVE) ? 'a' : '-',
390c06aa877Scedric 		    (ta->pfrt_flags & PFR_TFLAG_INACTIVE) ? 'i' : '-',
391c06aa877Scedric 		    (ta->pfrt_flags & PFR_TFLAG_REFERENCED) ? 'r' : '-',
3923e963a2eScedric 		    (ta->pfrt_flags & PFR_TFLAG_REFDANCHOR) ? 'h' : '-',
3933898e353Ssashan 		    (ta->pfrt_flags & PFR_TFLAG_COUNTERS) ? 'C' : '-');
3943898e353Ssashan 
3953898e353Ssashan 	printf("%s", ta->pfrt_name);
3963898e353Ssashan 	if (ta->pfrt_anchor[0] != '\0')
3973898e353Ssashan 		printf("@%s", ta->pfrt_anchor);
3983898e353Ssashan 
3993898e353Ssashan 	printf("\n");
400a987d4fdScedric }
401a987d4fdScedric 
402a987d4fdScedric void
40396e89413Scedric print_tstats(struct pfr_tstats *ts, int debug)
404a987d4fdScedric {
405a987d4fdScedric 	time_t	 time = ts->pfrts_tzero;
406a987d4fdScedric 	int	 dir, op;
407a7b9eedcSflorian 	char	*ct;
408a987d4fdScedric 
40996e89413Scedric 	if (!debug && !(ts->pfrts_flags & PFR_TFLAG_ACTIVE))
410bb75ae08Sdhartmei 		return;
411a7b9eedcSflorian 	ct = ctime(&time);
41296e89413Scedric 	print_table(&ts->pfrts_t, 1, debug);
413a987d4fdScedric 	printf("\tAddresses:   %d\n", ts->pfrts_cnt);
414a7b9eedcSflorian 	if (ct)
415a7b9eedcSflorian 		printf("\tCleared:     %s", ct);
416a7b9eedcSflorian 	else
417a7b9eedcSflorian 		printf("\tCleared:     %lld\n", time);
4183e963a2eScedric 	printf("\tReferences:  [ Anchors: %-18d Rules: %-18d ]\n",
4193e963a2eScedric 	    ts->pfrts_refcnt[PFR_REFCNT_ANCHOR],
4203e963a2eScedric 	    ts->pfrts_refcnt[PFR_REFCNT_RULE]);
421a987d4fdScedric 	printf("\tEvaluations: [ NoMatch: %-18llu Match: %-18llu ]\n",
422ef22e615Sdhartmei 	    (unsigned long long)ts->pfrts_nomatch,
423ef22e615Sdhartmei 	    (unsigned long long)ts->pfrts_match);
424a987d4fdScedric 	for (dir = 0; dir < PFR_DIR_MAX; dir++)
425a987d4fdScedric 		for (op = 0; op < PFR_OP_TABLE_MAX; op++)
426a987d4fdScedric 			printf("\t%-12s [ Packets: %-18llu Bytes: %-18llu ]\n",
427ef22e615Sdhartmei 			    stats_text[dir][op],
428ef22e615Sdhartmei 			    (unsigned long long)ts->pfrts_packets[dir][op],
429ef22e615Sdhartmei 			    (unsigned long long)ts->pfrts_bytes[dir][op]);
430a987d4fdScedric }
431a987d4fdScedric 
43242e05679Scedric int
43342e05679Scedric load_addr(struct pfr_buffer *b, int argc, char *argv[], char *file,
4347c8726d4Sbenno     int nonetwork, int opts)
435a987d4fdScedric {
436cbdc262eSmcbride 	int	ev = 0;
437a987d4fdScedric 	while (argc--)
4387c8726d4Sbenno 		if ((ev = append_addr(b, *argv++, nonetwork, opts)) == -1) {
43942e05679Scedric 			if (errno)
44042e05679Scedric 				warn("cannot decode %s", argv[-1]);
44142e05679Scedric 			return (-1);
442a987d4fdScedric 		}
443cbdc262eSmcbride 	if (ev == 1) { /* expected further append_addr call */
444cbdc262eSmcbride 		warnx("failed to decode %s", argv[-1]);
445cbdc262eSmcbride 		return (-1);
446cbdc262eSmcbride 	}
4477c8726d4Sbenno 	if (pfr_buf_load(b, file, nonetwork, opts)) {
44842e05679Scedric 		warn("cannot load %s", file);
44942e05679Scedric 		return (-1);
450a987d4fdScedric 	}
45142e05679Scedric 	return (0);
452a987d4fdScedric }
453a987d4fdScedric 
454a987d4fdScedric void
45518aff148Scedric print_addrx(struct pfr_addr *ad, struct pfr_addr *rad, int dns)
456a987d4fdScedric {
45742e05679Scedric 	char		ch, buf[256] = "{error}";
4585f03a6e1Smcbride 	char		fb[] = { ' ', 'M', 'A', 'D', 'C', 'Z', 'X', ' ', 'Y', ' ' };
4595eb10b9fScedric 	unsigned int	fback, hostnet;
460a987d4fdScedric 
461a987d4fdScedric 	fback = (rad != NULL) ? rad->pfra_fback : ad->pfra_fback;
462b8802620Scedric 	ch = (fback < sizeof(fb)/sizeof(*fb)) ? fb[fback] : '?';
463a987d4fdScedric 	hostnet = (ad->pfra_af == AF_INET6) ? 128 : 32;
464a987d4fdScedric 	inet_ntop(ad->pfra_af, &ad->pfra_u, buf, sizeof(buf));
465b8802620Scedric 	printf("%c %c%s", ch, (ad->pfra_not?'!':' '), buf);
466a987d4fdScedric 	if (ad->pfra_net < hostnet)
467a987d4fdScedric 		printf("/%d", ad->pfra_net);
468a987d4fdScedric 	if (rad != NULL && fback != PFR_FB_NONE) {
46949bf4173Sdhartmei 		if (strlcpy(buf, "{error}", sizeof(buf)) >= sizeof(buf))
47049bf4173Sdhartmei 			errx(1, "print_addrx: strlcpy");
471a987d4fdScedric 		inet_ntop(rad->pfra_af, &rad->pfra_u, buf, sizeof(buf));
472a987d4fdScedric 		printf("\t%c%s", (rad->pfra_not?'!':' '), buf);
473a987d4fdScedric 		if (rad->pfra_net < hostnet)
474a987d4fdScedric 			printf("/%d", rad->pfra_net);
475a987d4fdScedric 	}
476a987d4fdScedric 	if (rad != NULL && fback == PFR_FB_NONE)
477a987d4fdScedric 		printf("\t nomatch");
478a987d4fdScedric 	if (dns && ad->pfra_net == hostnet) {
47922d4dcb1Scedric 		char host[NI_MAXHOST];
4808717211fSderaadt 		struct sockaddr_storage ss;
481a987d4fdScedric 
48222d4dcb1Scedric 		strlcpy(host, "?", sizeof(host));
4838717211fSderaadt 		bzero(&ss, sizeof(ss));
4848717211fSderaadt 		ss.ss_family = ad->pfra_af;
4858717211fSderaadt 		if (ss.ss_family == AF_INET) {
4868717211fSderaadt 			struct sockaddr_in *sin = (struct sockaddr_in *)&ss;
4878717211fSderaadt 
4888717211fSderaadt 			sin->sin_len = sizeof(*sin);
4898717211fSderaadt 			sin->sin_addr = ad->pfra_ip4addr;
49022d4dcb1Scedric 		} else {
4918717211fSderaadt 			struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&ss;
4928717211fSderaadt 
4938717211fSderaadt 			sin6->sin6_len = sizeof(*sin6);
4948717211fSderaadt 			sin6->sin6_addr = ad->pfra_ip6addr;
49522d4dcb1Scedric 		}
4968717211fSderaadt 		if (getnameinfo((struct sockaddr *)&ss, ss.ss_len, host,
4978717211fSderaadt 		    sizeof(host), NULL, 0, NI_NAMEREQD) == 0)
498a987d4fdScedric 			printf("\t(%s)", host);
499a987d4fdScedric 	}
50036754172Smcbride 	if (ad->pfra_ifname[0] != '\0')
50136754172Smcbride 		printf("@%s", ad->pfra_ifname);
502a987d4fdScedric 	printf("\n");
503a987d4fdScedric }
504a987d4fdScedric 
505a987d4fdScedric void
50618aff148Scedric print_astats(struct pfr_astats *as, int dns)
507a987d4fdScedric {
508a987d4fdScedric 	time_t	 time = as->pfras_tzero;
509a987d4fdScedric 	int	 dir, op;
510a7b9eedcSflorian 	char	*ct;
511a987d4fdScedric 
512a7b9eedcSflorian 	ct = ctime(&time);
51318aff148Scedric 	print_addrx(&as->pfras_a, NULL, dns);
514a7b9eedcSflorian 	if (ct)
515a987d4fdScedric 		printf("\tCleared:     %s", ctime(&time));
516a7b9eedcSflorian 	else
517a7b9eedcSflorian 		printf("\tCleared:     %lld\n", time);
518cbdc262eSmcbride 	if (as->pfras_a.pfra_states)
519bcb11948Szinke 		printf("\tActive States:      %d\n", as->pfras_a.pfra_states);
520cbdc262eSmcbride 	if (as->pfras_a.pfra_type == PFRKE_COST)
521cbdc262eSmcbride 		printf("\tWeight:             %d\n", as->pfras_a.pfra_weight);
52236754172Smcbride 	if (as->pfras_a.pfra_ifname[0])
52336754172Smcbride 		printf("\tInterface:          %s\n", as->pfras_a.pfra_ifname);
524cbdc262eSmcbride 	if (as->pfras_a.pfra_fback == PFR_FB_NOCOUNT)
525cbdc262eSmcbride 		return;
526a987d4fdScedric 	for (dir = 0; dir < PFR_DIR_MAX; dir++)
527a987d4fdScedric 		for (op = 0; op < PFR_OP_ADDR_MAX; op++)
528401a01c8Sblambert 			printf("\t%-12s [ Packets: %-18llu Bytes: %-18llu ]\n",
529ef22e615Sdhartmei 			    stats_text[dir][op],
530ef22e615Sdhartmei 			    (unsigned long long)as->pfras_packets[dir][op],
531ef22e615Sdhartmei 			    (unsigned long long)as->pfras_bytes[dir][op]);
532a987d4fdScedric }
533a987d4fdScedric 
53442e05679Scedric int
5355b6c447dScedric pfctl_define_table(char *name, int flags, int addrs, const char *anchor,
53630269bc3Ssashan     struct pfr_buffer *ab, u_int32_t ticket, struct pfr_uktable *ukt)
537c06aa877Scedric {
53830269bc3Ssashan 	struct pfr_table tbl_buf;
53930269bc3Ssashan 	struct pfr_table *tbl;
540c06aa877Scedric 
54130269bc3Ssashan 	if (ukt == NULL) {
54230269bc3Ssashan 		bzero(&tbl_buf, sizeof(tbl_buf));
54330269bc3Ssashan 		tbl = &tbl_buf;
54430269bc3Ssashan 	} else {
54530269bc3Ssashan 		if (ab->pfrb_size != 0) {
54630269bc3Ssashan 			/*
54730269bc3Ssashan 			 * copy IP addresses which come with table from
54830269bc3Ssashan 			 * temporal buffer to buffer attached to table.
54930269bc3Ssashan 			 */
55030269bc3Ssashan 			ukt->pfrukt_addrs = *ab;
55130269bc3Ssashan 			ab->pfrb_size = 0;
55230269bc3Ssashan 			ab->pfrb_msize = 0;
55330269bc3Ssashan 			ab->pfrb_caddr = NULL;
55430269bc3Ssashan 		} else
55530269bc3Ssashan 			memset(&ukt->pfrukt_addrs, 0,
55630269bc3Ssashan 			    sizeof(struct pfr_buffer));
557c06aa877Scedric 
55830269bc3Ssashan 		tbl = &ukt->pfrukt_t;
55930269bc3Ssashan 	}
56030269bc3Ssashan 
56130269bc3Ssashan 	if (strlcpy(tbl->pfrt_name, name, sizeof(tbl->pfrt_name)) >=
56230269bc3Ssashan 	    sizeof(tbl->pfrt_name) || strlcpy(tbl->pfrt_anchor, anchor,
56330269bc3Ssashan 	    sizeof(tbl->pfrt_anchor)) >= sizeof(tbl->pfrt_anchor))
56430269bc3Ssashan 		errx(1, "%s: strlcpy", __func__);
56530269bc3Ssashan 	tbl->pfrt_flags = flags;
56630269bc3Ssashan 	DBGPRINT("%s %s@%s [%x]\n", __func__, tbl->pfrt_name,
56730269bc3Ssashan 	    tbl->pfrt_anchor, tbl->pfrt_flags);
56830269bc3Ssashan 
56930269bc3Ssashan 	/*
57030269bc3Ssashan 	 * non-root anchors processed by parse.y are loaded to kernel later.
57130269bc3Ssashan 	 * Here we load tables, which are either created for root anchor
57230269bc3Ssashan 	 * or by 'pfctl -t ... -T ...' command.
57330269bc3Ssashan 	 */
57430269bc3Ssashan 	if (ukt != NULL)
57530269bc3Ssashan 		return (0);
57630269bc3Ssashan 
57730269bc3Ssashan 	return pfr_ina_define(tbl, ab->pfrb_caddr, ab->pfrb_size, NULL,
5785b6c447dScedric 	    NULL, ticket, addrs ? PFR_FLAG_ADDRSTOO : 0);
579c06aa877Scedric }
580c06aa877Scedric 
58113bdc86aSdhartmei void
5820de3a0c9Skn warn_duplicate_tables(const char *tablename, const char *anchorname)
58332d3aaf0Scedric {
58432d3aaf0Scedric 	struct pfr_buffer b;
58532d3aaf0Scedric 	struct pfr_table *t;
58632d3aaf0Scedric 
58732d3aaf0Scedric 	bzero(&b, sizeof(b));
58832d3aaf0Scedric 	b.pfrb_type = PFRB_TABLES;
58932d3aaf0Scedric 	for (;;) {
59032d3aaf0Scedric 		pfr_buf_grow(&b, b.pfrb_size);
59132d3aaf0Scedric 		b.pfrb_size = b.pfrb_msize;
59232d3aaf0Scedric 		if (pfr_get_tables(NULL, b.pfrb_caddr,
59332d3aaf0Scedric 		    &b.pfrb_size, PFR_FLAG_ALLRSETS))
59432d3aaf0Scedric 			err(1, "pfr_get_tables");
59532d3aaf0Scedric 		if (b.pfrb_size <= b.pfrb_msize)
59632d3aaf0Scedric 			break;
59732d3aaf0Scedric 	}
59832d3aaf0Scedric 	PFRB_FOREACH(t, &b) {
59932d3aaf0Scedric 		if (!(t->pfrt_flags & PFR_TFLAG_ACTIVE))
60032d3aaf0Scedric 			continue;
6010de3a0c9Skn 		if (!strcmp(anchorname, t->pfrt_anchor))
60232d3aaf0Scedric 			continue;
6030de3a0c9Skn 		if (!strcmp(tablename, t->pfrt_name))
6040de3a0c9Skn 			warnx("warning: table <%s> already defined"
6050de3a0c9Skn 			    " in anchor \"%s\"", tablename,
6060de3a0c9Skn 			    t->pfrt_anchor[0] ? t->pfrt_anchor : "/");
60732d3aaf0Scedric 	}
60832d3aaf0Scedric 	pfr_buf_clear(&b);
60932d3aaf0Scedric }
61032d3aaf0Scedric 
61132d3aaf0Scedric void
612132c30ccShenning xprintf(int opts, const char *fmt, ...)
61302cc3c1dScedric {
61402cc3c1dScedric 	va_list args;
61502cc3c1dScedric 
61602cc3c1dScedric 	if (opts & PF_OPT_QUIET)
61702cc3c1dScedric 		return;
618ddcdc3a7Scedric 
61902cc3c1dScedric 	va_start(args, fmt);
62002cc3c1dScedric 	vfprintf(stderr, fmt, args);
621ddcdc3a7Scedric 	va_end(args);
622ddcdc3a7Scedric 
62302cc3c1dScedric 	if (opts & PF_OPT_DUMMYACTION)
62402cc3c1dScedric 		fprintf(stderr, " (dummy).\n");
62502cc3c1dScedric 	else if (opts & PF_OPT_NOACTION)
62602cc3c1dScedric 		fprintf(stderr, " (syntax only).\n");
62702cc3c1dScedric 	else
62802cc3c1dScedric 		fprintf(stderr, ".\n");
62902cc3c1dScedric }
630ec359bd5Scedric 
631ec359bd5Scedric 
632ec359bd5Scedric /* interface stuff */
633ec359bd5Scedric 
634c5e9690eSawolk void
6359ee451e7Scedric pfctl_show_ifaces(const char *filter, int opts)
636ec359bd5Scedric {
637ec359bd5Scedric 	struct pfr_buffer	 b;
638c1f13696Shenning 	struct pfi_kif		*p;
639ec359bd5Scedric 
640ec359bd5Scedric 	bzero(&b, sizeof(b));
641ec359bd5Scedric 	b.pfrb_type = PFRB_IFACES;
642ec359bd5Scedric 	for (;;) {
643edd74edcSsashan 		pfr_buf_grow(&b, 0);
644ec359bd5Scedric 		b.pfrb_size = b.pfrb_msize;
645e13f0105Skn 		if (pfi_get_ifaces(filter, b.pfrb_caddr, &b.pfrb_size))
646c802a0d9Skn 			errx(1, "%s", pf_strerror(errno));
647edd74edcSsashan 		if (b.pfrb_size < b.pfrb_msize)
648ec359bd5Scedric 			break;
649ec359bd5Scedric 	}
650c5b6504fSmcbride 	if (opts & PF_OPT_SHOWALL)
651c5b6504fSmcbride 		pfctl_print_title("INTERFACES:");
652ec359bd5Scedric 	PFRB_FOREACH(p, &b)
653ec359bd5Scedric 		print_iface(p, opts);
654ec359bd5Scedric }
655ec359bd5Scedric 
656ec359bd5Scedric void
657c1f13696Shenning print_iface(struct pfi_kif *p, int opts)
658ec359bd5Scedric {
659c1f13696Shenning 	time_t	 tzero = p->pfik_tzero;
660ec359bd5Scedric 	int	 i, af, dir, act;
661a7b9eedcSflorian 	char	*ct;
662ec359bd5Scedric 
663c1f13696Shenning 	printf("%s", p->pfik_name);
664dd9c8f06Sdhartmei 	if (opts & PF_OPT_VERBOSE) {
665dd9c8f06Sdhartmei 		if (p->pfik_flags & PFI_IFLAG_SKIP)
666dd9c8f06Sdhartmei 			printf(" (skip)");
667dd9c8f06Sdhartmei 	}
668ec359bd5Scedric 	printf("\n");
669ec359bd5Scedric 
670ec359bd5Scedric 	if (!(opts & PF_OPT_VERBOSE2))
671ec359bd5Scedric 		return;
672a7b9eedcSflorian 
673a7b9eedcSflorian 	ct = ctime(&tzero);
674a7b9eedcSflorian 	if (ct)
675a7b9eedcSflorian 		printf("\tCleared:     %s", ct);
676a7b9eedcSflorian 	else
677a7b9eedcSflorian 		printf("\tCleared:     %lld\n", tzero);
678ec359bd5Scedric 	printf("\tReferences:  [ States:  %-18d Rules: %-18d ]\n",
679c1f13696Shenning 	    p->pfik_states, p->pfik_rules);
680ec359bd5Scedric 	for (i = 0; i < 8; i++) {
681ec359bd5Scedric 		af = (i>>2) & 1;
682ec359bd5Scedric 		dir = (i>>1) &1;
683ec359bd5Scedric 		act = i & 1;
684ec359bd5Scedric 		printf("\t%-12s [ Packets: %-18llu Bytes: %-18llu ]\n",
685ef22e615Sdhartmei 		    istats_text[af][dir][act],
686c1f13696Shenning 		    (unsigned long long)p->pfik_packets[af][dir][act],
687c1f13696Shenning 		    (unsigned long long)p->pfik_bytes[af][dir][act]);
688ec359bd5Scedric 	}
689ec359bd5Scedric }
690