xref: /netbsd-src/usr.sbin/npf/npfctl/npf_show.c (revision b7b7574d3bf8eeb51a1fa3977b59142ec6434a55)
1 /*	$NetBSD: npf_show.c,v 1.14 2014/05/31 22:41:37 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.14 2014/05/31 22:41:37 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;
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