npf_show.c revision 1.28 1 /*-
2 * Copyright (c) 2013 The NetBSD Foundation, Inc.
3 * All rights reserved.
4 *
5 * This code is derived from software contributed to The NetBSD Foundation
6 * by Mindaugas Rasiukevicius.
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 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
18 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
20 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
21 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 * POSSIBILITY OF SUCH DAMAGE.
28 */
29
30 /*
31 * NPF configuration printing.
32 *
33 * Each rule having BPF byte-code has a binary description.
34 */
35
36 #include <sys/cdefs.h>
37 __RCSID("$NetBSD: npf_show.c,v 1.28 2019/07/23 00:52:02 rmind Exp $");
38
39 #include <sys/socket.h>
40 #define __FAVOR_BSD
41 #include <netinet/in.h>
42 #include <netinet/tcp.h>
43 #include <net/if.h>
44
45 #include <stdio.h>
46 #include <stdlib.h>
47 #include <string.h>
48 #include <stdbool.h>
49 #include <inttypes.h>
50 #include <errno.h>
51 #include <err.h>
52
53 #include "npfctl.h"
54
55 #define SEEN_SRC 0x01
56 #define SEEN_DST 0x02
57
58 typedef struct {
59 nl_config_t * conf;
60 FILE * fp;
61 long fpos;
62 u_int flags;
63 uint32_t curmark;
64 unsigned level;
65 } npf_conf_info_t;
66
67 static npf_conf_info_t stdout_ctx;
68
69 static void print_linesep(npf_conf_info_t *);
70
71 void
72 npfctl_show_init(void)
73 {
74 stdout_ctx.fp = stdout;
75 stdout_ctx.fpos = 0;
76 stdout_ctx.flags = 0;
77 stdout_ctx.level = 0;
78 }
79
80 /*
81 * Helper routines to print various pieces of information.
82 */
83
84 static void
85 print_indent(npf_conf_info_t *ctx, unsigned level)
86 {
87 if (level < ctx->level) {
88 /*
89 * Level decrease -- end of the group.
90 * Print the group closing curly bracket.
91 */
92 fputs("}\n", ctx->fp);
93 }
94 if (level == 0) {
95 /*
96 * Group level -- separate groups by a trailing new line.
97 */
98 print_linesep(ctx);
99 }
100 ctx->level = level;
101
102 while (level--) {
103 fprintf(ctx->fp, "\t");
104 }
105 }
106
107 static void
108 print_linesep(npf_conf_info_t *ctx)
109 {
110 if (ftell(ctx->fp) != ctx->fpos) {
111 fputs("\n", ctx->fp);
112 ctx->fpos = ftell(ctx->fp);
113 }
114 }
115
116 static size_t
117 tcpflags2string(char *buf, u_int tfl)
118 {
119 u_int i = 0;
120
121 if (tfl & TH_FIN) buf[i++] = 'F';
122 if (tfl & TH_SYN) buf[i++] = 'S';
123 if (tfl & TH_RST) buf[i++] = 'R';
124 if (tfl & TH_PUSH) buf[i++] = 'P';
125 if (tfl & TH_ACK) buf[i++] = 'A';
126 if (tfl & TH_URG) buf[i++] = 'U';
127 if (tfl & TH_ECE) buf[i++] = 'E';
128 if (tfl & TH_CWR) buf[i++] = 'C';
129 buf[i] = '\0';
130 return i;
131 }
132
133 static char *
134 print_family(npf_conf_info_t *ctx __unused, const uint32_t *words)
135 {
136 const int af = words[0];
137
138 switch (af) {
139 case AF_INET:
140 return estrdup("inet4");
141 case AF_INET6:
142 return estrdup("inet6");
143 default:
144 errx(EXIT_FAILURE, "invalid byte-code mark (family)");
145 }
146 return NULL;
147 }
148
149 static char *
150 print_address(npf_conf_info_t *ctx __unused, const uint32_t *words)
151 {
152 const int af = *words++;
153 const u_int mask = *words++;
154 const npf_addr_t *addr;
155 int alen = 0;
156
157 switch (af) {
158 case AF_INET:
159 alen = 4;
160 break;
161 case AF_INET6:
162 alen = 16;
163 break;
164 default:
165 errx(EXIT_FAILURE, "invalid byte-code mark (address)");
166 }
167 addr = (const npf_addr_t *)words;
168 return npfctl_print_addrmask(alen, "%a", addr, mask);
169 }
170
171 static char *
172 print_number(npf_conf_info_t *ctx __unused, const uint32_t *words)
173 {
174 char *p;
175 easprintf(&p, "%u", words[0]);
176 return p;
177 }
178
179 static char *
180 print_table(npf_conf_info_t *ctx, const uint32_t *words)
181 {
182 const unsigned tid = words[0];
183 const char *tname;
184 char *s = NULL;
185 bool ifaddr;
186
187 tname = npfctl_table_getname(ctx->conf, tid, &ifaddr);
188 easprintf(&s, ifaddr ? "ifaddrs(%s)" : "<%s>", tname);
189 return s;
190 }
191
192 static char *
193 print_proto(npf_conf_info_t *ctx, const uint32_t *words)
194 {
195 switch (words[0]) {
196 case IPPROTO_TCP:
197 return estrdup("tcp");
198 case IPPROTO_UDP:
199 return estrdup("udp");
200 case IPPROTO_ICMP:
201 return estrdup("icmp");
202 case IPPROTO_ICMPV6:
203 return estrdup("ipv6-icmp");
204 }
205 return print_number(ctx, words);
206 }
207
208 static char *
209 print_tcpflags(npf_conf_info_t *ctx __unused, const uint32_t *words)
210 {
211 const u_int tf = words[0], tf_mask = words[1];
212 char buf[16];
213
214 size_t n = tcpflags2string(buf, tf);
215 if (tf != tf_mask) {
216 buf[n++] = '/';
217 tcpflags2string(buf + n, tf_mask);
218 }
219 return estrdup(buf);
220 }
221
222 static char *
223 print_portrange(npf_conf_info_t *ctx, const uint32_t *words)
224 {
225 u_int fport = words[0], tport = words[1];
226 const char *any_str = "";
227 char *p;
228
229 if (ctx->curmark == BM_SRC_PORTS && (ctx->flags & SEEN_SRC) == 0)
230 any_str = "from any ";
231 if (ctx->curmark == BM_DST_PORTS && (ctx->flags & SEEN_DST) == 0)
232 any_str = "to any ";
233
234 if (fport != tport) {
235 easprintf(&p, "%sport %u:%u", any_str, fport, tport);
236 } else {
237 easprintf(&p, "%sport %u", any_str, fport);
238 }
239 return p;
240 }
241
242 /*
243 * The main keyword mapping tables defining the syntax:
244 * - Mapping of rule attributes (flags) to the keywords.
245 * - Mapping of the byte-code marks to the keywords.
246 */
247
248 #define F(name) __CONCAT(NPF_RULE_, name)
249 #define STATEFUL_ALL (NPF_RULE_STATEFUL | NPF_RULE_GSTATEFUL)
250 #define NAME_AT 2
251
252 static const struct attr_keyword_mapent {
253 uint32_t mask;
254 uint32_t flags;
255 const char * val;
256 } attr_keyword_map[] = {
257 { F(GROUP)|F(DYNAMIC), F(GROUP), "group" },
258 { F(DYNAMIC), F(DYNAMIC), "ruleset" },
259 { F(GROUP)|F(PASS), 0, "block" },
260 { F(GROUP)|F(PASS), F(PASS), "pass" },
261 { F(RETRST)|F(RETICMP), F(RETRST)|F(RETICMP), "return" },
262 { F(RETRST)|F(RETICMP), F(RETRST), "return-rst" },
263 { F(RETRST)|F(RETICMP), F(RETICMP), "return-icmp" },
264 { STATEFUL_ALL, F(STATEFUL), "stateful" },
265 { STATEFUL_ALL, STATEFUL_ALL, "stateful-all" },
266 { F(DIMASK), F(IN), "in" },
267 { F(DIMASK), F(OUT), "out" },
268 { F(FINAL), F(FINAL), "final" },
269 };
270
271 static const struct mark_keyword_mapent {
272 u_int mark;
273 const char * token;
274 const char * sep;
275 u_int set_flags;
276 char * (*printfn)(npf_conf_info_t *, const uint32_t *);
277 u_int fwords;
278 } mark_keyword_map[] = {
279 { BM_IPVER, "family %s", NULL, 0, print_family, 1 },
280 { BM_PROTO, "proto %s", ", ", 0, print_proto, 1 },
281 { BM_TCPFL, "flags %s", NULL, 0, print_tcpflags, 2 },
282 { BM_ICMP_TYPE, "icmp-type %s", NULL, 0, print_number, 1 },
283 { BM_ICMP_CODE, "code %s", NULL, 0, print_number, 1 },
284
285 { BM_SRC_CIDR, "from %s", ", ", SEEN_SRC, print_address, 6 },
286 { BM_SRC_TABLE, "from %s", NULL, SEEN_SRC, print_table, 1 },
287 { BM_SRC_PORTS, "%s", ", ", 0, print_portrange,2 },
288
289 { BM_DST_CIDR, "to %s", ", ", SEEN_DST, print_address, 6 },
290 { BM_DST_TABLE, "to %s", NULL, SEEN_DST, print_table, 1 },
291 { BM_DST_PORTS, "%s", ", ", 0, print_portrange,2 },
292 };
293
294 static const char * __attribute__((format_arg(2)))
295 verified_fmt(const char *fmt, const char *t __unused)
296 {
297 return fmt;
298 }
299
300 static char *
301 scan_marks(npf_conf_info_t *ctx, const struct mark_keyword_mapent *mk,
302 const uint32_t *marks, size_t mlen)
303 {
304 char buf[2048], *vals[256], *p;
305 size_t nvals = 0;
306
307 /* Scan for the marks and extract the values. */
308 mlen /= sizeof(uint32_t);
309 while (mlen > 2) {
310 const uint32_t m = *marks++;
311 const u_int nwords = *marks++;
312
313 if ((mlen -= 2) < nwords) {
314 errx(EXIT_FAILURE, "byte-code marking inconsistency");
315 }
316 if (m == mk->mark) {
317 /* Set the current mark and the flags. */
318 ctx->flags |= mk->set_flags;
319 ctx->curmark = m;
320
321 /* Value is processed by the print function. */
322 assert(mk->fwords == nwords);
323 vals[nvals++] = mk->printfn(ctx, marks);
324 }
325 marks += nwords;
326 mlen -= nwords;
327 }
328 if (nvals == 0) {
329 return NULL;
330 }
331 assert(nvals == 1 || mk->sep != NULL);
332
333 /*
334 * Join all the values and print. Add curly brackets if there
335 * is more than value and it can be a set.
336 */
337 if (!join(buf, sizeof(buf), nvals, vals, mk->sep ? mk->sep : "")) {
338 errx(EXIT_FAILURE, "out of memory while parsing the rule");
339 }
340 easprintf(&p, nvals > 1 ? "{ %s }" : "%s", buf);
341
342 for (u_int i = 0; i < nvals; i++) {
343 free(vals[i]);
344 }
345 return p;
346 }
347
348 static void
349 npfctl_print_id(npf_conf_info_t *ctx, nl_rule_t *rl)
350 {
351 uint64_t id = id = npf_rule_getid(rl);
352 fprintf(ctx->fp, "# id=\"%" PRIx64 "\" ", id);
353 }
354
355 static void
356 npfctl_print_filter(npf_conf_info_t *ctx, nl_rule_t *rl)
357 {
358 const void *marks;
359 size_t mlen, len;
360 const void *code;
361 int type;
362
363 marks = npf_rule_getinfo(rl, &mlen);
364 if (!marks && (code = npf_rule_getcode(rl, &type, &len)) != NULL) {
365 /*
366 * No marks, but the byte-code is present. This must
367 * have been filled by libpcap(3) or possibly an unknown
368 * to us byte-code.
369 */
370 fprintf(ctx->fp, "%s ", type == NPF_CODE_BPF ?
371 "pcap-filter \"...\"" : "unrecognized-bytecode");
372 return;
373 }
374 ctx->flags = 0;
375
376 /*
377 * BPF filter criteria described by the byte-code marks.
378 */
379 for (u_int i = 0; i < __arraycount(mark_keyword_map); i++) {
380 const struct mark_keyword_mapent *mk = &mark_keyword_map[i];
381 char *val;
382
383 if ((val = scan_marks(ctx, mk, marks, mlen)) != NULL) {
384 fprintf(ctx->fp, verified_fmt(mk->token, "%s"), val);
385 fputs(" ", ctx->fp);
386 free(val);
387 }
388 }
389 if (!mlen) {
390 fputs("all ", ctx->fp);
391 }
392 }
393
394 static void
395 npfctl_print_rule(npf_conf_info_t *ctx, nl_rule_t *rl)
396 {
397 const uint32_t attr = npf_rule_getattr(rl);
398 const char *rproc, *ifname, *name;
399
400 /* Rule attributes/flags. */
401 for (u_int i = 0; i < __arraycount(attr_keyword_map); i++) {
402 const struct attr_keyword_mapent *ak = &attr_keyword_map[i];
403
404 if (i == NAME_AT && (name = npf_rule_getname(rl)) != NULL) {
405 fprintf(ctx->fp, "\"%s\" ", name);
406 }
407 if ((attr & ak->mask) == ak->flags) {
408 fprintf(ctx->fp, "%s ", ak->val);
409 }
410 }
411 if ((ifname = npf_rule_getinterface(rl)) != NULL) {
412 fprintf(ctx->fp, "on %s ", ifname);
413 }
414 if (attr == (NPF_RULE_GROUP | NPF_RULE_IN | NPF_RULE_OUT) && !ifname) {
415 /* The default group is a special case. */
416 fprintf(ctx->fp, "default ");
417 }
418 if ((attr & NPF_DYNAMIC_GROUP) == NPF_RULE_GROUP) {
419 /* Group; done. */
420 fprintf(ctx->fp, "{ ");
421 goto out;
422 }
423
424 /* Print filter criteria. */
425 npfctl_print_filter(ctx, rl);
426
427 /* Rule procedure. */
428 if ((rproc = npf_rule_getproc(rl)) != NULL) {
429 fprintf(ctx->fp, "apply \"%s\" ", rproc);
430 }
431 out:
432 npfctl_print_id(ctx, rl);
433 fputs("\n", ctx->fp);
434 }
435
436 static void
437 npfctl_print_nat(npf_conf_info_t *ctx, nl_nat_t *nt)
438 {
439 const unsigned dynamic_natset = NPF_RULE_GROUP | NPF_RULE_DYNAMIC;
440 nl_rule_t *rl = (nl_nat_t *)nt;
441 const char *ifname, *algo, *seg1, *seg2, *arrow;
442 const npf_addr_t *addr;
443 npf_netmask_t mask;
444 in_port_t port;
445 size_t alen;
446 unsigned flags;
447 char *seg;
448
449 /* Get flags and the interface. */
450 flags = npf_nat_getflags(nt);
451 ifname = npf_rule_getinterface(rl);
452 assert(ifname != NULL);
453
454 if ((npf_rule_getattr(rl) & dynamic_natset) == dynamic_natset) {
455 const char *name = npf_rule_getname(rl);
456 fprintf(ctx->fp, "map ruleset \"%s\" on %s\n", name, ifname);
457 return;
458 }
459
460 /* Get the translation address or table (and port, if used). */
461 addr = npf_nat_getaddr(nt, &alen, &mask);
462 if (addr) {
463 seg = npfctl_print_addrmask(alen, "%a", addr, mask);
464 } else {
465 const unsigned tid = npf_nat_gettable(nt);
466 const char *tname;
467 bool ifaddr;
468
469 tname = npfctl_table_getname(ctx->conf, tid, &ifaddr);
470 easprintf(&seg, ifaddr ? "ifaddrs(%s)" : "<%s>", tname);
471 }
472
473 if ((port = npf_nat_getport(nt)) != 0) {
474 char *p;
475 easprintf(&p, "%s port %u", seg, ntohs(port));
476 free(seg), seg = p;
477 }
478 seg1 = seg2 = "any";
479
480 /* Get the NAT type and determine the translation segment. */
481 switch (npf_nat_gettype(nt)) {
482 case NPF_NATIN:
483 arrow = "<-";
484 seg1 = seg;
485 break;
486 case NPF_NATOUT:
487 arrow = "->";
488 seg2 = seg;
489 break;
490 default:
491 abort();
492 }
493
494 /* NAT algorithm. */
495 switch (npf_nat_getalgo(nt)) {
496 case NPF_ALGO_NETMAP:
497 algo = "algo netmap ";
498 break;
499 case NPF_ALGO_IPHASH:
500 algo = "algo ip-hash ";
501 break;
502 case NPF_ALGO_RR:
503 algo = "algo round-robin ";
504 break;
505 case NPF_ALGO_NPT66:
506 algo = "algo npt66";
507 break;
508 default:
509 algo = "";
510 break;
511 }
512
513 /* FIXME also handle "any" */
514
515 /* Print out the NAT policy with the filter criteria. */
516 fprintf(ctx->fp, "map %s %s %s%s%s %s %s pass ",
517 ifname, (flags & NPF_NAT_STATIC) ? "static" : "dynamic",
518 algo, (flags & NPF_NAT_PORTS) ? "" : "no-ports ",
519 seg1, arrow, seg2);
520 npfctl_print_filter(ctx, rl);
521 npfctl_print_id(ctx, rl);
522 fputs("\n", ctx->fp);
523 free(seg);
524 }
525
526 static void
527 npfctl_print_table(npf_conf_info_t *ctx, nl_table_t *tl)
528 {
529 const char *name = npf_table_getname(tl);
530 const unsigned type = npf_table_gettype(tl);
531 const char *table_types[] = {
532 [NPF_TABLE_IPSET] = "ipset",
533 [NPF_TABLE_LPM] = "lpm",
534 [NPF_TABLE_CONST] = "const",
535 };
536
537 if (name[0] == '.') {
538 /* Internal tables use dot and are hidden. */
539 return;
540 }
541 assert(type < __arraycount(table_types));
542 fprintf(ctx->fp, "table <%s> type %s\n", name, table_types[type]);
543 }
544
545 int
546 npfctl_config_show(int fd)
547 {
548 npf_conf_info_t *ctx = &stdout_ctx;
549 nl_config_t *ncf;
550 bool loaded;
551
552 if (fd) {
553 ncf = npf_config_retrieve(fd);
554 if (ncf == NULL) {
555 return errno;
556 }
557 loaded = npf_config_loaded_p(ncf);
558 fprintf(ctx->fp, "# filtering:\t%s\n# config:\t%s\n",
559 npf_config_active_p(ncf) ? "active" : "inactive",
560 loaded ? "loaded" : "empty");
561 print_linesep(ctx);
562 } else {
563 ncf = npfctl_config_ref();
564 (void)npf_config_build(ncf);
565 loaded = true;
566 }
567 ctx->conf = ncf;
568
569 if (loaded) {
570 nl_rule_t *rl;
571 nl_rproc_t *rp;
572 nl_nat_t *nt;
573 nl_table_t *tl;
574 nl_iter_t i;
575 unsigned level;
576
577 i = NPF_ITER_BEGIN;
578 while ((tl = npf_table_iterate(ncf, &i)) != NULL) {
579 npfctl_print_table(ctx, tl);
580 }
581 print_linesep(ctx);
582
583 i = NPF_ITER_BEGIN;
584 while ((rp = npf_rproc_iterate(ncf, &i)) != NULL) {
585 const char *rpname = npf_rproc_getname(rp);
586 fprintf(ctx->fp, "procedure \"%s\"\n", rpname);
587 }
588 print_linesep(ctx);
589
590 i = NPF_ITER_BEGIN;
591 while ((nt = npf_nat_iterate(ncf, &i)) != NULL) {
592 npfctl_print_nat(ctx, nt);
593 }
594 print_linesep(ctx);
595
596 i = NPF_ITER_BEGIN;
597 while ((rl = npf_rule_iterate(ncf, &i, &level)) != NULL) {
598 print_indent(ctx, level);
599 npfctl_print_rule(ctx, rl);
600 }
601 print_indent(ctx, 0);
602 }
603 npf_config_destroy(ncf);
604 return 0;
605 }
606
607 int
608 npfctl_ruleset_show(int fd, const char *ruleset_name)
609 {
610 npf_conf_info_t *ctx = &stdout_ctx;
611 nl_config_t *ncf;
612 nl_rule_t *rl;
613 unsigned level;
614 nl_iter_t i;
615 int error;
616
617 ncf = npf_config_create();
618 ctx->conf = ncf;
619
620 if ((error = _npf_ruleset_list(fd, ruleset_name, ncf)) != 0) {
621 return error;
622 }
623 i = NPF_ITER_BEGIN;
624 while ((rl = npf_rule_iterate(ncf, &i, &level)) != NULL) {
625 npfctl_print_rule(ctx, rl);
626 }
627 npf_config_destroy(ncf);
628 return error;
629 }
630