xref: /netbsd-src/usr.sbin/npf/npfctl/npf_show.c (revision bfcefd56cfc74946ba0f251734a8426a0b91e115)
1 /*	$NetBSD: npf_show.c,v 1.2 2013/09/20 03:03:52 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.2 2013/09/20 03:03:52 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 	FILE *		fp;
58 	long		fpos;
59 } npf_conf_info_t;
60 
61 static npf_conf_info_t	stdout_ctx = { .fp = stdout, .fpos = 0 };
62 
63 static void	print_indent(npf_conf_info_t *, u_int);
64 static void	print_linesep(npf_conf_info_t *);
65 
66 /*
67  * Helper routines to print various pieces of information.
68  */
69 
70 static void
71 print_indent(npf_conf_info_t *ctx, u_int level)
72 {
73 	if (level == 0) { /* XXX */
74 		print_linesep(ctx);
75 	}
76 	while (level--)
77 		fprintf(ctx->fp, "\t");
78 }
79 
80 static void
81 print_linesep(npf_conf_info_t *ctx)
82 {
83 	if (ftell(ctx->fp) != ctx->fpos) {
84 		fputs("\n", ctx->fp);
85 		ctx->fpos = ftell(ctx->fp);
86 	}
87 }
88 
89 static size_t
90 tcpflags2string(char *buf, u_int tfl)
91 {
92 	u_int i = 0;
93 
94 	if (tfl & TH_FIN)	buf[i++] = 'F';
95 	if (tfl & TH_SYN)	buf[i++] = 'S';
96 	if (tfl & TH_RST)	buf[i++] = 'R';
97 	if (tfl & TH_PUSH)	buf[i++] = 'P';
98 	if (tfl & TH_ACK)	buf[i++] = 'A';
99 	if (tfl & TH_URG)	buf[i++] = 'U';
100 	if (tfl & TH_ECE)	buf[i++] = 'E';
101 	if (tfl & TH_CWR)	buf[i++] = 'C';
102 	buf[i] = '\0';
103 	return i;
104 }
105 
106 static char *
107 print_family(const uint32_t *words)
108 {
109 	const int af = words[0];
110 
111 	switch (af) {
112 	case AF_INET:
113 		return estrdup("inet");
114 	case AF_INET6:
115 		return estrdup("inet6");
116 	default:
117 		errx(EXIT_FAILURE, "invalid byte-code mark (family)");
118 	}
119 	return NULL;
120 }
121 
122 static char *
123 print_address(const uint32_t *words)
124 {
125 	const int af = *words++;
126 	const u_int mask = *words++;
127 	const npf_addr_t *addr;
128 	int alen = 0;
129 
130 	switch (af) {
131 	case AF_INET:
132 		alen = 4;
133 		break;
134 	case AF_INET6:
135 		alen = 16;
136 		break;
137 	default:
138 		errx(EXIT_FAILURE, "invalid byte-code mark (address)");
139 	}
140 	addr = (const npf_addr_t *)words;
141 	return npfctl_print_addrmask(alen, addr, mask);
142 }
143 
144 static char *
145 print_number(const uint32_t *words)
146 {
147 	char *p;
148 	easprintf(&p, "%u", words[0]);
149 	return p;
150 }
151 
152 static char *
153 print_proto(const uint32_t *words)
154 {
155 	switch (words[0]) {
156 	case IPPROTO_TCP:
157 		return estrdup("tcp");
158 	case IPPROTO_UDP:
159 		return estrdup("udp");
160 	case IPPROTO_ICMP:
161 		return estrdup("icmp");
162 	case IPPROTO_ICMPV6:
163 		return estrdup("ipv6-icmp");
164 	}
165 	return print_number(words);
166 }
167 
168 static char *
169 print_tcpflags(const uint32_t *words)
170 {
171 	const u_int tf = words[0], tf_mask = words[1];
172 	char buf[16];
173 
174 	size_t n = tcpflags2string(buf, tf);
175 	if (tf != tf_mask) {
176 		buf[n++] = '/';
177 		tcpflags2string(buf + n, tf_mask);
178 	}
179 	return estrdup(buf);
180 }
181 
182 static char *
183 print_portrange(const uint32_t *words)
184 {
185 	u_int fport = words[0], tport = words[1];
186 	char *p;
187 
188 	if (fport != tport) {
189 		easprintf(&p, "%u:%u", fport, tport);
190 	} else {
191 		easprintf(&p, "%u", fport);
192 	}
193 	return p;
194 }
195 
196 /*
197  * The main keyword mapping tables defining the syntax:
198  * - Mapping of rule attributes (flags) to the keywords.
199  * - Mapping of the byte-code marks to the keywords.
200  */
201 
202 #define	F(name)		__CONCAT(NPF_RULE_, name)
203 #define	NAME_AT		2
204 
205 static const struct attr_keyword_mapent {
206 	uint32_t	mask;
207 	uint32_t	flags;
208 	const char *	val;
209 } attr_keyword_map[] = {
210 	{ F(GROUP)|F(DYNAMIC),	F(GROUP),		"group"		},
211 	{ F(DYNAMIC),		F(DYNAMIC),		"ruleset"	},
212 	{ F(GROUP)|F(PASS),	0,			"block"		},
213 	{ F(GROUP)|F(PASS),	F(PASS),		"pass"		},
214 	{ F(RETRST)|F(RETICMP),	F(RETRST)|F(RETICMP),	"return"	},
215 	{ F(RETRST)|F(RETICMP),	F(RETRST),		"return-rst"	},
216 	{ F(RETRST)|F(RETICMP),	F(RETICMP),		"return-icmp"	},
217 	{ F(STATEFUL),		F(STATEFUL),		"stateful"	},
218 	{ F(DIMASK),		F(IN),			"in"		},
219 	{ F(DIMASK),		F(OUT),			"out"		},
220 	{ F(FINAL),		F(FINAL),		"final"		},
221 };
222 
223 static const struct mark_keyword_mapent {
224 	u_int		mark;
225 	const char *	token;
226 	const char *	sep;
227 	char *		(*printfn)(const uint32_t *);
228 	u_int		fwords;
229 } mark_keyword_map[] = {
230 	{ BM_IPVER,	"family %s",	NULL,		print_family,	1 },
231 	{ BM_PROTO,	"proto %s",	NULL,		print_proto,	1 },
232 	{ BM_TCPFL,	"flags %s",	NULL,		print_tcpflags,	2 },
233 	{ BM_ICMP_TYPE,	"icmp-type %s",	NULL,		print_number,	1 },
234 	{ BM_ICMP_CODE,	"code %s",	NULL,		print_number,	1 },
235 
236 	{ BM_SRC_CIDR,	"from %s",	", ",		print_address,	6 },
237 	{ BM_SRC_TABLE,	"from <%s>",	NULL,		print_number,	1 },
238 	{ BM_SRC_PORTS,	"port %s",	", ",		print_portrange,2 },
239 
240 	{ BM_DST_CIDR,	"to %s",	", ",		print_address,	6 },
241 	{ BM_DST_TABLE,	"to <%s>",	NULL,		print_number,	1 },
242 	{ BM_DST_PORTS,	"port %s",	", ",		print_portrange,2 },
243 };
244 
245 static const char * __attribute__((format_arg(2)))
246 verified_fmt(const char *fmt, const char *t __unused)
247 {
248 	return fmt;
249 }
250 
251 static char *
252 scan_marks(npf_conf_info_t *ctx, const struct mark_keyword_mapent *mk,
253     const uint32_t *marks, size_t mlen)
254 {
255 	char buf[2048], *vals[256], *p;
256 	size_t nvals = 0;
257 
258 	/* Scan for the marks and extract the values. */
259 	mlen /= sizeof(uint32_t);
260 	while (mlen > 2) {
261 		const uint32_t m = *marks++;
262 		const u_int nwords = *marks++;
263 
264 		if ((mlen -= 2) < nwords) {
265 			errx(EXIT_FAILURE, "byte-code marking inconsistency");
266 		}
267 		if (m == mk->mark) {
268 			/* Value is processed by the print function. */
269 			assert(mk->fwords == nwords);
270 			vals[nvals++] = mk->printfn(marks);
271 		}
272 		marks += nwords;
273 		mlen -= nwords;
274 	}
275 	if (nvals == 0) {
276 		return NULL;
277 	}
278 	assert(nvals == 1 || mk->sep != NULL);
279 
280 	/*
281 	 * Join all the values and print.  Add curly brackets if there
282 	 * is more than value and it can be a set.
283 	 */
284 	if (!join(buf, sizeof(buf), nvals, vals, mk->sep ? mk->sep : "")) {
285 		errx(EXIT_FAILURE, "out of memory while parsing the rule");
286 	}
287 	easprintf(&p, nvals > 1 ? "{ %s }" : "%s", buf);
288 
289 	for (u_int i = 0; i < nvals; i++) {
290 		free(vals[i]);
291 	}
292 	return p;
293 }
294 
295 static void
296 npfctl_print_filter(npf_conf_info_t *ctx, nl_rule_t *rl)
297 {
298 	const void *marks;
299 	size_t mlen;
300 
301 	/* BPF filter criteria described by the byte-code marks. */
302 	marks = npf_rule_getinfo(rl, &mlen);
303 	for (u_int i = 0; i < __arraycount(mark_keyword_map); i++) {
304 		const struct mark_keyword_mapent *mk = &mark_keyword_map[i];
305 		char *val;
306 
307 		if ((val = scan_marks(ctx, mk, marks, mlen)) != NULL) {
308 			fprintf(ctx->fp, verified_fmt(mk->token, "%s"), val);
309 			fputs(" ", ctx->fp);
310 			free(val);
311 		}
312 	}
313 	if (!mlen) {
314 		fputs("all ", ctx->fp);
315 	}
316 }
317 
318 static void
319 npfctl_print_rule(npf_conf_info_t *ctx, nl_rule_t *rl)
320 {
321 	const uint32_t attr = npf_rule_getattr(rl);
322 	const char *rproc, *name;
323 	u_int if_idx;
324 
325 	/* Rule attributes/flags. */
326 	for (u_int i = 0; i < __arraycount(attr_keyword_map); i++) {
327 		const struct attr_keyword_mapent *ak = &attr_keyword_map[i];
328 
329 		if (i == NAME_AT && (name = npf_rule_getname(rl)) != NULL) {
330 			fprintf(ctx->fp, "\"%s\" ", name);
331 		}
332 		if ((attr & ak->mask) == ak->flags) {
333 			fprintf(ctx->fp, "%s ", ak->val);
334 		}
335 	}
336 	if ((if_idx = npf_rule_getinterface(rl)) != 0) {
337 		char ifnamebuf[IFNAMSIZ], *ifname;
338 		ifname = if_indextoname(if_idx, ifnamebuf);
339 		fprintf(ctx->fp, "on %s ", ifname);
340 	}
341 
342 	if ((attr & (NPF_RULE_GROUP | NPF_RULE_DYNAMIC)) == NPF_RULE_GROUP) {
343 		/* Group; done. */
344 		fputs("\n", ctx->fp);
345 		return;
346 	}
347 
348 	/* Print filter criteria. */
349 	npfctl_print_filter(ctx, rl);
350 
351 	/* Rule procedure. */
352 	if ((rproc = npf_rule_getproc(rl)) != NULL) {
353 		fprintf(ctx->fp, "apply \"%s\"", rproc);
354 	}
355 	fputs("\n", ctx->fp);
356 }
357 
358 static void
359 npfctl_print_nat(npf_conf_info_t *ctx, nl_nat_t *nt)
360 {
361 	nl_rule_t *rl = (nl_nat_t *)nt;
362 	const char *ifname, *seg1, *seg2, *arrow;
363 	char *seg, ifnamebuf[IFNAMSIZ];
364 	size_t if_idx, alen;
365 	npf_addr_t addr;
366 	in_port_t port;
367 
368 	/* Get the interface. */
369 	if_idx = npf_rule_getinterface(rl);
370 	ifname = if_indextoname(if_idx, ifnamebuf);
371 
372 	/* Get the translation address (and port, if used). */
373 	npf_nat_getmap(nt, &addr, &alen, &port);
374 	seg = npfctl_print_addrmask(alen, &addr, NPF_NO_NETMASK);
375 	if (port) {
376 		char *p;
377 		easprintf(&p, "%s port %u", seg, port);
378 		free(seg), seg = p;
379 	}
380 	seg1 = seg2 = "any";
381 
382 	/* Get the NAT type and determine the translation segment. */
383 	switch (npf_nat_gettype(nt)) {
384 	case NPF_NATIN:
385 		arrow = "<-";
386 		seg1 = seg;
387 		break;
388 	case NPF_NATOUT:
389 		arrow = "->";
390 		seg2 = seg;
391 		break;
392 	default:
393 		assert(false);
394 	}
395 
396 	/* Print out the NAT policy with the filter criteria. */
397 	fprintf(ctx->fp, "map %s dynamic %s %s %s pass ",
398 	    ifname, seg1, arrow, seg2);
399 	npfctl_print_filter(ctx, rl);
400 	fputs("\n", ctx->fp);
401 	free(seg);
402 }
403 
404 static void
405 npfctl_print_table(npf_conf_info_t *ctx, nl_table_t *tl)
406 {
407 	const u_int id = npf_table_getid(tl);
408 	const int type = npf_table_gettype(tl);
409 
410 	fprintf(ctx->fp, "table <%u> type %s\n", id,
411 	    (type == NPF_TABLE_HASH) ? "hash" :
412 	    (type == NPF_TABLE_TREE) ? "tree" :
413 	    "unknown");
414 }
415 
416 int
417 npfctl_config_show(int fd)
418 {
419 	npf_conf_info_t *ctx = &stdout_ctx;
420 	nl_config_t *ncf;
421 	bool active, loaded;
422 
423 	if (fd) {
424 		ncf = npf_config_retrieve(fd, &active, &loaded);
425 		if (ncf == NULL) {
426 			return errno;
427 		}
428 		fprintf(ctx->fp, "Filtering:\t%s\nConfiguration:\t%s\n",
429 		    active ? "active" : "inactive",
430 		    loaded ? "loaded" : "empty");
431 		print_linesep(ctx);
432 	} else {
433 		npfctl_config_send(0, NULL);
434 		ncf = npfctl_config_ref();
435 		loaded = true;
436 	}
437 
438 	if (loaded) {
439 		nl_rule_t *rl;
440 		nl_rproc_t *rp;
441 		nl_nat_t *nt;
442 		nl_table_t *tl;
443 		u_int level;
444 
445 		while ((tl = npf_table_iterate(ncf)) != NULL) {
446 			npfctl_print_table(ctx, tl);
447 		}
448 		print_linesep(ctx);
449 
450 		while ((rp = npf_rproc_iterate(ncf)) != NULL) {
451 			const char *rpname = npf_rproc_getname(rp);
452 			fprintf(ctx->fp, "procedure \"%s\"\n", rpname);
453 		}
454 		print_linesep(ctx);
455 
456 		while ((nt = npf_nat_iterate(ncf)) != NULL) {
457 			npfctl_print_nat(ctx, nt);
458 		}
459 		print_linesep(ctx);
460 
461 		while ((rl = npf_rule_iterate(ncf, &level)) != NULL) {
462 			print_indent(ctx, level);
463 			npfctl_print_rule(ctx, rl);
464 		}
465 		print_linesep(ctx);
466 	}
467 	npf_config_destroy(ncf);
468 	return 0;
469 }
470 
471 int
472 npfctl_ruleset_show(int fd, const char *ruleset_name)
473 {
474 	npf_conf_info_t *ctx = &stdout_ctx;
475 	nl_config_t *ncf;
476 	nl_rule_t *rl;
477 	u_int level;
478 	int error;
479 
480 	ncf = npf_config_create();
481 	if ((error = _npf_ruleset_list(fd, ruleset_name, ncf)) != 0) {
482 		return error;
483 	}
484 	while ((rl = npf_rule_iterate(ncf, &level)) != NULL) {
485 		npfctl_print_rule(ctx, rl);
486 	}
487 	npf_config_destroy(ncf);
488 	return error;
489 }
490