1 /* $OpenBSD: pfctl_table.c,v 1.59 2004/03/15 15:25:44 dhartmei Exp $ */
2 /* $DragonFly: src/usr.sbin/pfctl/pfctl_table.c,v 1.1 2004/09/21 21:25:28 joerg 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/pf/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 *,
57 const char *, const char *, int);
58 static void print_table(const struct pfr_table
*, int, int);
59 static void print_tstats(const struct pfr_tstats
*, int);
60 static int load_addr(struct pfr_buffer
*, int, char *[], char *, int);
61 static void print_addrx(const struct pfr_addr
*, const struct pfr_addr
*,
63 static void print_astats(const struct pfr_astats
*, int);
64 static void radix_perror(void);
65 static void xprintf(int, const char *, ...);
66 static void print_iface(const struct pfi_if
*, int);
67 static void oprintf(int, int, const char *, int *, int);
69 static const char *stats_text
[PFR_DIR_MAX
][PFR_OP_TABLE_MAX
] = {
70 { "In/Block:", "In/Pass:", "In/XPass:" },
71 { "Out/Block:", "Out/Pass:", "Out/XPass:" }
74 static const char *istats_text
[2][2][2] = {
75 { { "In4/Pass:", "In4/Block:" }, { "Out4/Pass:", "Out4/Block:" } },
76 { { "In6/Pass:", "In6/Block:" }, { "Out6/Pass:", "Out6/Block:" } }
79 #define RVTEST(fct) do { \
80 if ((!(opts & PF_OPT_NOACTION) || \
81 (opts & PF_OPT_DUMMYACTION)) && \
88 #define CREATE_TABLE do { \
89 table.pfrt_flags |= PFR_TFLAG_PERSIST; \
90 RVTEST(pfr_add_tables(&table, 1, &nadd, flags)); \
92 warn_namespace_collision(table.pfrt_name); \
93 xprintf(opts, "%d table created", nadd); \
94 if (opts & PF_OPT_NOACTION) \
97 table.pfrt_flags &= ~PFR_TFLAG_PERSIST; \
101 pfctl_clear_tables(const char *anchor
, const char *ruleset
, int opts
)
103 return pfctl_table(0, NULL
, NULL
, "-F", NULL
, anchor
, ruleset
, opts
);
107 pfctl_show_tables(const char *anchor
, const char *ruleset
, int opts
)
109 return pfctl_table(0, NULL
, NULL
, "-s", NULL
, anchor
, ruleset
, opts
);
113 pfctl_command_tables(int argc
, char *argv
[], char *tname
,
114 const char *command
, char *file
, const char *anchor
, const char *ruleset
,
117 if (tname
== NULL
|| command
== NULL
)
119 return pfctl_table(argc
, argv
, tname
, command
, file
, anchor
, ruleset
,
124 pfctl_table(int argc
, char *argv
[], char *tname
, const char *command
,
125 char *file
, const char *anchor
, const char *ruleset
, int opts
)
127 struct pfr_table table
;
128 struct pfr_buffer b
, b2
;
129 const struct pfr_addr
*a
, *a2
;
130 int nadd
= 0, ndel
= 0, nchange
= 0, nzero
= 0;
131 int rv
= 0, flags
= 0, nmatch
= 0;
136 if (opts
& PF_OPT_NOACTION
)
137 flags
|= PFR_FLAG_DUMMY
;
139 bzero(&b
, sizeof(b
));
140 bzero(&b2
, sizeof(b2
));
141 bzero(&table
, sizeof(table
));
143 if (strlen(tname
) >= PF_TABLE_NAME_SIZE
)
145 if (strlcpy(table
.pfrt_name
, tname
,
146 sizeof(table
.pfrt_name
)) >= sizeof(table
.pfrt_name
))
147 errx(1, "pfctl_table: strlcpy");
149 if (strlcpy(table
.pfrt_anchor
, anchor
,
150 sizeof(table
.pfrt_anchor
)) >= sizeof(table
.pfrt_anchor
) ||
151 strlcpy(table
.pfrt_ruleset
, ruleset
,
152 sizeof(table
.pfrt_ruleset
)) >= sizeof(table
.pfrt_ruleset
))
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
, "show")) {
258 b
.pfrb_type
= (opts
& PF_OPT_VERBOSE
) ?
259 PFRB_ASTATS
: PFRB_ADDRS
;
260 if (argc
|| file
!= NULL
)
263 pfr_buf_grow(&b
, b
.pfrb_size
);
264 b
.pfrb_size
= b
.pfrb_msize
;
265 if (opts
& PF_OPT_VERBOSE
)
266 RVTEST(pfr_get_astats(&table
, b
.pfrb_caddr
,
267 &b
.pfrb_size
, flags
));
269 RVTEST(pfr_get_addrs(&table
, b
.pfrb_caddr
,
270 &b
.pfrb_size
, flags
));
271 if (b
.pfrb_size
<= b
.pfrb_msize
)
275 if (opts
& PF_OPT_VERBOSE
)
276 print_astats(p
, opts
& PF_OPT_USEDNS
);
278 print_addrx(p
, NULL
, opts
& PF_OPT_USEDNS
);
279 } else if (!strcmp(command
, "test")) {
280 b
.pfrb_type
= PFRB_ADDRS
;
281 b2
.pfrb_type
= PFRB_ADDRS
;
283 if (load_addr(&b
, argc
, argv
, file
, 1))
285 if (opts
& PF_OPT_VERBOSE2
) {
286 flags
|= PFR_FLAG_REPLACE
;
288 if (pfr_buf_add(&b2
, a
))
289 err(1, "duplicate buffer");
291 RVTEST(pfr_tst_addrs(&table
, b
.pfrb_caddr
, b
.pfrb_size
,
293 xprintf(opts
, "%d/%d addresses match", nmatch
, b
.pfrb_size
);
294 if (opts
& PF_OPT_VERBOSE
&& !(opts
& PF_OPT_VERBOSE2
))
296 if (a
->pfra_fback
== PFR_FB_MATCH
)
298 opts
& PF_OPT_USEDNS
);
299 if (opts
& PF_OPT_VERBOSE2
) {
301 PFRB_FOREACH(a
, &b
) {
302 a2
= pfr_buf_next(&b2
, a2
);
303 print_addrx(a2
, a
, opts
& PF_OPT_USEDNS
);
306 if (nmatch
< b
.pfrb_size
)
308 } else if (!strcmp(command
, "zero")) {
309 if (argc
|| file
!= NULL
)
311 flags
|= PFR_FLAG_ADDRSTOO
;
312 RVTEST(pfr_clr_tstats(&table
, 1, &nzero
, flags
));
313 xprintf(opts
, "%d table/stats cleared", nzero
);
315 warnx("pfctl_table: unknown command '%s'", command
);
327 print_table(const struct pfr_table
*ta
, int verbose
, int debug
)
329 if (!debug
&& !(ta
->pfrt_flags
& PFR_TFLAG_ACTIVE
))
332 printf("%c%c%c%c%c%c\t%s",
333 (ta
->pfrt_flags
& PFR_TFLAG_CONST
) ? 'c' : '-',
334 (ta
->pfrt_flags
& PFR_TFLAG_PERSIST
) ? 'p' : '-',
335 (ta
->pfrt_flags
& PFR_TFLAG_ACTIVE
) ? 'a' : '-',
336 (ta
->pfrt_flags
& PFR_TFLAG_INACTIVE
) ? 'i' : '-',
337 (ta
->pfrt_flags
& PFR_TFLAG_REFERENCED
) ? 'r' : '-',
338 (ta
->pfrt_flags
& PFR_TFLAG_REFDANCHOR
) ? 'h' : '-',
340 if (ta
->pfrt_anchor
[0])
341 printf("\t%s", ta
->pfrt_anchor
);
342 if (ta
->pfrt_ruleset
[0])
343 printf(":%s", ta
->pfrt_ruleset
);
350 print_tstats(const struct pfr_tstats
*ts
, int debug
)
352 time_t rtime
= ts
->pfrts_tzero
;
355 if (!debug
&& !(ts
->pfrts_flags
& PFR_TFLAG_ACTIVE
))
357 print_table(&ts
->pfrts_t
, 1, debug
);
358 printf("\tAddresses: %d\n", ts
->pfrts_cnt
);
359 printf("\tCleared: %s", ctime(&rtime
));
360 printf("\tReferences: [ Anchors: %-18d Rules: %-18d ]\n",
361 ts
->pfrts_refcnt
[PFR_REFCNT_ANCHOR
],
362 ts
->pfrts_refcnt
[PFR_REFCNT_RULE
]);
363 printf("\tEvaluations: [ NoMatch: %-18llu Match: %-18llu ]\n",
364 (unsigned long long)ts
->pfrts_nomatch
,
365 (unsigned long long)ts
->pfrts_match
);
366 for (dir
= 0; dir
< PFR_DIR_MAX
; dir
++)
367 for (op
= 0; op
< PFR_OP_TABLE_MAX
; op
++)
368 printf("\t%-12s [ Packets: %-18llu Bytes: %-18llu ]\n",
370 (unsigned long long)ts
->pfrts_packets
[dir
][op
],
371 (unsigned long long)ts
->pfrts_bytes
[dir
][op
]);
375 load_addr(struct pfr_buffer
*b
, int argc
, char *argv
[], char *file
,
379 if (append_addr(b
, *argv
++, nonetwork
)) {
381 warn("cannot decode %s", argv
[-1]);
384 if (pfr_buf_load(b
, file
, nonetwork
, append_addr
)) {
385 warn("cannot load %s", file
);
392 print_addrx(const struct pfr_addr
*ad
, const struct pfr_addr
*rad
, int dns
)
394 char ch
, buf
[256] = "{error}";
395 char fb
[] = { ' ', 'M', 'A', 'D', 'C', 'Z', 'X', ' ', 'Y' };
396 unsigned int fback
, hostnet
;
398 fback
= (rad
!= NULL
) ? rad
->pfra_fback
: ad
->pfra_fback
;
399 ch
= (fback
< sizeof(fb
)/sizeof(*fb
)) ? fb
[fback
] : '?';
400 hostnet
= (ad
->pfra_af
== AF_INET6
) ? 128 : 32;
401 inet_ntop(ad
->pfra_af
, &ad
->pfra_u
, buf
, sizeof(buf
));
402 printf("%c %c%s", ch
, (ad
->pfra_not
?'!':' '), buf
);
403 if (ad
->pfra_net
< hostnet
)
404 printf("/%d", ad
->pfra_net
);
405 if (rad
!= NULL
&& fback
!= PFR_FB_NONE
) {
406 if (strlcpy(buf
, "{error}", sizeof(buf
)) >= sizeof(buf
))
407 errx(1, "print_addrx: strlcpy");
408 inet_ntop(rad
->pfra_af
, &rad
->pfra_u
, buf
, sizeof(buf
));
409 printf("\t%c%s", (rad
->pfra_not
?'!':' '), buf
);
410 if (rad
->pfra_net
< hostnet
)
411 printf("/%d", rad
->pfra_net
);
413 if (rad
!= NULL
&& fback
== PFR_FB_NONE
)
414 printf("\t nomatch");
415 if (dns
&& ad
->pfra_net
== hostnet
) {
416 char hostname
[NI_MAXHOST
];
417 union sockaddr_union sa
;
419 strlcpy(hostname
, "?", sizeof(hostname
));
420 bzero(&sa
, sizeof(sa
));
421 sa
.sa
.sa_family
= ad
->pfra_af
;
422 if (sa
.sa
.sa_family
== AF_INET
) {
423 sa
.sa
.sa_len
= sizeof(sa
.sin
);
424 sa
.sin
.sin_addr
= ad
->pfra_ip4addr
;
426 sa
.sa
.sa_len
= sizeof(sa
.sin6
);
427 sa
.sin6
.sin6_addr
= ad
->pfra_ip6addr
;
429 if (getnameinfo(&sa
.sa
, sa
.sa
.sa_len
, hostname
, sizeof(hostname
),
430 NULL
, 0, NI_NAMEREQD
) == 0)
431 printf("\t(%s)", hostname
);
437 print_astats(const struct pfr_astats
*as
, int dns
)
439 time_t rtime
= as
->pfras_tzero
;
442 print_addrx(&as
->pfras_a
, NULL
, dns
);
443 printf("\tCleared: %s", ctime(&rtime
));
444 for (dir
= 0; dir
< PFR_DIR_MAX
; dir
++)
445 for (op
= 0; op
< PFR_OP_ADDR_MAX
; op
++)
446 printf("\t%-12s [ Packets: %-18llu Bytes: %-18llu ]\n",
448 (unsigned long long)as
->pfras_packets
[dir
][op
],
449 (unsigned long long)as
->pfras_bytes
[dir
][op
]);
455 fprintf(stderr
, "%s: %s.\n", getprogname(), pfr_strerror(errno
));
459 pfctl_define_table(char *name
, int flags
, int addrs
, const char *anchor
,
460 const char *ruleset
, struct pfr_buffer
*ab
, u_int32_t ticket
)
462 struct pfr_table tbl
;
464 bzero(&tbl
, sizeof(tbl
));
465 if (strlcpy(tbl
.pfrt_name
, name
, sizeof(tbl
.pfrt_name
)) >=
466 sizeof(tbl
.pfrt_name
) || strlcpy(tbl
.pfrt_anchor
, anchor
,
467 sizeof(tbl
.pfrt_anchor
)) >= sizeof(tbl
.pfrt_anchor
) ||
468 strlcpy(tbl
.pfrt_ruleset
, ruleset
, sizeof(tbl
.pfrt_ruleset
)) >=
469 sizeof(tbl
.pfrt_ruleset
))
470 errx(1, "pfctl_define_table: strlcpy");
471 tbl
.pfrt_flags
= flags
;
473 return pfr_ina_define(&tbl
, ab
->pfrb_caddr
, ab
->pfrb_size
, NULL
,
474 NULL
, ticket
, addrs
? PFR_FLAG_ADDRSTOO
: 0);
478 warn_namespace_collision(const char *filter
)
481 const struct pfr_table
*t
;
482 const char *name
= NULL
, *lastcoll
= NULL
;
485 bzero(&b
, sizeof(b
));
486 b
.pfrb_type
= PFRB_TABLES
;
488 pfr_buf_grow(&b
, b
.pfrb_size
);
489 b
.pfrb_size
= b
.pfrb_msize
;
490 if (pfr_get_tables(NULL
, b
.pfrb_caddr
,
491 &b
.pfrb_size
, PFR_FLAG_ALLRSETS
))
492 err(1, "pfr_get_tables");
493 if (b
.pfrb_size
<= b
.pfrb_msize
)
496 PFRB_FOREACH(t
, &b
) {
497 if (!(t
->pfrt_flags
& PFR_TFLAG_ACTIVE
))
499 if (filter
!= NULL
&& strcmp(filter
, t
->pfrt_name
))
501 if (!t
->pfrt_anchor
[0])
503 else if (name
!= NULL
&& !strcmp(name
, t
->pfrt_name
)) {
510 warnx("warning: namespace collision with <%s> global table.",
513 warnx("warning: namespace collisions with %d global tables.",
519 xprintf(int opts
, const char *fmt
, ...)
523 if (opts
& PF_OPT_QUIET
)
527 vfprintf(stderr
, fmt
, args
);
530 if (opts
& PF_OPT_DUMMYACTION
)
531 fprintf(stderr
, " (dummy).\n");
532 else if (opts
& PF_OPT_NOACTION
)
533 fprintf(stderr
, " (syntax only).\n");
535 fprintf(stderr
, ".\n");
539 /* interface stuff */
542 pfctl_show_ifaces(const char *filter
, int opts
)
545 const struct pfi_if
*p
;
546 int i
= 0, f
= PFI_FLAG_GROUP
|PFI_FLAG_INSTANCE
;
548 if (filter
!= NULL
&& *filter
&& !isdigit(filter
[strlen(filter
)-1]))
549 f
&= ~PFI_FLAG_INSTANCE
;
550 bzero(&b
, sizeof(b
));
551 b
.pfrb_type
= PFRB_IFACES
;
553 pfr_buf_grow(&b
, b
.pfrb_size
);
554 b
.pfrb_size
= b
.pfrb_msize
;
555 if (pfi_get_ifaces(filter
, b
.pfrb_caddr
, &b
.pfrb_size
, f
)) {
559 if (b
.pfrb_size
<= b
.pfrb_msize
)
563 if (opts
& PF_OPT_SHOWALL
)
564 pfctl_print_title("INTERFACES:");
566 print_iface(p
, opts
);
571 print_iface(const struct pfi_if
*p
, int opts
)
573 time_t tzero
= p
->pfif_tzero
;
574 int flags
= (opts
& PF_OPT_VERBOSE
) ? p
->pfif_flags
: 0;
578 printf("%s", p
->pfif_name
);
579 oprintf(flags
, PFI_IFLAG_INSTANCE
, "instance", &first
, 0);
580 oprintf(flags
, PFI_IFLAG_GROUP
, "group", &first
, 0);
581 oprintf(flags
, PFI_IFLAG_CLONABLE
, "clonable", &first
, 0);
582 oprintf(flags
, PFI_IFLAG_DYNAMIC
, "dynamic", &first
, 0);
583 oprintf(flags
, PFI_IFLAG_ATTACHED
, "attached", &first
, 1);
586 if (!(opts
& PF_OPT_VERBOSE2
))
588 printf("\tCleared: %s", ctime(&tzero
));
589 printf("\tReferences: [ States: %-18d Rules: %-18d ]\n",
590 p
->pfif_states
, p
->pfif_rules
);
591 for (i
= 0; i
< 8; i
++) {
595 printf("\t%-12s [ Packets: %-18llu Bytes: %-18llu ]\n",
596 istats_text
[af
][dir
][act
],
597 (unsigned long long)p
->pfif_packets
[af
][dir
][act
],
598 (unsigned long long)p
->pfif_bytes
[af
][dir
][act
]);
603 oprintf(int flags
, int flag
, const char *s
, int *first
, int last
)
606 printf(*first
? "\t(%s" : ", %s", s
);