npf_show.c revision 1.13 1 /* $NetBSD: npf_show.c,v 1.13 2014/03/14 11:29:45 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.13 2014/03/14 11:29:45 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", NULL, 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;
320
321 /* BPF filter criteria described by the byte-code marks. */
322 marks = npf_rule_getinfo(rl, &mlen);
323 for (u_int i = 0; i < __arraycount(mark_keyword_map); i++) {
324 const struct mark_keyword_mapent *mk = &mark_keyword_map[i];
325 char *val;
326
327 if ((val = scan_marks(ctx, mk, marks, mlen)) != NULL) {
328 fprintf(ctx->fp, verified_fmt(mk->token, "%s"), val);
329 fputs(" ", ctx->fp);
330 free(val);
331 }
332 }
333 if (!mlen) {
334 fputs("all ", ctx->fp);
335 }
336 }
337
338 static void
339 npfctl_print_rule(npf_conf_info_t *ctx, nl_rule_t *rl)
340 {
341 const uint32_t attr = npf_rule_getattr(rl);
342 const char *rproc, *ifname, *name;
343
344 /* Rule attributes/flags. */
345 for (u_int i = 0; i < __arraycount(attr_keyword_map); i++) {
346 const struct attr_keyword_mapent *ak = &attr_keyword_map[i];
347
348 if (i == NAME_AT && (name = npf_rule_getname(rl)) != NULL) {
349 fprintf(ctx->fp, "\"%s\" ", name);
350 }
351 if ((attr & ak->mask) == ak->flags) {
352 fprintf(ctx->fp, "%s ", ak->val);
353 }
354 }
355 if ((ifname = npf_rule_getinterface(rl)) != NULL) {
356 fprintf(ctx->fp, "on %s ", ifname);
357 }
358
359 if ((attr & (NPF_RULE_GROUP | NPF_RULE_DYNAMIC)) == NPF_RULE_GROUP) {
360 /* Group; done. */
361 fputs("\n", ctx->fp);
362 return;
363 }
364
365 /* Print filter criteria. */
366 npfctl_print_filter(ctx, rl);
367
368 /* Rule procedure. */
369 if ((rproc = npf_rule_getproc(rl)) != NULL) {
370 fprintf(ctx->fp, "apply \"%s\"", rproc);
371 }
372 fputs("\n", ctx->fp);
373 }
374
375 static void
376 npfctl_print_nat(npf_conf_info_t *ctx, nl_nat_t *nt)
377 {
378 nl_rule_t *rl = (nl_nat_t *)nt;
379 const char *ifname, *seg1, *seg2, *arrow;
380 npf_addr_t addr;
381 in_port_t port;
382 size_t alen;
383 u_int flags;
384 char *seg;
385
386 /* Get the interface. */
387 ifname = npf_rule_getinterface(rl);
388 assert(ifname != NULL);
389
390 /* Get the translation address (and port, if used). */
391 npf_nat_getmap(nt, &addr, &alen, &port);
392 seg = npfctl_print_addrmask(alen, &addr, NPF_NO_NETMASK);
393 if (port) {
394 char *p;
395 easprintf(&p, "%s port %u", seg, ntohs(port));
396 free(seg), seg = p;
397 }
398 seg1 = seg2 = "any";
399
400 /* Get the NAT type and determine the translation segment. */
401 switch (npf_nat_gettype(nt)) {
402 case NPF_NATIN:
403 arrow = "<-";
404 seg1 = seg;
405 break;
406 case NPF_NATOUT:
407 arrow = "->";
408 seg2 = seg;
409 break;
410 default:
411 abort();
412 }
413 flags = npf_nat_getflags(nt);
414
415 /* Print out the NAT policy with the filter criteria. */
416 fprintf(ctx->fp, "map %s %s %s %s %s pass ",
417 ifname, (flags & NPF_NAT_STATIC) ? "static" : "dynamic",
418 seg1, arrow, seg2);
419 npfctl_print_filter(ctx, rl);
420 fputs("\n", ctx->fp);
421 free(seg);
422 }
423
424 static void
425 npfctl_print_table(npf_conf_info_t *ctx, nl_table_t *tl)
426 {
427 const char *name = npf_table_getname(tl);
428 const unsigned type = npf_table_gettype(tl);
429 const char *table_types[] = {
430 [NPF_TABLE_HASH] = "hash",
431 [NPF_TABLE_TREE] = "tree",
432 [NPF_TABLE_CDB] = "cdb",
433 };
434
435 if (name[0] == '.') {
436 /* Internal tables use dot and are hidden. */
437 return;
438 }
439 assert(type < __arraycount(table_types));
440 fprintf(ctx->fp, "table <%s> type %s\n", name, table_types[type]);
441 }
442
443 int
444 npfctl_config_show(int fd)
445 {
446 npf_conf_info_t *ctx = &stdout_ctx;
447 nl_config_t *ncf;
448 bool active, loaded;
449
450 if (fd) {
451 ncf = npf_config_retrieve(fd, &active, &loaded);
452 if (ncf == NULL) {
453 return errno;
454 }
455 fprintf(ctx->fp, "Filtering:\t%s\nConfiguration:\t%s\n",
456 active ? "active" : "inactive",
457 loaded ? "loaded" : "empty");
458 print_linesep(ctx);
459 } else {
460 npfctl_config_send(0, NULL);
461 ncf = npfctl_config_ref();
462 loaded = true;
463 }
464 ctx->conf = ncf;
465
466 if (loaded) {
467 nl_rule_t *rl;
468 nl_rproc_t *rp;
469 nl_nat_t *nt;
470 nl_table_t *tl;
471 u_int level;
472
473 while ((tl = npf_table_iterate(ncf)) != NULL) {
474 npfctl_print_table(ctx, tl);
475 }
476 print_linesep(ctx);
477
478 while ((rp = npf_rproc_iterate(ncf)) != NULL) {
479 const char *rpname = npf_rproc_getname(rp);
480 fprintf(ctx->fp, "procedure \"%s\"\n", rpname);
481 }
482 print_linesep(ctx);
483
484 while ((nt = npf_nat_iterate(ncf)) != NULL) {
485 npfctl_print_nat(ctx, nt);
486 }
487 print_linesep(ctx);
488
489 while ((rl = npf_rule_iterate(ncf, &level)) != NULL) {
490 print_indent(ctx, level);
491 npfctl_print_rule(ctx, rl);
492 }
493 print_linesep(ctx);
494 }
495 npf_config_destroy(ncf);
496 return 0;
497 }
498
499 int
500 npfctl_ruleset_show(int fd, const char *ruleset_name)
501 {
502 npf_conf_info_t *ctx = &stdout_ctx;
503 nl_config_t *ncf;
504 nl_rule_t *rl;
505 u_int level;
506 int error;
507
508 ncf = npf_config_create();
509 ctx->conf = ncf;
510
511 if ((error = _npf_ruleset_list(fd, ruleset_name, ncf)) != 0) {
512 return error;
513 }
514 while ((rl = npf_rule_iterate(ncf, &level)) != NULL) {
515 npfctl_print_rule(ctx, rl);
516 }
517 npf_config_destroy(ncf);
518 return error;
519 }
520