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