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