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