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