npf_show.c revision 1.26 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.26 2018/09/29 14:41:36 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 } npf_conf_info_t;
65
66 static npf_conf_info_t stdout_ctx;
67
68 static void print_indent(npf_conf_info_t *, u_int);
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 }
78
79 /*
80 * Helper routines to print various pieces of information.
81 */
82
83 static void
84 print_indent(npf_conf_info_t *ctx, u_int level)
85 {
86 if (level == 0) { /* XXX */
87 print_linesep(ctx);
88 }
89 while (level--)
90 fprintf(ctx->fp, "\t");
91 }
92
93 static void
94 print_linesep(npf_conf_info_t *ctx)
95 {
96 if (ftell(ctx->fp) != ctx->fpos) {
97 fputs("\n", ctx->fp);
98 ctx->fpos = ftell(ctx->fp);
99 }
100 }
101
102 static size_t
103 tcpflags2string(char *buf, u_int tfl)
104 {
105 u_int i = 0;
106
107 if (tfl & TH_FIN) buf[i++] = 'F';
108 if (tfl & TH_SYN) buf[i++] = 'S';
109 if (tfl & TH_RST) buf[i++] = 'R';
110 if (tfl & TH_PUSH) buf[i++] = 'P';
111 if (tfl & TH_ACK) buf[i++] = 'A';
112 if (tfl & TH_URG) buf[i++] = 'U';
113 if (tfl & TH_ECE) buf[i++] = 'E';
114 if (tfl & TH_CWR) buf[i++] = 'C';
115 buf[i] = '\0';
116 return i;
117 }
118
119 static char *
120 print_family(npf_conf_info_t *ctx __unused, const uint32_t *words)
121 {
122 const int af = words[0];
123
124 switch (af) {
125 case AF_INET:
126 return estrdup("inet4");
127 case AF_INET6:
128 return estrdup("inet6");
129 default:
130 errx(EXIT_FAILURE, "invalid byte-code mark (family)");
131 }
132 return NULL;
133 }
134
135 static char *
136 print_address(npf_conf_info_t *ctx __unused, const uint32_t *words)
137 {
138 const int af = *words++;
139 const u_int mask = *words++;
140 const npf_addr_t *addr;
141 int alen = 0;
142
143 switch (af) {
144 case AF_INET:
145 alen = 4;
146 break;
147 case AF_INET6:
148 alen = 16;
149 break;
150 default:
151 errx(EXIT_FAILURE, "invalid byte-code mark (address)");
152 }
153 addr = (const npf_addr_t *)words;
154 return npfctl_print_addrmask(alen, "%a", addr, mask);
155 }
156
157 static char *
158 print_number(npf_conf_info_t *ctx __unused, const uint32_t *words)
159 {
160 char *p;
161 easprintf(&p, "%u", words[0]);
162 return p;
163 }
164
165 static char *
166 print_table(npf_conf_info_t *ctx, const uint32_t *words)
167 {
168 unsigned tid = words[0];
169 nl_table_t *tl;
170 char *p = NULL;
171
172 /* XXX: Iterating all as we need to rewind for the next call. */
173 while ((tl = npf_table_iterate(ctx->conf)) != NULL) {
174 if (!p && npf_table_getid(tl) == tid) {
175 easprintf(&p, "%s", npf_table_getname(tl));
176 }
177 }
178 assert(p != NULL);
179 return p;
180 }
181
182 static char *
183 print_proto(npf_conf_info_t *ctx, const uint32_t *words)
184 {
185 switch (words[0]) {
186 case IPPROTO_TCP:
187 return estrdup("tcp");
188 case IPPROTO_UDP:
189 return estrdup("udp");
190 case IPPROTO_ICMP:
191 return estrdup("icmp");
192 case IPPROTO_ICMPV6:
193 return estrdup("ipv6-icmp");
194 }
195 return print_number(ctx, words);
196 }
197
198 static char *
199 print_tcpflags(npf_conf_info_t *ctx __unused, const uint32_t *words)
200 {
201 const u_int tf = words[0], tf_mask = words[1];
202 char buf[16];
203
204 size_t n = tcpflags2string(buf, tf);
205 if (tf != tf_mask) {
206 buf[n++] = '/';
207 tcpflags2string(buf + n, tf_mask);
208 }
209 return estrdup(buf);
210 }
211
212 static char *
213 print_portrange(npf_conf_info_t *ctx, const uint32_t *words)
214 {
215 u_int fport = words[0], tport = words[1];
216 const char *any_str = "";
217 char *p;
218
219 if (ctx->curmark == BM_SRC_PORTS && (ctx->flags & SEEN_SRC) == 0)
220 any_str = "from any ";
221 if (ctx->curmark == BM_DST_PORTS && (ctx->flags & SEEN_DST) == 0)
222 any_str = "to any ";
223
224 if (fport != tport) {
225 easprintf(&p, "%sport %u:%u", any_str, fport, tport);
226 } else {
227 easprintf(&p, "%sport %u", any_str, fport);
228 }
229 return p;
230 }
231
232 /*
233 * The main keyword mapping tables defining the syntax:
234 * - Mapping of rule attributes (flags) to the keywords.
235 * - Mapping of the byte-code marks to the keywords.
236 */
237
238 #define F(name) __CONCAT(NPF_RULE_, name)
239 #define STATEFUL_ENDS (NPF_RULE_STATEFUL | NPF_RULE_MULTIENDS)
240 #define NAME_AT 2
241
242 static const struct attr_keyword_mapent {
243 uint32_t mask;
244 uint32_t flags;
245 const char * val;
246 } attr_keyword_map[] = {
247 { F(GROUP)|F(DYNAMIC), F(GROUP), "group" },
248 { F(DYNAMIC), F(DYNAMIC), "ruleset" },
249 { F(GROUP)|F(PASS), 0, "block" },
250 { F(GROUP)|F(PASS), F(PASS), "pass" },
251 { F(RETRST)|F(RETICMP), F(RETRST)|F(RETICMP), "return" },
252 { F(RETRST)|F(RETICMP), F(RETRST), "return-rst" },
253 { F(RETRST)|F(RETICMP), F(RETICMP), "return-icmp" },
254 { STATEFUL_ENDS, F(STATEFUL), "stateful" },
255 { STATEFUL_ENDS, STATEFUL_ENDS, "stateful-ends" },
256 { F(DIMASK), F(IN), "in" },
257 { F(DIMASK), F(OUT), "out" },
258 { F(FINAL), F(FINAL), "final" },
259 };
260
261 static const struct mark_keyword_mapent {
262 u_int mark;
263 const char * token;
264 const char * sep;
265 u_int set_flags;
266 char * (*printfn)(npf_conf_info_t *, const uint32_t *);
267 u_int fwords;
268 } mark_keyword_map[] = {
269 { BM_IPVER, "family %s", NULL, 0, print_family, 1 },
270 { BM_PROTO, "proto %s", ", ", 0, print_proto, 1 },
271 { BM_TCPFL, "flags %s", NULL, 0, print_tcpflags, 2 },
272 { BM_ICMP_TYPE, "icmp-type %s", NULL, 0, print_number, 1 },
273 { BM_ICMP_CODE, "code %s", NULL, 0, print_number, 1 },
274
275 { BM_SRC_CIDR, "from %s", ", ", SEEN_SRC, print_address, 6 },
276 { BM_SRC_TABLE, "from <%s>", NULL, SEEN_SRC, print_table, 1 },
277 { BM_SRC_PORTS, "%s", ", ", 0, print_portrange,2 },
278
279 { BM_DST_CIDR, "to %s", ", ", SEEN_DST, print_address, 6 },
280 { BM_DST_TABLE, "to <%s>", NULL, SEEN_DST, print_table, 1 },
281 { BM_DST_PORTS, "%s", ", ", 0, print_portrange,2 },
282 };
283
284 static const char * __attribute__((format_arg(2)))
285 verified_fmt(const char *fmt, const char *t __unused)
286 {
287 return fmt;
288 }
289
290 static char *
291 scan_marks(npf_conf_info_t *ctx, const struct mark_keyword_mapent *mk,
292 const uint32_t *marks, size_t mlen)
293 {
294 char buf[2048], *vals[256], *p;
295 size_t nvals = 0;
296
297 /* Scan for the marks and extract the values. */
298 mlen /= sizeof(uint32_t);
299 while (mlen > 2) {
300 const uint32_t m = *marks++;
301 const u_int nwords = *marks++;
302
303 if ((mlen -= 2) < nwords) {
304 errx(EXIT_FAILURE, "byte-code marking inconsistency");
305 }
306 if (m == mk->mark) {
307 /* Set the current mark and the flags. */
308 ctx->flags |= mk->set_flags;
309 ctx->curmark = m;
310
311 /* Value is processed by the print function. */
312 assert(mk->fwords == nwords);
313 vals[nvals++] = mk->printfn(ctx, marks);
314 }
315 marks += nwords;
316 mlen -= nwords;
317 }
318 if (nvals == 0) {
319 return NULL;
320 }
321 assert(nvals == 1 || mk->sep != NULL);
322
323 /*
324 * Join all the values and print. Add curly brackets if there
325 * is more than value and it can be a set.
326 */
327 if (!join(buf, sizeof(buf), nvals, vals, mk->sep ? mk->sep : "")) {
328 errx(EXIT_FAILURE, "out of memory while parsing the rule");
329 }
330 easprintf(&p, nvals > 1 ? "{ %s }" : "%s", buf);
331
332 for (u_int i = 0; i < nvals; i++) {
333 free(vals[i]);
334 }
335 return p;
336 }
337
338 static void
339 npfctl_print_id(npf_conf_info_t *ctx, nl_rule_t *rl)
340 {
341 uint64_t id = id = npf_rule_getid(rl);
342 fprintf(ctx->fp, "# id=\"%" PRIx64 "\" ", id);
343 }
344
345 static void
346 npfctl_print_filter(npf_conf_info_t *ctx, nl_rule_t *rl)
347 {
348 const void *marks;
349 size_t mlen, len;
350 const void *code;
351 int type;
352
353 marks = npf_rule_getinfo(rl, &mlen);
354 if (!marks && (code = npf_rule_getcode(rl, &type, &len)) != NULL) {
355 /*
356 * No marks, but the byte-code is present. This must
357 * have been filled by libpcap(3) or possibly an unknown
358 * to us byte-code.
359 */
360 fprintf(ctx->fp, "%s ", type == NPF_CODE_BPF ?
361 "pcap-filter \"...\"" : "unrecognized-bytecode");
362 return;
363 }
364 ctx->flags = 0;
365
366 /*
367 * BPF filter criteria described by the byte-code marks.
368 */
369 for (u_int i = 0; i < __arraycount(mark_keyword_map); i++) {
370 const struct mark_keyword_mapent *mk = &mark_keyword_map[i];
371 char *val;
372
373 if ((val = scan_marks(ctx, mk, marks, mlen)) != NULL) {
374 fprintf(ctx->fp, verified_fmt(mk->token, "%s"), val);
375 fputs(" ", ctx->fp);
376 free(val);
377 }
378 }
379 if (!mlen) {
380 fputs("all ", ctx->fp);
381 }
382 }
383
384 static void
385 npfctl_print_rule(npf_conf_info_t *ctx, nl_rule_t *rl)
386 {
387 const uint32_t attr = npf_rule_getattr(rl);
388 const char *rproc, *ifname, *name;
389
390 /* Rule attributes/flags. */
391 for (u_int i = 0; i < __arraycount(attr_keyword_map); i++) {
392 const struct attr_keyword_mapent *ak = &attr_keyword_map[i];
393
394 if (i == NAME_AT && (name = npf_rule_getname(rl)) != NULL) {
395 fprintf(ctx->fp, "\"%s\" ", name);
396 }
397 if ((attr & ak->mask) == ak->flags) {
398 fprintf(ctx->fp, "%s ", ak->val);
399 }
400 }
401 if ((ifname = npf_rule_getinterface(rl)) != NULL) {
402 fprintf(ctx->fp, "on %s ", ifname);
403 }
404
405 if ((attr & NPF_DYNAMIC_GROUP) == NPF_RULE_GROUP) {
406 /* Group; done. */
407 goto out;
408 }
409
410 /* Print filter criteria. */
411 npfctl_print_filter(ctx, rl);
412
413 /* Rule procedure. */
414 if ((rproc = npf_rule_getproc(rl)) != NULL) {
415 fprintf(ctx->fp, "apply \"%s\" ", rproc);
416 }
417
418 out:
419 npfctl_print_id(ctx, rl);
420 fputs("\n", ctx->fp);
421 }
422
423 static void
424 npfctl_print_nat(npf_conf_info_t *ctx, nl_nat_t *nt)
425 {
426 nl_rule_t *rl = (nl_nat_t *)nt;
427 const char *ifname, *seg1, *seg2, *arrow;
428 npf_addr_t addr;
429 in_port_t port;
430 size_t alen;
431 u_int flags;
432 char *seg;
433
434 /* Get the interface. */
435 ifname = npf_rule_getinterface(rl);
436 assert(ifname != NULL);
437
438 /* Get the translation address (and port, if used). */
439 npf_nat_getmap(nt, &addr, &alen, &port);
440 seg = npfctl_print_addrmask(alen, "%a", &addr, NPF_NO_NETMASK);
441 if (port) {
442 char *p;
443 easprintf(&p, "%s port %u", seg, ntohs(port));
444 free(seg), seg = p;
445 }
446 seg1 = seg2 = "any";
447
448 /* Get the NAT type and determine the translation segment. */
449 switch (npf_nat_gettype(nt)) {
450 case NPF_NATIN:
451 arrow = "<-";
452 seg1 = seg;
453 break;
454 case NPF_NATOUT:
455 arrow = "->";
456 seg2 = seg;
457 break;
458 default:
459 abort();
460 }
461 flags = npf_nat_getflags(nt);
462
463 /* Print out the NAT policy with the filter criteria. */
464 fprintf(ctx->fp, "map %s %s %s%s%s %s %s pass ",
465 ifname, (flags & NPF_NAT_STATIC) ? "static" : "dynamic",
466 "" /* XXX algo, */,
467 (flags & NPF_NAT_PORTS) ? "" : "no-ports ",
468 seg1, arrow, seg2);
469 npfctl_print_filter(ctx, rl);
470 npfctl_print_id(ctx, rl);
471 fputs("\n", ctx->fp);
472 free(seg);
473 }
474
475 static void
476 npfctl_print_table(npf_conf_info_t *ctx, nl_table_t *tl)
477 {
478 const char *name = npf_table_getname(tl);
479 const unsigned type = npf_table_gettype(tl);
480 const char *table_types[] = {
481 [NPF_TABLE_HASH] = "hash",
482 [NPF_TABLE_TREE] = "tree",
483 [NPF_TABLE_CDB] = "cdb",
484 };
485
486 if (name[0] == '.') {
487 /* Internal tables use dot and are hidden. */
488 return;
489 }
490 assert(type < __arraycount(table_types));
491 fprintf(ctx->fp, "table <%s> type %s\n", name, table_types[type]);
492 }
493
494 int
495 npfctl_config_show(int fd)
496 {
497 npf_conf_info_t *ctx = &stdout_ctx;
498 nl_config_t *ncf;
499 bool loaded;
500
501 if (fd) {
502 ncf = npf_config_retrieve(fd);
503 if (ncf == NULL) {
504 return errno;
505 }
506 loaded = npf_config_loaded_p(ncf);
507 fprintf(ctx->fp, "# filtering:\t%s\n# config:\t%s\n",
508 npf_config_active_p(ncf) ? "active" : "inactive",
509 loaded ? "loaded" : "empty");
510 print_linesep(ctx);
511 } else {
512 ncf = npfctl_config_ref();
513 (void)npf_config_build(ncf);
514 loaded = true;
515 }
516 ctx->conf = ncf;
517
518 if (loaded) {
519 nl_rule_t *rl;
520 nl_rproc_t *rp;
521 nl_nat_t *nt;
522 nl_table_t *tl;
523 u_int level;
524
525 while ((tl = npf_table_iterate(ncf)) != NULL) {
526 npfctl_print_table(ctx, tl);
527 }
528 print_linesep(ctx);
529
530 while ((rp = npf_rproc_iterate(ncf)) != NULL) {
531 const char *rpname = npf_rproc_getname(rp);
532 fprintf(ctx->fp, "procedure \"%s\"\n", rpname);
533 }
534 print_linesep(ctx);
535
536 while ((nt = npf_nat_iterate(ncf)) != NULL) {
537 npfctl_print_nat(ctx, nt);
538 }
539 print_linesep(ctx);
540
541 while ((rl = npf_rule_iterate(ncf, &level)) != NULL) {
542 print_indent(ctx, level);
543 npfctl_print_rule(ctx, rl);
544 }
545 print_linesep(ctx);
546 }
547 npf_config_destroy(ncf);
548 return 0;
549 }
550
551 int
552 npfctl_ruleset_show(int fd, const char *ruleset_name)
553 {
554 npf_conf_info_t *ctx = &stdout_ctx;
555 nl_config_t *ncf;
556 nl_rule_t *rl;
557 u_int level;
558 int error;
559
560 ncf = npf_config_create();
561 ctx->conf = ncf;
562
563 if ((error = _npf_ruleset_list(fd, ruleset_name, ncf)) != 0) {
564 return error;
565 }
566 while ((rl = npf_rule_iterate(ncf, &level)) != NULL) {
567 npfctl_print_rule(ctx, rl);
568 }
569 npf_config_destroy(ncf);
570 return error;
571 }
572