Home | History | Annotate | Line # | Download | only in npfctl
      1  1.1  rmind /*-
      2  1.1  rmind  * Copyright (c) 2009-2020 The NetBSD Foundation, Inc.
      3  1.1  rmind  * All rights reserved.
      4  1.1  rmind  *
      5  1.1  rmind  * This material is based upon work partially supported by The
      6  1.1  rmind  * NetBSD Foundation under a contract with 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 #include <sys/cdefs.h>
     31  1.1  rmind __RCSID("$NetBSD: npf_cmd.c,v 1.1 2020/05/30 14:16:56 rmind Exp $");
     32  1.1  rmind 
     33  1.1  rmind #include <stdio.h>
     34  1.1  rmind #include <string.h>
     35  1.1  rmind #include <stdlib.h>
     36  1.1  rmind #include <unistd.h>
     37  1.1  rmind #include <errno.h>
     38  1.1  rmind #include <err.h>
     39  1.1  rmind 
     40  1.1  rmind #ifdef __NetBSD__
     41  1.1  rmind #include <sha1.h>
     42  1.1  rmind #define SHA_DIGEST_LENGTH SHA1_DIGEST_LENGTH
     43  1.1  rmind #else
     44  1.1  rmind #include <openssl/sha.h>
     45  1.1  rmind #endif
     46  1.1  rmind 
     47  1.1  rmind #include "npfctl.h"
     48  1.1  rmind 
     49  1.1  rmind ////////////////////////////////////////////////////////////////////////////
     50  1.1  rmind //
     51  1.1  rmind // NPFCTL RULE COMMANDS
     52  1.1  rmind //
     53  1.1  rmind 
     54  1.1  rmind #ifdef __NetBSD__
     55  1.1  rmind static unsigned char *
     56  1.1  rmind SHA1(const unsigned char *d, size_t l, unsigned char *md)
     57  1.1  rmind {
     58  1.1  rmind 	SHA1_CTX c;
     59  1.1  rmind 
     60  1.1  rmind 	SHA1Init(&c);
     61  1.1  rmind 	SHA1Update(&c, d, l);
     62  1.1  rmind 	SHA1Final(md, &c);
     63  1.1  rmind 	return md;
     64  1.1  rmind }
     65  1.1  rmind #endif
     66  1.1  rmind 
     67  1.1  rmind static void
     68  1.1  rmind npfctl_generate_key(nl_rule_t *rl, void *key)
     69  1.1  rmind {
     70  1.1  rmind 	void *meta;
     71  1.1  rmind 	size_t len;
     72  1.1  rmind 
     73  1.1  rmind 	if ((meta = npf_rule_export(rl, &len)) == NULL) {
     74  1.1  rmind 		errx(EXIT_FAILURE, "error generating rule key");
     75  1.1  rmind 	}
     76  1.1  rmind 	__CTASSERT(NPF_RULE_MAXKEYLEN >= SHA_DIGEST_LENGTH);
     77  1.1  rmind 	memset(key, 0, NPF_RULE_MAXKEYLEN);
     78  1.1  rmind 	SHA1(meta, len, key);
     79  1.1  rmind 	free(meta);
     80  1.1  rmind }
     81  1.1  rmind 
     82  1.1  rmind int
     83  1.1  rmind npfctl_nat_ruleset_p(const char *name, bool *natset)
     84  1.1  rmind {
     85  1.1  rmind 	const size_t preflen = sizeof(NPF_RULESET_MAP_PREF) - 1;
     86  1.1  rmind 	*natset = strncmp(name, NPF_RULESET_MAP_PREF, preflen) == 0;
     87  1.1  rmind 	return (*natset && strlen(name) <= preflen) ? -1 : 0;
     88  1.1  rmind }
     89  1.1  rmind 
     90  1.1  rmind static nl_rule_t *
     91  1.1  rmind npfctl_parse_rule(int argc, char **argv, parse_entry_t entry)
     92  1.1  rmind {
     93  1.1  rmind 	char rule_string[1024];
     94  1.1  rmind 	nl_rule_t *rl;
     95  1.1  rmind 
     96  1.1  rmind 	/* Get the rule string and parse it. */
     97  1.1  rmind 	if (!join(rule_string, sizeof(rule_string), argc, argv, " ")) {
     98  1.1  rmind 		errx(EXIT_FAILURE, "command too long");
     99  1.1  rmind 	}
    100  1.1  rmind 	npfctl_parse_string(rule_string, entry);
    101  1.1  rmind 	if ((rl = npfctl_rule_ref()) == NULL) {
    102  1.1  rmind 		errx(EXIT_FAILURE, "could not parse the rule");
    103  1.1  rmind 	}
    104  1.1  rmind 	return rl;
    105  1.1  rmind }
    106  1.1  rmind 
    107  1.1  rmind void
    108  1.1  rmind npfctl_rule(int fd, int argc, char **argv)
    109  1.1  rmind {
    110  1.1  rmind 	static const struct ruleops_s {
    111  1.1  rmind 		const char *	cmd;
    112  1.1  rmind 		int		action;
    113  1.1  rmind 		bool		extra_arg;
    114  1.1  rmind 	} ruleops[] = {
    115  1.1  rmind 		{ "add",	NPF_CMD_RULE_ADD,	true	},
    116  1.1  rmind 		{ "rem",	NPF_CMD_RULE_REMKEY,	true	},
    117  1.1  rmind 		{ "del",	NPF_CMD_RULE_REMKEY,	true	},
    118  1.1  rmind 		{ "rem-id",	NPF_CMD_RULE_REMOVE,	true	},
    119  1.1  rmind 		{ "list",	NPF_CMD_RULE_LIST,	false	},
    120  1.1  rmind 		{ "flush",	NPF_CMD_RULE_FLUSH,	false	},
    121  1.1  rmind 		{ NULL,		0,			0	}
    122  1.1  rmind 	};
    123  1.1  rmind 	uint8_t key[NPF_RULE_MAXKEYLEN];
    124  1.1  rmind 	const char *ruleset_name = argv[0];
    125  1.1  rmind 	const char *cmd = argv[1];
    126  1.1  rmind 	int error, action = 0;
    127  1.1  rmind 	bool extra_arg, natset;
    128  1.1  rmind 	parse_entry_t entry;
    129  1.1  rmind 	uint64_t rule_id;
    130  1.1  rmind 	nl_rule_t *rl;
    131  1.1  rmind 
    132  1.1  rmind 	for (unsigned n = 0; ruleops[n].cmd != NULL; n++) {
    133  1.1  rmind 		if (strcmp(cmd, ruleops[n].cmd) == 0) {
    134  1.1  rmind 			action = ruleops[n].action;
    135  1.1  rmind 			extra_arg = ruleops[n].extra_arg;
    136  1.1  rmind 			break;
    137  1.1  rmind 		}
    138  1.1  rmind 	}
    139  1.1  rmind 	argc -= 2;
    140  1.1  rmind 	argv += 2;
    141  1.1  rmind 
    142  1.1  rmind 	if (!action || (extra_arg && argc == 0)) {
    143  1.1  rmind 		usage();
    144  1.1  rmind 	}
    145  1.1  rmind 
    146  1.1  rmind 	if (npfctl_nat_ruleset_p(ruleset_name, &natset) != 0) {
    147  1.1  rmind 		errx(EXIT_FAILURE,
    148  1.1  rmind 		    "invalid NAT ruleset name (note: the name must be "
    149  1.1  rmind 		    "prefixed with `" NPF_RULESET_MAP_PREF "`)");
    150  1.1  rmind 	}
    151  1.1  rmind 	entry = natset ? NPFCTL_PARSE_MAP : NPFCTL_PARSE_RULE;
    152  1.1  rmind 
    153  1.1  rmind 	switch (action) {
    154  1.1  rmind 	case NPF_CMD_RULE_ADD:
    155  1.1  rmind 		rl = npfctl_parse_rule(argc, argv, entry);
    156  1.1  rmind 		npfctl_generate_key(rl, key);
    157  1.1  rmind 		npf_rule_setkey(rl, key, sizeof(key));
    158  1.1  rmind 		error = npf_ruleset_add(fd, ruleset_name, rl, &rule_id);
    159  1.1  rmind 		break;
    160  1.1  rmind 	case NPF_CMD_RULE_REMKEY:
    161  1.1  rmind 		rl = npfctl_parse_rule(argc, argv, entry);
    162  1.1  rmind 		npfctl_generate_key(rl, key);
    163  1.1  rmind 		error = npf_ruleset_remkey(fd, ruleset_name, key, sizeof(key));
    164  1.1  rmind 		break;
    165  1.1  rmind 	case NPF_CMD_RULE_REMOVE:
    166  1.1  rmind 		rule_id = strtoull(argv[0], NULL, 16);
    167  1.1  rmind 		error = npf_ruleset_remove(fd, ruleset_name, rule_id);
    168  1.1  rmind 		break;
    169  1.1  rmind 	case NPF_CMD_RULE_LIST:
    170  1.1  rmind 		error = npfctl_ruleset_show(fd, ruleset_name);
    171  1.1  rmind 		break;
    172  1.1  rmind 	case NPF_CMD_RULE_FLUSH:
    173  1.1  rmind 		error = npf_ruleset_flush(fd, ruleset_name);
    174  1.1  rmind 		break;
    175  1.1  rmind 	default:
    176  1.1  rmind 		abort();
    177  1.1  rmind 	}
    178  1.1  rmind 
    179  1.1  rmind 	switch (error) {
    180  1.1  rmind 	case 0:
    181  1.1  rmind 		/* Success. */
    182  1.1  rmind 		break;
    183  1.1  rmind 	case ESRCH:
    184  1.1  rmind 		errx(EXIT_FAILURE, "ruleset \"%s\" not found", ruleset_name);
    185  1.1  rmind 	case ENOENT:
    186  1.1  rmind 		errx(EXIT_FAILURE, "rule was not found");
    187  1.1  rmind 	default:
    188  1.1  rmind 		errx(EXIT_FAILURE, "rule operation: %s", strerror(error));
    189  1.1  rmind 	}
    190  1.1  rmind 	if (action == NPF_CMD_RULE_ADD) {
    191  1.1  rmind 		printf("OK %" PRIx64 "\n", rule_id);
    192  1.1  rmind 	}
    193  1.1  rmind }
    194  1.1  rmind 
    195  1.1  rmind ////////////////////////////////////////////////////////////////////////////
    196  1.1  rmind //
    197  1.1  rmind // NPFCTL TABLE COMMANDS
    198  1.1  rmind //
    199  1.1  rmind 
    200  1.1  rmind static int
    201  1.1  rmind npfctl_table_type(const char *typename)
    202  1.1  rmind {
    203  1.1  rmind 	static const struct tbltype_s {
    204  1.1  rmind 		const char *	name;
    205  1.1  rmind 		unsigned	type;
    206  1.1  rmind 	} tbltypes[] = {
    207  1.1  rmind 		{ "ipset",	NPF_TABLE_IPSET	},
    208  1.1  rmind 		{ "lpm",	NPF_TABLE_LPM	},
    209  1.1  rmind 		{ "const",	NPF_TABLE_CONST	},
    210  1.1  rmind 		{ NULL,		0		}
    211  1.1  rmind 	};
    212  1.1  rmind 
    213  1.1  rmind 	for (unsigned i = 0; tbltypes[i].name != NULL; i++) {
    214  1.1  rmind 		if (strcmp(typename, tbltypes[i].name) == 0) {
    215  1.1  rmind 			return tbltypes[i].type;
    216  1.1  rmind 		}
    217  1.1  rmind 	}
    218  1.1  rmind 	return 0;
    219  1.1  rmind }
    220  1.1  rmind 
    221  1.1  rmind void
    222  1.1  rmind npfctl_table_replace(int fd, int argc, char **argv)
    223  1.1  rmind {
    224  1.1  rmind 	const char *name, *newname, *path, *typename = NULL;
    225  1.1  rmind 	nl_config_t *ncf;
    226  1.1  rmind 	nl_table_t *t;
    227  1.1  rmind 	unsigned type = 0;
    228  1.1  rmind 	int c, tid = -1;
    229  1.1  rmind 	FILE *fp;
    230  1.1  rmind 
    231  1.1  rmind 	name = newname = argv[0];
    232  1.1  rmind 	optind = 2;
    233  1.1  rmind 	while ((c = getopt(argc, argv, "n:t:")) != -1) {
    234  1.1  rmind 		switch (c) {
    235  1.1  rmind 		case 't':
    236  1.1  rmind 			typename = optarg;
    237  1.1  rmind 			break;
    238  1.1  rmind 		case 'n':
    239  1.1  rmind 			newname = optarg;
    240  1.1  rmind 			break;
    241  1.1  rmind 		default:
    242  1.1  rmind 			errx(EXIT_FAILURE,
    243  1.1  rmind 			    "Usage: %s table \"table-name\" replace "
    244  1.1  rmind 			    "[-n \"name\"] [-t <type>] <table-file>\n",
    245  1.1  rmind 			    getprogname());
    246  1.1  rmind 		}
    247  1.1  rmind 	}
    248  1.1  rmind 	argc -= optind;
    249  1.1  rmind 	argv += optind;
    250  1.1  rmind 
    251  1.1  rmind 	if (typename && (type = npfctl_table_type(typename)) == 0) {
    252  1.1  rmind 		errx(EXIT_FAILURE, "unsupported table type '%s'", typename);
    253  1.1  rmind 	}
    254  1.1  rmind 
    255  1.1  rmind 	if (argc != 1) {
    256  1.1  rmind 		usage();
    257  1.1  rmind 	}
    258  1.1  rmind 
    259  1.1  rmind 	path = argv[0];
    260  1.1  rmind 	if (strcmp(path, "-") == 0) {
    261  1.1  rmind 		path = "stdin";
    262  1.1  rmind 		fp = stdin;
    263  1.1  rmind 	} else if ((fp = fopen(path, "r")) == NULL) {
    264  1.1  rmind 		err(EXIT_FAILURE, "open '%s'", path);
    265  1.1  rmind 	}
    266  1.1  rmind 
    267  1.1  rmind 	/* Get existing config to lookup ID of existing table */
    268  1.1  rmind 	if ((ncf = npf_config_retrieve(fd)) == NULL) {
    269  1.1  rmind 		err(EXIT_FAILURE, "npf_config_retrieve()");
    270  1.1  rmind 	}
    271  1.1  rmind 	if ((t = npfctl_table_getbyname(ncf, name)) == NULL) {
    272  1.1  rmind 		errx(EXIT_FAILURE,
    273  1.1  rmind 		    "table '%s' not found in the active configuration", name);
    274  1.1  rmind 	}
    275  1.1  rmind 	tid = npf_table_getid(t);
    276  1.1  rmind 	if (!type) {
    277  1.1  rmind 		type = npf_table_gettype(t);
    278  1.1  rmind 	}
    279  1.1  rmind 	npf_config_destroy(ncf);
    280  1.1  rmind 
    281  1.1  rmind 	if ((t = npfctl_load_table(newname, tid, type, path, fp)) == NULL) {
    282  1.1  rmind 		err(EXIT_FAILURE, "table load failed");
    283  1.1  rmind 	}
    284  1.1  rmind 
    285  1.1  rmind 	if (npf_table_replace(fd, t, NULL)) {
    286  1.1  rmind 		err(EXIT_FAILURE, "npf_table_replace(<%s>)", name);
    287  1.1  rmind 	}
    288  1.1  rmind }
    289  1.1  rmind 
    290  1.1  rmind void
    291  1.1  rmind npfctl_table(int fd, int argc, char **argv)
    292  1.1  rmind {
    293  1.1  rmind 	static const struct tblops_s {
    294  1.1  rmind 		const char *	cmd;
    295  1.1  rmind 		int		action;
    296  1.1  rmind 	} tblops[] = {
    297  1.1  rmind 		{ "add",	NPF_CMD_TABLE_ADD		},
    298  1.1  rmind 		{ "rem",	NPF_CMD_TABLE_REMOVE		},
    299  1.1  rmind 		{ "del",	NPF_CMD_TABLE_REMOVE		},
    300  1.1  rmind 		{ "test",	NPF_CMD_TABLE_LOOKUP		},
    301  1.1  rmind 		{ "list",	NPF_CMD_TABLE_LIST		},
    302  1.1  rmind 		{ "flush",	NPF_CMD_TABLE_FLUSH		},
    303  1.1  rmind 		{ NULL,		0				}
    304  1.1  rmind 	};
    305  1.1  rmind 	npf_ioctl_table_t nct;
    306  1.1  rmind 	fam_addr_mask_t fam;
    307  1.1  rmind 	size_t buflen = 512;
    308  1.1  rmind 	char *cmd, *arg;
    309  1.1  rmind 	int n, alen;
    310  1.1  rmind 
    311  1.1  rmind 	/* Default action is list. */
    312  1.1  rmind 	memset(&nct, 0, sizeof(npf_ioctl_table_t));
    313  1.1  rmind 	nct.nct_name = argv[0];
    314  1.1  rmind 	cmd = argv[1];
    315  1.1  rmind 
    316  1.1  rmind 	for (n = 0; tblops[n].cmd != NULL; n++) {
    317  1.1  rmind 		if (strcmp(cmd, tblops[n].cmd) != 0) {
    318  1.1  rmind 			continue;
    319  1.1  rmind 		}
    320  1.1  rmind 		nct.nct_cmd = tblops[n].action;
    321  1.1  rmind 		break;
    322  1.1  rmind 	}
    323  1.1  rmind 	if (tblops[n].cmd == NULL) {
    324  1.1  rmind 		errx(EXIT_FAILURE, "invalid command '%s'", cmd);
    325  1.1  rmind 	}
    326  1.1  rmind 
    327  1.1  rmind 	switch (nct.nct_cmd) {
    328  1.1  rmind 	case NPF_CMD_TABLE_LIST:
    329  1.1  rmind 	case NPF_CMD_TABLE_FLUSH:
    330  1.1  rmind 		arg = NULL;
    331  1.1  rmind 		break;
    332  1.1  rmind 	default:
    333  1.1  rmind 		if (argc < 3) {
    334  1.1  rmind 			usage();
    335  1.1  rmind 		}
    336  1.1  rmind 		arg = argv[2];
    337  1.1  rmind 	}
    338  1.1  rmind 
    339  1.1  rmind again:
    340  1.1  rmind 	switch (nct.nct_cmd) {
    341  1.1  rmind 	case NPF_CMD_TABLE_LIST:
    342  1.1  rmind 		nct.nct_data.buf.buf = ecalloc(1, buflen);
    343  1.1  rmind 		nct.nct_data.buf.len = buflen;
    344  1.1  rmind 		break;
    345  1.1  rmind 	case NPF_CMD_TABLE_FLUSH:
    346  1.1  rmind 		break;
    347  1.1  rmind 	default:
    348  1.1  rmind 		if (!npfctl_parse_cidr(arg, &fam, &alen)) {
    349  1.1  rmind 			errx(EXIT_FAILURE, "invalid CIDR '%s'", arg);
    350  1.1  rmind 		}
    351  1.1  rmind 		nct.nct_data.ent.alen = alen;
    352  1.1  rmind 		memcpy(&nct.nct_data.ent.addr, &fam.fam_addr, alen);
    353  1.1  rmind 		nct.nct_data.ent.mask = fam.fam_mask;
    354  1.1  rmind 	}
    355  1.1  rmind 
    356  1.1  rmind 	if (ioctl(fd, IOC_NPF_TABLE, &nct) != -1) {
    357  1.1  rmind 		errno = 0;
    358  1.1  rmind 	}
    359  1.1  rmind 	switch (errno) {
    360  1.1  rmind 	case 0:
    361  1.1  rmind 		break;
    362  1.1  rmind 	case EEXIST:
    363  1.1  rmind 		errx(EXIT_FAILURE, "entry already exists or is conflicting");
    364  1.1  rmind 	case ENOENT:
    365  1.1  rmind 		errx(EXIT_FAILURE, "not found");
    366  1.1  rmind 	case EINVAL:
    367  1.1  rmind 		errx(EXIT_FAILURE, "invalid address, mask or table ID");
    368  1.1  rmind 	case ENOMEM:
    369  1.1  rmind 		if (nct.nct_cmd == NPF_CMD_TABLE_LIST) {
    370  1.1  rmind 			/* XXX */
    371  1.1  rmind 			free(nct.nct_data.buf.buf);
    372  1.1  rmind 			buflen <<= 1;
    373  1.1  rmind 			goto again;
    374  1.1  rmind 		}
    375  1.1  rmind 		/* FALLTHROUGH */
    376  1.1  rmind 	default:
    377  1.1  rmind 		err(EXIT_FAILURE, "ioctl(IOC_NPF_TABLE)");
    378  1.1  rmind 	}
    379  1.1  rmind 
    380  1.1  rmind 	if (nct.nct_cmd == NPF_CMD_TABLE_LIST) {
    381  1.1  rmind 		npf_ioctl_ent_t *ent = nct.nct_data.buf.buf;
    382  1.1  rmind 		char *buf;
    383  1.1  rmind 
    384  1.1  rmind 		while (nct.nct_data.buf.len--) {
    385  1.1  rmind 			if (!ent->alen)
    386  1.1  rmind 				break;
    387  1.1  rmind 			buf = npfctl_print_addrmask(ent->alen, "%a",
    388  1.1  rmind 			    &ent->addr, ent->mask);
    389  1.1  rmind 			puts(buf);
    390  1.1  rmind 			ent++;
    391  1.1  rmind 		}
    392  1.1  rmind 		free(nct.nct_data.buf.buf);
    393  1.1  rmind 	} else {
    394  1.1  rmind 		printf("%s: %s\n", getprogname(),
    395  1.1  rmind 		    nct.nct_cmd == NPF_CMD_TABLE_LOOKUP ?
    396  1.1  rmind 		    "match" : "success");
    397  1.1  rmind 	}
    398  1.1  rmind }
    399  1.1  rmind 
    400  1.1  rmind ////////////////////////////////////////////////////////////////////////////
    401  1.1  rmind //
    402  1.1  rmind // NPFCTL CONNECTION COMMANDS
    403  1.1  rmind //
    404  1.1  rmind 
    405  1.1  rmind typedef struct {
    406  1.1  rmind 	FILE *		fp;
    407  1.1  rmind 	unsigned	alen;
    408  1.1  rmind 	const char *	ifname;
    409  1.1  rmind 	bool		nat;
    410  1.1  rmind 	bool		nowide;
    411  1.1  rmind 	bool		name;
    412  1.1  rmind 
    413  1.1  rmind 	bool		v4;
    414  1.1  rmind 	unsigned	pwidth;
    415  1.1  rmind } npf_conn_filter_t;
    416  1.1  rmind 
    417  1.1  rmind static int
    418  1.1  rmind npfctl_conn_print(unsigned alen, const npf_addr_t *a, const in_port_t *p,
    419  1.1  rmind     const char *ifname, void *arg)
    420  1.1  rmind {
    421  1.1  rmind 	const npf_conn_filter_t *fil = arg;
    422  1.1  rmind 	char *addrstr, *src, *dst;
    423  1.1  rmind 	const char *fmt;
    424  1.1  rmind 	FILE *fp = fil->fp;
    425  1.1  rmind 	bool nat_conn;
    426  1.1  rmind 
    427  1.1  rmind 	/*
    428  1.1  rmind 	 * Filter connection entries by IP version, interface and/or
    429  1.1  rmind 	 * applicability of NAT.
    430  1.1  rmind 	 */
    431  1.1  rmind 	if (alen != fil->alen) {
    432  1.1  rmind 		return 0;
    433  1.1  rmind 	}
    434  1.1  rmind 	if (fil->ifname && (!ifname || strcmp(ifname, fil->ifname) != 0)) {
    435  1.1  rmind 		return 0;
    436  1.1  rmind 	}
    437  1.1  rmind 	nat_conn = !npfctl_addr_iszero(&a[2]) || p[2] != 0;
    438  1.1  rmind 	if (fil->nat && !nat_conn) {
    439  1.1  rmind 		return 0;
    440  1.1  rmind 	}
    441  1.1  rmind 
    442  1.1  rmind 	fmt = fil->name ? "%A" : (fil->v4 ? "%a" : "[%a]");
    443  1.1  rmind 
    444  1.1  rmind 	addrstr = npfctl_print_addrmask(alen, fmt, &a[0], NPF_NO_NETMASK);
    445  1.1  rmind 	easprintf(&src, "%s:%d", addrstr, p[0]);
    446  1.1  rmind 	free(addrstr);
    447  1.1  rmind 
    448  1.1  rmind 	addrstr = npfctl_print_addrmask(alen, fmt, &a[1], NPF_NO_NETMASK);
    449  1.1  rmind 	easprintf(&dst, "%s:%d", addrstr, p[1]);
    450  1.1  rmind 	free(addrstr);
    451  1.1  rmind 
    452  1.1  rmind 	fprintf(fp, "%-*s %-*s ", fil->pwidth, src, fil->pwidth, dst);
    453  1.1  rmind 	free(src);
    454  1.1  rmind 	free(dst);
    455  1.1  rmind 
    456  1.1  rmind 	fprintf(fp, "%-10s ", ifname ? ifname : "-");
    457  1.1  rmind 	if (nat_conn) {
    458  1.1  rmind 		addrstr = npfctl_print_addrmask(alen, fmt, &a[2], NPF_NO_NETMASK);
    459  1.1  rmind 		fprintf(fp, "%s", addrstr);
    460  1.1  rmind 		free(addrstr);
    461  1.1  rmind 		if (p[2]) {
    462  1.1  rmind 			fprintf(fp, ":%d", p[2]);
    463  1.1  rmind 		}
    464  1.1  rmind 	}
    465  1.1  rmind 	fputc('\n', fp);
    466  1.1  rmind 	return 1;
    467  1.1  rmind }
    468  1.1  rmind 
    469  1.1  rmind static void
    470  1.1  rmind npf_conn_list_v(int fd, unsigned alen, npf_conn_filter_t *f)
    471  1.1  rmind {
    472  1.1  rmind 	f->alen = alen;
    473  1.1  rmind 	f->v4 = alen == sizeof(struct in_addr);
    474  1.1  rmind 	f->pwidth = f->nowide ? 0 : ((f->v4 ? 15 : 40) + 1 + 5);
    475  1.1  rmind 	if (npf_conn_list(fd, npfctl_conn_print, f) != 0) {
    476  1.1  rmind 		err(EXIT_FAILURE, "npf_conn_list");
    477  1.1  rmind 	}
    478  1.1  rmind }
    479  1.1  rmind 
    480  1.1  rmind int
    481  1.1  rmind npfctl_conn_list(int fd, int argc, char **argv)
    482  1.1  rmind {
    483  1.1  rmind 	npf_conn_filter_t f;
    484  1.1  rmind 	bool header = true;
    485  1.1  rmind 	unsigned alen = 0;
    486  1.1  rmind 	int c;
    487  1.1  rmind 
    488  1.1  rmind 	argc--;
    489  1.1  rmind 	argv++;
    490  1.1  rmind 
    491  1.1  rmind 	memset(&f, 0, sizeof(f));
    492  1.1  rmind 	f.fp = stdout;
    493  1.1  rmind 
    494  1.1  rmind 	while ((c = getopt(argc, argv, "46hi:nNW")) != -1) {
    495  1.1  rmind 		switch (c) {
    496  1.1  rmind 		case '4':
    497  1.1  rmind 			alen = sizeof(struct in_addr);
    498  1.1  rmind 			break;
    499  1.1  rmind 		case '6':
    500  1.1  rmind 			alen = sizeof(struct in6_addr);
    501  1.1  rmind 			break;
    502  1.1  rmind 		case 'h':
    503  1.1  rmind 			header = false;
    504  1.1  rmind 			break;
    505  1.1  rmind 		case 'i':
    506  1.1  rmind 			f.ifname = optarg;
    507  1.1  rmind 			break;
    508  1.1  rmind 		case 'n':
    509  1.1  rmind 			f.nat = true;
    510  1.1  rmind 			break;
    511  1.1  rmind 		case 'N':
    512  1.1  rmind 			f.name = true;
    513  1.1  rmind 			break;
    514  1.1  rmind 		case 'W':
    515  1.1  rmind 			f.nowide = true;
    516  1.1  rmind 			break;
    517  1.1  rmind 		default:
    518  1.1  rmind 			errx(EXIT_FAILURE,
    519  1.1  rmind 			    "Usage: %s list [-46hnNW] [-i <ifname>]\n",
    520  1.1  rmind 			    getprogname());
    521  1.1  rmind 		}
    522  1.1  rmind 	}
    523  1.1  rmind 
    524  1.1  rmind 	if (header) {
    525  1.1  rmind 		fprintf(f.fp, "# %-*s %-*s %-*s %s\n",
    526  1.1  rmind 		    21 - 2, "src-addr:port",
    527  1.1  rmind 		    21, "dst-addr:port",
    528  1.1  rmind 		    10, "interface",
    529  1.1  rmind 		    "nat-addr:port");
    530  1.1  rmind 	}
    531  1.1  rmind 
    532  1.1  rmind 	if (!alen || alen == sizeof(struct in_addr)) {
    533  1.1  rmind 		npf_conn_list_v(fd, sizeof(struct in_addr), &f);
    534  1.1  rmind 	}
    535  1.1  rmind 	if (!alen || alen == sizeof(struct in6_addr)) {
    536  1.1  rmind 		npf_conn_list_v(fd, sizeof(struct in6_addr), &f);
    537  1.1  rmind 	}
    538  1.1  rmind 
    539  1.1  rmind 	return 0;
    540  1.1  rmind }
    541