Home | History | Annotate | Line # | Download | only in npfctl
npf_show.c revision 1.3
      1 /*	$NetBSD: npf_show.c,v 1.3 2013/11/08 00:38:26 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.3 2013/11/08 00:38:26 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, *ifname, *name;
    323 
    324 	/* Rule attributes/flags. */
    325 	for (u_int i = 0; i < __arraycount(attr_keyword_map); i++) {
    326 		const struct attr_keyword_mapent *ak = &attr_keyword_map[i];
    327 
    328 		if (i == NAME_AT && (name = npf_rule_getname(rl)) != NULL) {
    329 			fprintf(ctx->fp, "\"%s\" ", name);
    330 		}
    331 		if ((attr & ak->mask) == ak->flags) {
    332 			fprintf(ctx->fp, "%s ", ak->val);
    333 		}
    334 	}
    335 	if ((ifname = npf_rule_getinterface(rl)) != NULL) {
    336 		fprintf(ctx->fp, "on %s ", ifname);
    337 	}
    338 
    339 	if ((attr & (NPF_RULE_GROUP | NPF_RULE_DYNAMIC)) == NPF_RULE_GROUP) {
    340 		/* Group; done. */
    341 		fputs("\n", ctx->fp);
    342 		return;
    343 	}
    344 
    345 	/* Print filter criteria. */
    346 	npfctl_print_filter(ctx, rl);
    347 
    348 	/* Rule procedure. */
    349 	if ((rproc = npf_rule_getproc(rl)) != NULL) {
    350 		fprintf(ctx->fp, "apply \"%s\"", rproc);
    351 	}
    352 	fputs("\n", ctx->fp);
    353 }
    354 
    355 static void
    356 npfctl_print_nat(npf_conf_info_t *ctx, nl_nat_t *nt)
    357 {
    358 	nl_rule_t *rl = (nl_nat_t *)nt;
    359 	const char *ifname, *seg1, *seg2, *arrow;
    360 	npf_addr_t addr;
    361 	in_port_t port;
    362 	size_t alen;
    363 	char *seg;
    364 
    365 	/* Get the interface. */
    366 	ifname = npf_rule_getinterface(rl);
    367 	assert(ifname != NULL);
    368 
    369 	/* Get the translation address (and port, if used). */
    370 	npf_nat_getmap(nt, &addr, &alen, &port);
    371 	seg = npfctl_print_addrmask(alen, &addr, NPF_NO_NETMASK);
    372 	if (port) {
    373 		char *p;
    374 		easprintf(&p, "%s port %u", seg, port);
    375 		free(seg), seg = p;
    376 	}
    377 	seg1 = seg2 = "any";
    378 
    379 	/* Get the NAT type and determine the translation segment. */
    380 	switch (npf_nat_gettype(nt)) {
    381 	case NPF_NATIN:
    382 		arrow = "<-";
    383 		seg1 = seg;
    384 		break;
    385 	case NPF_NATOUT:
    386 		arrow = "->";
    387 		seg2 = seg;
    388 		break;
    389 	default:
    390 		assert(false);
    391 	}
    392 
    393 	/* Print out the NAT policy with the filter criteria. */
    394 	fprintf(ctx->fp, "map %s dynamic %s %s %s pass ",
    395 	    ifname, seg1, arrow, seg2);
    396 	npfctl_print_filter(ctx, rl);
    397 	fputs("\n", ctx->fp);
    398 	free(seg);
    399 }
    400 
    401 static void
    402 npfctl_print_table(npf_conf_info_t *ctx, nl_table_t *tl)
    403 {
    404 	const u_int id = npf_table_getid(tl);
    405 	const int type = npf_table_gettype(tl);
    406 
    407 	fprintf(ctx->fp, "table <%u> type %s\n", id,
    408 	    (type == NPF_TABLE_HASH) ? "hash" :
    409 	    (type == NPF_TABLE_TREE) ? "tree" :
    410 	    "unknown");
    411 }
    412 
    413 int
    414 npfctl_config_show(int fd)
    415 {
    416 	npf_conf_info_t *ctx = &stdout_ctx;
    417 	nl_config_t *ncf;
    418 	bool active, loaded;
    419 
    420 	if (fd) {
    421 		ncf = npf_config_retrieve(fd, &active, &loaded);
    422 		if (ncf == NULL) {
    423 			return errno;
    424 		}
    425 		fprintf(ctx->fp, "Filtering:\t%s\nConfiguration:\t%s\n",
    426 		    active ? "active" : "inactive",
    427 		    loaded ? "loaded" : "empty");
    428 		print_linesep(ctx);
    429 	} else {
    430 		npfctl_config_send(0, NULL);
    431 		ncf = npfctl_config_ref();
    432 		loaded = true;
    433 	}
    434 
    435 	if (loaded) {
    436 		nl_rule_t *rl;
    437 		nl_rproc_t *rp;
    438 		nl_nat_t *nt;
    439 		nl_table_t *tl;
    440 		u_int level;
    441 
    442 		while ((tl = npf_table_iterate(ncf)) != NULL) {
    443 			npfctl_print_table(ctx, tl);
    444 		}
    445 		print_linesep(ctx);
    446 
    447 		while ((rp = npf_rproc_iterate(ncf)) != NULL) {
    448 			const char *rpname = npf_rproc_getname(rp);
    449 			fprintf(ctx->fp, "procedure \"%s\"\n", rpname);
    450 		}
    451 		print_linesep(ctx);
    452 
    453 		while ((nt = npf_nat_iterate(ncf)) != NULL) {
    454 			npfctl_print_nat(ctx, nt);
    455 		}
    456 		print_linesep(ctx);
    457 
    458 		while ((rl = npf_rule_iterate(ncf, &level)) != NULL) {
    459 			print_indent(ctx, level);
    460 			npfctl_print_rule(ctx, rl);
    461 		}
    462 		print_linesep(ctx);
    463 	}
    464 	npf_config_destroy(ncf);
    465 	return 0;
    466 }
    467 
    468 int
    469 npfctl_ruleset_show(int fd, const char *ruleset_name)
    470 {
    471 	npf_conf_info_t *ctx = &stdout_ctx;
    472 	nl_config_t *ncf;
    473 	nl_rule_t *rl;
    474 	u_int level;
    475 	int error;
    476 
    477 	ncf = npf_config_create();
    478 	if ((error = _npf_ruleset_list(fd, ruleset_name, ncf)) != 0) {
    479 		return error;
    480 	}
    481 	while ((rl = npf_rule_iterate(ncf, &level)) != NULL) {
    482 		npfctl_print_rule(ctx, rl);
    483 	}
    484 	npf_config_destroy(ncf);
    485 	return error;
    486 }
    487