1 /* $OpenBSD: pfctl_table.c,v 1.68 2008/06/21 10:34:08 mcbride Exp $ */
4 * Copyright (c) 2002 Cedric Berger
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
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.
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.
33 #include <sys/types.h>
34 #include <sys/ioctl.h>
35 #include <sys/socket.h>
38 #include <net/pf/pfvar.h>
39 #include <arpa/inet.h>
51 #include "pfctl_parser.h"
54 extern void usage(void);
55 static int pfctl_table(int, char *[], char *, const char *, char *,
57 static void print_table(const struct pfr_table
*, int, int);
58 static void print_tstats(const struct pfr_tstats
*, int);
59 static int load_addr(struct pfr_buffer
*, int, char *[], char *, int);
60 static void print_addrx(const struct pfr_addr
*, const struct pfr_addr
*,
62 static void print_astats(const struct pfr_astats
*, int);
63 static void radix_perror(void);
64 static void xprintf(int, const char *, ...) __printflike(2, 3);
65 static void print_iface(const 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 const 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")) {
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
)
276 PFRB_FOREACH(p
, &b
) {
277 ((struct pfr_astats
*)p
)->pfras_a
.pfra_fback
= 0;
278 if (time(NULL
) - ((struct pfr_astats
*)p
)->pfras_tzero
>
281 &((struct pfr_astats
*)p
)->pfras_a
))
282 err(1, "duplicate buffer");
285 if (opts
& PF_OPT_VERBOSE
)
286 flags
|= PFR_FLAG_FEEDBACK
;
287 RVTEST(pfr_del_addrs(&table
, b2
.pfrb_caddr
, b2
.pfrb_size
,
289 xprintf(opts
, "%d/%d addresses expired", ndel
, b2
.pfrb_size
);
290 if (opts
& PF_OPT_VERBOSE
)
292 if ((opts
& PF_OPT_VERBOSE2
) || a
->pfra_fback
)
294 opts
& PF_OPT_USEDNS
);
295 } else if (!strcmp(command
, "show")) {
296 b
.pfrb_type
= (opts
& PF_OPT_VERBOSE
) ?
297 PFRB_ASTATS
: PFRB_ADDRS
;
298 if (argc
|| file
!= NULL
)
301 pfr_buf_grow(&b
, b
.pfrb_size
);
302 b
.pfrb_size
= b
.pfrb_msize
;
303 if (opts
& PF_OPT_VERBOSE
)
304 RVTEST(pfr_get_astats(&table
, b
.pfrb_caddr
,
305 &b
.pfrb_size
, flags
));
307 RVTEST(pfr_get_addrs(&table
, b
.pfrb_caddr
,
308 &b
.pfrb_size
, flags
));
309 if (b
.pfrb_size
<= b
.pfrb_msize
)
313 if (opts
& PF_OPT_VERBOSE
)
314 print_astats(p
, opts
& PF_OPT_USEDNS
);
316 print_addrx(p
, NULL
, opts
& PF_OPT_USEDNS
);
317 } else if (!strcmp(command
, "test")) {
318 b
.pfrb_type
= PFRB_ADDRS
;
319 b2
.pfrb_type
= PFRB_ADDRS
;
321 if (load_addr(&b
, argc
, argv
, file
, 1))
323 if (opts
& PF_OPT_VERBOSE2
) {
324 flags
|= PFR_FLAG_REPLACE
;
326 if (pfr_buf_add(&b2
, a
))
327 err(1, "duplicate buffer");
329 RVTEST(pfr_tst_addrs(&table
, b
.pfrb_caddr
, b
.pfrb_size
,
331 xprintf(opts
, "%d/%d addresses match", nmatch
, b
.pfrb_size
);
332 if ((opts
& PF_OPT_VERBOSE
) && !(opts
& PF_OPT_VERBOSE2
))
334 if (a
->pfra_fback
== PFR_FB_MATCH
)
336 opts
& PF_OPT_USEDNS
);
337 if (opts
& PF_OPT_VERBOSE2
) {
339 PFRB_FOREACH(a
, &b
) {
340 a2
= pfr_buf_next(&b2
, a2
);
341 print_addrx(a2
, a
, opts
& PF_OPT_USEDNS
);
344 if (nmatch
< b
.pfrb_size
)
346 } else if (!strcmp(command
, "zero")) {
347 if (argc
|| file
!= NULL
)
349 flags
|= PFR_FLAG_ADDRSTOO
;
350 RVTEST(pfr_clr_tstats(&table
, 1, &nzero
, flags
));
351 xprintf(opts
, "%d table/stats cleared", nzero
);
353 warnx("pfctl_table: unknown command '%s'", command
);
365 print_table(const struct pfr_table
*ta
, int verbose
, int debug
)
367 if (!debug
&& !(ta
->pfrt_flags
& PFR_TFLAG_ACTIVE
))
370 printf("%c%c%c%c%c%c%c\t%s",
371 (ta
->pfrt_flags
& PFR_TFLAG_CONST
) ? 'c' : '-',
372 (ta
->pfrt_flags
& PFR_TFLAG_PERSIST
) ? 'p' : '-',
373 (ta
->pfrt_flags
& PFR_TFLAG_ACTIVE
) ? 'a' : '-',
374 (ta
->pfrt_flags
& PFR_TFLAG_INACTIVE
) ? 'i' : '-',
375 (ta
->pfrt_flags
& PFR_TFLAG_REFERENCED
) ? 'r' : '-',
376 (ta
->pfrt_flags
& PFR_TFLAG_REFDANCHOR
) ? 'h' : '-',
377 (ta
->pfrt_flags
& PFR_TFLAG_COUNTERS
) ? 'C' : '-',
379 if (ta
->pfrt_anchor
[0])
380 printf("\t%s", ta
->pfrt_anchor
);
387 print_tstats(const struct pfr_tstats
*ts
, int debug
)
389 time_t rtime
= ts
->pfrts_tzero
;
392 if (!debug
&& !(ts
->pfrts_flags
& PFR_TFLAG_ACTIVE
))
394 print_table(&ts
->pfrts_t
, 1, debug
);
395 printf("\tAddresses: %d\n", ts
->pfrts_cnt
);
396 printf("\tCleared: %s", ctime(&rtime
));
397 printf("\tReferences: [ Anchors: %-18d Rules: %-18d ]\n",
398 ts
->pfrts_refcnt
[PFR_REFCNT_ANCHOR
],
399 ts
->pfrts_refcnt
[PFR_REFCNT_RULE
]);
400 printf("\tEvaluations: [ NoMatch: %-18llu Match: %-18llu ]\n",
401 (unsigned long long)ts
->pfrts_nomatch
,
402 (unsigned long long)ts
->pfrts_match
);
403 for (dir
= 0; dir
< PFR_DIR_MAX
; dir
++)
404 for (op
= 0; op
< PFR_OP_TABLE_MAX
; op
++)
405 printf("\t%-12s [ Packets: %-18llu Bytes: %-18llu ]\n",
407 (unsigned long long)ts
->pfrts_packets
[dir
][op
],
408 (unsigned long long)ts
->pfrts_bytes
[dir
][op
]);
412 load_addr(struct pfr_buffer
*b
, int argc
, char *argv
[], char *file
,
416 if (append_addr(b
, *argv
++, nonetwork
)) {
418 warn("cannot decode %s", argv
[-1]);
421 if (pfr_buf_load(b
, file
, nonetwork
, append_addr
)) {
422 warn("cannot load %s", file
);
429 print_addrx(const struct pfr_addr
*ad
, const struct pfr_addr
*rad
, int dns
)
431 char ch
, buf
[256] = "{error}";
432 char fb
[] = { ' ', 'M', 'A', 'D', 'C', 'Z', 'X', ' ', 'Y', ' ' };
433 unsigned int fback
, hostnet
;
435 fback
= (rad
!= NULL
) ? rad
->pfra_fback
: ad
->pfra_fback
;
436 ch
= (fback
< sizeof(fb
)/sizeof(*fb
)) ? fb
[fback
] : '?';
437 hostnet
= (ad
->pfra_af
== AF_INET6
) ? 128 : 32;
438 inet_ntop(ad
->pfra_af
, &ad
->pfra_u
, buf
, sizeof(buf
));
439 printf("%c %c%s", ch
, (ad
->pfra_not
?'!':' '), buf
);
440 if (ad
->pfra_net
< hostnet
)
441 printf("/%d", ad
->pfra_net
);
442 if (rad
!= NULL
&& fback
!= PFR_FB_NONE
) {
443 if (strlcpy(buf
, "{error}", sizeof(buf
)) >= sizeof(buf
))
444 errx(1, "print_addrx: strlcpy");
445 inet_ntop(rad
->pfra_af
, &rad
->pfra_u
, buf
, sizeof(buf
));
446 printf("\t%c%s", (rad
->pfra_not
?'!':' '), buf
);
447 if (rad
->pfra_net
< hostnet
)
448 printf("/%d", rad
->pfra_net
);
450 if (rad
!= NULL
&& fback
== PFR_FB_NONE
)
451 printf("\t nomatch");
452 if (dns
&& ad
->pfra_net
== hostnet
) {
453 char hostname
[NI_MAXHOST
];
454 union sockaddr_union sa
;
456 strlcpy(hostname
, "?", sizeof(hostname
));
457 bzero(&sa
, sizeof(sa
));
458 sa
.sa
.sa_family
= ad
->pfra_af
;
459 if (sa
.sa
.sa_family
== AF_INET
) {
460 sa
.sa
.sa_len
= sizeof(sa
.sin
);
461 sa
.sin
.sin_addr
= ad
->pfra_ip4addr
;
463 sa
.sa
.sa_len
= sizeof(sa
.sin6
);
464 sa
.sin6
.sin6_addr
= ad
->pfra_ip6addr
;
466 if (getnameinfo(&sa
.sa
, sa
.sa
.sa_len
, hostname
, sizeof(hostname
),
467 NULL
, 0, NI_NAMEREQD
) == 0)
468 printf("\t(%s)", hostname
);
474 print_astats(const struct pfr_astats
*as
, int dns
)
476 time_t rtime
= as
->pfras_tzero
;
479 print_addrx(&as
->pfras_a
, NULL
, dns
);
480 printf("\tCleared: %s", ctime(&rtime
));
481 if (as
->pfras_a
.pfra_fback
== PFR_FB_NOCOUNT
)
483 for (dir
= 0; dir
< PFR_DIR_MAX
; dir
++)
484 for (op
= 0; op
< PFR_OP_ADDR_MAX
; op
++)
485 printf("\t%-12s [ Packets: %-18llu Bytes: %-18llu ]\n",
487 (unsigned long long)as
->pfras_packets
[dir
][op
],
488 (unsigned long long)as
->pfras_bytes
[dir
][op
]);
494 fprintf(stderr
, "%s: %s.\n", getprogname(), pfr_strerror(errno
));
498 pfctl_define_table(char *name
, int flags
, int addrs
, const char *anchor
,
499 struct pfr_buffer
*ab
, u_int32_t ticket
)
501 struct pfr_table tbl
;
503 bzero(&tbl
, sizeof(tbl
));
504 if (strlcpy(tbl
.pfrt_name
, name
, sizeof(tbl
.pfrt_name
)) >=
505 sizeof(tbl
.pfrt_name
) || strlcpy(tbl
.pfrt_anchor
, anchor
,
506 sizeof(tbl
.pfrt_anchor
)) >= sizeof(tbl
.pfrt_anchor
))
507 errx(1, "pfctl_define_table: strlcpy");
508 tbl
.pfrt_flags
= flags
;
510 return pfr_ina_define(&tbl
, ab
->pfrb_caddr
, ab
->pfrb_size
, NULL
,
511 NULL
, ticket
, addrs
? PFR_FLAG_ADDRSTOO
: 0);
515 warn_namespace_collision(const char *filter
)
518 const struct pfr_table
*t
;
519 const char *name
= NULL
, *lastcoll
;
522 bzero(&b
, sizeof(b
));
523 b
.pfrb_type
= PFRB_TABLES
;
525 pfr_buf_grow(&b
, b
.pfrb_size
);
526 b
.pfrb_size
= b
.pfrb_msize
;
527 if (pfr_get_tables(NULL
, b
.pfrb_caddr
,
528 &b
.pfrb_size
, PFR_FLAG_ALLRSETS
))
529 err(1, "pfr_get_tables");
530 if (b
.pfrb_size
<= b
.pfrb_msize
)
533 PFRB_FOREACH(t
, &b
) {
534 if (!(t
->pfrt_flags
& PFR_TFLAG_ACTIVE
))
536 if (filter
!= NULL
&& strcmp(filter
, t
->pfrt_name
))
538 if (!t
->pfrt_anchor
[0])
540 else if (name
!= NULL
&& !strcmp(name
, t
->pfrt_name
)) {
547 warnx("warning: namespace collision with <%s> global table.",
550 warnx("warning: namespace collisions with %d global tables.",
556 xprintf(int opts
, const char *fmt
, ...)
560 if (opts
& PF_OPT_QUIET
)
564 vfprintf(stderr
, fmt
, args
);
567 if (opts
& PF_OPT_DUMMYACTION
)
568 fprintf(stderr
, " (dummy).\n");
569 else if (opts
& PF_OPT_NOACTION
)
570 fprintf(stderr
, " (syntax only).\n");
572 fprintf(stderr
, ".\n");
576 /* interface stuff */
579 pfctl_show_ifaces(const char *filter
, int opts
)
582 const struct pfi_kif
*p
;
585 bzero(&b
, sizeof(b
));
586 b
.pfrb_type
= PFRB_IFACES
;
588 pfr_buf_grow(&b
, b
.pfrb_size
);
589 b
.pfrb_size
= b
.pfrb_msize
;
590 if (pfi_get_ifaces(filter
, b
.pfrb_caddr
, &b
.pfrb_size
)) {
594 if (b
.pfrb_size
<= b
.pfrb_msize
)
598 if (opts
& PF_OPT_SHOWALL
)
599 pfctl_print_title("INTERFACES:");
601 print_iface(p
, opts
);
606 print_iface(const struct pfi_kif
*p
, int opts
)
608 time_t tzero
= p
->pfik_tzero
;
611 printf("%s", p
->pfik_name
);
612 if (opts
& PF_OPT_VERBOSE
) {
613 if (p
->pfik_flags
& PFI_IFLAG_SKIP
)
618 if (!(opts
& PF_OPT_VERBOSE2
))
620 printf("\tCleared: %s", ctime(&tzero
));
621 printf("\tReferences: [ States: %-18d Rules: %-18d ]\n",
622 p
->pfik_states
, p
->pfik_rules
);
623 for (i
= 0; i
< 8; i
++) {
627 printf("\t%-12s [ Packets: %-18llu Bytes: %-18llu ]\n",
628 istats_text
[af
][dir
][act
],
629 (unsigned long long)p
->pfik_packets
[af
][dir
][act
],
630 (unsigned long long)p
->pfik_bytes
[af
][dir
][act
]);