npf_show.c revision 1.27 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.27 2019/01/19 21:19:32 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_ENDS (NPF_RULE_STATEFUL | NPF_RULE_MULTIENDS)
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_ENDS, F(STATEFUL), "stateful" },
265 { STATEFUL_ENDS, STATEFUL_ENDS, "stateful-ends" },
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 nl_rule_t *rl = (nl_nat_t *)nt;
440 const char *ifname, *algo, *seg1, *seg2, *arrow;
441 const npf_addr_t *addr;
442 npf_netmask_t mask;
443 in_port_t port;
444 size_t alen;
445 u_int flags;
446 char *seg;
447
448 /* Get the interface. */
449 ifname = npf_rule_getinterface(rl);
450 assert(ifname != NULL);
451
452 /* Get the translation address or table (and port, if used). */
453 addr = npf_nat_getaddr(nt, &alen, &mask);
454 if (addr) {
455 seg = npfctl_print_addrmask(alen, "%a", addr, mask);
456 } else {
457 const unsigned tid = npf_nat_gettable(nt);
458 const char *tname;
459 bool ifaddr;
460
461 tname = npfctl_table_getname(ctx->conf, tid, &ifaddr);
462 easprintf(&seg, ifaddr ? "ifaddrs(%s)" : "<%s>", tname);
463 }
464
465 if ((port = npf_nat_getport(nt)) != 0) {
466 char *p;
467 easprintf(&p, "%s port %u", seg, ntohs(port));
468 free(seg), seg = p;
469 }
470 seg1 = seg2 = "any";
471
472 /* Get the NAT type and determine the translation segment. */
473 switch (npf_nat_gettype(nt)) {
474 case NPF_NATIN:
475 arrow = "<-";
476 seg1 = seg;
477 break;
478 case NPF_NATOUT:
479 arrow = "->";
480 seg2 = seg;
481 break;
482 default:
483 abort();
484 }
485 flags = npf_nat_getflags(nt);
486
487 /* NAT algorithm. */
488 switch (npf_nat_getalgo(nt)) {
489 case NPF_ALGO_NETMAP:
490 algo = "algo netmap ";
491 break;
492 case NPF_ALGO_IPHASH:
493 algo = "algo ip-hash ";
494 break;
495 case NPF_ALGO_RR:
496 algo = "algo round-robin ";
497 break;
498 case NPF_ALGO_NPT66:
499 algo = "algo npt66";
500 break;
501 default:
502 algo = "";
503 break;
504 }
505
506 /* FIXME also handle "any" */
507
508 /* Print out the NAT policy with the filter criteria. */
509 fprintf(ctx->fp, "map %s %s %s%s%s %s %s pass ",
510 ifname, (flags & NPF_NAT_STATIC) ? "static" : "dynamic",
511 algo, (flags & NPF_NAT_PORTS) ? "" : "no-ports ",
512 seg1, arrow, seg2);
513 npfctl_print_filter(ctx, rl);
514 npfctl_print_id(ctx, rl);
515 fputs("\n", ctx->fp);
516 free(seg);
517 }
518
519 static void
520 npfctl_print_table(npf_conf_info_t *ctx, nl_table_t *tl)
521 {
522 const char *name = npf_table_getname(tl);
523 const unsigned type = npf_table_gettype(tl);
524 const char *table_types[] = {
525 [NPF_TABLE_IPSET] = "ipset",
526 [NPF_TABLE_LPM] = "lpm",
527 [NPF_TABLE_CONST] = "const",
528 };
529
530 if (name[0] == '.') {
531 /* Internal tables use dot and are hidden. */
532 return;
533 }
534 assert(type < __arraycount(table_types));
535 fprintf(ctx->fp, "table <%s> type %s\n", name, table_types[type]);
536 }
537
538 int
539 npfctl_config_show(int fd)
540 {
541 npf_conf_info_t *ctx = &stdout_ctx;
542 nl_config_t *ncf;
543 bool loaded;
544
545 if (fd) {
546 ncf = npf_config_retrieve(fd);
547 if (ncf == NULL) {
548 return errno;
549 }
550 loaded = npf_config_loaded_p(ncf);
551 fprintf(ctx->fp, "# filtering:\t%s\n# config:\t%s\n",
552 npf_config_active_p(ncf) ? "active" : "inactive",
553 loaded ? "loaded" : "empty");
554 print_linesep(ctx);
555 } else {
556 ncf = npfctl_config_ref();
557 (void)npf_config_build(ncf);
558 loaded = true;
559 }
560 ctx->conf = ncf;
561
562 if (loaded) {
563 nl_rule_t *rl;
564 nl_rproc_t *rp;
565 nl_nat_t *nt;
566 nl_table_t *tl;
567 unsigned level;
568
569 while ((tl = npf_table_iterate(ncf)) != NULL) {
570 npfctl_print_table(ctx, tl);
571 }
572 print_linesep(ctx);
573
574 while ((rp = npf_rproc_iterate(ncf)) != NULL) {
575 const char *rpname = npf_rproc_getname(rp);
576 fprintf(ctx->fp, "procedure \"%s\"\n", rpname);
577 }
578 print_linesep(ctx);
579
580 while ((nt = npf_nat_iterate(ncf)) != NULL) {
581 npfctl_print_nat(ctx, nt);
582 }
583 print_linesep(ctx);
584
585 while ((rl = npf_rule_iterate(ncf, &level)) != NULL) {
586 print_indent(ctx, level);
587 npfctl_print_rule(ctx, rl);
588 }
589 print_indent(ctx, 0);
590 }
591 npf_config_destroy(ncf);
592 return 0;
593 }
594
595 int
596 npfctl_ruleset_show(int fd, const char *ruleset_name)
597 {
598 npf_conf_info_t *ctx = &stdout_ctx;
599 nl_config_t *ncf;
600 nl_rule_t *rl;
601 unsigned level;
602 int error;
603
604 ncf = npf_config_create();
605 ctx->conf = ncf;
606
607 if ((error = _npf_ruleset_list(fd, ruleset_name, ncf)) != 0) {
608 return error;
609 }
610 while ((rl = npf_rule_iterate(ncf, &level)) != NULL) {
611 npfctl_print_rule(ctx, rl);
612 }
613 npf_config_destroy(ncf);
614 return error;
615 }
616