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