Deleted Added
full compact
pfctl_table.c (126354) pfctl_table.c (126355)
1/* $FreeBSD: head/contrib/pf/pfctl/pfctl_table.c 126355 2004-02-28 17:32:53Z mlaier $ */
1/* $OpenBSD: pfctl_table.c,v 1.50 2003/08/29 21:47:36 cedric Exp $ */
2
3/*
4 * Copyright (c) 2002 Cedric Berger
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 <net/pfvar.h>
39#include <arpa/inet.h>
40
41#include <ctype.h>
42#include <err.h>
43#include <errno.h>
44#include <netdb.h>
45#include <stdarg.h>
46#include <stdio.h>
47#include <stdlib.h>
48#include <string.h>
49#include <time.h>
50
2/* $OpenBSD: pfctl_table.c,v 1.50 2003/08/29 21:47:36 cedric Exp $ */
3
4/*
5 * Copyright (c) 2002 Cedric Berger
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 *
12 * - Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * - Redistributions in binary form must reproduce the above
15 * copyright notice, this list of conditions and the following
16 * disclaimer in the documentation and/or other materials provided
17 * with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
22 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
23 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
24 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
25 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
26 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
27 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
29 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30 * POSSIBILITY OF SUCH DAMAGE.
31 *
32 */
33
34#include <sys/types.h>
35#include <sys/ioctl.h>
36#include <sys/socket.h>
37
38#include <net/if.h>
39#include <net/pfvar.h>
40#include <arpa/inet.h>
41
42#include <ctype.h>
43#include <err.h>
44#include <errno.h>
45#include <netdb.h>
46#include <stdarg.h>
47#include <stdio.h>
48#include <stdlib.h>
49#include <string.h>
50#include <time.h>
51
52#if defined(__FreeBSD__)
53#include <inttypes.h>
54#else
55#define PRIu64 "llu"
56#endif
57
51#include "pfctl_parser.h"
52#include "pfctl.h"
53
54extern void usage(void);
55static int pfctl_table(int, char *[], char *, const char *, char *,
56 const char *, const char *, int);
57static void print_table(struct pfr_table *, int, int);
58static void print_tstats(struct pfr_tstats *, int);
59static int load_addr(struct pfr_buffer *, int, char *[], char *, int);
60static void print_addrx(struct pfr_addr *, struct pfr_addr *, int);
61static void print_astats(struct pfr_astats *, int);
62static void radix_perror(void);
63static void xprintf(int, const char *, ...);
64
65static const char *stats_text[PFR_DIR_MAX][PFR_OP_TABLE_MAX] = {
66 { "In/Block:", "In/Pass:", "In/XPass:" },
67 { "Out/Block:", "Out/Pass:", "Out/XPass:" }
68};
69
70#define RVTEST(fct) do { \
71 if ((!(opts & PF_OPT_NOACTION) || \
72 (opts & PF_OPT_DUMMYACTION)) && \
73 (fct)) { \
74 radix_perror(); \
75 goto _error; \
76 } \
77 } while (0)
78
79#define CREATE_TABLE do { \
80 table.pfrt_flags |= PFR_TFLAG_PERSIST; \
81 RVTEST(pfr_add_tables(&table, 1, &nadd, flags)); \
82 if (nadd) { \
83 warn_namespace_collision(table.pfrt_name); \
84 xprintf(opts, "%d table created", nadd); \
85 if (opts & PF_OPT_NOACTION) \
86 return (0); \
87 } \
88 table.pfrt_flags &= ~PFR_TFLAG_PERSIST; \
89 } while(0)
90
91int
92pfctl_clear_tables(const char *anchor, const char *ruleset, int opts)
93{
94 return pfctl_table(0, NULL, NULL, "-F", NULL, anchor, ruleset, opts);
95}
96
97int
98pfctl_show_tables(const char *anchor, const char *ruleset, int opts)
99{
100 return pfctl_table(0, NULL, NULL, "-s", NULL, anchor, ruleset, opts);
101}
102
103int
104pfctl_command_tables(int argc, char *argv[], char *tname,
105 const char *command, char *file, const char *anchor, const char *ruleset,
106 int opts)
107{
108 if (tname == NULL || command == NULL)
109 usage();
110 return pfctl_table(argc, argv, tname, command, file, anchor, ruleset,
111 opts);
112}
113
114int
115pfctl_table(int argc, char *argv[], char *tname, const char *command,
116 char *file, const char *anchor, const char *ruleset, int opts)
117{
118 struct pfr_table table;
119 struct pfr_buffer b, b2;
120 struct pfr_addr *a, *a2;
121 int nadd = 0, ndel = 0, nchange = 0, nzero = 0;
122 int rv = 0, flags = 0, nmatch = 0;
123 void *p;
124
125 if (command == NULL)
126 usage();
127 if (opts & PF_OPT_NOACTION)
128 flags |= PFR_FLAG_DUMMY;
129
130 bzero(&b, sizeof(b));
131 bzero(&b2, sizeof(b2));
132 bzero(&table, sizeof(table));
133 if (tname != NULL) {
134 if (strlen(tname) >= PF_TABLE_NAME_SIZE)
135 usage();
136 if (strlcpy(table.pfrt_name, tname,
137 sizeof(table.pfrt_name)) >= sizeof(table.pfrt_name))
138 errx(1, "pfctl_table: strlcpy");
139 }
140 if (strlcpy(table.pfrt_anchor, anchor,
141 sizeof(table.pfrt_anchor)) >= sizeof(table.pfrt_anchor) ||
142 strlcpy(table.pfrt_ruleset, ruleset,
143 sizeof(table.pfrt_ruleset)) >= sizeof(table.pfrt_ruleset))
144 errx(1, "pfctl_table: strlcpy");
145
146 if (!strcmp(command, "-F")) {
147 if (argc || file != NULL)
148 usage();
149 RVTEST(pfr_clr_tables(&table, &ndel, flags));
150 xprintf(opts, "%d tables deleted", ndel);
151 } else if (!strcmp(command, "-s")) {
152 b.pfrb_type = (opts & PF_OPT_VERBOSE2) ?
153 PFRB_TSTATS : PFRB_TABLES;
154 if (argc || file != NULL)
155 usage();
156 for (;;) {
157 pfr_buf_grow(&b, b.pfrb_size);
158 b.pfrb_size = b.pfrb_msize;
159 if (opts & PF_OPT_VERBOSE2)
160 RVTEST(pfr_get_tstats(&table,
161 b.pfrb_caddr, &b.pfrb_size, flags));
162 else
163 RVTEST(pfr_get_tables(&table,
164 b.pfrb_caddr, &b.pfrb_size, flags));
165 if (b.pfrb_size <= b.pfrb_msize)
166 break;
167 }
168 PFRB_FOREACH(p, &b)
169 if (opts & PF_OPT_VERBOSE2)
170 print_tstats(p, opts & PF_OPT_DEBUG);
171 else
172 print_table(p, opts & PF_OPT_VERBOSE,
173 opts & PF_OPT_DEBUG);
174 } else if (!strcmp(command, "kill")) {
175 if (argc || file != NULL)
176 usage();
177 RVTEST(pfr_del_tables(&table, 1, &ndel, flags));
178 xprintf(opts, "%d table deleted", ndel);
179 } else if (!strcmp(command, "flush")) {
180 if (argc || file != NULL)
181 usage();
182 RVTEST(pfr_clr_addrs(&table, &ndel, flags));
183 xprintf(opts, "%d addresses deleted", ndel);
184 } else if (!strcmp(command, "add")) {
185 b.pfrb_type = PFRB_ADDRS;
186 if (load_addr(&b, argc, argv, file, 0))
187 goto _error;
188 CREATE_TABLE;
189 if (opts & PF_OPT_VERBOSE)
190 flags |= PFR_FLAG_FEEDBACK;
191 RVTEST(pfr_add_addrs(&table, b.pfrb_caddr, b.pfrb_size,
192 &nadd, flags));
193 xprintf(opts, "%d/%d addresses added", nadd, b.pfrb_size);
194 if (opts & PF_OPT_VERBOSE)
195 PFRB_FOREACH(a, &b)
196 if ((opts & PF_OPT_VERBOSE2) || a->pfra_fback)
197 print_addrx(a, NULL,
198 opts & PF_OPT_USEDNS);
199 } else if (!strcmp(command, "delete")) {
200 b.pfrb_type = PFRB_ADDRS;
201 if (load_addr(&b, argc, argv, file, 0))
202 goto _error;
203 if (opts & PF_OPT_VERBOSE)
204 flags |= PFR_FLAG_FEEDBACK;
205 RVTEST(pfr_del_addrs(&table, b.pfrb_caddr, b.pfrb_size,
206 &ndel, flags));
207 xprintf(opts, "%d/%d addresses deleted", ndel, b.pfrb_size);
208 if (opts & PF_OPT_VERBOSE)
209 PFRB_FOREACH(a, &b)
210 if ((opts & PF_OPT_VERBOSE2) || a->pfra_fback)
211 print_addrx(a, NULL,
212 opts & PF_OPT_USEDNS);
213 } else if (!strcmp(command, "replace")) {
214 b.pfrb_type = PFRB_ADDRS;
215 if (load_addr(&b, argc, argv, file, 0))
216 goto _error;
217 CREATE_TABLE;
218 if (opts & PF_OPT_VERBOSE)
219 flags |= PFR_FLAG_FEEDBACK;
220 for (;;) {
221 int sz2 = b.pfrb_msize;
222
223 RVTEST(pfr_set_addrs(&table, b.pfrb_caddr, b.pfrb_size,
224 &sz2, &nadd, &ndel, &nchange, flags));
225 if (sz2 <= b.pfrb_msize) {
226 b.pfrb_size = sz2;
227 break;
228 } else
229 pfr_buf_grow(&b, sz2);
230 }
231 if (nadd)
232 xprintf(opts, "%d addresses added", nadd);
233 if (ndel)
234 xprintf(opts, "%d addresses deleted", ndel);
235 if (nchange)
236 xprintf(opts, "%d addresses changed", nchange);
237 if (!nadd && !ndel && !nchange)
238 xprintf(opts, "no changes");
239 if (opts & PF_OPT_VERBOSE)
240 PFRB_FOREACH(a, &b)
241 if ((opts & PF_OPT_VERBOSE2) || a->pfra_fback)
242 print_addrx(a, NULL,
243 opts & PF_OPT_USEDNS);
244 } else if (!strcmp(command, "show")) {
245 b.pfrb_type = (opts & PF_OPT_VERBOSE) ?
246 PFRB_ASTATS : PFRB_ADDRS;
247 if (argc || file != NULL)
248 usage();
249 for (;;) {
250 pfr_buf_grow(&b, b.pfrb_size);
251 b.pfrb_size = b.pfrb_msize;
252 if (opts & PF_OPT_VERBOSE)
253 RVTEST(pfr_get_astats(&table, b.pfrb_caddr,
254 &b.pfrb_size, flags));
255 else
256 RVTEST(pfr_get_addrs(&table, b.pfrb_caddr,
257 &b.pfrb_size, flags));
258 if (b.pfrb_size <= b.pfrb_msize)
259 break;
260 }
261 PFRB_FOREACH(p, &b)
262 if (opts & PF_OPT_VERBOSE)
263 print_astats(p, opts & PF_OPT_USEDNS);
264 else
265 print_addrx(p, NULL, opts & PF_OPT_USEDNS);
266 } else if (!strcmp(command, "test")) {
267 b.pfrb_type = PFRB_ADDRS;
268 b2.pfrb_type = PFRB_ADDRS;
269
270 if (load_addr(&b, argc, argv, file, 1))
271 goto _error;
272 if (opts & PF_OPT_VERBOSE2) {
273 flags |= PFR_FLAG_REPLACE;
274 PFRB_FOREACH(a, &b)
275 if (pfr_buf_add(&b2, a))
276 err(1, "duplicate buffer");
277 }
278 RVTEST(pfr_tst_addrs(&table, b.pfrb_caddr, b.pfrb_size,
279 &nmatch, flags));
280 xprintf(opts, "%d/%d addresses match", nmatch, b.pfrb_size);
281 if (opts & PF_OPT_VERBOSE && !(opts & PF_OPT_VERBOSE2))
282 PFRB_FOREACH(a, &b)
283 if (a->pfra_fback == PFR_FB_MATCH)
284 print_addrx(a, NULL,
285 opts & PF_OPT_USEDNS);
286 if (opts & PF_OPT_VERBOSE2) {
287 a2 = NULL;
288 PFRB_FOREACH(a, &b) {
289 a2 = pfr_buf_next(&b2, a2);
290 print_addrx(a2, a, opts & PF_OPT_USEDNS);
291 }
292 }
293 if (nmatch < b.pfrb_size)
294 rv = 2;
295 } else if (!strcmp(command, "zero")) {
296 if (argc || file != NULL)
297 usage();
298 flags |= PFR_FLAG_ADDRSTOO;
299 RVTEST(pfr_clr_tstats(&table, 1, &nzero, flags));
300 xprintf(opts, "%d table/stats cleared", nzero);
301 } else
302 warnx("pfctl_table: unknown command '%s'", command);
303 goto _cleanup;
304
305_error:
306 rv = -1;
307_cleanup:
308 pfr_buf_clear(&b);
309 pfr_buf_clear(&b2);
310 return (rv);
311}
312
313void
314print_table(struct pfr_table *ta, int verbose, int debug)
315{
316 if (!debug && !(ta->pfrt_flags & PFR_TFLAG_ACTIVE))
317 return;
318 if (verbose) {
319 printf("%c%c%c%c%c%c\t%s",
320 (ta->pfrt_flags & PFR_TFLAG_CONST) ? 'c' : '-',
321 (ta->pfrt_flags & PFR_TFLAG_PERSIST) ? 'p' : '-',
322 (ta->pfrt_flags & PFR_TFLAG_ACTIVE) ? 'a' : '-',
323 (ta->pfrt_flags & PFR_TFLAG_INACTIVE) ? 'i' : '-',
324 (ta->pfrt_flags & PFR_TFLAG_REFERENCED) ? 'r' : '-',
325 (ta->pfrt_flags & PFR_TFLAG_REFDANCHOR) ? 'h' : '-',
326 ta->pfrt_name);
327 if (ta->pfrt_anchor[0])
328 printf("\t%s", ta->pfrt_anchor);
329 if (ta->pfrt_ruleset[0])
330 printf(":%s", ta->pfrt_ruleset);
331 puts("");
332 } else
333 puts(ta->pfrt_name);
334}
335
336void
337print_tstats(struct pfr_tstats *ts, int debug)
338{
339 time_t time = ts->pfrts_tzero;
340 int dir, op;
341
342 if (!debug && !(ts->pfrts_flags & PFR_TFLAG_ACTIVE))
343 return;
344 print_table(&ts->pfrts_t, 1, debug);
345 printf("\tAddresses: %d\n", ts->pfrts_cnt);
346 printf("\tCleared: %s", ctime(&time));
347 printf("\tReferences: [ Anchors: %-18d Rules: %-18d ]\n",
348 ts->pfrts_refcnt[PFR_REFCNT_ANCHOR],
349 ts->pfrts_refcnt[PFR_REFCNT_RULE]);
58#include "pfctl_parser.h"
59#include "pfctl.h"
60
61extern void usage(void);
62static int pfctl_table(int, char *[], char *, const char *, char *,
63 const char *, const char *, int);
64static void print_table(struct pfr_table *, int, int);
65static void print_tstats(struct pfr_tstats *, int);
66static int load_addr(struct pfr_buffer *, int, char *[], char *, int);
67static void print_addrx(struct pfr_addr *, struct pfr_addr *, int);
68static void print_astats(struct pfr_astats *, int);
69static void radix_perror(void);
70static void xprintf(int, const char *, ...);
71
72static const char *stats_text[PFR_DIR_MAX][PFR_OP_TABLE_MAX] = {
73 { "In/Block:", "In/Pass:", "In/XPass:" },
74 { "Out/Block:", "Out/Pass:", "Out/XPass:" }
75};
76
77#define RVTEST(fct) do { \
78 if ((!(opts & PF_OPT_NOACTION) || \
79 (opts & PF_OPT_DUMMYACTION)) && \
80 (fct)) { \
81 radix_perror(); \
82 goto _error; \
83 } \
84 } while (0)
85
86#define CREATE_TABLE do { \
87 table.pfrt_flags |= PFR_TFLAG_PERSIST; \
88 RVTEST(pfr_add_tables(&table, 1, &nadd, flags)); \
89 if (nadd) { \
90 warn_namespace_collision(table.pfrt_name); \
91 xprintf(opts, "%d table created", nadd); \
92 if (opts & PF_OPT_NOACTION) \
93 return (0); \
94 } \
95 table.pfrt_flags &= ~PFR_TFLAG_PERSIST; \
96 } while(0)
97
98int
99pfctl_clear_tables(const char *anchor, const char *ruleset, int opts)
100{
101 return pfctl_table(0, NULL, NULL, "-F", NULL, anchor, ruleset, opts);
102}
103
104int
105pfctl_show_tables(const char *anchor, const char *ruleset, int opts)
106{
107 return pfctl_table(0, NULL, NULL, "-s", NULL, anchor, ruleset, opts);
108}
109
110int
111pfctl_command_tables(int argc, char *argv[], char *tname,
112 const char *command, char *file, const char *anchor, const char *ruleset,
113 int opts)
114{
115 if (tname == NULL || command == NULL)
116 usage();
117 return pfctl_table(argc, argv, tname, command, file, anchor, ruleset,
118 opts);
119}
120
121int
122pfctl_table(int argc, char *argv[], char *tname, const char *command,
123 char *file, const char *anchor, const char *ruleset, int opts)
124{
125 struct pfr_table table;
126 struct pfr_buffer b, b2;
127 struct pfr_addr *a, *a2;
128 int nadd = 0, ndel = 0, nchange = 0, nzero = 0;
129 int rv = 0, flags = 0, nmatch = 0;
130 void *p;
131
132 if (command == NULL)
133 usage();
134 if (opts & PF_OPT_NOACTION)
135 flags |= PFR_FLAG_DUMMY;
136
137 bzero(&b, sizeof(b));
138 bzero(&b2, sizeof(b2));
139 bzero(&table, sizeof(table));
140 if (tname != NULL) {
141 if (strlen(tname) >= PF_TABLE_NAME_SIZE)
142 usage();
143 if (strlcpy(table.pfrt_name, tname,
144 sizeof(table.pfrt_name)) >= sizeof(table.pfrt_name))
145 errx(1, "pfctl_table: strlcpy");
146 }
147 if (strlcpy(table.pfrt_anchor, anchor,
148 sizeof(table.pfrt_anchor)) >= sizeof(table.pfrt_anchor) ||
149 strlcpy(table.pfrt_ruleset, ruleset,
150 sizeof(table.pfrt_ruleset)) >= sizeof(table.pfrt_ruleset))
151 errx(1, "pfctl_table: strlcpy");
152
153 if (!strcmp(command, "-F")) {
154 if (argc || file != NULL)
155 usage();
156 RVTEST(pfr_clr_tables(&table, &ndel, flags));
157 xprintf(opts, "%d tables deleted", ndel);
158 } else if (!strcmp(command, "-s")) {
159 b.pfrb_type = (opts & PF_OPT_VERBOSE2) ?
160 PFRB_TSTATS : PFRB_TABLES;
161 if (argc || file != NULL)
162 usage();
163 for (;;) {
164 pfr_buf_grow(&b, b.pfrb_size);
165 b.pfrb_size = b.pfrb_msize;
166 if (opts & PF_OPT_VERBOSE2)
167 RVTEST(pfr_get_tstats(&table,
168 b.pfrb_caddr, &b.pfrb_size, flags));
169 else
170 RVTEST(pfr_get_tables(&table,
171 b.pfrb_caddr, &b.pfrb_size, flags));
172 if (b.pfrb_size <= b.pfrb_msize)
173 break;
174 }
175 PFRB_FOREACH(p, &b)
176 if (opts & PF_OPT_VERBOSE2)
177 print_tstats(p, opts & PF_OPT_DEBUG);
178 else
179 print_table(p, opts & PF_OPT_VERBOSE,
180 opts & PF_OPT_DEBUG);
181 } else if (!strcmp(command, "kill")) {
182 if (argc || file != NULL)
183 usage();
184 RVTEST(pfr_del_tables(&table, 1, &ndel, flags));
185 xprintf(opts, "%d table deleted", ndel);
186 } else if (!strcmp(command, "flush")) {
187 if (argc || file != NULL)
188 usage();
189 RVTEST(pfr_clr_addrs(&table, &ndel, flags));
190 xprintf(opts, "%d addresses deleted", ndel);
191 } else if (!strcmp(command, "add")) {
192 b.pfrb_type = PFRB_ADDRS;
193 if (load_addr(&b, argc, argv, file, 0))
194 goto _error;
195 CREATE_TABLE;
196 if (opts & PF_OPT_VERBOSE)
197 flags |= PFR_FLAG_FEEDBACK;
198 RVTEST(pfr_add_addrs(&table, b.pfrb_caddr, b.pfrb_size,
199 &nadd, flags));
200 xprintf(opts, "%d/%d addresses added", nadd, b.pfrb_size);
201 if (opts & PF_OPT_VERBOSE)
202 PFRB_FOREACH(a, &b)
203 if ((opts & PF_OPT_VERBOSE2) || a->pfra_fback)
204 print_addrx(a, NULL,
205 opts & PF_OPT_USEDNS);
206 } else if (!strcmp(command, "delete")) {
207 b.pfrb_type = PFRB_ADDRS;
208 if (load_addr(&b, argc, argv, file, 0))
209 goto _error;
210 if (opts & PF_OPT_VERBOSE)
211 flags |= PFR_FLAG_FEEDBACK;
212 RVTEST(pfr_del_addrs(&table, b.pfrb_caddr, b.pfrb_size,
213 &ndel, flags));
214 xprintf(opts, "%d/%d addresses deleted", ndel, b.pfrb_size);
215 if (opts & PF_OPT_VERBOSE)
216 PFRB_FOREACH(a, &b)
217 if ((opts & PF_OPT_VERBOSE2) || a->pfra_fback)
218 print_addrx(a, NULL,
219 opts & PF_OPT_USEDNS);
220 } else if (!strcmp(command, "replace")) {
221 b.pfrb_type = PFRB_ADDRS;
222 if (load_addr(&b, argc, argv, file, 0))
223 goto _error;
224 CREATE_TABLE;
225 if (opts & PF_OPT_VERBOSE)
226 flags |= PFR_FLAG_FEEDBACK;
227 for (;;) {
228 int sz2 = b.pfrb_msize;
229
230 RVTEST(pfr_set_addrs(&table, b.pfrb_caddr, b.pfrb_size,
231 &sz2, &nadd, &ndel, &nchange, flags));
232 if (sz2 <= b.pfrb_msize) {
233 b.pfrb_size = sz2;
234 break;
235 } else
236 pfr_buf_grow(&b, sz2);
237 }
238 if (nadd)
239 xprintf(opts, "%d addresses added", nadd);
240 if (ndel)
241 xprintf(opts, "%d addresses deleted", ndel);
242 if (nchange)
243 xprintf(opts, "%d addresses changed", nchange);
244 if (!nadd && !ndel && !nchange)
245 xprintf(opts, "no changes");
246 if (opts & PF_OPT_VERBOSE)
247 PFRB_FOREACH(a, &b)
248 if ((opts & PF_OPT_VERBOSE2) || a->pfra_fback)
249 print_addrx(a, NULL,
250 opts & PF_OPT_USEDNS);
251 } else if (!strcmp(command, "show")) {
252 b.pfrb_type = (opts & PF_OPT_VERBOSE) ?
253 PFRB_ASTATS : PFRB_ADDRS;
254 if (argc || file != NULL)
255 usage();
256 for (;;) {
257 pfr_buf_grow(&b, b.pfrb_size);
258 b.pfrb_size = b.pfrb_msize;
259 if (opts & PF_OPT_VERBOSE)
260 RVTEST(pfr_get_astats(&table, b.pfrb_caddr,
261 &b.pfrb_size, flags));
262 else
263 RVTEST(pfr_get_addrs(&table, b.pfrb_caddr,
264 &b.pfrb_size, flags));
265 if (b.pfrb_size <= b.pfrb_msize)
266 break;
267 }
268 PFRB_FOREACH(p, &b)
269 if (opts & PF_OPT_VERBOSE)
270 print_astats(p, opts & PF_OPT_USEDNS);
271 else
272 print_addrx(p, NULL, opts & PF_OPT_USEDNS);
273 } else if (!strcmp(command, "test")) {
274 b.pfrb_type = PFRB_ADDRS;
275 b2.pfrb_type = PFRB_ADDRS;
276
277 if (load_addr(&b, argc, argv, file, 1))
278 goto _error;
279 if (opts & PF_OPT_VERBOSE2) {
280 flags |= PFR_FLAG_REPLACE;
281 PFRB_FOREACH(a, &b)
282 if (pfr_buf_add(&b2, a))
283 err(1, "duplicate buffer");
284 }
285 RVTEST(pfr_tst_addrs(&table, b.pfrb_caddr, b.pfrb_size,
286 &nmatch, flags));
287 xprintf(opts, "%d/%d addresses match", nmatch, b.pfrb_size);
288 if (opts & PF_OPT_VERBOSE && !(opts & PF_OPT_VERBOSE2))
289 PFRB_FOREACH(a, &b)
290 if (a->pfra_fback == PFR_FB_MATCH)
291 print_addrx(a, NULL,
292 opts & PF_OPT_USEDNS);
293 if (opts & PF_OPT_VERBOSE2) {
294 a2 = NULL;
295 PFRB_FOREACH(a, &b) {
296 a2 = pfr_buf_next(&b2, a2);
297 print_addrx(a2, a, opts & PF_OPT_USEDNS);
298 }
299 }
300 if (nmatch < b.pfrb_size)
301 rv = 2;
302 } else if (!strcmp(command, "zero")) {
303 if (argc || file != NULL)
304 usage();
305 flags |= PFR_FLAG_ADDRSTOO;
306 RVTEST(pfr_clr_tstats(&table, 1, &nzero, flags));
307 xprintf(opts, "%d table/stats cleared", nzero);
308 } else
309 warnx("pfctl_table: unknown command '%s'", command);
310 goto _cleanup;
311
312_error:
313 rv = -1;
314_cleanup:
315 pfr_buf_clear(&b);
316 pfr_buf_clear(&b2);
317 return (rv);
318}
319
320void
321print_table(struct pfr_table *ta, int verbose, int debug)
322{
323 if (!debug && !(ta->pfrt_flags & PFR_TFLAG_ACTIVE))
324 return;
325 if (verbose) {
326 printf("%c%c%c%c%c%c\t%s",
327 (ta->pfrt_flags & PFR_TFLAG_CONST) ? 'c' : '-',
328 (ta->pfrt_flags & PFR_TFLAG_PERSIST) ? 'p' : '-',
329 (ta->pfrt_flags & PFR_TFLAG_ACTIVE) ? 'a' : '-',
330 (ta->pfrt_flags & PFR_TFLAG_INACTIVE) ? 'i' : '-',
331 (ta->pfrt_flags & PFR_TFLAG_REFERENCED) ? 'r' : '-',
332 (ta->pfrt_flags & PFR_TFLAG_REFDANCHOR) ? 'h' : '-',
333 ta->pfrt_name);
334 if (ta->pfrt_anchor[0])
335 printf("\t%s", ta->pfrt_anchor);
336 if (ta->pfrt_ruleset[0])
337 printf(":%s", ta->pfrt_ruleset);
338 puts("");
339 } else
340 puts(ta->pfrt_name);
341}
342
343void
344print_tstats(struct pfr_tstats *ts, int debug)
345{
346 time_t time = ts->pfrts_tzero;
347 int dir, op;
348
349 if (!debug && !(ts->pfrts_flags & PFR_TFLAG_ACTIVE))
350 return;
351 print_table(&ts->pfrts_t, 1, debug);
352 printf("\tAddresses: %d\n", ts->pfrts_cnt);
353 printf("\tCleared: %s", ctime(&time));
354 printf("\tReferences: [ Anchors: %-18d Rules: %-18d ]\n",
355 ts->pfrts_refcnt[PFR_REFCNT_ANCHOR],
356 ts->pfrts_refcnt[PFR_REFCNT_RULE]);
350 printf("\tEvaluations: [ NoMatch: %-18llu Match: %-18llu ]\n",
357 printf("\tEvaluations: [ NoMatch: %-18"PRIu64" Match: %-18"PRIu64" ]\n",
351 ts->pfrts_nomatch, ts->pfrts_match);
352 for (dir = 0; dir < PFR_DIR_MAX; dir++)
353 for (op = 0; op < PFR_OP_TABLE_MAX; op++)
358 ts->pfrts_nomatch, ts->pfrts_match);
359 for (dir = 0; dir < PFR_DIR_MAX; dir++)
360 for (op = 0; op < PFR_OP_TABLE_MAX; op++)
354 printf("\t%-12s [ Packets: %-18llu Bytes: %-18llu ]\n",
361 printf("\t%-12s [ Packets: %-18"PRIu64" Bytes: %-18"PRIu64" ]\n",
355 stats_text[dir][op],
356 ts->pfrts_packets[dir][op],
357 ts->pfrts_bytes[dir][op]);
358}
359
360int
361load_addr(struct pfr_buffer *b, int argc, char *argv[], char *file,
362 int nonetwork)
363{
364 while (argc--)
365 if (append_addr(b, *argv++, nonetwork)) {
366 if (errno)
367 warn("cannot decode %s", argv[-1]);
368 return (-1);
369 }
370 if (pfr_buf_load(b, file, nonetwork, append_addr)) {
371 warn("cannot load %s", file);
372 return (-1);
373 }
374 return (0);
375}
376
377void
378print_addrx(struct pfr_addr *ad, struct pfr_addr *rad, int dns)
379{
380 char ch, buf[256] = "{error}";
381 char fb[] = { ' ', 'M', 'A', 'D', 'C', 'Z', 'X', ' ', 'Y' };
382 unsigned int fback, hostnet;
383
384 fback = (rad != NULL) ? rad->pfra_fback : ad->pfra_fback;
385 ch = (fback < sizeof(fb)/sizeof(*fb)) ? fb[fback] : '?';
386 hostnet = (ad->pfra_af == AF_INET6) ? 128 : 32;
387 inet_ntop(ad->pfra_af, &ad->pfra_u, buf, sizeof(buf));
388 printf("%c %c%s", ch, (ad->pfra_not?'!':' '), buf);
389 if (ad->pfra_net < hostnet)
390 printf("/%d", ad->pfra_net);
391 if (rad != NULL && fback != PFR_FB_NONE) {
392 if (strlcpy(buf, "{error}", sizeof(buf)) >= sizeof(buf))
393 errx(1, "print_addrx: strlcpy");
394 inet_ntop(rad->pfra_af, &rad->pfra_u, buf, sizeof(buf));
395 printf("\t%c%s", (rad->pfra_not?'!':' '), buf);
396 if (rad->pfra_net < hostnet)
397 printf("/%d", rad->pfra_net);
398 }
399 if (rad != NULL && fback == PFR_FB_NONE)
400 printf("\t nomatch");
401 if (dns && ad->pfra_net == hostnet) {
402 char host[NI_MAXHOST];
403 union sockaddr_union sa;
404
405 strlcpy(host, "?", sizeof(host));
406 bzero(&sa, sizeof(sa));
407 sa.sa.sa_family = ad->pfra_af;
408 if (sa.sa.sa_family == AF_INET) {
409 sa.sa.sa_len = sizeof(sa.sin);
410 sa.sin.sin_addr = ad->pfra_ip4addr;
411 } else {
412 sa.sa.sa_len = sizeof(sa.sin6);
413 sa.sin6.sin6_addr = ad->pfra_ip6addr;
414 }
415 if (getnameinfo(&sa.sa, sa.sa.sa_len, host, sizeof(host),
416 NULL, 0, NI_NAMEREQD) == 0)
417 printf("\t(%s)", host);
418 }
419 printf("\n");
420}
421
422void
423print_astats(struct pfr_astats *as, int dns)
424{
425 time_t time = as->pfras_tzero;
426 int dir, op;
427
428 print_addrx(&as->pfras_a, NULL, dns);
429 printf("\tCleared: %s", ctime(&time));
430 for (dir = 0; dir < PFR_DIR_MAX; dir++)
431 for (op = 0; op < PFR_OP_ADDR_MAX; op++)
362 stats_text[dir][op],
363 ts->pfrts_packets[dir][op],
364 ts->pfrts_bytes[dir][op]);
365}
366
367int
368load_addr(struct pfr_buffer *b, int argc, char *argv[], char *file,
369 int nonetwork)
370{
371 while (argc--)
372 if (append_addr(b, *argv++, nonetwork)) {
373 if (errno)
374 warn("cannot decode %s", argv[-1]);
375 return (-1);
376 }
377 if (pfr_buf_load(b, file, nonetwork, append_addr)) {
378 warn("cannot load %s", file);
379 return (-1);
380 }
381 return (0);
382}
383
384void
385print_addrx(struct pfr_addr *ad, struct pfr_addr *rad, int dns)
386{
387 char ch, buf[256] = "{error}";
388 char fb[] = { ' ', 'M', 'A', 'D', 'C', 'Z', 'X', ' ', 'Y' };
389 unsigned int fback, hostnet;
390
391 fback = (rad != NULL) ? rad->pfra_fback : ad->pfra_fback;
392 ch = (fback < sizeof(fb)/sizeof(*fb)) ? fb[fback] : '?';
393 hostnet = (ad->pfra_af == AF_INET6) ? 128 : 32;
394 inet_ntop(ad->pfra_af, &ad->pfra_u, buf, sizeof(buf));
395 printf("%c %c%s", ch, (ad->pfra_not?'!':' '), buf);
396 if (ad->pfra_net < hostnet)
397 printf("/%d", ad->pfra_net);
398 if (rad != NULL && fback != PFR_FB_NONE) {
399 if (strlcpy(buf, "{error}", sizeof(buf)) >= sizeof(buf))
400 errx(1, "print_addrx: strlcpy");
401 inet_ntop(rad->pfra_af, &rad->pfra_u, buf, sizeof(buf));
402 printf("\t%c%s", (rad->pfra_not?'!':' '), buf);
403 if (rad->pfra_net < hostnet)
404 printf("/%d", rad->pfra_net);
405 }
406 if (rad != NULL && fback == PFR_FB_NONE)
407 printf("\t nomatch");
408 if (dns && ad->pfra_net == hostnet) {
409 char host[NI_MAXHOST];
410 union sockaddr_union sa;
411
412 strlcpy(host, "?", sizeof(host));
413 bzero(&sa, sizeof(sa));
414 sa.sa.sa_family = ad->pfra_af;
415 if (sa.sa.sa_family == AF_INET) {
416 sa.sa.sa_len = sizeof(sa.sin);
417 sa.sin.sin_addr = ad->pfra_ip4addr;
418 } else {
419 sa.sa.sa_len = sizeof(sa.sin6);
420 sa.sin6.sin6_addr = ad->pfra_ip6addr;
421 }
422 if (getnameinfo(&sa.sa, sa.sa.sa_len, host, sizeof(host),
423 NULL, 0, NI_NAMEREQD) == 0)
424 printf("\t(%s)", host);
425 }
426 printf("\n");
427}
428
429void
430print_astats(struct pfr_astats *as, int dns)
431{
432 time_t time = as->pfras_tzero;
433 int dir, op;
434
435 print_addrx(&as->pfras_a, NULL, dns);
436 printf("\tCleared: %s", ctime(&time));
437 for (dir = 0; dir < PFR_DIR_MAX; dir++)
438 for (op = 0; op < PFR_OP_ADDR_MAX; op++)
432 printf("\t%-12s [ Packets: %-18llu Bytes: %-18llu ]\n",
439 printf("\t%-12s [ Packets: %-18"PRIu64" Bytes: %-18"PRIu64" ]\n",
433 stats_text[dir][op],
434 as->pfras_packets[dir][op],
435 as->pfras_bytes[dir][op]);
436}
437
438void
439radix_perror(void)
440{
441 extern char *__progname;
442 fprintf(stderr, "%s: %s.\n", __progname, pfr_strerror(errno));
443}
444
445int
446pfctl_define_table(char *name, int flags, int addrs, const char *anchor,
447 const char *ruleset, struct pfr_buffer *ab, u_int32_t ticket)
448{
449 struct pfr_table tbl;
450
451 bzero(&tbl, sizeof(tbl));
452 if (strlcpy(tbl.pfrt_name, name,
453 sizeof(tbl.pfrt_name)) >= sizeof(tbl.pfrt_name) ||
454 strlcpy(tbl.pfrt_anchor, anchor,
455 sizeof(tbl.pfrt_anchor)) >= sizeof(tbl.pfrt_anchor) ||
456 strlcpy(tbl.pfrt_ruleset, ruleset,
457 sizeof(tbl.pfrt_ruleset)) >= sizeof(tbl.pfrt_ruleset))
458 errx(1, "pfctl_define_table: strlcpy");
459 tbl.pfrt_flags = flags;
460
461 return pfr_ina_define(&tbl, ab->pfrb_caddr, ab->pfrb_size, NULL,
462 NULL, ticket, addrs ? PFR_FLAG_ADDRSTOO : 0);
463}
464
465void
466warn_namespace_collision(const char *filter)
467{
468 struct pfr_buffer b;
469 struct pfr_table *t;
470 const char *name = NULL, *lastcoll;
471 int coll = 0;
472
473 bzero(&b, sizeof(b));
474 b.pfrb_type = PFRB_TABLES;
475 for (;;) {
476 pfr_buf_grow(&b, b.pfrb_size);
477 b.pfrb_size = b.pfrb_msize;
478 if (pfr_get_tables(NULL, b.pfrb_caddr,
479 &b.pfrb_size, PFR_FLAG_ALLRSETS))
480 err(1, "pfr_get_tables");
481 if (b.pfrb_size <= b.pfrb_msize)
482 break;
483 }
484 PFRB_FOREACH(t, &b) {
485 if (!(t->pfrt_flags & PFR_TFLAG_ACTIVE))
486 continue;
487 if (filter != NULL && strcmp(filter, t->pfrt_name))
488 continue;
489 if (!t->pfrt_anchor[0])
490 name = t->pfrt_name;
491 else if (name != NULL && !strcmp(name, t->pfrt_name)) {
492 coll++;
493 lastcoll = name;
494 name = NULL;
495 }
496 }
497 if (coll == 1)
498 warnx("warning: namespace collision with <%s> global table.",
499 lastcoll);
500 else if (coll > 1)
501 warnx("warning: namespace collisions with %d global tables.",
502 coll);
503 pfr_buf_clear(&b);
504}
505
506void
507xprintf(int opts, const char *fmt, ...)
508{
509 va_list args;
510
511 if (opts & PF_OPT_QUIET)
512 return;
513
514 va_start(args, fmt);
515 vfprintf(stderr, fmt, args);
516 va_end(args);
517
518 if (opts & PF_OPT_DUMMYACTION)
519 fprintf(stderr, " (dummy).\n");
520 else if (opts & PF_OPT_NOACTION)
521 fprintf(stderr, " (syntax only).\n");
522 else
523 fprintf(stderr, ".\n");
524}
440 stats_text[dir][op],
441 as->pfras_packets[dir][op],
442 as->pfras_bytes[dir][op]);
443}
444
445void
446radix_perror(void)
447{
448 extern char *__progname;
449 fprintf(stderr, "%s: %s.\n", __progname, pfr_strerror(errno));
450}
451
452int
453pfctl_define_table(char *name, int flags, int addrs, const char *anchor,
454 const char *ruleset, struct pfr_buffer *ab, u_int32_t ticket)
455{
456 struct pfr_table tbl;
457
458 bzero(&tbl, sizeof(tbl));
459 if (strlcpy(tbl.pfrt_name, name,
460 sizeof(tbl.pfrt_name)) >= sizeof(tbl.pfrt_name) ||
461 strlcpy(tbl.pfrt_anchor, anchor,
462 sizeof(tbl.pfrt_anchor)) >= sizeof(tbl.pfrt_anchor) ||
463 strlcpy(tbl.pfrt_ruleset, ruleset,
464 sizeof(tbl.pfrt_ruleset)) >= sizeof(tbl.pfrt_ruleset))
465 errx(1, "pfctl_define_table: strlcpy");
466 tbl.pfrt_flags = flags;
467
468 return pfr_ina_define(&tbl, ab->pfrb_caddr, ab->pfrb_size, NULL,
469 NULL, ticket, addrs ? PFR_FLAG_ADDRSTOO : 0);
470}
471
472void
473warn_namespace_collision(const char *filter)
474{
475 struct pfr_buffer b;
476 struct pfr_table *t;
477 const char *name = NULL, *lastcoll;
478 int coll = 0;
479
480 bzero(&b, sizeof(b));
481 b.pfrb_type = PFRB_TABLES;
482 for (;;) {
483 pfr_buf_grow(&b, b.pfrb_size);
484 b.pfrb_size = b.pfrb_msize;
485 if (pfr_get_tables(NULL, b.pfrb_caddr,
486 &b.pfrb_size, PFR_FLAG_ALLRSETS))
487 err(1, "pfr_get_tables");
488 if (b.pfrb_size <= b.pfrb_msize)
489 break;
490 }
491 PFRB_FOREACH(t, &b) {
492 if (!(t->pfrt_flags & PFR_TFLAG_ACTIVE))
493 continue;
494 if (filter != NULL && strcmp(filter, t->pfrt_name))
495 continue;
496 if (!t->pfrt_anchor[0])
497 name = t->pfrt_name;
498 else if (name != NULL && !strcmp(name, t->pfrt_name)) {
499 coll++;
500 lastcoll = name;
501 name = NULL;
502 }
503 }
504 if (coll == 1)
505 warnx("warning: namespace collision with <%s> global table.",
506 lastcoll);
507 else if (coll > 1)
508 warnx("warning: namespace collisions with %d global tables.",
509 coll);
510 pfr_buf_clear(&b);
511}
512
513void
514xprintf(int opts, const char *fmt, ...)
515{
516 va_list args;
517
518 if (opts & PF_OPT_QUIET)
519 return;
520
521 va_start(args, fmt);
522 vfprintf(stderr, fmt, args);
523 va_end(args);
524
525 if (opts & PF_OPT_DUMMYACTION)
526 fprintf(stderr, " (dummy).\n");
527 else if (opts & PF_OPT_NOACTION)
528 fprintf(stderr, " (syntax only).\n");
529 else
530 fprintf(stderr, ".\n");
531}