Home | History | Annotate | Line # | Download | only in npfctl
npf_show.c revision 1.35
      1   1.1     rmind /*-
      2  1.32     rmind  * Copyright (c) 2013-2020 The NetBSD Foundation, Inc.
      3   1.1     rmind  * All rights reserved.
      4   1.1     rmind  *
      5   1.1     rmind  * This code is derived from software contributed to The NetBSD Foundation
      6   1.1     rmind  * by Mindaugas Rasiukevicius.
      7   1.1     rmind  *
      8   1.1     rmind  * Redistribution and use in source and binary forms, with or without
      9   1.1     rmind  * modification, are permitted provided that the following conditions
     10   1.1     rmind  * are met:
     11   1.1     rmind  * 1. Redistributions of source code must retain the above copyright
     12   1.1     rmind  *    notice, this list of conditions and the following disclaimer.
     13   1.1     rmind  * 2. Redistributions in binary form must reproduce the above copyright
     14   1.1     rmind  *    notice, this list of conditions and the following disclaimer in the
     15   1.1     rmind  *    documentation and/or other materials provided with the distribution.
     16   1.1     rmind  *
     17   1.1     rmind  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     18   1.1     rmind  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     19   1.1     rmind  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     20   1.1     rmind  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     21   1.1     rmind  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     22   1.1     rmind  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     23   1.1     rmind  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     24   1.1     rmind  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     25   1.1     rmind  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     26   1.1     rmind  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     27   1.1     rmind  * POSSIBILITY OF SUCH DAMAGE.
     28   1.1     rmind  */
     29   1.1     rmind 
     30   1.1     rmind /*
     31   1.1     rmind  * NPF configuration printing.
     32   1.1     rmind  *
     33   1.1     rmind  * Each rule having BPF byte-code has a binary description.
     34   1.1     rmind  */
     35   1.1     rmind 
     36   1.1     rmind #include <sys/cdefs.h>
     37  1.35       joe __RCSID("$NetBSD: npf_show.c,v 1.35 2025/06/01 00:33:51 joe Exp $");
     38   1.1     rmind 
     39   1.1     rmind #include <sys/socket.h>
     40  1.20  christos #define	__FAVOR_BSD
     41   1.1     rmind #include <netinet/in.h>
     42   1.1     rmind #include <netinet/tcp.h>
     43   1.1     rmind #include <net/if.h>
     44   1.1     rmind 
     45   1.1     rmind #include <stdio.h>
     46   1.1     rmind #include <stdlib.h>
     47   1.1     rmind #include <string.h>
     48   1.1     rmind #include <stdbool.h>
     49   1.1     rmind #include <inttypes.h>
     50   1.1     rmind #include <errno.h>
     51   1.1     rmind #include <err.h>
     52   1.1     rmind 
     53   1.1     rmind #include "npfctl.h"
     54   1.1     rmind 
     55  1.32     rmind #define	SEEN_PROTO	0x01
     56  1.32     rmind 
     57  1.32     rmind typedef struct {
     58  1.32     rmind 	char **		values;
     59  1.32     rmind 	unsigned	count;
     60  1.32     rmind } elem_list_t;
     61  1.32     rmind 
     62  1.32     rmind enum {
     63  1.32     rmind 	LIST_PROTO = 0, LIST_SADDR, LIST_DADDR, LIST_SPORT, LIST_DPORT,
     64  1.32     rmind 	LIST_COUNT,
     65  1.32     rmind };
     66  1.18     rmind 
     67   1.1     rmind typedef struct {
     68   1.4     rmind 	nl_config_t *	conf;
     69  1.32     rmind 	bool		validating;
     70  1.32     rmind 
     71   1.1     rmind 	FILE *		fp;
     72   1.1     rmind 	long		fpos;
     73  1.32     rmind 	long		fposln;
     74  1.32     rmind 	int		glevel;
     75  1.32     rmind 
     76  1.32     rmind 	unsigned	flags;
     77  1.18     rmind 	uint32_t	curmark;
     78  1.32     rmind 	uint64_t	seen_marks;
     79  1.32     rmind 	elem_list_t	list[LIST_COUNT];
     80  1.32     rmind 
     81   1.1     rmind } npf_conf_info_t;
     82   1.1     rmind 
     83   1.1     rmind static void	print_linesep(npf_conf_info_t *);
     84   1.1     rmind 
     85  1.32     rmind static npf_conf_info_t *
     86  1.20  christos npfctl_show_init(void)
     87  1.20  christos {
     88  1.32     rmind 	static npf_conf_info_t stdout_ctx;
     89  1.32     rmind 	memset(&stdout_ctx, 0, sizeof(npf_conf_info_t));
     90  1.32     rmind 	stdout_ctx.glevel = -1;
     91  1.20  christos 	stdout_ctx.fp = stdout;
     92  1.32     rmind 	return &stdout_ctx;
     93  1.32     rmind }
     94  1.32     rmind 
     95  1.32     rmind static void
     96  1.32     rmind list_push(elem_list_t *list, char *val)
     97  1.32     rmind {
     98  1.32     rmind 	const unsigned n = list->count;
     99  1.32     rmind 	char **values;
    100  1.32     rmind 
    101  1.32     rmind 	if ((values = calloc(n + 1, sizeof(char *))) == NULL) {
    102  1.32     rmind 		err(EXIT_FAILURE, "calloc");
    103  1.32     rmind 	}
    104  1.32     rmind 	for (unsigned i = 0; i < n; i++) {
    105  1.32     rmind 		values[i] = list->values[i];
    106  1.32     rmind 	}
    107  1.32     rmind 	values[n] = val;
    108  1.32     rmind 	free(list->values);
    109  1.32     rmind 	list->values = values;
    110  1.32     rmind 	list->count++;
    111  1.32     rmind }
    112  1.32     rmind 
    113  1.32     rmind static char *
    114  1.32     rmind list_join_free(elem_list_t *list, const bool use_br, const char *sep)
    115  1.32     rmind {
    116  1.32     rmind 	char *s, buf[2048];
    117  1.32     rmind 
    118  1.32     rmind 	if (!join(buf, sizeof(buf), list->count, list->values, sep)) {
    119  1.32     rmind 		errx(EXIT_FAILURE, "out of memory while parsing the rule");
    120  1.32     rmind 	}
    121  1.32     rmind 	easprintf(&s, (use_br && list->count > 1) ? "{ %s }" : "%s", buf);
    122  1.32     rmind 	for (unsigned i = 0; i < list->count; i++) {
    123  1.32     rmind 		free(list->values[i]);
    124  1.32     rmind 	}
    125  1.32     rmind 	free(list->values);
    126  1.32     rmind 	list->values = NULL;
    127  1.32     rmind 	list->count = 0;
    128  1.32     rmind 	return s;
    129  1.20  christos }
    130  1.20  christos 
    131   1.1     rmind /*
    132   1.1     rmind  * Helper routines to print various pieces of information.
    133   1.1     rmind  */
    134   1.1     rmind 
    135   1.1     rmind static void
    136  1.27     rmind print_indent(npf_conf_info_t *ctx, unsigned level)
    137   1.1     rmind {
    138  1.32     rmind 	if (ctx->glevel >= 0 && level <= (unsigned)ctx->glevel) {
    139  1.27     rmind 		/*
    140  1.27     rmind 		 * Level decrease -- end of the group.
    141  1.27     rmind 		 * Print the group closing curly bracket.
    142  1.27     rmind 		 */
    143  1.32     rmind 		ctx->fpos += fprintf(ctx->fp, "}\n\n");
    144  1.32     rmind 		ctx->glevel = -1;
    145   1.1     rmind 	}
    146  1.27     rmind 	while (level--) {
    147  1.32     rmind 		ctx->fpos += fprintf(ctx->fp, "\t");
    148  1.27     rmind 	}
    149   1.1     rmind }
    150   1.1     rmind 
    151   1.1     rmind static void
    152   1.1     rmind print_linesep(npf_conf_info_t *ctx)
    153   1.1     rmind {
    154  1.32     rmind 	if (ctx->fpos != ctx->fposln) {
    155  1.32     rmind 		ctx->fpos += fprintf(ctx->fp, "\n");
    156  1.32     rmind 		ctx->fposln = ctx->fpos;
    157   1.1     rmind 	}
    158   1.1     rmind }
    159   1.1     rmind 
    160   1.1     rmind static size_t
    161  1.32     rmind tcpflags2string(char *buf, unsigned tfl)
    162   1.1     rmind {
    163  1.32     rmind 	unsigned i = 0;
    164   1.1     rmind 
    165   1.1     rmind 	if (tfl & TH_FIN)	buf[i++] = 'F';
    166   1.1     rmind 	if (tfl & TH_SYN)	buf[i++] = 'S';
    167   1.1     rmind 	if (tfl & TH_RST)	buf[i++] = 'R';
    168   1.1     rmind 	if (tfl & TH_PUSH)	buf[i++] = 'P';
    169   1.1     rmind 	if (tfl & TH_ACK)	buf[i++] = 'A';
    170   1.1     rmind 	if (tfl & TH_URG)	buf[i++] = 'U';
    171   1.1     rmind 	if (tfl & TH_ECE)	buf[i++] = 'E';
    172  1.30  christos 	if (tfl & TH_CWR)	buf[i++] = 'W';
    173   1.1     rmind 	buf[i] = '\0';
    174   1.1     rmind 	return i;
    175   1.1     rmind }
    176   1.1     rmind 
    177   1.1     rmind static char *
    178  1.26     rmind print_family(npf_conf_info_t *ctx __unused, const uint32_t *words)
    179   1.1     rmind {
    180   1.1     rmind 	const int af = words[0];
    181   1.1     rmind 
    182   1.1     rmind 	switch (af) {
    183   1.1     rmind 	case AF_INET:
    184  1.10     rmind 		return estrdup("inet4");
    185   1.1     rmind 	case AF_INET6:
    186   1.1     rmind 		return estrdup("inet6");
    187   1.1     rmind 	default:
    188   1.1     rmind 		errx(EXIT_FAILURE, "invalid byte-code mark (family)");
    189   1.1     rmind 	}
    190   1.1     rmind 	return NULL;
    191   1.1     rmind }
    192   1.1     rmind 
    193   1.1     rmind static char *
    194  1.26     rmind print_address(npf_conf_info_t *ctx __unused, const uint32_t *words)
    195   1.1     rmind {
    196   1.1     rmind 	const int af = *words++;
    197  1.32     rmind 	const unsigned mask = *words++;
    198   1.1     rmind 	const npf_addr_t *addr;
    199   1.1     rmind 	int alen = 0;
    200   1.1     rmind 
    201   1.1     rmind 	switch (af) {
    202   1.1     rmind 	case AF_INET:
    203   1.1     rmind 		alen = 4;
    204   1.1     rmind 		break;
    205   1.1     rmind 	case AF_INET6:
    206   1.1     rmind 		alen = 16;
    207   1.1     rmind 		break;
    208   1.1     rmind 	default:
    209   1.1     rmind 		errx(EXIT_FAILURE, "invalid byte-code mark (address)");
    210   1.1     rmind 	}
    211   1.1     rmind 	addr = (const npf_addr_t *)words;
    212  1.21  christos 	return npfctl_print_addrmask(alen, "%a", addr, mask);
    213   1.1     rmind }
    214   1.1     rmind 
    215   1.1     rmind static char *
    216  1.26     rmind print_number(npf_conf_info_t *ctx __unused, const uint32_t *words)
    217   1.1     rmind {
    218   1.1     rmind 	char *p;
    219   1.1     rmind 	easprintf(&p, "%u", words[0]);
    220   1.1     rmind 	return p;
    221   1.1     rmind }
    222   1.1     rmind 
    223   1.1     rmind static char *
    224   1.4     rmind print_table(npf_conf_info_t *ctx, const uint32_t *words)
    225   1.4     rmind {
    226  1.27     rmind 	const unsigned tid = words[0];
    227  1.27     rmind 	const char *tname;
    228  1.27     rmind 	char *s = NULL;
    229  1.27     rmind 	bool ifaddr;
    230  1.27     rmind 
    231  1.27     rmind 	tname = npfctl_table_getname(ctx->conf, tid, &ifaddr);
    232  1.27     rmind 	easprintf(&s, ifaddr ? "ifaddrs(%s)" : "<%s>", tname);
    233  1.27     rmind 	return s;
    234   1.4     rmind }
    235   1.4     rmind 
    236   1.4     rmind static char *
    237   1.4     rmind print_proto(npf_conf_info_t *ctx, const uint32_t *words)
    238   1.1     rmind {
    239  1.32     rmind 	ctx->flags |= SEEN_PROTO;
    240   1.1     rmind 	switch (words[0]) {
    241   1.1     rmind 	case IPPROTO_TCP:
    242   1.1     rmind 		return estrdup("tcp");
    243   1.1     rmind 	case IPPROTO_UDP:
    244   1.1     rmind 		return estrdup("udp");
    245   1.1     rmind 	case IPPROTO_ICMP:
    246   1.1     rmind 		return estrdup("icmp");
    247   1.1     rmind 	case IPPROTO_ICMPV6:
    248   1.1     rmind 		return estrdup("ipv6-icmp");
    249   1.1     rmind 	}
    250   1.4     rmind 	return print_number(ctx, words);
    251   1.1     rmind }
    252   1.1     rmind 
    253   1.1     rmind static char *
    254  1.26     rmind print_tcpflags(npf_conf_info_t *ctx __unused, const uint32_t *words)
    255   1.1     rmind {
    256  1.32     rmind 	const unsigned tf = words[0], tf_mask = words[1];
    257  1.32     rmind 	char buf[32];
    258  1.32     rmind 	size_t n;
    259   1.1     rmind 
    260  1.32     rmind 	if ((ctx->flags & SEEN_PROTO) == 0) {
    261  1.32     rmind 		/*
    262  1.32     rmind 		 * Note: the TCP flag matching might be without 'proto tcp'
    263  1.32     rmind 		 * when using a plain 'stateful' rule.  In such case, just
    264  1.32     rmind 		 * skip showing of the flags as they are implicit.
    265  1.32     rmind 		 */
    266  1.32     rmind 		return NULL;
    267  1.32     rmind 	}
    268  1.32     rmind 	n = tcpflags2string(buf, tf);
    269   1.1     rmind 	if (tf != tf_mask) {
    270   1.1     rmind 		buf[n++] = '/';
    271   1.1     rmind 		tcpflags2string(buf + n, tf_mask);
    272   1.1     rmind 	}
    273   1.1     rmind 	return estrdup(buf);
    274   1.1     rmind }
    275   1.1     rmind 
    276   1.1     rmind static char *
    277  1.29     rmind print_portrange(npf_conf_info_t *ctx __unused, const uint32_t *words)
    278   1.1     rmind {
    279  1.32     rmind 	unsigned fport = words[0], tport = words[1];
    280   1.1     rmind 	char *p;
    281   1.1     rmind 
    282   1.1     rmind 	if (fport != tport) {
    283  1.29     rmind 		easprintf(&p, "%u-%u", fport, tport);
    284   1.1     rmind 	} else {
    285  1.29     rmind 		easprintf(&p, "%u", fport);
    286   1.1     rmind 	}
    287   1.1     rmind 	return p;
    288   1.1     rmind }
    289   1.1     rmind 
    290   1.1     rmind /*
    291   1.1     rmind  * The main keyword mapping tables defining the syntax:
    292   1.1     rmind  * - Mapping of rule attributes (flags) to the keywords.
    293   1.1     rmind  * - Mapping of the byte-code marks to the keywords.
    294   1.1     rmind  */
    295   1.1     rmind 
    296   1.1     rmind #define	F(name)		__CONCAT(NPF_RULE_, name)
    297  1.28     rmind #define	STATEFUL_ALL	(NPF_RULE_STATEFUL | NPF_RULE_GSTATEFUL)
    298   1.1     rmind #define	NAME_AT		2
    299   1.1     rmind 
    300   1.1     rmind static const struct attr_keyword_mapent {
    301   1.1     rmind 	uint32_t	mask;
    302   1.1     rmind 	uint32_t	flags;
    303   1.1     rmind 	const char *	val;
    304   1.1     rmind } attr_keyword_map[] = {
    305   1.1     rmind 	{ F(GROUP)|F(DYNAMIC),	F(GROUP),		"group"		},
    306  1.32     rmind 	{ F(GROUP)|F(DYNAMIC),	F(GROUP)|F(DYNAMIC),	"ruleset"	},
    307   1.1     rmind 	{ F(GROUP)|F(PASS),	0,			"block"		},
    308   1.1     rmind 	{ F(GROUP)|F(PASS),	F(PASS),		"pass"		},
    309   1.1     rmind 	{ F(RETRST)|F(RETICMP),	F(RETRST)|F(RETICMP),	"return"	},
    310   1.1     rmind 	{ F(RETRST)|F(RETICMP),	F(RETRST),		"return-rst"	},
    311   1.1     rmind 	{ F(RETRST)|F(RETICMP),	F(RETICMP),		"return-icmp"	},
    312  1.28     rmind 	{ STATEFUL_ALL,		F(STATEFUL),		"stateful"	},
    313  1.28     rmind 	{ STATEFUL_ALL,		STATEFUL_ALL,		"stateful-all"	},
    314   1.1     rmind 	{ F(DIMASK),		F(IN),			"in"		},
    315   1.1     rmind 	{ F(DIMASK),		F(OUT),			"out"		},
    316   1.1     rmind 	{ F(FINAL),		F(FINAL),		"final"		},
    317   1.1     rmind };
    318   1.1     rmind 
    319   1.1     rmind static const struct mark_keyword_mapent {
    320  1.32     rmind 	unsigned	mark;
    321  1.32     rmind 	const char *	format;
    322  1.32     rmind 	int		list_id;
    323   1.4     rmind 	char *		(*printfn)(npf_conf_info_t *, const uint32_t *);
    324  1.32     rmind 	unsigned	fwords;
    325   1.1     rmind } mark_keyword_map[] = {
    326  1.32     rmind 	{ BM_IPVER,	"family %s",	LIST_PROTO,	print_family,	1 },
    327  1.32     rmind 	{ BM_PROTO,	"proto %s",	LIST_PROTO,	print_proto,	1 },
    328  1.32     rmind 	{ BM_TCPFL,	"flags %s",	LIST_PROTO,	print_tcpflags,	2 },
    329  1.32     rmind 	{ BM_ICMP_TYPE,	"icmp-type %s",	LIST_PROTO,	print_number,	1 },
    330  1.32     rmind 	{ BM_ICMP_CODE,	"code %s",	LIST_PROTO,	print_number,	1 },
    331  1.32     rmind 
    332  1.32     rmind 	{ BM_SRC_NEG,	NULL,		-1,		NULL,		0 },
    333  1.32     rmind 	{ BM_SRC_CIDR,	NULL,		LIST_SADDR,	print_address,	6 },
    334  1.32     rmind 	{ BM_SRC_TABLE,	NULL,		LIST_SADDR,	print_table,	1 },
    335  1.32     rmind 	{ BM_SRC_PORTS,	NULL,		LIST_SPORT,	print_portrange,2 },
    336  1.32     rmind 
    337  1.32     rmind 	{ BM_DST_NEG,	NULL,		-1,		NULL,		0 },
    338  1.32     rmind 	{ BM_DST_CIDR,	NULL,		LIST_DADDR,	print_address,	6 },
    339  1.32     rmind 	{ BM_DST_TABLE,	NULL,		LIST_DADDR,	print_table,	1 },
    340  1.32     rmind 	{ BM_DST_PORTS,	NULL,		LIST_DPORT,	print_portrange,2 },
    341   1.1     rmind };
    342   1.1     rmind 
    343   1.1     rmind static const char * __attribute__((format_arg(2)))
    344   1.1     rmind verified_fmt(const char *fmt, const char *t __unused)
    345   1.1     rmind {
    346   1.1     rmind 	return fmt;
    347   1.1     rmind }
    348   1.1     rmind 
    349  1.32     rmind static void
    350   1.1     rmind scan_marks(npf_conf_info_t *ctx, const struct mark_keyword_mapent *mk,
    351   1.1     rmind     const uint32_t *marks, size_t mlen)
    352   1.1     rmind {
    353  1.32     rmind 	elem_list_t sublist, *target_list;
    354  1.32     rmind 
    355  1.32     rmind 	/*
    356  1.32     rmind 	 * If format is used for this mark, then collect multiple elements
    357  1.32     rmind 	 * in into the list, merge and re-push the set into the target list.
    358  1.32     rmind 	 *
    359  1.32     rmind 	 * Currently, this is applicable only for 'proto { tcp, udp }'.
    360  1.32     rmind 	 */
    361  1.32     rmind 	memset(&sublist, 0, sizeof(elem_list_t));
    362  1.32     rmind 	target_list = mk->format ? &sublist : &ctx->list[mk->list_id];
    363   1.1     rmind 
    364   1.1     rmind 	/* Scan for the marks and extract the values. */
    365   1.1     rmind 	mlen /= sizeof(uint32_t);
    366   1.1     rmind 	while (mlen > 2) {
    367   1.1     rmind 		const uint32_t m = *marks++;
    368  1.32     rmind 		const unsigned nwords = *marks++;
    369   1.1     rmind 
    370   1.1     rmind 		if ((mlen -= 2) < nwords) {
    371   1.1     rmind 			errx(EXIT_FAILURE, "byte-code marking inconsistency");
    372   1.1     rmind 		}
    373   1.1     rmind 		if (m == mk->mark) {
    374  1.32     rmind 			/*
    375  1.32     rmind 			 * Set the current mark and note it as seen.
    376  1.32     rmind 			 * Value is processed by the print function,
    377  1.32     rmind 			 * otherwise we just need to note the mark.
    378  1.32     rmind 			 */
    379  1.32     rmind 			ctx->curmark = m;
    380  1.32     rmind 			assert(BM_COUNT < (sizeof(uint64_t) * CHAR_BIT));
    381  1.34   mlelstv 			ctx->seen_marks |= UINT64_C(1) << m;
    382  1.32     rmind 			assert(mk->fwords == nwords);
    383  1.29     rmind 
    384  1.32     rmind 			if (mk->printfn) {
    385  1.32     rmind 				char *val;
    386  1.18     rmind 
    387  1.32     rmind 				if ((val = mk->printfn(ctx, marks)) != NULL) {
    388  1.32     rmind 					list_push(target_list, val);
    389  1.32     rmind 				}
    390  1.29     rmind 			}
    391   1.1     rmind 		}
    392   1.1     rmind 		marks += nwords;
    393   1.1     rmind 		mlen -= nwords;
    394   1.1     rmind 	}
    395   1.1     rmind 
    396  1.32     rmind 	if (sublist.count) {
    397  1.32     rmind 		char *val, *elements;
    398   1.1     rmind 
    399  1.32     rmind 		elements = list_join_free(&sublist, true, ", ");
    400  1.32     rmind 		easprintf(&val, verified_fmt(mk->format, "%s"), elements );
    401  1.32     rmind 		list_push(&ctx->list[mk->list_id], val);
    402  1.32     rmind 		free(elements);
    403   1.1     rmind 	}
    404   1.1     rmind }
    405   1.1     rmind 
    406   1.1     rmind static void
    407  1.23  christos npfctl_print_id(npf_conf_info_t *ctx, nl_rule_t *rl)
    408  1.23  christos {
    409  1.32     rmind 	const uint64_t id = npf_rule_getid(rl);
    410  1.32     rmind 
    411  1.32     rmind 	if (id) {
    412  1.32     rmind 		ctx->fpos += fprintf(ctx->fp, "# id=\"%" PRIx64 "\" ", id);
    413  1.32     rmind 	}
    414  1.23  christos }
    415  1.23  christos 
    416  1.23  christos static void
    417  1.32     rmind npfctl_print_filter_generic(npf_conf_info_t *ctx)
    418  1.32     rmind {
    419  1.32     rmind 	elem_list_t *list = &ctx->list[LIST_PROTO];
    420  1.32     rmind 
    421  1.32     rmind 	if (list->count) {
    422  1.32     rmind 		char *elements = list_join_free(list, false, " ");
    423  1.32     rmind 		ctx->fpos += fprintf(ctx->fp, "%s ", elements);
    424  1.32     rmind 		free(elements);
    425  1.32     rmind 	}
    426  1.32     rmind }
    427  1.32     rmind 
    428  1.32     rmind static bool
    429  1.32     rmind npfctl_print_filter_seg(npf_conf_info_t *ctx, unsigned which)
    430  1.32     rmind {
    431  1.32     rmind 	static const struct {
    432  1.32     rmind 		const char *	keyword;
    433  1.32     rmind 		unsigned	alist;
    434  1.32     rmind 		unsigned	plist;
    435  1.32     rmind 		unsigned	negbm;
    436  1.32     rmind 	} refs[] = {
    437  1.32     rmind 		[NPF_SRC] = {
    438  1.32     rmind 			.keyword	= "from",
    439  1.32     rmind 			.alist		= LIST_SADDR,
    440  1.32     rmind 			.plist		= LIST_SPORT,
    441  1.32     rmind 			.negbm		= UINT64_C(1) << BM_SRC_NEG,
    442  1.32     rmind 		},
    443  1.32     rmind 		[NPF_DST] = {
    444  1.32     rmind 			.keyword	= "to",
    445  1.32     rmind 			.alist		= LIST_DADDR,
    446  1.32     rmind 			.plist		= LIST_DPORT,
    447  1.32     rmind 			.negbm		= UINT64_C(1) << BM_DST_NEG,
    448  1.32     rmind 		}
    449  1.32     rmind 	};
    450  1.32     rmind 	const char *neg = !!(ctx->seen_marks & refs[which].negbm) ? "! " : "";
    451  1.32     rmind 	const char *kwd = refs[which].keyword;
    452  1.32     rmind 	bool seen_filter = false;
    453  1.32     rmind 	elem_list_t *list;
    454  1.32     rmind 	char *elements;
    455  1.32     rmind 
    456  1.32     rmind 	list = &ctx->list[refs[which].alist];
    457  1.32     rmind 	if (list->count != 0) {
    458  1.32     rmind 		seen_filter = true;
    459  1.32     rmind 		elements = list_join_free(list, true, ", ");
    460  1.32     rmind 		ctx->fpos += fprintf(ctx->fp, "%s %s%s ", kwd, neg, elements);
    461  1.32     rmind 		free(elements);
    462  1.32     rmind 	}
    463  1.32     rmind 
    464  1.32     rmind 	list = &ctx->list[refs[which].plist];
    465  1.32     rmind 	if (list->count != 0) {
    466  1.32     rmind 		if (!seen_filter) {
    467  1.32     rmind 			ctx->fpos += fprintf(ctx->fp, "%s any ", kwd);
    468  1.32     rmind 			seen_filter = true;
    469  1.32     rmind 		}
    470  1.32     rmind 		elements = list_join_free(list, true, ", ");
    471  1.32     rmind 		ctx->fpos += fprintf(ctx->fp, "port %s ", elements);
    472  1.32     rmind 		free(elements);
    473  1.32     rmind 	}
    474  1.32     rmind 	return seen_filter;
    475  1.32     rmind }
    476  1.32     rmind 
    477  1.32     rmind static bool
    478   1.1     rmind npfctl_print_filter(npf_conf_info_t *ctx, nl_rule_t *rl)
    479   1.1     rmind {
    480   1.1     rmind 	const void *marks;
    481  1.16     rmind 	size_t mlen, len;
    482  1.16     rmind 	const void *code;
    483  1.32     rmind 	bool seenf = false;
    484  1.16     rmind 	int type;
    485   1.1     rmind 
    486   1.1     rmind 	marks = npf_rule_getinfo(rl, &mlen);
    487  1.16     rmind 	if (!marks && (code = npf_rule_getcode(rl, &type, &len)) != NULL) {
    488  1.16     rmind 		/*
    489  1.16     rmind 		 * No marks, but the byte-code is present.  This must
    490  1.16     rmind 		 * have been filled by libpcap(3) or possibly an unknown
    491  1.16     rmind 		 * to us byte-code.
    492  1.16     rmind 		 */
    493  1.32     rmind 		ctx->fpos += fprintf(ctx->fp, "%s ", type == NPF_CODE_BPF ?
    494  1.16     rmind 		    "pcap-filter \"...\"" : "unrecognized-bytecode");
    495  1.32     rmind 		return true;
    496  1.16     rmind 	}
    497  1.19     rmind 	ctx->flags = 0;
    498  1.16     rmind 
    499  1.16     rmind 	/*
    500  1.16     rmind 	 * BPF filter criteria described by the byte-code marks.
    501  1.16     rmind 	 */
    502  1.34   mlelstv 	ctx->seen_marks = 0;
    503  1.32     rmind 	for (unsigned i = 0; i < __arraycount(mark_keyword_map); i++) {
    504   1.1     rmind 		const struct mark_keyword_mapent *mk = &mark_keyword_map[i];
    505  1.32     rmind 		scan_marks(ctx, mk, marks, mlen);
    506   1.1     rmind 	}
    507  1.32     rmind 	npfctl_print_filter_generic(ctx);
    508  1.32     rmind 	seenf |= npfctl_print_filter_seg(ctx, NPF_SRC);
    509  1.32     rmind 	seenf |= npfctl_print_filter_seg(ctx, NPF_DST);
    510  1.32     rmind 	return seenf;
    511   1.1     rmind }
    512   1.1     rmind 
    513  1.35       joe static char *
    514  1.35       joe print_guid(char *buf, struct r_id id, int size)
    515  1.35       joe {
    516  1.35       joe 	if (id.op == NPF_OP_XRG) {
    517  1.35       joe 		snprintf(buf, size, "%u <> %u", id.id[0], id.id[1]);
    518  1.35       joe 	} else if (id.op == NPF_OP_IRG) {
    519  1.35       joe 		snprintf(buf, size, "%u >< %u", id.id[0], id.id[1]);
    520  1.35       joe 	} else if (id.op == NPF_OP_EQ ) {
    521  1.35       joe 		snprintf(buf, size, "%u", id.id[0]);
    522  1.35       joe 	} else if (id.op == NPF_OP_NE) {
    523  1.35       joe 		snprintf(buf, size, "!= %u", id.id[0]);
    524  1.35       joe 	} else if (id.op == NPF_OP_LE) {
    525  1.35       joe 		snprintf(buf, size, "<= %u", id.id[0]);
    526  1.35       joe 	} else if (id.op == NPF_OP_LT) {
    527  1.35       joe 		snprintf(buf, size, "< %u", id.id[0]);
    528  1.35       joe 	} else if (id.op == NPF_OP_GE) {
    529  1.35       joe 		snprintf(buf, size, ">= %u", id.id[0]);
    530  1.35       joe 	} else if (id.op == NPF_OP_GT) {
    531  1.35       joe 		snprintf(buf, size, "> %u", id.id[0]);
    532  1.35       joe 	} else {
    533  1.35       joe 		return NULL;
    534  1.35       joe 	}
    535  1.35       joe 	return buf;
    536  1.35       joe }
    537  1.35       joe #define BUF_SIZE	40
    538   1.1     rmind static void
    539  1.32     rmind npfctl_print_rule(npf_conf_info_t *ctx, nl_rule_t *rl, unsigned level)
    540   1.1     rmind {
    541   1.1     rmind 	const uint32_t attr = npf_rule_getattr(rl);
    542   1.3     rmind 	const char *rproc, *ifname, *name;
    543  1.32     rmind 	bool dyn_ruleset;
    544  1.35       joe 	struct r_id rid;
    545  1.35       joe 	char buf[BUF_SIZE];
    546   1.1     rmind 
    547   1.1     rmind 	/* Rule attributes/flags. */
    548  1.32     rmind 	for (unsigned i = 0; i < __arraycount(attr_keyword_map); i++) {
    549   1.1     rmind 		const struct attr_keyword_mapent *ak = &attr_keyword_map[i];
    550   1.1     rmind 
    551   1.1     rmind 		if (i == NAME_AT && (name = npf_rule_getname(rl)) != NULL) {
    552  1.32     rmind 			ctx->fpos += fprintf(ctx->fp, "\"%s\" ", name);
    553   1.1     rmind 		}
    554   1.1     rmind 		if ((attr & ak->mask) == ak->flags) {
    555  1.32     rmind 			ctx->fpos += fprintf(ctx->fp, "%s ", ak->val);
    556   1.1     rmind 		}
    557   1.1     rmind 	}
    558   1.3     rmind 	if ((ifname = npf_rule_getinterface(rl)) != NULL) {
    559  1.32     rmind 		ctx->fpos += fprintf(ctx->fp, "on %s ", ifname);
    560   1.1     rmind 	}
    561  1.27     rmind 	if (attr == (NPF_RULE_GROUP | NPF_RULE_IN | NPF_RULE_OUT) && !ifname) {
    562  1.27     rmind 		/* The default group is a special case. */
    563  1.32     rmind 		ctx->fpos += fprintf(ctx->fp, "default ");
    564  1.27     rmind 	}
    565  1.16     rmind 	if ((attr & NPF_DYNAMIC_GROUP) == NPF_RULE_GROUP) {
    566   1.1     rmind 		/* Group; done. */
    567  1.32     rmind 		ctx->fpos += fprintf(ctx->fp, "{ ");
    568  1.32     rmind 		ctx->glevel = level;
    569  1.23  christos 		goto out;
    570   1.1     rmind 	}
    571   1.1     rmind 
    572   1.1     rmind 	/* Print filter criteria. */
    573  1.32     rmind 	dyn_ruleset = (attr & NPF_DYNAMIC_GROUP) == NPF_DYNAMIC_GROUP;
    574  1.32     rmind 	if (!npfctl_print_filter(ctx, rl) && !dyn_ruleset) {
    575  1.32     rmind 		ctx->fpos += fprintf(ctx->fp, "all ");
    576  1.32     rmind 	}
    577   1.1     rmind 
    578  1.35       joe 	if (!npf_rule_getrid(&rid, rl, "r_user")) {
    579  1.35       joe 		ctx->fpos += fprintf(ctx->fp, "user %s ", print_guid(buf, rid, BUF_SIZE));
    580  1.35       joe 	}
    581  1.35       joe 
    582  1.35       joe 	if (!npf_rule_getrid(&rid, rl, "r_group")) {
    583  1.35       joe 		ctx->fpos += fprintf(ctx->fp, "group %s ", print_guid(buf, rid, BUF_SIZE));
    584  1.35       joe 	}
    585  1.35       joe 
    586   1.1     rmind 	/* Rule procedure. */
    587   1.1     rmind 	if ((rproc = npf_rule_getproc(rl)) != NULL) {
    588  1.32     rmind 		ctx->fpos += fprintf(ctx->fp, "apply \"%s\" ", rproc);
    589  1.16     rmind 	}
    590  1.23  christos out:
    591  1.23  christos 	npfctl_print_id(ctx, rl);
    592  1.32     rmind 	ctx->fpos += fprintf(ctx->fp, "\n");
    593   1.1     rmind }
    594   1.1     rmind 
    595   1.1     rmind static void
    596   1.1     rmind npfctl_print_nat(npf_conf_info_t *ctx, nl_nat_t *nt)
    597   1.1     rmind {
    598  1.28     rmind 	const unsigned dynamic_natset = NPF_RULE_GROUP | NPF_RULE_DYNAMIC;
    599   1.1     rmind 	nl_rule_t *rl = (nl_nat_t *)nt;
    600  1.27     rmind 	const char *ifname, *algo, *seg1, *seg2, *arrow;
    601  1.27     rmind 	const npf_addr_t *addr;
    602  1.27     rmind 	npf_netmask_t mask;
    603   1.1     rmind 	in_port_t port;
    604   1.3     rmind 	size_t alen;
    605  1.28     rmind 	unsigned flags;
    606   1.3     rmind 	char *seg;
    607   1.1     rmind 
    608  1.28     rmind 	/* Get flags and the interface. */
    609  1.28     rmind 	flags = npf_nat_getflags(nt);
    610   1.3     rmind 	ifname = npf_rule_getinterface(rl);
    611   1.3     rmind 	assert(ifname != NULL);
    612   1.1     rmind 
    613  1.28     rmind 	if ((npf_rule_getattr(rl) & dynamic_natset) == dynamic_natset) {
    614  1.28     rmind 		const char *name = npf_rule_getname(rl);
    615  1.32     rmind 		ctx->fpos += fprintf(ctx->fp,
    616  1.32     rmind 		    "map ruleset \"%s\" on %s\n", name, ifname);
    617  1.28     rmind 		return;
    618  1.28     rmind 	}
    619  1.28     rmind 
    620  1.27     rmind 	/* Get the translation address or table (and port, if used). */
    621  1.27     rmind 	addr = npf_nat_getaddr(nt, &alen, &mask);
    622  1.27     rmind 	if (addr) {
    623  1.27     rmind 		seg = npfctl_print_addrmask(alen, "%a", addr, mask);
    624  1.27     rmind 	} else {
    625  1.27     rmind 		const unsigned tid = npf_nat_gettable(nt);
    626  1.27     rmind 		const char *tname;
    627  1.27     rmind 		bool ifaddr;
    628  1.27     rmind 
    629  1.27     rmind 		tname = npfctl_table_getname(ctx->conf, tid, &ifaddr);
    630  1.27     rmind 		easprintf(&seg, ifaddr ? "ifaddrs(%s)" : "<%s>", tname);
    631  1.27     rmind 	}
    632  1.27     rmind 
    633  1.27     rmind 	if ((port = npf_nat_getport(nt)) != 0) {
    634   1.1     rmind 		char *p;
    635  1.12     rmind 		easprintf(&p, "%s port %u", seg, ntohs(port));
    636   1.1     rmind 		free(seg), seg = p;
    637   1.1     rmind 	}
    638   1.1     rmind 	seg1 = seg2 = "any";
    639   1.1     rmind 
    640   1.1     rmind 	/* Get the NAT type and determine the translation segment. */
    641   1.1     rmind 	switch (npf_nat_gettype(nt)) {
    642   1.1     rmind 	case NPF_NATIN:
    643   1.1     rmind 		arrow = "<-";
    644   1.1     rmind 		seg1 = seg;
    645   1.1     rmind 		break;
    646   1.1     rmind 	case NPF_NATOUT:
    647   1.1     rmind 		arrow = "->";
    648   1.1     rmind 		seg2 = seg;
    649   1.1     rmind 		break;
    650   1.1     rmind 	default:
    651   1.9     rmind 		abort();
    652   1.1     rmind 	}
    653   1.1     rmind 
    654  1.27     rmind 	/* NAT algorithm. */
    655  1.27     rmind 	switch (npf_nat_getalgo(nt)) {
    656  1.27     rmind 	case NPF_ALGO_NETMAP:
    657  1.27     rmind 		algo = "algo netmap ";
    658  1.27     rmind 		break;
    659  1.27     rmind 	case NPF_ALGO_IPHASH:
    660  1.27     rmind 		algo = "algo ip-hash ";
    661  1.27     rmind 		break;
    662  1.27     rmind 	case NPF_ALGO_RR:
    663  1.27     rmind 		algo = "algo round-robin ";
    664  1.27     rmind 		break;
    665  1.27     rmind 	case NPF_ALGO_NPT66:
    666  1.32     rmind 		algo = "algo npt66 ";
    667  1.27     rmind 		break;
    668  1.27     rmind 	default:
    669  1.27     rmind 		algo = "";
    670  1.27     rmind 		break;
    671  1.27     rmind 	}
    672  1.27     rmind 
    673  1.32     rmind 	/* XXX also handle "any" */
    674  1.27     rmind 
    675   1.1     rmind 	/* Print out the NAT policy with the filter criteria. */
    676  1.32     rmind 	ctx->fpos += fprintf(ctx->fp, "map %s %s %s%s%s %s %s pass ",
    677   1.9     rmind 	    ifname, (flags & NPF_NAT_STATIC) ? "static" : "dynamic",
    678  1.27     rmind 	    algo, (flags & NPF_NAT_PORTS) ? "" : "no-ports ",
    679   1.9     rmind 	    seg1, arrow, seg2);
    680   1.1     rmind 	npfctl_print_filter(ctx, rl);
    681  1.23  christos 	npfctl_print_id(ctx, rl);
    682  1.32     rmind 	ctx->fpos += fprintf(ctx->fp, "\n");
    683   1.1     rmind 	free(seg);
    684   1.1     rmind }
    685   1.1     rmind 
    686   1.1     rmind static void
    687   1.1     rmind npfctl_print_table(npf_conf_info_t *ctx, nl_table_t *tl)
    688   1.1     rmind {
    689   1.4     rmind 	const char *name = npf_table_getname(tl);
    690  1.11     rmind 	const unsigned type = npf_table_gettype(tl);
    691  1.11     rmind 	const char *table_types[] = {
    692  1.27     rmind 		[NPF_TABLE_IPSET]	= "ipset",
    693  1.27     rmind 		[NPF_TABLE_LPM]		= "lpm",
    694  1.27     rmind 		[NPF_TABLE_CONST]	= "const",
    695  1.11     rmind 	};
    696   1.1     rmind 
    697   1.5     rmind 	if (name[0] == '.') {
    698   1.5     rmind 		/* Internal tables use dot and are hidden. */
    699   1.5     rmind 		return;
    700   1.5     rmind 	}
    701  1.11     rmind 	assert(type < __arraycount(table_types));
    702  1.32     rmind 	ctx->fpos += fprintf(ctx->fp,
    703  1.32     rmind 	    "table <%s> type %s\n", name, table_types[type]);
    704  1.32     rmind }
    705  1.32     rmind 
    706  1.32     rmind static void
    707  1.32     rmind npfctl_print_params(npf_conf_info_t *ctx, nl_config_t *ncf)
    708  1.32     rmind {
    709  1.32     rmind 	nl_iter_t i = NPF_ITER_BEGIN;
    710  1.32     rmind 	int val, defval, *dval;
    711  1.32     rmind 	const char *name;
    712  1.32     rmind 
    713  1.32     rmind 	dval = ctx->validating ? NULL : &defval;
    714  1.32     rmind 	while ((name = npf_param_iterate(ncf, &i, &val, dval)) != NULL) {
    715  1.32     rmind 		if (dval && val == *dval) {
    716  1.32     rmind 			continue;
    717  1.32     rmind 		}
    718  1.32     rmind 		ctx->fpos += fprintf(ctx->fp, "set %s %d\n", name, val);
    719  1.32     rmind 	}
    720  1.32     rmind 	print_linesep(ctx);
    721   1.1     rmind }
    722   1.1     rmind 
    723   1.1     rmind int
    724   1.1     rmind npfctl_config_show(int fd)
    725   1.1     rmind {
    726  1.32     rmind 	npf_conf_info_t *ctx = npfctl_show_init();
    727   1.1     rmind 	nl_config_t *ncf;
    728  1.20  christos 	bool loaded;
    729   1.1     rmind 
    730   1.1     rmind 	if (fd) {
    731  1.20  christos 		ncf = npf_config_retrieve(fd);
    732   1.1     rmind 		if (ncf == NULL) {
    733   1.1     rmind 			return errno;
    734   1.1     rmind 		}
    735  1.20  christos 		loaded = npf_config_loaded_p(ncf);
    736  1.32     rmind 		ctx->validating = false;
    737  1.32     rmind 		ctx->fpos += fprintf(ctx->fp,
    738  1.32     rmind 		    "# filtering:\t%s\n# config:\t%s\n",
    739  1.20  christos 		    npf_config_active_p(ncf) ? "active" : "inactive",
    740   1.1     rmind 		    loaded ? "loaded" : "empty");
    741   1.1     rmind 		print_linesep(ctx);
    742   1.1     rmind 	} else {
    743   1.1     rmind 		ncf = npfctl_config_ref();
    744  1.31     rmind 		npfctl_config_build();
    745  1.32     rmind 		ctx->validating = true;
    746   1.1     rmind 		loaded = true;
    747   1.1     rmind 	}
    748   1.4     rmind 	ctx->conf = ncf;
    749   1.1     rmind 
    750   1.1     rmind 	if (loaded) {
    751   1.1     rmind 		nl_rule_t *rl;
    752   1.1     rmind 		nl_rproc_t *rp;
    753   1.1     rmind 		nl_nat_t *nt;
    754   1.1     rmind 		nl_table_t *tl;
    755  1.28     rmind 		nl_iter_t i;
    756  1.27     rmind 		unsigned level;
    757   1.1     rmind 
    758  1.32     rmind 		npfctl_print_params(ctx, ncf);
    759  1.32     rmind 
    760  1.28     rmind 		i = NPF_ITER_BEGIN;
    761  1.28     rmind 		while ((tl = npf_table_iterate(ncf, &i)) != NULL) {
    762   1.1     rmind 			npfctl_print_table(ctx, tl);
    763   1.1     rmind 		}
    764   1.1     rmind 		print_linesep(ctx);
    765   1.1     rmind 
    766  1.28     rmind 		i = NPF_ITER_BEGIN;
    767  1.28     rmind 		while ((rp = npf_rproc_iterate(ncf, &i)) != NULL) {
    768   1.1     rmind 			const char *rpname = npf_rproc_getname(rp);
    769  1.32     rmind 			ctx->fpos += fprintf(ctx->fp,
    770  1.32     rmind 			    "procedure \"%s\"\n", rpname);
    771   1.1     rmind 		}
    772   1.1     rmind 		print_linesep(ctx);
    773   1.1     rmind 
    774  1.28     rmind 		i = NPF_ITER_BEGIN;
    775  1.28     rmind 		while ((nt = npf_nat_iterate(ncf, &i)) != NULL) {
    776   1.1     rmind 			npfctl_print_nat(ctx, nt);
    777   1.1     rmind 		}
    778   1.1     rmind 		print_linesep(ctx);
    779   1.1     rmind 
    780  1.28     rmind 		i = NPF_ITER_BEGIN;
    781  1.28     rmind 		while ((rl = npf_rule_iterate(ncf, &i, &level)) != NULL) {
    782   1.1     rmind 			print_indent(ctx, level);
    783  1.32     rmind 			npfctl_print_rule(ctx, rl, level);
    784   1.1     rmind 		}
    785  1.27     rmind 		print_indent(ctx, 0);
    786   1.1     rmind 	}
    787   1.1     rmind 	npf_config_destroy(ncf);
    788   1.1     rmind 	return 0;
    789   1.1     rmind }
    790   1.1     rmind 
    791   1.1     rmind int
    792   1.1     rmind npfctl_ruleset_show(int fd, const char *ruleset_name)
    793   1.1     rmind {
    794  1.32     rmind 	npf_conf_info_t *ctx = npfctl_show_init();
    795   1.1     rmind 	nl_config_t *ncf;
    796   1.1     rmind 	nl_rule_t *rl;
    797  1.27     rmind 	unsigned level;
    798  1.28     rmind 	nl_iter_t i;
    799   1.1     rmind 	int error;
    800   1.1     rmind 
    801   1.1     rmind 	ncf = npf_config_create();
    802   1.4     rmind 	ctx->conf = ncf;
    803   1.4     rmind 
    804   1.1     rmind 	if ((error = _npf_ruleset_list(fd, ruleset_name, ncf)) != 0) {
    805   1.1     rmind 		return error;
    806   1.1     rmind 	}
    807  1.28     rmind 	i = NPF_ITER_BEGIN;
    808  1.28     rmind 	while ((rl = npf_rule_iterate(ncf, &i, &level)) != NULL) {
    809  1.32     rmind 		npfctl_print_rule(ctx, rl, 0);
    810   1.1     rmind 	}
    811   1.1     rmind 	npf_config_destroy(ncf);
    812   1.1     rmind 	return error;
    813   1.1     rmind }
    814