Home | History | Annotate | Line # | Download | only in npfctl
npf_data.c revision 1.6
      1  1.6    rmind /*	$NetBSD: npf_data.c,v 1.6 2011/01/18 20:33:45 rmind Exp $	*/
      2  1.1    rmind 
      3  1.1    rmind /*-
      4  1.6    rmind  * Copyright (c) 2009-2011 The NetBSD Foundation, Inc.
      5  1.1    rmind  * All rights reserved.
      6  1.1    rmind  *
      7  1.1    rmind  * Redistribution and use in source and binary forms, with or without
      8  1.1    rmind  * modification, are permitted provided that the following conditions
      9  1.1    rmind  * are met:
     10  1.1    rmind  * 1. Redistributions of source code must retain the above copyright
     11  1.1    rmind  *    notice, this list of conditions and the following disclaimer.
     12  1.1    rmind  * 2. Redistributions in binary form must reproduce the above copyright
     13  1.1    rmind  *    notice, this list of conditions and the following disclaimer in the
     14  1.1    rmind  *    documentation and/or other materials provided with the distribution.
     15  1.1    rmind  *
     16  1.1    rmind  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     17  1.1    rmind  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     18  1.1    rmind  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     19  1.1    rmind  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     20  1.1    rmind  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     21  1.1    rmind  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     22  1.1    rmind  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     23  1.1    rmind  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     24  1.1    rmind  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     25  1.1    rmind  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     26  1.1    rmind  * POSSIBILITY OF SUCH DAMAGE.
     27  1.1    rmind  */
     28  1.1    rmind 
     29  1.1    rmind /*
     30  1.1    rmind  * NPF proplib(9) dictionary producer.
     31  1.1    rmind  *
     32  1.1    rmind  * XXX: Needs some clean-up.
     33  1.1    rmind  */
     34  1.1    rmind 
     35  1.4    rmind #include <sys/cdefs.h>
     36  1.6    rmind __RCSID("$NetBSD: npf_data.c,v 1.6 2011/01/18 20:33:45 rmind Exp $");
     37  1.4    rmind 
     38  1.1    rmind #include <sys/types.h>
     39  1.1    rmind #include <sys/socket.h>
     40  1.1    rmind #include <sys/ioctl.h>
     41  1.1    rmind #include <net/if.h>
     42  1.3    rmind #include <netinet/tcp.h>
     43  1.1    rmind 
     44  1.1    rmind #include <arpa/inet.h>
     45  1.1    rmind #include <prop/proplib.h>
     46  1.1    rmind 
     47  1.1    rmind #include <stdlib.h>
     48  1.1    rmind #include <string.h>
     49  1.1    rmind #include <unistd.h>
     50  1.1    rmind #include <ctype.h>
     51  1.1    rmind #include <err.h>
     52  1.1    rmind #include <ifaddrs.h>
     53  1.1    rmind #include <netdb.h>
     54  1.1    rmind #include <assert.h>
     55  1.1    rmind 
     56  1.1    rmind #include "npfctl.h"
     57  1.1    rmind 
     58  1.1    rmind static struct ifaddrs *		ifs_list = NULL;
     59  1.1    rmind 
     60  1.1    rmind static prop_dictionary_t	npf_dict, settings_dict;
     61  1.6    rmind static prop_array_t		nat_arr, tables_arr, rproc_arr, rules_arr;
     62  1.1    rmind 
     63  1.1    rmind static pri_t			gr_prio_counter = 1;
     64  1.1    rmind static pri_t			rl_prio_counter = 1;
     65  1.1    rmind static pri_t			nat_prio_counter = 1;
     66  1.6    rmind static u_int			rproc_id_counter = 1;
     67  1.1    rmind 
     68  1.1    rmind void
     69  1.1    rmind npfctl_init_data(void)
     70  1.1    rmind {
     71  1.1    rmind 
     72  1.1    rmind 	if (getifaddrs(&ifs_list) == -1)
     73  1.1    rmind 		err(EXIT_FAILURE, "getifaddrs");
     74  1.1    rmind 
     75  1.1    rmind 	npf_dict = prop_dictionary_create();
     76  1.1    rmind 
     77  1.1    rmind 	nat_arr = prop_array_create();
     78  1.3    rmind 	prop_dictionary_set(npf_dict, "translation", nat_arr);
     79  1.1    rmind 
     80  1.1    rmind 	settings_dict = prop_dictionary_create();
     81  1.1    rmind 	prop_dictionary_set(npf_dict, "settings", settings_dict);
     82  1.1    rmind 
     83  1.1    rmind 	tables_arr = prop_array_create();
     84  1.1    rmind 	prop_dictionary_set(npf_dict, "tables", tables_arr);
     85  1.1    rmind 
     86  1.6    rmind 	rproc_arr = prop_array_create();
     87  1.6    rmind 	prop_dictionary_set(npf_dict, "rprocs", rproc_arr);
     88  1.6    rmind 
     89  1.1    rmind 	rules_arr = prop_array_create();
     90  1.1    rmind 	prop_dictionary_set(npf_dict, "rules", rules_arr);
     91  1.1    rmind }
     92  1.1    rmind 
     93  1.1    rmind int
     94  1.1    rmind npfctl_ioctl_send(int fd)
     95  1.1    rmind {
     96  1.1    rmind 	int ret = 0, errval;
     97  1.1    rmind 
     98  1.6    rmind #ifdef _NPF_TESTING
     99  1.3    rmind 	prop_dictionary_externalize_to_file(npf_dict, "./npf.plist");
    100  1.1    rmind #else
    101  1.1    rmind 	errval = prop_dictionary_send_ioctl(npf_dict, fd, IOC_NPF_RELOAD);
    102  1.1    rmind 	if (errval) {
    103  1.6    rmind 		errx(EXIT_FAILURE, "npfctl_ioctl_send: %s\n", strerror(errval));
    104  1.1    rmind 	}
    105  1.1    rmind #endif
    106  1.1    rmind 	prop_object_release(npf_dict);
    107  1.1    rmind 	return ret;
    108  1.1    rmind }
    109  1.1    rmind 
    110  1.5    rmind int
    111  1.6    rmind npfctl_ioctl_flushse(int fd)
    112  1.6    rmind {
    113  1.6    rmind 	prop_dictionary_t sesdict;
    114  1.6    rmind 	prop_array_t selist;
    115  1.6    rmind 	int errval;
    116  1.6    rmind 
    117  1.6    rmind 	sesdict = prop_dictionary_create();
    118  1.6    rmind 	selist = prop_array_create();
    119  1.6    rmind 	prop_dictionary_set(sesdict, "session-list", selist);
    120  1.6    rmind 	errval = prop_dictionary_send_ioctl(sesdict, fd, IOC_NPF_SESSIONS_LOAD);
    121  1.6    rmind 	if (errval) {
    122  1.6    rmind 		errx(EXIT_FAILURE, "npfctl_ioctl_flushse: %s\n",
    123  1.6    rmind 		    strerror(errval));
    124  1.6    rmind 	}
    125  1.6    rmind 	prop_object_release(sesdict);
    126  1.6    rmind 	return errval;
    127  1.6    rmind }
    128  1.6    rmind 
    129  1.6    rmind int
    130  1.5    rmind npfctl_ioctl_sendse(int fd)
    131  1.5    rmind {
    132  1.5    rmind 	prop_dictionary_t sesdict;
    133  1.5    rmind 	int error;
    134  1.5    rmind 
    135  1.5    rmind 	sesdict = prop_dictionary_internalize_from_file(NPF_SESSDB_PATH);
    136  1.5    rmind 	if (sesdict == NULL) {
    137  1.5    rmind 		errx(EXIT_FAILURE, "npfctl: no sessions saved "
    138  1.5    rmind 		    "('%s' does not exist)", NPF_SESSDB_PATH);
    139  1.5    rmind 	}
    140  1.5    rmind 	error = prop_dictionary_send_ioctl(sesdict, fd, IOC_NPF_SESSIONS_LOAD);
    141  1.5    rmind 	prop_object_release(sesdict);
    142  1.5    rmind 	if (error) {
    143  1.5    rmind 		err(EXIT_FAILURE, "npfctl_ioctl_sendse");
    144  1.5    rmind 	}
    145  1.5    rmind 	return 0;
    146  1.5    rmind }
    147  1.5    rmind 
    148  1.5    rmind int
    149  1.5    rmind npfctl_ioctl_recvse(int fd)
    150  1.5    rmind {
    151  1.5    rmind 	prop_dictionary_t sesdict;
    152  1.5    rmind 	int error;
    153  1.5    rmind 
    154  1.5    rmind 	error = prop_dictionary_recv_ioctl(fd, IOC_NPF_SESSIONS_SAVE, &sesdict);
    155  1.5    rmind 	if (error) {
    156  1.5    rmind 		err(EXIT_FAILURE, "prop_array_recv_ioctl");
    157  1.5    rmind 	}
    158  1.5    rmind 	if (!prop_dictionary_externalize_to_file(sesdict, NPF_SESSDB_PATH)) {
    159  1.5    rmind 		errx(EXIT_FAILURE, "could not save to '%s'", NPF_SESSDB_PATH);
    160  1.5    rmind 	}
    161  1.5    rmind 	prop_object_release(sesdict);
    162  1.5    rmind 	return 0;
    163  1.5    rmind }
    164  1.5    rmind 
    165  1.1    rmind /*
    166  1.1    rmind  * Helper routines:
    167  1.1    rmind  *
    168  1.1    rmind  *	npfctl_getif() - get interface addresses and index number from name.
    169  1.1    rmind  *	npfctl_parse_v4mask() - parse address/mask integers from CIDR block.
    170  1.3    rmind  *	npfctl_parse_port() - parse port number (which may be a service name).
    171  1.3    rmind  *	npfctl_parse_tcpfl() - parse TCP flags.
    172  1.1    rmind  */
    173  1.1    rmind 
    174  1.6    rmind struct ifaddrs *
    175  1.6    rmind npfctl_getif(char *ifname, unsigned int *if_idx, bool reqaddr)
    176  1.1    rmind {
    177  1.1    rmind 	struct ifaddrs *ifent;
    178  1.1    rmind 	struct sockaddr_in *sin;
    179  1.1    rmind 
    180  1.1    rmind 	for (ifent = ifs_list; ifent != NULL; ifent = ifent->ifa_next) {
    181  1.1    rmind 		sin = (struct sockaddr_in *)ifent->ifa_addr;
    182  1.6    rmind 		if (sin->sin_family != AF_INET && reqaddr)
    183  1.1    rmind 			continue;
    184  1.1    rmind 		if (strcmp(ifent->ifa_name, ifname) == 0)
    185  1.1    rmind 			break;
    186  1.1    rmind 	}
    187  1.1    rmind 	if (ifent) {
    188  1.1    rmind 		*if_idx = if_nametoindex(ifname);
    189  1.1    rmind 	}
    190  1.1    rmind 	return ifent;
    191  1.1    rmind }
    192  1.1    rmind 
    193  1.1    rmind bool
    194  1.3    rmind npfctl_parse_v4mask(char *ostr, in_addr_t *addr, in_addr_t *mask)
    195  1.1    rmind {
    196  1.3    rmind 	char *str = xstrdup(ostr);
    197  1.1    rmind 	char *p = strchr(str, '/');
    198  1.1    rmind 	u_int bits;
    199  1.3    rmind 	bool ret;
    200  1.1    rmind 
    201  1.1    rmind 	/* In network byte order. */
    202  1.1    rmind 	if (p) {
    203  1.1    rmind 		*p++ = '\0';
    204  1.1    rmind 		bits = (u_int)atoi(p);
    205  1.1    rmind 		*mask = bits ? htonl(0xffffffff << (32 - bits)) : 0;
    206  1.1    rmind 	} else {
    207  1.1    rmind 		*mask = 0xffffffff;
    208  1.1    rmind 	}
    209  1.3    rmind 	ret = inet_aton(str, (struct in_addr *)addr) != 0;
    210  1.3    rmind 	free(str);
    211  1.3    rmind 	return ret;
    212  1.3    rmind }
    213  1.3    rmind 
    214  1.3    rmind static bool
    215  1.3    rmind npfctl_parse_port(char *ostr, bool *range, in_port_t *fport, in_port_t *tport)
    216  1.3    rmind {
    217  1.3    rmind 	char *str = xstrdup(ostr), *sep;
    218  1.3    rmind 
    219  1.3    rmind 	*range = false;
    220  1.3    rmind 	if ((sep = strchr(str, ':')) != NULL) {
    221  1.3    rmind 		/* Port range (only numeric). */
    222  1.3    rmind 		*range = true;
    223  1.3    rmind 		*sep = '\0';
    224  1.3    rmind 
    225  1.3    rmind 	} else if (isalpha((unsigned char)*str)) {
    226  1.3    rmind 		struct servent *se;
    227  1.3    rmind 
    228  1.3    rmind 		se = getservbyname(str, NULL);
    229  1.3    rmind 		if (se == NULL) {
    230  1.3    rmind 			free(str);
    231  1.3    rmind 			return false;
    232  1.3    rmind 		}
    233  1.3    rmind 		*fport = se->s_port;
    234  1.3    rmind 	} else {
    235  1.3    rmind 		*fport = htons(atoi(str));
    236  1.3    rmind 	}
    237  1.3    rmind 	*tport = sep ? htons(atoi(sep + 1)) : *fport;
    238  1.3    rmind 	free(str);
    239  1.3    rmind 	return true;
    240  1.1    rmind }
    241  1.1    rmind 
    242  1.1    rmind static void
    243  1.1    rmind npfctl_parse_cidr(char *str, in_addr_t *addr, in_addr_t *mask)
    244  1.1    rmind {
    245  1.1    rmind 
    246  1.6    rmind 	if (strcmp(str, "any") == 0) {
    247  1.6    rmind 		*addr = 0x0;
    248  1.6    rmind 		*mask = 0x0;
    249  1.6    rmind 
    250  1.6    rmind 	} else if (isalpha((unsigned char)*str)) {
    251  1.1    rmind 		struct ifaddrs *ifa;
    252  1.1    rmind 		struct sockaddr_in *sin;
    253  1.1    rmind 		u_int idx;
    254  1.1    rmind 
    255  1.6    rmind 		if ((ifa = npfctl_getif(str, &idx, true)) == NULL) {
    256  1.1    rmind 			errx(EXIT_FAILURE, "invalid interface '%s'", str);
    257  1.1    rmind 		}
    258  1.1    rmind 		/* Interface address. */
    259  1.1    rmind 		sin = (struct sockaddr_in *)ifa->ifa_addr;
    260  1.1    rmind 		*addr = sin->sin_addr.s_addr;
    261  1.1    rmind 		*mask = 0xffffffff;
    262  1.1    rmind 
    263  1.1    rmind 	} else if (!npfctl_parse_v4mask(str, addr, mask)) {
    264  1.1    rmind 		errx(EXIT_FAILURE, "invalid CIDR '%s'\n", str);
    265  1.1    rmind 	}
    266  1.1    rmind }
    267  1.1    rmind 
    268  1.3    rmind static bool
    269  1.3    rmind npfctl_parse_tcpfl(char *s, uint8_t *tfl, uint8_t *tfl_mask)
    270  1.3    rmind {
    271  1.3    rmind 	uint8_t tcpfl = 0;
    272  1.3    rmind 	bool mask = false;
    273  1.3    rmind 
    274  1.3    rmind 	while (*s) {
    275  1.3    rmind 		switch (*s) {
    276  1.3    rmind 		case 'F': tcpfl |= TH_FIN; break;
    277  1.3    rmind 		case 'S': tcpfl |= TH_SYN; break;
    278  1.3    rmind 		case 'R': tcpfl |= TH_RST; break;
    279  1.3    rmind 		case 'P': tcpfl |= TH_PUSH; break;
    280  1.3    rmind 		case 'A': tcpfl |= TH_ACK; break;
    281  1.3    rmind 		case 'U': tcpfl |= TH_URG; break;
    282  1.3    rmind 		case 'E': tcpfl |= TH_ECE; break;
    283  1.3    rmind 		case 'W': tcpfl |= TH_CWR; break;
    284  1.3    rmind 		case '/':
    285  1.3    rmind 			*s = '\0';
    286  1.3    rmind 			*tfl = tcpfl;
    287  1.3    rmind 			tcpfl = 0;
    288  1.3    rmind 			mask = true;
    289  1.3    rmind 			break;
    290  1.3    rmind 		default:
    291  1.3    rmind 			return false;
    292  1.3    rmind 		}
    293  1.3    rmind 		s++;
    294  1.3    rmind 	}
    295  1.3    rmind 	if (!mask) {
    296  1.3    rmind 		*tfl = tcpfl;
    297  1.3    rmind 	}
    298  1.3    rmind 	*tfl_mask = tcpfl;
    299  1.3    rmind 	return true;
    300  1.3    rmind }
    301  1.3    rmind 
    302  1.1    rmind /*
    303  1.1    rmind  * NPF table creation and construction routines.
    304  1.1    rmind  */
    305  1.1    rmind 
    306  1.1    rmind prop_dictionary_t
    307  1.1    rmind npfctl_lookup_table(char *tidstr)
    308  1.1    rmind {
    309  1.1    rmind 	prop_dictionary_t tl;
    310  1.1    rmind 	prop_object_iterator_t it;
    311  1.1    rmind 	prop_object_t obj;
    312  1.1    rmind 	u_int tid;
    313  1.1    rmind 
    314  1.1    rmind 	tid = atoi(tidstr);
    315  1.6    rmind 	it = prop_array_iterator(tables_arr);
    316  1.1    rmind 	while ((tl = prop_object_iterator_next(it)) != NULL) {
    317  1.1    rmind 		obj = prop_dictionary_get(tl, "id");
    318  1.1    rmind 		if (tid == prop_number_integer_value(obj))
    319  1.1    rmind 			break;
    320  1.1    rmind 	}
    321  1.1    rmind 	return tl;
    322  1.1    rmind }
    323  1.1    rmind 
    324  1.1    rmind prop_dictionary_t
    325  1.6    rmind npfctl_construct_table(int id, int type)
    326  1.1    rmind {
    327  1.1    rmind 	prop_dictionary_t tl;
    328  1.1    rmind 
    329  1.1    rmind 	tl = prop_dictionary_create();
    330  1.1    rmind 	/* TODO: 1. check ID range 2. check if not a duplicate */
    331  1.1    rmind 	prop_dictionary_set(tl, "id", prop_number_create_integer(id));
    332  1.6    rmind 	prop_dictionary_set(tl, "type", prop_number_create_integer(type));
    333  1.6    rmind 	prop_dictionary_set(tl, "entries", prop_array_create());
    334  1.6    rmind 	prop_array_add(tables_arr, tl);
    335  1.6    rmind 	return tl;
    336  1.1    rmind }
    337  1.1    rmind 
    338  1.1    rmind void
    339  1.6    rmind npfctl_fill_table(prop_dictionary_t tl, char *fname)
    340  1.1    rmind {
    341  1.1    rmind 	prop_dictionary_t entdict;
    342  1.1    rmind 	prop_array_t tblents;
    343  1.1    rmind 	char *buf;
    344  1.1    rmind 	FILE *fp;
    345  1.1    rmind 	size_t n;
    346  1.1    rmind 	int l;
    347  1.1    rmind 
    348  1.1    rmind 	tblents = prop_dictionary_get(tl, "entries");
    349  1.1    rmind 	assert(tblents != NULL);
    350  1.1    rmind 
    351  1.1    rmind 	fp = fopen(fname, "r");
    352  1.1    rmind 	if (fp == NULL) {
    353  1.6    rmind 		err(EXIT_FAILURE, "open '%s'", fname);
    354  1.1    rmind 	}
    355  1.1    rmind 	l = 1;
    356  1.1    rmind 	buf = NULL;
    357  1.1    rmind 	while (getline(&buf, &n, fp) != -1) {
    358  1.1    rmind 		in_addr_t addr, mask;
    359  1.1    rmind 
    360  1.1    rmind 		if (*buf == '\n' || *buf == '#')
    361  1.1    rmind 			continue;
    362  1.1    rmind 
    363  1.1    rmind 		/* IPv4 CIDR: a.b.c.d/mask */
    364  1.1    rmind 		if (!npfctl_parse_v4mask(buf, &addr, &mask))
    365  1.1    rmind 			errx(EXIT_FAILURE, "invalid table entry at line %d", l);
    366  1.1    rmind 
    367  1.1    rmind 		/* Create and add table entry. */
    368  1.1    rmind 		entdict = prop_dictionary_create();
    369  1.1    rmind 		prop_dictionary_set(entdict, "addr",
    370  1.1    rmind 		    prop_number_create_integer(addr));
    371  1.1    rmind 		prop_dictionary_set(entdict, "mask",
    372  1.1    rmind 		    prop_number_create_integer(mask));
    373  1.1    rmind 		prop_array_add(tblents, entdict);
    374  1.1    rmind 		l++;
    375  1.1    rmind 	}
    376  1.1    rmind 	if (buf != NULL) {
    377  1.1    rmind 		free(buf);
    378  1.1    rmind 	}
    379  1.1    rmind }
    380  1.1    rmind 
    381  1.1    rmind /*
    382  1.1    rmind  * npfctl_mk_rule: create a rule (or group) dictionary.
    383  1.1    rmind  *
    384  1.1    rmind  * Note: group is a rule containing subrules.  It has no n-code, however.
    385  1.1    rmind  */
    386  1.1    rmind prop_dictionary_t
    387  1.6    rmind npfctl_mk_rule(bool group, prop_dictionary_t parent)
    388  1.1    rmind {
    389  1.1    rmind 	prop_dictionary_t rl;
    390  1.6    rmind 	prop_array_t subrl, rlset;
    391  1.1    rmind 	pri_t pri;
    392  1.1    rmind 
    393  1.1    rmind 	rl = prop_dictionary_create();
    394  1.1    rmind 	if (group) {
    395  1.1    rmind 		subrl = prop_array_create();
    396  1.1    rmind 		prop_dictionary_set(rl, "subrules", subrl);
    397  1.1    rmind 		/* Give new priority, reset rule priority counter. */
    398  1.1    rmind 		pri = gr_prio_counter++;
    399  1.1    rmind 		rl_prio_counter = 1;
    400  1.1    rmind 	} else {
    401  1.1    rmind 		pri = rl_prio_counter++;
    402  1.1    rmind 	}
    403  1.6    rmind 	prop_dictionary_set(rl, "priority", prop_number_create_integer(pri));
    404  1.1    rmind 
    405  1.1    rmind 	if (parent) {
    406  1.1    rmind 		rlset = prop_dictionary_get(parent, "subrules");
    407  1.1    rmind 		assert(rlset != NULL);
    408  1.1    rmind 	} else {
    409  1.1    rmind 		rlset = rules_arr;
    410  1.1    rmind 	}
    411  1.1    rmind 	prop_array_add(rlset, rl);
    412  1.6    rmind 	return rl;
    413  1.1    rmind }
    414  1.1    rmind 
    415  1.1    rmind void
    416  1.6    rmind npfctl_rule_setattr(prop_dictionary_t rl, int attr, u_int iface)
    417  1.1    rmind {
    418  1.5    rmind 	prop_number_t attrnum, ifnum;
    419  1.1    rmind 
    420  1.1    rmind 	attrnum = prop_number_create_integer(attr);
    421  1.1    rmind 	prop_dictionary_set(rl, "attributes", attrnum);
    422  1.1    rmind 	if (iface) {
    423  1.6    rmind 		ifnum = prop_number_create_integer(iface);
    424  1.1    rmind 		prop_dictionary_set(rl, "interface", ifnum);
    425  1.1    rmind 	}
    426  1.1    rmind }
    427  1.1    rmind 
    428  1.1    rmind /*
    429  1.1    rmind  * Main rule generation routines.
    430  1.1    rmind  */
    431  1.1    rmind 
    432  1.1    rmind static void
    433  1.1    rmind npfctl_rulenc_v4cidr(void **nc, int nblocks[], var_t *dat, bool sd)
    434  1.1    rmind {
    435  1.1    rmind 	element_t *el = dat->v_elements;
    436  1.1    rmind 	int foff;
    437  1.1    rmind 
    438  1.1    rmind 	/* If table, generate a single table matching block. */
    439  1.1    rmind 	if (dat->v_type == VAR_TABLE) {
    440  1.1    rmind 		u_int tid = atoi(el->e_data);
    441  1.1    rmind 
    442  1.1    rmind 		nblocks[0]--;
    443  1.1    rmind 		foff = npfctl_failure_offset(nblocks);
    444  1.1    rmind 		npfctl_gennc_tbl(nc, foff, tid, sd);
    445  1.1    rmind 		return;
    446  1.1    rmind 	}
    447  1.1    rmind 
    448  1.1    rmind 	/* Generate v4 CIDR matching blocks. */
    449  1.1    rmind 	for (el = dat->v_elements; el != NULL; el = el->e_next) {
    450  1.1    rmind 		in_addr_t addr, mask;
    451  1.1    rmind 
    452  1.1    rmind 		npfctl_parse_cidr(el->e_data, &addr, &mask);
    453  1.1    rmind 
    454  1.1    rmind 		nblocks[1]--;
    455  1.1    rmind 		foff = npfctl_failure_offset(nblocks);
    456  1.1    rmind 		npfctl_gennc_v4cidr(nc, foff, addr, mask, sd);
    457  1.1    rmind 	}
    458  1.1    rmind }
    459  1.1    rmind 
    460  1.1    rmind static void
    461  1.5    rmind npfctl_rulenc_ports(void **nc, int nblocks[], var_t *dat, bool tcpudp,
    462  1.5    rmind     bool both, bool sd)
    463  1.1    rmind {
    464  1.1    rmind 	element_t *el = dat->v_elements;
    465  1.1    rmind 	int foff;
    466  1.1    rmind 
    467  1.1    rmind 	assert(dat->v_type != VAR_TABLE);
    468  1.1    rmind 
    469  1.1    rmind 	/* Generate TCP/UDP port matching blocks. */
    470  1.1    rmind 	for (el = dat->v_elements; el != NULL; el = el->e_next) {
    471  1.3    rmind 		in_port_t fport, tport;
    472  1.3    rmind 		bool range;
    473  1.1    rmind 
    474  1.3    rmind 		if (!npfctl_parse_port(el->e_data, &range, &fport, &tport)) {
    475  1.3    rmind 			errx(EXIT_FAILURE, "invalid service '%s'", el->e_data);
    476  1.1    rmind 		}
    477  1.1    rmind 		nblocks[0]--;
    478  1.5    rmind 		foff = both ? 0 : npfctl_failure_offset(nblocks);
    479  1.3    rmind 		npfctl_gennc_ports(nc, foff, fport, tport, tcpudp, sd);
    480  1.1    rmind 	}
    481  1.1    rmind }
    482  1.1    rmind 
    483  1.1    rmind static void
    484  1.1    rmind npfctl_rulenc_block(void **nc, int nblocks[], var_t *cidr, var_t *ports,
    485  1.1    rmind     bool both, bool tcpudp, bool sd)
    486  1.1    rmind {
    487  1.1    rmind 
    488  1.1    rmind 	npfctl_rulenc_v4cidr(nc, nblocks, cidr, sd);
    489  1.1    rmind 	if (ports == NULL) {
    490  1.1    rmind 		return;
    491  1.1    rmind 	}
    492  1.5    rmind 	npfctl_rulenc_ports(nc, nblocks, ports, tcpudp, both, sd);
    493  1.1    rmind 	if (!both) {
    494  1.1    rmind 		return;
    495  1.1    rmind 	}
    496  1.5    rmind 	npfctl_rulenc_ports(nc, nblocks, ports, !tcpudp, false, sd);
    497  1.1    rmind }
    498  1.1    rmind 
    499  1.1    rmind void
    500  1.3    rmind npfctl_rule_protodata(prop_dictionary_t rl, char *proto, char *tcp_flags,
    501  1.3    rmind     int icmp_type, int icmp_code,
    502  1.3    rmind     var_t *from, var_t *fports, var_t *to, var_t *tports)
    503  1.1    rmind {
    504  1.1    rmind 	prop_data_t ncdata;
    505  1.1    rmind 	bool icmp, tcpudp, both;
    506  1.3    rmind 	int foff, nblocks[3] = { 0, 0, 0 };
    507  1.1    rmind 	void *ncptr, *nc;
    508  1.1    rmind 	size_t sz;
    509  1.1    rmind 
    510  1.1    rmind 	/*
    511  1.1    rmind 	 * Default: both TCP and UDP.
    512  1.1    rmind 	 */
    513  1.1    rmind 	icmp = false;
    514  1.1    rmind 	tcpudp = true;
    515  1.1    rmind 	if (proto == NULL) {
    516  1.5    rmind 		both = true;
    517  1.1    rmind 		goto skip_proto;
    518  1.1    rmind 	}
    519  1.5    rmind 	both = false;
    520  1.1    rmind 
    521  1.1    rmind 	if (strcmp(proto, "icmp") == 0) {
    522  1.1    rmind 		/* ICMP case. */
    523  1.1    rmind 		fports = NULL;
    524  1.1    rmind 		tports = NULL;
    525  1.1    rmind 		icmp = true;
    526  1.1    rmind 
    527  1.1    rmind 	} else if (strcmp(proto, "tcp") == 0) {
    528  1.1    rmind 		/* Just TCP. */
    529  1.1    rmind 		tcpudp = true;
    530  1.1    rmind 
    531  1.1    rmind 	} else if (strcmp(proto, "udp") == 0) {
    532  1.1    rmind 		/* Just UDP. */
    533  1.1    rmind 		tcpudp = false;
    534  1.1    rmind 
    535  1.1    rmind 	} else {
    536  1.1    rmind 		/* Default. */
    537  1.1    rmind 	}
    538  1.1    rmind skip_proto:
    539  1.6    rmind 	if (icmp || icmp_type != -1) {
    540  1.3    rmind 		assert(tcp_flags == NULL);
    541  1.3    rmind 		icmp = true;
    542  1.3    rmind 		nblocks[2] += 1;
    543  1.3    rmind 	}
    544  1.3    rmind 	if (tcpudp && tcp_flags) {
    545  1.3    rmind 		assert(icmp_type == -1 && icmp_code == -1);
    546  1.3    rmind 		nblocks[2] += 1;
    547  1.3    rmind 	}
    548  1.1    rmind 
    549  1.1    rmind 	/* Calculate how blocks to determince n-code. */
    550  1.1    rmind 	if (from && from->v_count) {
    551  1.1    rmind 		if (from->v_type == VAR_TABLE)
    552  1.1    rmind 			nblocks[0] += 1;
    553  1.1    rmind 		else
    554  1.1    rmind 			nblocks[1] += from->v_count;
    555  1.1    rmind 		if (fports && fports->v_count)
    556  1.1    rmind 			nblocks[0] += fports->v_count * (both ? 2 : 1);
    557  1.1    rmind 	}
    558  1.1    rmind 	if (to && to->v_count) {
    559  1.1    rmind 		if (to->v_type == VAR_TABLE)
    560  1.1    rmind 			nblocks[0] += 1;
    561  1.1    rmind 		else
    562  1.1    rmind 			nblocks[1] += to->v_count;
    563  1.1    rmind 		if (tports && tports->v_count)
    564  1.1    rmind 			nblocks[0] += tports->v_count * (both ? 2 : 1);
    565  1.1    rmind 	}
    566  1.1    rmind 
    567  1.3    rmind 	/* Any n-code to generate? */
    568  1.6    rmind 	if (!icmp && (nblocks[0] + nblocks[1] + nblocks[2]) == 0) {
    569  1.3    rmind 		/* Done, if none. */
    570  1.3    rmind 		return;
    571  1.3    rmind 	}
    572  1.3    rmind 
    573  1.1    rmind 	/* Allocate memory for the n-code. */
    574  1.1    rmind 	sz = npfctl_calc_ncsize(nblocks);
    575  1.1    rmind 	ncptr = malloc(sz);
    576  1.1    rmind 	if (ncptr == NULL) {
    577  1.1    rmind 		perror("malloc");
    578  1.1    rmind 		exit(EXIT_FAILURE);
    579  1.1    rmind 	}
    580  1.1    rmind 	nc = ncptr;
    581  1.1    rmind 
    582  1.3    rmind 	/*
    583  1.3    rmind 	 * Generate v4 CIDR matching blocks and TCP/UDP port matching.
    584  1.3    rmind 	 */
    585  1.1    rmind 	if (from) {
    586  1.1    rmind 		npfctl_rulenc_block(&nc, nblocks, from, fports,
    587  1.1    rmind 		    both, tcpudp, true);
    588  1.1    rmind 	}
    589  1.1    rmind 	if (to) {
    590  1.1    rmind 		npfctl_rulenc_block(&nc, nblocks, to, tports,
    591  1.1    rmind 		    both, tcpudp, false);
    592  1.1    rmind 	}
    593  1.3    rmind 
    594  1.1    rmind 	if (icmp) {
    595  1.3    rmind 		/*
    596  1.3    rmind 		 * ICMP case.
    597  1.3    rmind 		 */
    598  1.3    rmind 		nblocks[2]--;
    599  1.3    rmind 		foff = npfctl_failure_offset(nblocks);
    600  1.3    rmind 		npfctl_gennc_icmp(&nc, foff, icmp_type, icmp_code);
    601  1.3    rmind 
    602  1.3    rmind 	} else if (tcpudp && tcp_flags) {
    603  1.3    rmind 		/*
    604  1.3    rmind 		 * TCP case, flags.
    605  1.3    rmind 		 */
    606  1.3    rmind 		uint8_t tfl = 0, tfl_mask;
    607  1.3    rmind 
    608  1.3    rmind 		nblocks[2]--;
    609  1.3    rmind 		foff = npfctl_failure_offset(nblocks);
    610  1.3    rmind 		if (!npfctl_parse_tcpfl(tcp_flags, &tfl, &tfl_mask)) {
    611  1.3    rmind 			errx(EXIT_FAILURE, "invalid TCP flags '%s'", tcp_flags);
    612  1.3    rmind 		}
    613  1.3    rmind 		npfctl_gennc_tcpfl(&nc, foff, tfl, tfl_mask);
    614  1.1    rmind 	}
    615  1.1    rmind 	npfctl_gennc_complete(&nc);
    616  1.1    rmind 
    617  1.3    rmind 	if ((uintptr_t)nc - (uintptr_t)ncptr != sz) {
    618  1.2  jnemeth 		errx(EXIT_FAILURE, "n-code size got wrong (%tu != %zu)",
    619  1.1    rmind 		    (uintptr_t)nc - (uintptr_t)ncptr, sz);
    620  1.3    rmind 	}
    621  1.1    rmind 
    622  1.1    rmind #ifdef DEBUG
    623  1.1    rmind 	uint32_t *op = ncptr;
    624  1.1    rmind 	size_t n = sz;
    625  1.1    rmind 	do {
    626  1.1    rmind 		DPRINTF(("\t> |0x%02x|\n", (u_int)*op));
    627  1.1    rmind 		op++;
    628  1.1    rmind 		n -= sizeof(*op);
    629  1.1    rmind 	} while (n);
    630  1.1    rmind #endif
    631  1.1    rmind 
    632  1.1    rmind 	/* Create a final memory block of data, ready to send. */
    633  1.1    rmind 	ncdata = prop_data_create_data(ncptr, sz);
    634  1.1    rmind 	if (ncdata == NULL) {
    635  1.1    rmind 		perror("prop_data_create_data");
    636  1.1    rmind 		exit(EXIT_FAILURE);
    637  1.1    rmind 	}
    638  1.1    rmind 	prop_dictionary_set(rl, "ncode", ncdata);
    639  1.1    rmind 	free(ncptr);
    640  1.1    rmind }
    641  1.1    rmind 
    642  1.1    rmind /*
    643  1.6    rmind  * Rule procedure construction routines.
    644  1.6    rmind  */
    645  1.6    rmind 
    646  1.6    rmind prop_dictionary_t
    647  1.6    rmind npfctl_mk_rproc(void)
    648  1.6    rmind {
    649  1.6    rmind 	prop_dictionary_t rp;
    650  1.6    rmind 
    651  1.6    rmind 	rp = prop_dictionary_create();
    652  1.6    rmind 	prop_dictionary_set(rp, "id",
    653  1.6    rmind 	    prop_number_create_unsigned_integer(rproc_id_counter++));
    654  1.6    rmind 	prop_array_add(rproc_arr, rp);
    655  1.6    rmind 	return rp;
    656  1.6    rmind }
    657  1.6    rmind 
    658  1.6    rmind bool
    659  1.6    rmind npfctl_find_rproc(prop_dictionary_t rl, char *name)
    660  1.6    rmind {
    661  1.6    rmind 	prop_dictionary_t rp;
    662  1.6    rmind 	prop_object_iterator_t it;
    663  1.6    rmind 	prop_object_t obj;
    664  1.6    rmind 
    665  1.6    rmind 	it = prop_array_iterator(rproc_arr);
    666  1.6    rmind 	while ((rp = prop_object_iterator_next(it)) != NULL) {
    667  1.6    rmind 		obj = prop_dictionary_get(rp, "name");
    668  1.6    rmind 		if (strcmp(prop_string_cstring(obj), name) == 0)
    669  1.6    rmind 			break;
    670  1.6    rmind 	}
    671  1.6    rmind 	if (rp == NULL) {
    672  1.6    rmind 		return false;
    673  1.6    rmind 	}
    674  1.6    rmind 	prop_dictionary_set(rl, "rproc-id", prop_dictionary_get(rp, "id"));
    675  1.6    rmind 	return true;
    676  1.6    rmind }
    677  1.6    rmind 
    678  1.6    rmind /*
    679  1.1    rmind  * NAT policy construction routines.
    680  1.1    rmind  */
    681  1.1    rmind 
    682  1.1    rmind prop_dictionary_t
    683  1.1    rmind npfctl_mk_nat(void)
    684  1.1    rmind {
    685  1.1    rmind 	prop_dictionary_t rl;
    686  1.1    rmind 	pri_t pri;
    687  1.1    rmind 
    688  1.1    rmind 	/* NAT policy is rule with extra info. */
    689  1.1    rmind 	rl = prop_dictionary_create();
    690  1.1    rmind 	pri = nat_prio_counter++;
    691  1.6    rmind 	prop_dictionary_set(rl, "priority", prop_number_create_integer(pri));
    692  1.6    rmind 	prop_array_add(nat_arr, rl);
    693  1.1    rmind 	return rl;
    694  1.1    rmind }
    695  1.1    rmind 
    696  1.1    rmind void
    697  1.3    rmind npfctl_nat_setup(prop_dictionary_t rl, int type, int flags,
    698  1.6    rmind     u_int iface, char *taddr, char *rport)
    699  1.1    rmind {
    700  1.3    rmind 	int attr = NPF_RULE_PASS | NPF_RULE_FINAL;
    701  1.6    rmind 	in_addr_t addr, _dummy;
    702  1.5    rmind 	prop_data_t addrdat;
    703  1.1    rmind 
    704  1.3    rmind 	/* Translation type and flags. */
    705  1.3    rmind 	prop_dictionary_set(rl, "type",
    706  1.3    rmind 	    prop_number_create_integer(type));
    707  1.3    rmind 	prop_dictionary_set(rl, "flags",
    708  1.3    rmind 	    prop_number_create_integer(flags));
    709  1.3    rmind 
    710  1.1    rmind 	/* Interface and attributes. */
    711  1.3    rmind 	attr |= (type == NPF_NATOUT) ? NPF_RULE_OUT : NPF_RULE_IN;
    712  1.6    rmind 	npfctl_rule_setattr(rl, attr, iface);
    713  1.1    rmind 
    714  1.6    rmind 	/* Translation IP. */
    715  1.6    rmind 	npfctl_parse_cidr(taddr, &addr, &_dummy);
    716  1.5    rmind 	addrdat = prop_data_create_data(&addr, sizeof(in_addr_t));
    717  1.5    rmind 	if (addrdat == NULL) {
    718  1.4    rmind 		err(EXIT_FAILURE, "prop_data_create_data");
    719  1.4    rmind 	}
    720  1.5    rmind 	prop_dictionary_set(rl, "translation-ip", addrdat);
    721  1.3    rmind 
    722  1.3    rmind 	/* Translation port (for redirect case). */
    723  1.3    rmind 	if (rport) {
    724  1.3    rmind 		in_port_t port;
    725  1.3    rmind 		bool range;
    726  1.3    rmind 
    727  1.3    rmind 		if (!npfctl_parse_port(rport, &range, &port, &port)) {
    728  1.3    rmind 			errx(EXIT_FAILURE, "invalid service '%s'", rport);
    729  1.3    rmind 		}
    730  1.3    rmind 		if (range) {
    731  1.3    rmind 			errx(EXIT_FAILURE, "range is not supported for 'rdr'");
    732  1.3    rmind 		}
    733  1.4    rmind 		prop_dictionary_set(rl, "translation-port",
    734  1.3    rmind 		    prop_number_create_integer(port));
    735  1.3    rmind 	}
    736  1.1    rmind }
    737