Home | History | Annotate | Line # | Download | only in npfctl
npf_data.c revision 1.1
      1 /*	$NetBSD: npf_data.c,v 1.1 2010/08/22 18:56:23 rmind Exp $	*/
      2 
      3 /*-
      4  * Copyright (c) 2009-2010 The NetBSD Foundation, Inc.
      5  * All rights reserved.
      6  *
      7  * Redistribution and use in source and binary forms, with or without
      8  * modification, are permitted provided that the following conditions
      9  * are met:
     10  * 1. Redistributions of source code must retain the above copyright
     11  *    notice, this list of conditions and the following disclaimer.
     12  * 2. Redistributions in binary form must reproduce the above copyright
     13  *    notice, this list of conditions and the following disclaimer in the
     14  *    documentation and/or other materials provided with the distribution.
     15  *
     16  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     17  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     18  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     19  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     20  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     21  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     22  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     23  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     24  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     25  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     26  * POSSIBILITY OF SUCH DAMAGE.
     27  */
     28 
     29 /*
     30  * NPF proplib(9) dictionary producer.
     31  *
     32  * XXX: Needs some clean-up.
     33  */
     34 
     35 #include <sys/types.h>
     36 #include <sys/socket.h>
     37 #include <sys/ioctl.h>
     38 #include <net/if.h>
     39 
     40 #include <arpa/inet.h>
     41 #include <prop/proplib.h>
     42 
     43 #include <stdlib.h>
     44 #include <string.h>
     45 #include <unistd.h>
     46 #include <ctype.h>
     47 #include <err.h>
     48 #include <ifaddrs.h>
     49 #include <netdb.h>
     50 #include <assert.h>
     51 
     52 #include "npfctl.h"
     53 
     54 static struct ifaddrs *		ifs_list = NULL;
     55 
     56 static prop_dictionary_t	npf_dict, settings_dict;
     57 static prop_array_t		nat_arr, tables_arr, rules_arr;
     58 
     59 static pri_t			gr_prio_counter = 1;
     60 static pri_t			rl_prio_counter = 1;
     61 static pri_t			nat_prio_counter = 1;
     62 
     63 void
     64 npfctl_init_data(void)
     65 {
     66 	prop_number_t ver;
     67 
     68 	if (getifaddrs(&ifs_list) == -1)
     69 		err(EXIT_FAILURE, "getifaddrs");
     70 
     71 	npf_dict = prop_dictionary_create();
     72 
     73 	ver = prop_number_create_integer(NPF_VERSION);
     74 	prop_dictionary_set(npf_dict, "version", ver);
     75 
     76 	nat_arr = prop_array_create();
     77 	prop_dictionary_set(npf_dict, "nat", nat_arr);
     78 
     79 	settings_dict = prop_dictionary_create();
     80 	prop_dictionary_set(npf_dict, "settings", settings_dict);
     81 
     82 	tables_arr = prop_array_create();
     83 	prop_dictionary_set(npf_dict, "tables", tables_arr);
     84 
     85 	rules_arr = prop_array_create();
     86 	prop_dictionary_set(npf_dict, "rules", rules_arr);
     87 }
     88 
     89 int
     90 npfctl_ioctl_send(int fd)
     91 {
     92 	int ret = 0, errval;
     93 
     94 #ifdef DEBUG
     95 	prop_dictionary_externalize_to_file(npf_dict, "/tmp/npf.plist");
     96 #else
     97 	errval = prop_dictionary_send_ioctl(npf_dict, fd, IOC_NPF_RELOAD);
     98 	if (errval) {
     99 		errx(EXIT_FAILURE, "npf_ioctl_send: %s\n", strerror(errval));
    100 		ret = -1;
    101 	}
    102 #endif
    103 	prop_object_release(npf_dict);
    104 	return ret;
    105 }
    106 
    107 /*
    108  * Helper routines:
    109  *
    110  *	npfctl_getif() - get interface addresses and index number from name.
    111  *	npfctl_servname2port() - get service ports from name.
    112  *	npfctl_parse_v4mask() - parse address/mask integers from CIDR block.
    113  */
    114 
    115 static struct ifaddrs *
    116 npfctl_getif(char *ifname, unsigned int *if_idx)
    117 {
    118 	struct ifaddrs *ifent;
    119 	struct sockaddr_in *sin;
    120 
    121 	for (ifent = ifs_list; ifent != NULL; ifent = ifent->ifa_next) {
    122 		sin = (struct sockaddr_in *)ifent->ifa_addr;
    123 
    124 		if (sin->sin_family != AF_INET)
    125 			continue;
    126 		if (strcmp(ifent->ifa_name, ifname) == 0)
    127 			break;
    128 	}
    129 	if (ifent) {
    130 		*if_idx = if_nametoindex(ifname);
    131 	}
    132 	return ifent;
    133 }
    134 
    135 static int
    136 npfctl_servname2port(char *name)
    137 {
    138 	struct servent *se;
    139 
    140 	se = getservbyname(name, NULL);
    141 	return se ? se->s_port : -1;
    142 }
    143 
    144 bool
    145 npfctl_parse_v4mask(char *str, in_addr_t *addr, in_addr_t *mask)
    146 {
    147 	char *p = strchr(str, '/');
    148 	u_int bits;
    149 
    150 	/* In network byte order. */
    151 	if (p) {
    152 		*p++ = '\0';
    153 		bits = (u_int)atoi(p);
    154 		*mask = bits ? htonl(0xffffffff << (32 - bits)) : 0;
    155 	} else {
    156 		*mask = 0xffffffff;
    157 	}
    158 	return inet_aton(str, (struct in_addr *)addr) != 0;
    159 }
    160 
    161 static void
    162 npfctl_parse_cidr(char *str, in_addr_t *addr, in_addr_t *mask)
    163 {
    164 
    165 	if (isalpha((unsigned char)*str)) {
    166 		struct ifaddrs *ifa;
    167 		struct sockaddr_in *sin;
    168 		u_int idx;
    169 
    170 		if ((ifa = npfctl_getif(str, &idx)) == NULL) {
    171 			errx(EXIT_FAILURE, "invalid interface '%s'", str);
    172 		}
    173 		/* Interface address. */
    174 		sin = (struct sockaddr_in *)ifa->ifa_addr;
    175 		*addr = sin->sin_addr.s_addr;
    176 		*mask = 0xffffffff;
    177 
    178 	} else if (!npfctl_parse_v4mask(str, addr, mask)) {
    179 		errx(EXIT_FAILURE, "invalid CIDR '%s'\n", str);
    180 	}
    181 }
    182 
    183 /*
    184  * NPF table creation and construction routines.
    185  */
    186 
    187 prop_dictionary_t
    188 npfctl_lookup_table(char *tidstr)
    189 {
    190 	prop_dictionary_t tl;
    191 	prop_object_iterator_t it;
    192 	prop_object_t obj;
    193 	u_int tid;
    194 
    195 	if ((it = prop_array_iterator(tables_arr)) == NULL)
    196 		err(EXIT_FAILURE, "prop_array_iterator");
    197 
    198 	tid = atoi(tidstr);
    199 	while ((tl = prop_object_iterator_next(it)) != NULL) {
    200 		obj = prop_dictionary_get(tl, "id");
    201 		if (tid == prop_number_integer_value(obj))
    202 			break;
    203 	}
    204 	return tl;
    205 }
    206 
    207 prop_dictionary_t
    208 npfctl_mk_table(void)
    209 {
    210 	prop_dictionary_t tl;
    211 	prop_array_t tlist;
    212 
    213 	tl = prop_dictionary_create();
    214 	tlist = prop_array_create();
    215 	prop_dictionary_set(tl, "entries", tlist);
    216 
    217 	return tl;
    218 }
    219 
    220 void
    221 npfctl_table_setup(prop_dictionary_t tl, char *idstr, char *typestr)
    222 {
    223 	prop_number_t typenum;
    224 	unsigned int id;
    225 
    226 	id = atoi(idstr);
    227 	/* TODO: 1. check ID range 2. check if not a duplicate */
    228 	prop_dictionary_set(tl, "id", prop_number_create_integer(id));
    229 
    230 	if (strcmp(typestr, "hash")) {
    231 		typenum = prop_number_create_integer(NPF_TABLE_HASH);
    232 	} else if (strcmp(typestr, "tree")) {
    233 		typenum = prop_number_create_integer(NPF_TABLE_RBTREE);
    234 	} else {
    235 		errx(EXIT_FAILURE, "invalid table type '%s'\n", typestr);
    236 	}
    237 	prop_dictionary_set(tl, "type", typenum);
    238 }
    239 
    240 void
    241 npfctl_construct_table(prop_dictionary_t tl, char *fname)
    242 {
    243 	prop_dictionary_t entdict;
    244 	prop_array_t tblents;
    245 	char *buf;
    246 	FILE *fp;
    247 	size_t n;
    248 	int l;
    249 
    250 	tblents = prop_dictionary_get(tl, "entries");
    251 	assert(tblents != NULL);
    252 
    253 	fp = fopen(fname, "r");
    254 	if (fp == NULL) {
    255 		err(EXIT_FAILURE, "fopen");
    256 	}
    257 	l = 1;
    258 	buf = NULL;
    259 	while (getline(&buf, &n, fp) != -1) {
    260 		in_addr_t addr, mask;
    261 
    262 		if (*buf == '\n' || *buf == '#')
    263 			continue;
    264 
    265 		/* IPv4 CIDR: a.b.c.d/mask */
    266 		if (!npfctl_parse_v4mask(buf, &addr, &mask))
    267 			errx(EXIT_FAILURE, "invalid table entry at line %d", l);
    268 
    269 		/* Create and add table entry. */
    270 		entdict = prop_dictionary_create();
    271 		prop_dictionary_set(entdict, "addr",
    272 		    prop_number_create_integer(addr));
    273 		prop_dictionary_set(entdict, "mask",
    274 		    prop_number_create_integer(mask));
    275 		prop_array_add(tblents, entdict);
    276 		l++;
    277 	}
    278 	if (buf != NULL) {
    279 		free(buf);
    280 	}
    281 }
    282 
    283 void
    284 npfctl_add_table(prop_dictionary_t tl)
    285 {
    286 
    287 	prop_array_add(tables_arr, tl);
    288 }
    289 
    290 /*
    291  * npfctl_mk_rule: create a rule (or group) dictionary.
    292  *
    293  * Note: group is a rule containing subrules.  It has no n-code, however.
    294  */
    295 prop_dictionary_t
    296 npfctl_mk_rule(bool group)
    297 {
    298 	prop_dictionary_t rl;
    299 	prop_array_t subrl;
    300 	pri_t pri;
    301 
    302 	rl = prop_dictionary_create();
    303 	if (group) {
    304 		subrl = prop_array_create();
    305 		prop_dictionary_set(rl, "subrules", subrl);
    306 		/* Give new priority, reset rule priority counter. */
    307 		pri = gr_prio_counter++;
    308 		rl_prio_counter = 1;
    309 	} else {
    310 		pri = rl_prio_counter++;
    311 	}
    312 	prop_dictionary_set(rl, "priority",
    313 	    prop_number_create_integer(pri));
    314 
    315 	return rl;
    316 }
    317 
    318 void
    319 npfctl_add_rule(prop_dictionary_t rl, prop_dictionary_t parent)
    320 {
    321 	prop_array_t rlset;
    322 
    323 	if (parent) {
    324 		rlset = prop_dictionary_get(parent, "subrules");
    325 		assert(rlset != NULL);
    326 	} else {
    327 		rlset = rules_arr;
    328 	}
    329 	prop_array_add(rlset, rl);
    330 }
    331 
    332 void
    333 npfctl_rule_setattr(prop_dictionary_t rl, int attr, char *iface)
    334 {
    335 	prop_number_t attrnum;
    336 
    337 	attrnum = prop_number_create_integer(attr);
    338 	prop_dictionary_set(rl, "attributes", attrnum);
    339 	if (iface) {
    340 		prop_number_t ifnum;
    341 		unsigned int if_idx;
    342 
    343 		if (npfctl_getif(iface, &if_idx) == NULL) {
    344 			errx(EXIT_FAILURE, "invalid interface '%s'", iface);
    345 		}
    346 		ifnum = prop_number_create_integer(if_idx);
    347 		prop_dictionary_set(rl, "interface", ifnum);
    348 	}
    349 }
    350 
    351 /*
    352  * Main rule generation routines.
    353  */
    354 
    355 static void
    356 npfctl_rulenc_v4cidr(void **nc, int nblocks[], var_t *dat, bool sd)
    357 {
    358 	element_t *el = dat->v_elements;
    359 	int foff;
    360 
    361 	/* If table, generate a single table matching block. */
    362 	if (dat->v_type == VAR_TABLE) {
    363 		u_int tid = atoi(el->e_data);
    364 
    365 		nblocks[0]--;
    366 		foff = npfctl_failure_offset(nblocks);
    367 		npfctl_gennc_tbl(nc, foff, tid, sd);
    368 		return;
    369 	}
    370 
    371 	/* Generate v4 CIDR matching blocks. */
    372 	for (el = dat->v_elements; el != NULL; el = el->e_next) {
    373 		in_addr_t addr, mask;
    374 
    375 		npfctl_parse_cidr(el->e_data, &addr, &mask);
    376 
    377 		nblocks[1]--;
    378 		foff = npfctl_failure_offset(nblocks);
    379 		npfctl_gennc_v4cidr(nc, foff, addr, mask, sd);
    380 	}
    381 }
    382 
    383 static void
    384 npfctl_rulenc_ports(void **nc, int nblocks[], var_t *dat, bool tcpudp, bool sd)
    385 {
    386 	element_t *el = dat->v_elements;
    387 	int foff;
    388 
    389 	assert(dat->v_type != VAR_TABLE);
    390 
    391 	/* Generate TCP/UDP port matching blocks. */
    392 	for (el = dat->v_elements; el != NULL; el = el->e_next) {
    393 		int pfrom, pto;
    394 		char *sep;
    395 
    396 		if ((sep = strchr(el->e_data, ':')) != NULL) {
    397 			/* Port range (only numeric). */
    398 			*sep = '\0';
    399 		}
    400 		if (isalpha((unsigned char)*el->e_data)) {
    401 			pfrom = npfctl_servname2port(el->e_data);
    402 			if (pfrom == -1) {
    403 				errx(EXIT_FAILURE, "invalid service '%s'",
    404 				    el->e_data);
    405 			}
    406 		} else {
    407 			pfrom = htons(atoi(el->e_data));
    408 		}
    409 		pto = sep ? htons(atoi(sep + 1)) : pfrom;
    410 
    411 		nblocks[0]--;
    412 		foff = npfctl_failure_offset(nblocks);
    413 		npfctl_gennc_ports(nc, foff, pfrom, pto, tcpudp, sd);
    414 	}
    415 }
    416 
    417 static void
    418 npfctl_rulenc_block(void **nc, int nblocks[], var_t *cidr, var_t *ports,
    419     bool both, bool tcpudp, bool sd)
    420 {
    421 
    422 	npfctl_rulenc_v4cidr(nc, nblocks, cidr, sd);
    423 	if (ports == NULL) {
    424 		return;
    425 	}
    426 	npfctl_rulenc_ports(nc, nblocks, ports, tcpudp, sd);
    427 	if (!both) {
    428 		return;
    429 	}
    430 	npfctl_rulenc_ports(nc, nblocks, ports, !tcpudp, sd);
    431 }
    432 
    433 void
    434 npfctl_rule_protodata(prop_dictionary_t rl, char *proto, var_t *from,
    435     var_t *fports, var_t *to, var_t *tports)
    436 {
    437 	prop_data_t ncdata;
    438 	bool icmp, tcpudp, both;
    439 	int nblocks[2] = { 0, 0 };
    440 	void *ncptr, *nc;
    441 	size_t sz;
    442 
    443 	/*
    444 	 * Default: both TCP and UDP.
    445 	 */
    446 	icmp = false;
    447 	tcpudp = true;
    448 	both = false;
    449 	if (proto == NULL) {
    450 		goto skip_proto;
    451 	}
    452 
    453 	if (strcmp(proto, "icmp") == 0) {
    454 		/* ICMP case. */
    455 		fports = NULL;
    456 		tports = NULL;
    457 		icmp = true;
    458 		nblocks[0] += 1;
    459 
    460 	} else if (strcmp(proto, "tcp") == 0) {
    461 		/* Just TCP. */
    462 		tcpudp = true;
    463 
    464 	} else if (strcmp(proto, "udp") == 0) {
    465 		/* Just UDP. */
    466 		tcpudp = false;
    467 
    468 	} else {
    469 		/* Default. */
    470 	}
    471 skip_proto:
    472 
    473 	/* Calculate how blocks to determince n-code. */
    474 	if (from && from->v_count) {
    475 		if (from->v_type == VAR_TABLE)
    476 			nblocks[0] += 1;
    477 		else
    478 			nblocks[1] += from->v_count;
    479 		if (fports && fports->v_count)
    480 			nblocks[0] += fports->v_count * (both ? 2 : 1);
    481 	}
    482 	if (to && to->v_count) {
    483 		if (to->v_type == VAR_TABLE)
    484 			nblocks[0] += 1;
    485 		else
    486 			nblocks[1] += to->v_count;
    487 		if (tports && tports->v_count)
    488 			nblocks[0] += tports->v_count * (both ? 2 : 1);
    489 	}
    490 
    491 	/* Allocate memory for the n-code. */
    492 	sz = npfctl_calc_ncsize(nblocks);
    493 	ncptr = malloc(sz);
    494 	if (ncptr == NULL) {
    495 		perror("malloc");
    496 		exit(EXIT_FAILURE);
    497 	}
    498 	nc = ncptr;
    499 
    500 	/* Ethernet fragment (ETHERTYPE_IP), XXX. */
    501 	npfctl_gennc_ether(&nc, npfctl_failure_offset(nblocks), htons(0x0800));
    502 
    503 	/* Generate v4 CIDR matching blocks and TCP/UDP port matching. */
    504 	if (from) {
    505 		npfctl_rulenc_block(&nc, nblocks, from, fports,
    506 		    both, tcpudp, true);
    507 	}
    508 	if (to) {
    509 		npfctl_rulenc_block(&nc, nblocks, to, tports,
    510 		    both, tcpudp, false);
    511 	}
    512 	/* ICMP case. */
    513 	if (icmp) {
    514 		const int foff = npfctl_failure_offset(nblocks);
    515 		npfctl_gennc_icmp(&nc, foff, -1, -1);
    516 	}
    517 	npfctl_gennc_complete(&nc);
    518 
    519 	if ((uintptr_t)nc - (uintptr_t)ncptr != sz)
    520 		errx(EXIT_FAILURE, "n-code size got wrong (%lu != %lu)",
    521 		    (uintptr_t)nc - (uintptr_t)ncptr, sz);
    522 
    523 #ifdef DEBUG
    524 	uint32_t *op = ncptr;
    525 	size_t n = sz;
    526 	do {
    527 		DPRINTF(("\t> |0x%02x|\n", (u_int)*op));
    528 		op++;
    529 		n -= sizeof(*op);
    530 	} while (n);
    531 #endif
    532 
    533 	/* Create a final memory block of data, ready to send. */
    534 	ncdata = prop_data_create_data(ncptr, sz);
    535 	if (ncdata == NULL) {
    536 		perror("prop_data_create_data");
    537 		exit(EXIT_FAILURE);
    538 	}
    539 	prop_dictionary_set(rl, "ncode", ncdata);
    540 	free(ncptr);
    541 }
    542 
    543 /*
    544  * NAT policy construction routines.
    545  */
    546 
    547 prop_dictionary_t
    548 npfctl_mk_nat(void)
    549 {
    550 	prop_dictionary_t rl;
    551 	pri_t pri;
    552 
    553 	/* NAT policy is rule with extra info. */
    554 	rl = prop_dictionary_create();
    555 	pri = nat_prio_counter++;
    556 	prop_dictionary_set(rl, "priority",
    557 	    prop_number_create_integer(pri));
    558 	return rl;
    559 }
    560 
    561 void
    562 npfctl_add_nat(prop_dictionary_t nat)
    563 {
    564 	prop_array_add(nat_arr, nat);
    565 }
    566 
    567 void
    568 npfctl_nat_setup(prop_dictionary_t rl, char *iface, char *gwip)
    569 {
    570 	const int attr = NPF_RULE_PASS | NPF_RULE_OUT | NPF_RULE_FINAL;
    571 	in_addr_t addr, mask;
    572 
    573 	/* Interface and attributes. */
    574 	npfctl_rule_setattr(rl, attr, iface);
    575 
    576 	/* Gateway IP, XXX should be no mask. */
    577 	npfctl_parse_cidr(gwip, &addr, &mask);
    578 	prop_dictionary_set(rl, "gateway_ip", prop_number_create_integer(addr));
    579 }
    580