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