2 /* $OpenBSD: pfctl_table.c,v 1.66 2007/03/01 17:20:54 deraadt Exp $ */
5 * Copyright (c) 2002 Cedric Berger
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
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.
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.
34 #include <sys/types.h>
35 #include <sys/ioctl.h>
36 #include <sys/socket.h>
39 #include <net/pfvar.h>
40 #include <arpa/inet.h>
52 #include "pfctl_parser.h"
55 extern void usage(void);
56 static int pfctl_table(int, char *[], char *, const char *, char *,
58 static void print_table(struct pfr_table
*, int, int);
59 static void print_tstats(struct pfr_tstats
*, int);
60 static int load_addr(struct pfr_buffer
*, int, char *[], char *, int);
61 static void print_addrx(struct pfr_addr
*, struct pfr_addr
*, int);
62 static void print_astats(struct pfr_astats
*, int);
63 static void radix_perror(void);
64 static void xprintf(int, const char *, ...);
65 static void print_iface(struct pfi_kif
*, int);
67 static const char *stats_text
[PFR_DIR_MAX
][PFR_OP_TABLE_MAX
] = {
68 { "In/Block:", "In/Pass:", "In/XPass:" },
69 { "Out/Block:", "Out/Pass:", "Out/XPass:" }
72 static const char *istats_text
[2][2][2] = {
73 { { "In4/Pass:", "In4/Block:" }, { "Out4/Pass:", "Out4/Block:" } },
74 { { "In6/Pass:", "In6/Block:" }, { "Out6/Pass:", "Out6/Block:" } }
77 #define RVTEST(fct) do { \
78 if ((!(opts & PF_OPT_NOACTION) || \
79 (opts & PF_OPT_DUMMYACTION)) && \
86 #define CREATE_TABLE do { \
87 table.pfrt_flags |= PFR_TFLAG_PERSIST; \
88 if ((!(opts & PF_OPT_NOACTION) || \
89 (opts & PF_OPT_DUMMYACTION)) && \
90 (pfr_add_tables(&table, 1, &nadd, flags)) && \
96 warn_namespace_collision(table.pfrt_name); \
97 xprintf(opts, "%d table created", nadd); \
98 if (opts & PF_OPT_NOACTION) \
101 table.pfrt_flags &= ~PFR_TFLAG_PERSIST; \
105 pfctl_clear_tables(const char *anchor
, int opts
)
107 return pfctl_table(0, NULL
, NULL
, "-F", NULL
, anchor
, opts
);
111 pfctl_show_tables(const char *anchor
, int opts
)
113 return pfctl_table(0, NULL
, NULL
, "-s", NULL
, anchor
, opts
);
117 pfctl_command_tables(int argc
, char *argv
[], char *tname
,
118 const char *command
, char *file
, const char *anchor
, int opts
)
120 if (tname
== NULL
|| command
== NULL
)
122 return pfctl_table(argc
, argv
, tname
, command
, file
, anchor
, opts
);
126 pfctl_table(int argc
, char *argv
[], char *tname
, const char *command
,
127 char *file
, const char *anchor
, int opts
)
129 struct pfr_table table
;
130 struct pfr_buffer b
, b2
;
131 struct pfr_addr
*a
, *a2
;
132 int nadd
= 0, ndel
= 0, nchange
= 0, nzero
= 0;
133 int rv
= 0, flags
= 0, nmatch
= 0;
138 if (opts
& PF_OPT_NOACTION
)
139 flags
|= PFR_FLAG_DUMMY
;
141 bzero(&b
, sizeof(b
));
142 bzero(&b2
, sizeof(b2
));
143 bzero(&table
, sizeof(table
));
145 if (strlen(tname
) >= PF_TABLE_NAME_SIZE
)
147 if (strlcpy(table
.pfrt_name
, tname
,
148 sizeof(table
.pfrt_name
)) >= sizeof(table
.pfrt_name
))
149 errx(1, "pfctl_table: strlcpy");
151 if (strlcpy(table
.pfrt_anchor
, anchor
,
152 sizeof(table
.pfrt_anchor
)) >= sizeof(table
.pfrt_anchor
))
153 errx(1, "pfctl_table: strlcpy");
155 if (!strcmp(command
, "-F")) {
156 if (argc
|| file
!= NULL
)
158 RVTEST(pfr_clr_tables(&table
, &ndel
, flags
));
159 xprintf(opts
, "%d tables deleted", ndel
);
160 } else if (!strcmp(command
, "-s")) {
161 b
.pfrb_type
= (opts
& PF_OPT_VERBOSE2
) ?
162 PFRB_TSTATS
: PFRB_TABLES
;
163 if (argc
|| file
!= NULL
)
166 pfr_buf_grow(&b
, b
.pfrb_size
);
167 b
.pfrb_size
= b
.pfrb_msize
;
168 if (opts
& PF_OPT_VERBOSE2
)
169 RVTEST(pfr_get_tstats(&table
,
170 b
.pfrb_caddr
, &b
.pfrb_size
, flags
));
172 RVTEST(pfr_get_tables(&table
,
173 b
.pfrb_caddr
, &b
.pfrb_size
, flags
));
174 if (b
.pfrb_size
<= b
.pfrb_msize
)
178 if ((opts
& PF_OPT_SHOWALL
) && b
.pfrb_size
> 0)
179 pfctl_print_title("TABLES:");
182 if (opts
& PF_OPT_VERBOSE2
)
183 print_tstats(p
, opts
& PF_OPT_DEBUG
);
185 print_table(p
, opts
& PF_OPT_VERBOSE
,
186 opts
& PF_OPT_DEBUG
);
187 } else if (!strcmp(command
, "kill")) {
188 if (argc
|| file
!= NULL
)
190 RVTEST(pfr_del_tables(&table
, 1, &ndel
, flags
));
191 xprintf(opts
, "%d table deleted", ndel
);
192 } else if (!strcmp(command
, "flush")) {
193 if (argc
|| file
!= NULL
)
195 RVTEST(pfr_clr_addrs(&table
, &ndel
, flags
));
196 xprintf(opts
, "%d addresses deleted", ndel
);
197 } else if (!strcmp(command
, "add")) {
198 b
.pfrb_type
= PFRB_ADDRS
;
199 if (load_addr(&b
, argc
, argv
, file
, 0))
202 if (opts
& PF_OPT_VERBOSE
)
203 flags
|= PFR_FLAG_FEEDBACK
;
204 RVTEST(pfr_add_addrs(&table
, b
.pfrb_caddr
, b
.pfrb_size
,
206 xprintf(opts
, "%d/%d addresses added", nadd
, b
.pfrb_size
);
207 if (opts
& PF_OPT_VERBOSE
)
209 if ((opts
& PF_OPT_VERBOSE2
) || a
->pfra_fback
)
211 opts
& PF_OPT_USEDNS
);
212 } else if (!strcmp(command
, "delete")) {
213 b
.pfrb_type
= PFRB_ADDRS
;
214 if (load_addr(&b
, argc
, argv
, file
, 0))
216 if (opts
& PF_OPT_VERBOSE
)
217 flags
|= PFR_FLAG_FEEDBACK
;
218 RVTEST(pfr_del_addrs(&table
, b
.pfrb_caddr
, b
.pfrb_size
,
220 xprintf(opts
, "%d/%d addresses deleted", ndel
, b
.pfrb_size
);
221 if (opts
& PF_OPT_VERBOSE
)
223 if ((opts
& PF_OPT_VERBOSE2
) || a
->pfra_fback
)
225 opts
& PF_OPT_USEDNS
);
226 } else if (!strcmp(command
, "replace")) {
227 b
.pfrb_type
= PFRB_ADDRS
;
228 if (load_addr(&b
, argc
, argv
, file
, 0))
231 if (opts
& PF_OPT_VERBOSE
)
232 flags
|= PFR_FLAG_FEEDBACK
;
234 int sz2
= b
.pfrb_msize
;
236 RVTEST(pfr_set_addrs(&table
, b
.pfrb_caddr
, b
.pfrb_size
,
237 &sz2
, &nadd
, &ndel
, &nchange
, flags
));
238 if (sz2
<= b
.pfrb_msize
) {
242 pfr_buf_grow(&b
, sz2
);
245 xprintf(opts
, "%d addresses added", nadd
);
247 xprintf(opts
, "%d addresses deleted", ndel
);
249 xprintf(opts
, "%d addresses changed", nchange
);
250 if (!nadd
&& !ndel
&& !nchange
)
251 xprintf(opts
, "no changes");
252 if (opts
& PF_OPT_VERBOSE
)
254 if ((opts
& PF_OPT_VERBOSE2
) || a
->pfra_fback
)
256 opts
& PF_OPT_USEDNS
);
257 } else if (!strcmp(command
, "expire")) {
258 const char *errstr
= NULL
; /* XXX gcc */
261 b
.pfrb_type
= PFRB_ASTATS
;
262 b2
.pfrb_type
= PFRB_ADDRS
;
263 if (argc
!= 1 || file
!= NULL
)
265 lifetime
= strtonum(*argv
, 0, UINT_MAX
, &errstr
);
267 errx(1, "expiry time: %s", errstr
);
269 pfr_buf_grow(&b
, b
.pfrb_size
);
270 b
.pfrb_size
= b
.pfrb_msize
;
271 RVTEST(pfr_get_astats(&table
, b
.pfrb_caddr
,
272 &b
.pfrb_size
, flags
));
273 if (b
.pfrb_size
<= b
.pfrb_msize
)
277 if (time(NULL
) - ((struct pfr_astats
*)p
)->pfras_tzero
>
280 &((struct pfr_astats
*)p
)->pfras_a
))
281 err(1, "duplicate buffer");
283 if (opts
& PF_OPT_VERBOSE
)
284 flags
|= PFR_FLAG_FEEDBACK
;
285 RVTEST(pfr_del_addrs(&table
, b2
.pfrb_caddr
, b2
.pfrb_size
,
287 xprintf(opts
, "%d/%d addresses expired", ndel
, b2
.pfrb_size
);
288 if (opts
& PF_OPT_VERBOSE
)
290 if ((opts
& PF_OPT_VERBOSE2
) || a
->pfra_fback
)
292 opts
& PF_OPT_USEDNS
);
293 } else if (!strcmp(command
, "show")) {
294 b
.pfrb_type
= (opts
& PF_OPT_VERBOSE
) ?
295 PFRB_ASTATS
: PFRB_ADDRS
;
296 if (argc
|| file
!= NULL
)
299 pfr_buf_grow(&b
, b
.pfrb_size
);
300 b
.pfrb_size
= b
.pfrb_msize
;
301 if (opts
& PF_OPT_VERBOSE
)
302 RVTEST(pfr_get_astats(&table
, b
.pfrb_caddr
,
303 &b
.pfrb_size
, flags
));
305 RVTEST(pfr_get_addrs(&table
, b
.pfrb_caddr
,
306 &b
.pfrb_size
, flags
));
307 if (b
.pfrb_size
<= b
.pfrb_msize
)
311 if (opts
& PF_OPT_VERBOSE
)
312 print_astats(p
, opts
& PF_OPT_USEDNS
);
314 print_addrx(p
, NULL
, opts
& PF_OPT_USEDNS
);
315 } else if (!strcmp(command
, "test")) {
316 b
.pfrb_type
= PFRB_ADDRS
;
317 b2
.pfrb_type
= PFRB_ADDRS
;
319 if (load_addr(&b
, argc
, argv
, file
, 1))
321 if (opts
& PF_OPT_VERBOSE2
) {
322 flags
|= PFR_FLAG_REPLACE
;
324 if (pfr_buf_add(&b2
, a
))
325 err(1, "duplicate buffer");
327 RVTEST(pfr_tst_addrs(&table
, b
.pfrb_caddr
, b
.pfrb_size
,
329 xprintf(opts
, "%d/%d addresses match", nmatch
, b
.pfrb_size
);
330 if ((opts
& PF_OPT_VERBOSE
) && !(opts
& PF_OPT_VERBOSE2
))
332 if (a
->pfra_fback
== PFR_FB_MATCH
)
334 opts
& PF_OPT_USEDNS
);
335 if (opts
& PF_OPT_VERBOSE2
) {
337 PFRB_FOREACH(a
, &b
) {
338 a2
= pfr_buf_next(&b2
, a2
);
339 print_addrx(a2
, a
, opts
& PF_OPT_USEDNS
);
342 if (nmatch
< b
.pfrb_size
)
344 } else if (!strcmp(command
, "zero")) {
345 if (argc
|| file
!= NULL
)
347 flags
|= PFR_FLAG_ADDRSTOO
;
348 RVTEST(pfr_clr_tstats(&table
, 1, &nzero
, flags
));
349 xprintf(opts
, "%d table/stats cleared", nzero
);
351 warnx("pfctl_table: unknown command '%s'", command
);
363 print_table(struct pfr_table
*ta
, int verbose
, int debug
)
365 if (!debug
&& !(ta
->pfrt_flags
& PFR_TFLAG_ACTIVE
))
368 printf("%c%c%c%c%c%c\t%s",
369 (ta
->pfrt_flags
& PFR_TFLAG_CONST
) ? 'c' : '-',
370 (ta
->pfrt_flags
& PFR_TFLAG_PERSIST
) ? 'p' : '-',
371 (ta
->pfrt_flags
& PFR_TFLAG_ACTIVE
) ? 'a' : '-',
372 (ta
->pfrt_flags
& PFR_TFLAG_INACTIVE
) ? 'i' : '-',
373 (ta
->pfrt_flags
& PFR_TFLAG_REFERENCED
) ? 'r' : '-',
374 (ta
->pfrt_flags
& PFR_TFLAG_REFDANCHOR
) ? 'h' : '-',
376 if (ta
->pfrt_anchor
[0])
377 printf("\t%s", ta
->pfrt_anchor
);
384 print_tstats(struct pfr_tstats
*ts
, int debug
)
386 time_t time
= ts
->pfrts_tzero
;
389 if (!debug
&& !(ts
->pfrts_flags
& PFR_TFLAG_ACTIVE
))
391 print_table(&ts
->pfrts_t
, 1, debug
);
392 printf("\tAddresses: %d\n", ts
->pfrts_cnt
);
393 printf("\tCleared: %s", ctime(&time
));
394 printf("\tReferences: [ Anchors: %-18d Rules: %-18d ]\n",
395 ts
->pfrts_refcnt
[PFR_REFCNT_ANCHOR
],
396 ts
->pfrts_refcnt
[PFR_REFCNT_RULE
]);
397 printf("\tEvaluations: [ NoMatch: %-18llu Match: %-18llu ]\n",
398 (unsigned long long)ts
->pfrts_nomatch
,
399 (unsigned long long)ts
->pfrts_match
);
400 for (dir
= 0; dir
< PFR_DIR_MAX
; dir
++)
401 for (op
= 0; op
< PFR_OP_TABLE_MAX
; op
++)
402 printf("\t%-12s [ Packets: %-18llu Bytes: %-18llu ]\n",
404 (unsigned long long)ts
->pfrts_packets
[dir
][op
],
405 (unsigned long long)ts
->pfrts_bytes
[dir
][op
]);
409 load_addr(struct pfr_buffer
*b
, int argc
, char *argv
[], char *file
,
413 if (append_addr(b
, *argv
++, nonetwork
)) {
415 warn("cannot decode %s", argv
[-1]);
418 if (pfr_buf_load(b
, file
, nonetwork
, append_addr
)) {
419 warn("cannot load %s", file
);
426 print_addrx(struct pfr_addr
*ad
, struct pfr_addr
*rad
, int dns
)
428 char ch
, buf
[256] = "{error}";
429 char fb
[] = { ' ', 'M', 'A', 'D', 'C', 'Z', 'X', ' ', 'Y' };
430 unsigned int fback
, hostnet
;
432 fback
= (rad
!= NULL
) ? rad
->pfra_fback
: ad
->pfra_fback
;
433 ch
= (fback
< sizeof(fb
)/sizeof(*fb
)) ? fb
[fback
] : '?';
434 hostnet
= (ad
->pfra_af
== AF_INET6
) ? 128 : 32;
435 inet_ntop(ad
->pfra_af
, &ad
->pfra_u
, buf
, sizeof(buf
));
436 printf("%c %c%s", ch
, (ad
->pfra_not
?'!':' '), buf
);
437 if (ad
->pfra_net
< hostnet
)
438 printf("/%d", ad
->pfra_net
);
439 if (rad
!= NULL
&& fback
!= PFR_FB_NONE
) {
440 if (strlcpy(buf
, "{error}", sizeof(buf
)) >= sizeof(buf
))
441 errx(1, "print_addrx: strlcpy");
442 inet_ntop(rad
->pfra_af
, &rad
->pfra_u
, buf
, sizeof(buf
));
443 printf("\t%c%s", (rad
->pfra_not
?'!':' '), buf
);
444 if (rad
->pfra_net
< hostnet
)
445 printf("/%d", rad
->pfra_net
);
447 if (rad
!= NULL
&& fback
== PFR_FB_NONE
)
448 printf("\t nomatch");
449 if (dns
&& ad
->pfra_net
== hostnet
) {
450 char host
[NI_MAXHOST
];
451 union sockaddr_union sa
;
453 strlcpy(host
, "?", sizeof(host
));
454 bzero(&sa
, sizeof(sa
));
455 sa
.sa
.sa_family
= ad
->pfra_af
;
456 if (sa
.sa
.sa_family
== AF_INET
) {
457 sa
.sa
.sa_len
= sizeof(sa
.sin
);
458 sa
.sin
.sin_addr
= ad
->pfra_ip4addr
;
460 sa
.sa
.sa_len
= sizeof(sa
.sin6
);
461 sa
.sin6
.sin6_addr
= ad
->pfra_ip6addr
;
463 if (getnameinfo(&sa
.sa
, sa
.sa
.sa_len
, host
, sizeof(host
),
464 NULL
, 0, NI_NAMEREQD
) == 0)
465 printf("\t(%s)", host
);
471 print_astats(struct pfr_astats
*as
, int dns
)
473 time_t time
= as
->pfras_tzero
;
476 print_addrx(&as
->pfras_a
, NULL
, dns
);
477 printf("\tCleared: %s", ctime(&time
));
478 for (dir
= 0; dir
< PFR_DIR_MAX
; dir
++)
479 for (op
= 0; op
< PFR_OP_ADDR_MAX
; op
++)
480 printf("\t%-12s [ Packets: %-18llu Bytes: %-18llu ]\n",
482 (unsigned long long)as
->pfras_packets
[dir
][op
],
483 (unsigned long long)as
->pfras_bytes
[dir
][op
]);
489 extern char *__progname
;
490 fprintf(stderr
, "%s: %s.\n", __progname
, pfr_strerror(errno
));
494 pfctl_define_table(char *name
, int flags
, int addrs
, const char *anchor
,
495 struct pfr_buffer
*ab
, u_int32_t ticket
)
497 struct pfr_table tbl
;
499 bzero(&tbl
, sizeof(tbl
));
500 if (strlcpy(tbl
.pfrt_name
, name
, sizeof(tbl
.pfrt_name
)) >=
501 sizeof(tbl
.pfrt_name
) || strlcpy(tbl
.pfrt_anchor
, anchor
,
502 sizeof(tbl
.pfrt_anchor
)) >= sizeof(tbl
.pfrt_anchor
))
503 errx(1, "pfctl_define_table: strlcpy");
504 tbl
.pfrt_flags
= flags
;
506 return pfr_ina_define(&tbl
, ab
->pfrb_caddr
, ab
->pfrb_size
, NULL
,
507 NULL
, ticket
, addrs
? PFR_FLAG_ADDRSTOO
: 0);
511 warn_namespace_collision(const char *filter
)
515 const char *name
= NULL
, *lastcoll
= NULL
;
518 bzero(&b
, sizeof(b
));
519 b
.pfrb_type
= PFRB_TABLES
;
521 pfr_buf_grow(&b
, b
.pfrb_size
);
522 b
.pfrb_size
= b
.pfrb_msize
;
523 if (pfr_get_tables(NULL
, b
.pfrb_caddr
,
524 &b
.pfrb_size
, PFR_FLAG_ALLRSETS
))
525 err(1, "pfr_get_tables");
526 if (b
.pfrb_size
<= b
.pfrb_msize
)
529 PFRB_FOREACH(t
, &b
) {
530 if (!(t
->pfrt_flags
& PFR_TFLAG_ACTIVE
))
532 if (filter
!= NULL
&& strcmp(filter
, t
->pfrt_name
))
534 if (!t
->pfrt_anchor
[0])
536 else if (name
!= NULL
&& !strcmp(name
, t
->pfrt_name
)) {
543 warnx("warning: namespace collision with <%s> global table.",
546 warnx("warning: namespace collisions with %d global tables.",
552 xprintf(int opts
, const char *fmt
, ...)
556 if (opts
& PF_OPT_QUIET
)
560 vfprintf(stderr
, fmt
, args
);
563 if (opts
& PF_OPT_DUMMYACTION
)
564 fprintf(stderr
, " (dummy).\n");
565 else if (opts
& PF_OPT_NOACTION
)
566 fprintf(stderr
, " (syntax only).\n");
568 fprintf(stderr
, ".\n");
572 /* interface stuff */
575 pfctl_show_ifaces(const char *filter
, int opts
)
581 bzero(&b
, sizeof(b
));
582 b
.pfrb_type
= PFRB_IFACES
;
584 pfr_buf_grow(&b
, b
.pfrb_size
);
585 b
.pfrb_size
= b
.pfrb_msize
;
586 if (pfi_get_ifaces(filter
, b
.pfrb_caddr
, &b
.pfrb_size
)) {
590 if (b
.pfrb_size
<= b
.pfrb_msize
)
594 if (opts
& PF_OPT_SHOWALL
)
595 pfctl_print_title("INTERFACES:");
597 print_iface(p
, opts
);
602 print_iface(struct pfi_kif
*p
, int opts
)
604 time_t tzero
= p
->pfik_tzero
;
607 printf("%s", p
->pfik_name
);
608 if (opts
& PF_OPT_VERBOSE
) {
609 if (p
->pfik_flags
& PFI_IFLAG_SKIP
)
614 if (!(opts
& PF_OPT_VERBOSE2
))
616 printf("\tCleared: %s", ctime(&tzero
));
617 printf("\tReferences: [ States: %-18d Rules: %-18d ]\n",
618 p
->pfik_states
, p
->pfik_rules
);
619 for (i
= 0; i
< 8; i
++) {
623 printf("\t%-12s [ Packets: %-18llu Bytes: %-18llu ]\n",
624 istats_text
[af
][dir
][act
],
625 (unsigned long long)p
->pfik_packets
[af
][dir
][act
],
626 (unsigned long long)p
->pfik_bytes
[af
][dir
][act
]);