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