npf_show.c revision 1.9 1 /* $NetBSD: npf_show.c,v 1.9 2014/02/07 23:45:22 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.9 2014/02/07 23:45:22 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("inet");
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 NAME_AT 2
222
223 static const struct attr_keyword_mapent {
224 uint32_t mask;
225 uint32_t flags;
226 const char * val;
227 } attr_keyword_map[] = {
228 { F(GROUP)|F(DYNAMIC), F(GROUP), "group" },
229 { F(DYNAMIC), F(DYNAMIC), "ruleset" },
230 { F(GROUP)|F(PASS), 0, "block" },
231 { F(GROUP)|F(PASS), F(PASS), "pass" },
232 { F(RETRST)|F(RETICMP), F(RETRST)|F(RETICMP), "return" },
233 { F(RETRST)|F(RETICMP), F(RETRST), "return-rst" },
234 { F(RETRST)|F(RETICMP), F(RETICMP), "return-icmp" },
235 { F(STATEFUL), F(STATEFUL), "stateful" },
236 { F(DIMASK), F(IN), "in" },
237 { F(DIMASK), F(OUT), "out" },
238 { F(FINAL), F(FINAL), "final" },
239 };
240
241 static const struct mark_keyword_mapent {
242 u_int mark;
243 const char * token;
244 const char * sep;
245 char * (*printfn)(npf_conf_info_t *, const uint32_t *);
246 u_int fwords;
247 } mark_keyword_map[] = {
248 { BM_IPVER, "family %s", NULL, print_family, 1 },
249 { BM_PROTO, "proto %s", NULL, print_proto, 1 },
250 { BM_TCPFL, "flags %s", NULL, print_tcpflags, 2 },
251 { BM_ICMP_TYPE, "icmp-type %s", NULL, print_number, 1 },
252 { BM_ICMP_CODE, "code %s", NULL, print_number, 1 },
253
254 { BM_SRC_CIDR, "from %s", ", ", print_address, 6 },
255 { BM_SRC_TABLE, "from <%s>", NULL, print_table, 1 },
256 { BM_SRC_PORTS, "port %s", ", ", print_portrange,2 },
257
258 { BM_DST_CIDR, "to %s", ", ", print_address, 6 },
259 { BM_DST_TABLE, "to <%s>", NULL, print_table, 1 },
260 { BM_DST_PORTS, "port %s", ", ", print_portrange,2 },
261 };
262
263 static const char * __attribute__((format_arg(2)))
264 verified_fmt(const char *fmt, const char *t __unused)
265 {
266 return fmt;
267 }
268
269 static char *
270 scan_marks(npf_conf_info_t *ctx, const struct mark_keyword_mapent *mk,
271 const uint32_t *marks, size_t mlen)
272 {
273 char buf[2048], *vals[256], *p;
274 size_t nvals = 0;
275
276 /* Scan for the marks and extract the values. */
277 mlen /= sizeof(uint32_t);
278 while (mlen > 2) {
279 const uint32_t m = *marks++;
280 const u_int nwords = *marks++;
281
282 if ((mlen -= 2) < nwords) {
283 errx(EXIT_FAILURE, "byte-code marking inconsistency");
284 }
285 if (m == mk->mark) {
286 /* Value is processed by the print function. */
287 assert(mk->fwords == nwords);
288 vals[nvals++] = mk->printfn(ctx, marks);
289 }
290 marks += nwords;
291 mlen -= nwords;
292 }
293 if (nvals == 0) {
294 return NULL;
295 }
296 assert(nvals == 1 || mk->sep != NULL);
297
298 /*
299 * Join all the values and print. Add curly brackets if there
300 * is more than value and it can be a set.
301 */
302 if (!join(buf, sizeof(buf), nvals, vals, mk->sep ? mk->sep : "")) {
303 errx(EXIT_FAILURE, "out of memory while parsing the rule");
304 }
305 easprintf(&p, nvals > 1 ? "{ %s }" : "%s", buf);
306
307 for (u_int i = 0; i < nvals; i++) {
308 free(vals[i]);
309 }
310 return p;
311 }
312
313 static void
314 npfctl_print_filter(npf_conf_info_t *ctx, nl_rule_t *rl)
315 {
316 const void *marks;
317 size_t mlen;
318
319 /* BPF filter criteria described by the byte-code marks. */
320 marks = npf_rule_getinfo(rl, &mlen);
321 for (u_int i = 0; i < __arraycount(mark_keyword_map); i++) {
322 const struct mark_keyword_mapent *mk = &mark_keyword_map[i];
323 char *val;
324
325 if ((val = scan_marks(ctx, mk, marks, mlen)) != NULL) {
326 fprintf(ctx->fp, verified_fmt(mk->token, "%s"), val);
327 fputs(" ", ctx->fp);
328 free(val);
329 }
330 }
331 if (!mlen) {
332 fputs("all ", ctx->fp);
333 }
334 }
335
336 static void
337 npfctl_print_rule(npf_conf_info_t *ctx, nl_rule_t *rl)
338 {
339 const uint32_t attr = npf_rule_getattr(rl);
340 const char *rproc, *ifname, *name;
341
342 /* Rule attributes/flags. */
343 for (u_int i = 0; i < __arraycount(attr_keyword_map); i++) {
344 const struct attr_keyword_mapent *ak = &attr_keyword_map[i];
345
346 if (i == NAME_AT && (name = npf_rule_getname(rl)) != NULL) {
347 fprintf(ctx->fp, "\"%s\" ", name);
348 }
349 if ((attr & ak->mask) == ak->flags) {
350 fprintf(ctx->fp, "%s ", ak->val);
351 }
352 }
353 if ((ifname = npf_rule_getinterface(rl)) != NULL) {
354 fprintf(ctx->fp, "on %s ", ifname);
355 }
356
357 if ((attr & (NPF_RULE_GROUP | NPF_RULE_DYNAMIC)) == NPF_RULE_GROUP) {
358 /* Group; done. */
359 fputs("\n", ctx->fp);
360 return;
361 }
362
363 /* Print filter criteria. */
364 npfctl_print_filter(ctx, rl);
365
366 /* Rule procedure. */
367 if ((rproc = npf_rule_getproc(rl)) != NULL) {
368 fprintf(ctx->fp, "apply \"%s\"", rproc);
369 }
370 fputs("\n", ctx->fp);
371 }
372
373 static void
374 npfctl_print_nat(npf_conf_info_t *ctx, nl_nat_t *nt)
375 {
376 nl_rule_t *rl = (nl_nat_t *)nt;
377 const char *ifname, *seg1, *seg2, *arrow;
378 npf_addr_t addr;
379 in_port_t port;
380 size_t alen;
381 u_int flags;
382 char *seg;
383
384 /* Get the interface. */
385 ifname = npf_rule_getinterface(rl);
386 assert(ifname != NULL);
387
388 /* Get the translation address (and port, if used). */
389 npf_nat_getmap(nt, &addr, &alen, &port);
390 seg = npfctl_print_addrmask(alen, &addr, NPF_NO_NETMASK);
391 if (port) {
392 char *p;
393 easprintf(&p, "%s port %u", seg, port);
394 free(seg), seg = p;
395 }
396 seg1 = seg2 = "any";
397
398 /* Get the NAT type and determine the translation segment. */
399 switch (npf_nat_gettype(nt)) {
400 case NPF_NATIN:
401 arrow = "<-";
402 seg1 = seg;
403 break;
404 case NPF_NATOUT:
405 arrow = "->";
406 seg2 = seg;
407 break;
408 default:
409 abort();
410 }
411 flags = npf_nat_getflags(nt);
412
413 /* Print out the NAT policy with the filter criteria. */
414 fprintf(ctx->fp, "map %s %s %s %s %s pass ",
415 ifname, (flags & NPF_NAT_STATIC) ? "static" : "dynamic",
416 seg1, arrow, seg2);
417 npfctl_print_filter(ctx, rl);
418 fputs("\n", ctx->fp);
419 free(seg);
420 }
421
422 static void
423 npfctl_print_table(npf_conf_info_t *ctx, nl_table_t *tl)
424 {
425 const char *name = npf_table_getname(tl);
426 const int type = npf_table_gettype(tl);
427
428 if (name[0] == '.') {
429 /* Internal tables use dot and are hidden. */
430 return;
431 }
432
433 fprintf(ctx->fp, "table <%s> type %s\n", name,
434 (type == NPF_TABLE_HASH) ? "hash" :
435 (type == NPF_TABLE_TREE) ? "tree" :
436 "unknown");
437 }
438
439 int
440 npfctl_config_show(int fd)
441 {
442 npf_conf_info_t *ctx = &stdout_ctx;
443 nl_config_t *ncf;
444 bool active, loaded;
445
446 if (fd) {
447 ncf = npf_config_retrieve(fd, &active, &loaded);
448 if (ncf == NULL) {
449 return errno;
450 }
451 fprintf(ctx->fp, "Filtering:\t%s\nConfiguration:\t%s\n",
452 active ? "active" : "inactive",
453 loaded ? "loaded" : "empty");
454 print_linesep(ctx);
455 } else {
456 npfctl_config_send(0, NULL);
457 ncf = npfctl_config_ref();
458 loaded = true;
459 }
460 ctx->conf = ncf;
461
462 if (loaded) {
463 nl_rule_t *rl;
464 nl_rproc_t *rp;
465 nl_nat_t *nt;
466 nl_table_t *tl;
467 u_int level;
468
469 while ((tl = npf_table_iterate(ncf)) != NULL) {
470 npfctl_print_table(ctx, tl);
471 }
472 print_linesep(ctx);
473
474 while ((rp = npf_rproc_iterate(ncf)) != NULL) {
475 const char *rpname = npf_rproc_getname(rp);
476 fprintf(ctx->fp, "procedure \"%s\"\n", rpname);
477 }
478 print_linesep(ctx);
479
480 while ((nt = npf_nat_iterate(ncf)) != NULL) {
481 npfctl_print_nat(ctx, nt);
482 }
483 print_linesep(ctx);
484
485 while ((rl = npf_rule_iterate(ncf, &level)) != NULL) {
486 print_indent(ctx, level);
487 npfctl_print_rule(ctx, rl);
488 }
489 print_linesep(ctx);
490 }
491 npf_config_destroy(ncf);
492 return 0;
493 }
494
495 int
496 npfctl_ruleset_show(int fd, const char *ruleset_name)
497 {
498 npf_conf_info_t *ctx = &stdout_ctx;
499 nl_config_t *ncf;
500 nl_rule_t *rl;
501 u_int level;
502 int error;
503
504 ncf = npf_config_create();
505 ctx->conf = ncf;
506
507 if ((error = _npf_ruleset_list(fd, ruleset_name, ncf)) != 0) {
508 return error;
509 }
510 while ((rl = npf_rule_iterate(ncf, &level)) != NULL) {
511 npfctl_print_rule(ctx, rl);
512 }
513 npf_config_destroy(ncf);
514 return error;
515 }
516