xref: /dragonfly/sbin/ipfw3/ipfw3nat.c (revision ae24b5e0)
1 /*
2  * Copyright (c) 2016 The DragonFly Project.  All rights reserved.
3  *
4  * This code is derived from software contributed to The DragonFly Project
5  * by Bill Yuan <bycn82@dragonflybsd.org>
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  *
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in
15  *    the documentation and/or other materials provided with the
16  *    distribution.
17  * 3. Neither the name of The DragonFly Project nor the names of its
18  *    contributors may be used to endorse or promote products derived
19  *    from this software without specific, prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
25  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  */
34 
35 #include <sys/param.h>
36 #include <sys/mbuf.h>
37 #include <sys/socket.h>
38 #include <sys/sockio.h>
39 #include <sys/sysctl.h>
40 #include <sys/time.h>
41 #include <sys/wait.h>
42 
43 #include <arpa/inet.h>
44 #include <ctype.h>
45 #include <dlfcn.h>
46 #include <err.h>
47 #include <errno.h>
48 #include <grp.h>
49 #include <limits.h>
50 #include <netdb.h>
51 #include <pwd.h>
52 #include <sysexits.h>
53 #include <signal.h>
54 #include <stdio.h>
55 #include <stdlib.h>
56 #include <stdarg.h>
57 #include <string.h>
58 #include <timeconv.h>
59 #include <unistd.h>
60 
61 #include <netinet/in.h>
62 #include <netinet/in_systm.h>
63 #include <netinet/ip.h>
64 #include <netinet/ip_icmp.h>
65 #include <netinet/tcp.h>
66 #include <net/if.h>
67 #include <net/if_dl.h>
68 #include <net/route.h>
69 #include <net/ethernet.h>
70 
71 #include "../../sys/net/ipfw3/ip_fw3.h"
72 #include "../../sys/net/ipfw3/ip_fw3_table.h"
73 #include "../../sys/net/ipfw3/ip_fw3_sync.h"
74 #include "../../sys/net/dummynet3/ip_dummynet3.h"
75 #include "../../sys/net/libalias/alias.h"
76 #include "../../sys/net/ipfw3_basic/ip_fw3_basic.h"
77 #include "../../sys/net/ipfw3_nat/ip_fw3_nat.h"
78 
79 #include "ipfw3.h"
80 #include "ipfw3nat.h"
81 
82 extern int verbose;
83 extern int do_time;
84 extern int do_quiet;
85 extern int do_force;
86 
87 struct char_int_map nat_params[] = {
88 	{ "ip", 		TOK_IP },
89 	{ "if", 		TOK_IF },
90 	{ "log", 		TOK_ALOG },
91 	{ "deny_in", 		TOK_DENY_INC },
92 	{ "same_ports", 	TOK_SAME_PORTS },
93 	{ "unreg_only", 	TOK_UNREG_ONLY },
94 	{ "reset", 		TOK_RESET_ADDR },
95 	{ "reverse", 		TOK_ALIAS_REV },
96 	{ "proxy_only", 	TOK_PROXY_ONLY },
97 	{ "redirect_addr", 	TOK_REDIR_ADDR },
98 	{ "redirect_port", 	TOK_REDIR_PORT },
99 	{ "redirect_proto", 	TOK_REDIR_PROTO },
100 	{ NULL, 0 },
101 };
102 
103 
104 void
105 nat_config(int ac, char **av)
106 {
107 	struct cfg_nat *n;	/* Nat instance configuration. */
108 	int i, len, off, tok;
109 	char *id, buf[NAT_BUF_LEN]; 	/* Buffer for serialized data. */
110 
111 	len = NAT_BUF_LEN;
112 	/* Offset in buf: save space for n at the beginning. */
113 	off = sizeof(struct cfg_nat);
114 	memset(buf, 0, sizeof(buf));
115 	n = (struct cfg_nat *)buf;
116 
117 	NEXT_ARG;
118 	/* Nat id. */
119 	if (ac && isdigit(**av)) {
120 		id = *av;
121 		i = atoi(*av);
122 		NEXT_ARG;
123 		n->id = i;
124 	} else
125 		errx(EX_DATAERR, "missing nat id");
126 	if (ac == 0)
127 		errx(EX_DATAERR, "missing option");
128 
129 	while (ac > 0) {
130 		tok = match_token(nat_params, *av);
131 		NEXT_ARG;
132 		switch (tok) {
133 		case TOK_IP:
134 			if (ac == 0)
135 				errx(EX_DATAERR, "missing option");
136 			if (!inet_aton(av[0], &(n->ip)))
137 				errx(EX_DATAERR, "bad ip addr `%s'", av[0]);
138 			NEXT_ARG;
139 			break;
140 		case TOK_IF:
141 			if (ac == 0)
142 				errx(EX_DATAERR, "missing option");
143 			set_addr_dynamic(av[0], n);
144 			NEXT_ARG;
145 			break;
146 		case TOK_ALOG:
147 			n->mode |= PKT_ALIAS_LOG;
148 			break;
149 		case TOK_DENY_INC:
150 			n->mode |= PKT_ALIAS_DENY_INCOMING;
151 			break;
152 		case TOK_SAME_PORTS:
153 			n->mode |= PKT_ALIAS_SAME_PORTS;
154 			break;
155 		case TOK_UNREG_ONLY:
156 			n->mode |= PKT_ALIAS_UNREGISTERED_ONLY;
157 			break;
158 		case TOK_RESET_ADDR:
159 			n->mode |= PKT_ALIAS_RESET_ON_ADDR_CHANGE;
160 			break;
161 		case TOK_ALIAS_REV:
162 			n->mode |= PKT_ALIAS_REVERSE;
163 			break;
164 		case TOK_PROXY_ONLY:
165 			n->mode |= PKT_ALIAS_PROXY_ONLY;
166 			break;
167 		/*
168 		 * All the setup_redir_* functions work
169 		 * directly in the final
170 		 * buffer, see above for details.
171 		 */
172 		case TOK_REDIR_ADDR:
173 		case TOK_REDIR_PORT:
174 		case TOK_REDIR_PROTO:
175 			switch (tok) {
176 			case TOK_REDIR_ADDR:
177 				i = setup_redir_addr(&buf[off], len, &ac, &av);
178 				break;
179 			case TOK_REDIR_PORT:
180 				i = setup_redir_port(&buf[off], len, &ac, &av);
181 				break;
182 			case TOK_REDIR_PROTO:
183 				i = setup_redir_proto(&buf[off], len, &ac, &av);
184 				break;
185 			}
186 			n->redir_cnt++;
187 			off += i;
188 			len -= i;
189 			break;
190 		default:
191 			errx(EX_DATAERR, "unrecognised option ``%s''", av[-1]);
192 		}
193 	}
194 	i = do_set_x(IP_FW_NAT_ADD, buf, off);
195 	if (i) {
196 		err(1, "do_set_x(%s)", "IP_FW_NAT_ADD");
197 	}
198 
199 	/* After every modification, we show the resultant rule. */
200 	int _ac = 2;
201 	char *_av[] = {"config", id};
202 	nat_show(_ac, _av);
203 }
204 
205 void
206 nat_show_config(char *buf)
207 {
208 	struct cfg_nat *n;
209 	struct cfg_redir *t;
210 	struct cfg_spool *s;
211 	struct protoent *p;
212 	int i, cnt, flag, off;
213 
214 	n = (struct cfg_nat *)buf;
215 	flag = 1;
216 	off = sizeof(*n);
217 	printf("ipfw3 nat %u config", n->id);
218 	if (strlen(n->if_name) != 0)
219 		printf(" if %s", n->if_name);
220 	else if (n->ip.s_addr != 0)
221 		printf(" ip %s", inet_ntoa(n->ip));
222 	while (n->mode != 0) {
223 		if (n->mode & PKT_ALIAS_LOG) {
224 			printf(" log");
225 			n->mode &= ~PKT_ALIAS_LOG;
226 		} else if (n->mode & PKT_ALIAS_DENY_INCOMING) {
227 			printf(" deny_in");
228 			n->mode &= ~PKT_ALIAS_DENY_INCOMING;
229 		} else if (n->mode & PKT_ALIAS_SAME_PORTS) {
230 			printf(" same_ports");
231 			n->mode &= ~PKT_ALIAS_SAME_PORTS;
232 		} else if (n->mode & PKT_ALIAS_UNREGISTERED_ONLY) {
233 			printf(" unreg_only");
234 			n->mode &= ~PKT_ALIAS_UNREGISTERED_ONLY;
235 		} else if (n->mode & PKT_ALIAS_RESET_ON_ADDR_CHANGE) {
236 			printf(" reset");
237 			n->mode &= ~PKT_ALIAS_RESET_ON_ADDR_CHANGE;
238 		} else if (n->mode & PKT_ALIAS_REVERSE) {
239 			printf(" reverse");
240 			n->mode &= ~PKT_ALIAS_REVERSE;
241 		} else if (n->mode & PKT_ALIAS_PROXY_ONLY) {
242 			printf(" proxy_only");
243 			n->mode &= ~PKT_ALIAS_PROXY_ONLY;
244 		}
245 	}
246 
247 	/* Print all the redirect's data configuration. */
248 	for (cnt = 0; cnt < n->redir_cnt; cnt++) {
249 		t = (struct cfg_redir *)&buf[off];
250 		off += SOF_REDIR;
251 		switch (t->mode) {
252 		case REDIR_ADDR:
253 			printf(" redirect_addr");
254 			if (t->spool_cnt == 0) {
255 				printf(" %s", inet_ntoa(t->laddr));
256 			} else {
257 				for (i = 0; i < t->spool_cnt; i++) {
258 					s = (struct cfg_spool *)&buf[off];
259 					if (i)
260 						printf(", ");
261 					else
262 						printf(" ");
263 					printf("%s", inet_ntoa(s->addr));
264 					off += SOF_SPOOL;
265 				}
266 			}
267 			printf(" %s", inet_ntoa(t->paddr));
268 			break;
269 		case REDIR_PORT:
270 			p = getprotobynumber(t->proto);
271 			printf(" redirect_port %s ", p->p_name);
272 			if (!t->spool_cnt) {
273 				printf("%s:%u", inet_ntoa(t->laddr), t->lport);
274 				if (t->pport_cnt > 1) {
275 					printf("-%u", t->lport +
276 							t->pport_cnt - 1);
277 				}
278 			} else
279 				for (i=0; i < t->spool_cnt; i++) {
280 					s = (struct cfg_spool *)&buf[off];
281 					if (i) {
282 						printf(", ");
283 					}
284 					printf("%s:%u", inet_ntoa(s->addr),
285 							s->port);
286 					off += SOF_SPOOL;
287 				}
288 
289 			printf(" ");
290 			if (t->paddr.s_addr) {
291 				printf("%s:", inet_ntoa(t->paddr));
292 			}
293 			printf("%u", t->pport);
294 			if (!t->spool_cnt && t->pport_cnt > 1) {
295 				printf("-%u", t->pport + t->pport_cnt - 1);
296 			}
297 
298 			if (t->raddr.s_addr) {
299 				printf(" %s", inet_ntoa(t->raddr));
300 				if (t->rport) {
301 					printf(":%u", t->rport);
302 					if (!t->spool_cnt && t->rport_cnt > 1) {
303 						printf("-%u", t->rport +
304 							t->rport_cnt - 1);
305 					}
306 				}
307 			}
308 			break;
309 		case REDIR_PROTO:
310 			p = getprotobynumber(t->proto);
311 			printf(" redirect_proto %s %s", p->p_name,
312 					inet_ntoa(t->laddr));
313 			if (t->paddr.s_addr != 0) {
314 				printf(" %s", inet_ntoa(t->paddr));
315 				if (t->raddr.s_addr) {
316 					printf(" %s", inet_ntoa(t->raddr));
317 				}
318 			}
319 			break;
320 		default:
321 			errx(EX_DATAERR, "unknown redir mode");
322 			break;
323 		}
324 	}
325 	printf("\n");
326 }
327 
328 
329 void
330 nat_show(int ac, char **av)
331 {
332 	struct cfg_nat *n;
333 	struct cfg_redir *e;
334 	int i, nbytes, nalloc, size;
335 	int nat_cnt, redir_cnt, nat_id;
336 	uint8_t *data;
337 
338 	nalloc = 1024;
339 	size = 0;
340 	data = NULL;
341 
342 	NEXT_ARG;
343 
344 	if (ac == 0)
345 		nat_id = 0;
346 	else
347 		nat_id = strtoul(*av, NULL, 10);
348 
349 	nbytes = nalloc;
350 	while (nbytes >= nalloc) {
351 		nalloc = nalloc * 2;
352 		nbytes = nalloc;
353 		if ((data = realloc(data, nbytes)) == NULL) {
354 			err(EX_OSERR, "realloc");
355 		}
356 		if (do_get_x(IP_FW_NAT_GET, data, &nbytes) < 0) {
357 			err(EX_OSERR, "do_get_x(IP_FW_NAT_GET)");
358 		}
359 	}
360 
361 	if (nbytes == 0) {
362 		exit(EX_OK);
363 	}
364 
365 	nat_cnt = *((int *)data);
366 	for (i = sizeof(nat_cnt); nat_cnt; nat_cnt--) {
367 		n = (struct cfg_nat *)&data[i];
368 		if (n->id >= 0 && n->id <= IPFW_DEFAULT_RULE) {
369 			if (nat_id == 0 || n->id == nat_id)
370 				nat_show_config(&data[i]);
371 		}
372 		i += sizeof(struct cfg_nat);
373 		for (redir_cnt = 0; redir_cnt < n->redir_cnt; redir_cnt++) {
374 			e = (struct cfg_redir *)&data[i];
375 			i += sizeof(struct cfg_redir) +
376 				e->spool_cnt * sizeof(struct cfg_spool);
377 		}
378 	}
379 }
380 
381 int
382 setup_redir_port(char *spool_buf, int len, int *_ac, char ***_av)
383 {
384 	char **av, *sep, *protoName;
385 	char tmp_spool_buf[NAT_BUF_LEN];
386 	int ac, space, lsnat;
387 	struct cfg_redir *r;
388 	struct cfg_spool *tmp;
389 	u_short numLocalPorts;
390 	port_range portRange;
391 
392 	av = *_av;
393 	ac = *_ac;
394 	space = 0;
395 	lsnat = 0;
396 	numLocalPorts = 0;
397 
398 	if (len >= SOF_REDIR) {
399 		r = (struct cfg_redir *)spool_buf;
400 		/* Skip cfg_redir at beginning of buf. */
401 		spool_buf = &spool_buf[SOF_REDIR];
402 		space = SOF_REDIR;
403 		len -= SOF_REDIR;
404 	} else {
405 		goto nospace;
406 	}
407 
408 	r->mode = REDIR_PORT;
409 	/*
410 	 * Extract protocol.
411 	 */
412 	if (ac == 0)
413 		errx (EX_DATAERR, "redirect_port: missing protocol");
414 
415 	r->proto = str2proto(*av);
416 	protoName = *av;
417 	INC_ARGCV();
418 
419 	/*
420 	 * Extract local address.
421 	 */
422 	if (ac == 0)
423 		errx (EX_DATAERR, "redirect_port: missing local address");
424 
425 	sep = strchr(*av, ',');
426 	/* LSNAT redirection syntax. */
427 	if (sep) {
428 		r->laddr.s_addr = INADDR_NONE;
429 		r->lport = ~0;
430 		numLocalPorts = 1;
431 		/* Preserve av, copy spool servers to tmp_spool_buf. */
432 		strncpy(tmp_spool_buf, *av, strlen(*av)+1);
433 		lsnat = 1;
434 	} else {
435 		if (str2addr_portrange (*av, &r->laddr,
436 					protoName, &portRange) != 0)
437 			errx(EX_DATAERR, "redirect_port:"
438 				"invalid local port range");
439 
440 		r->lport = GETLOPORT(portRange);
441 		numLocalPorts = GETNUMPORTS(portRange);
442 	}
443 	INC_ARGCV();
444 
445 	/*
446 	 * Extract public port and optionally address.
447 	 */
448 	if (ac == 0)
449 		errx (EX_DATAERR, "redirect_port: missing public port");
450 
451 	sep = strchr (*av, ':');
452 	if (sep) {
453 		if (str2addr_portrange (*av, &r->paddr, protoName, &portRange) != 0)
454 			errx(EX_DATAERR, "redirect_port:"
455 				"invalid public port range");
456 	} else {
457 		r->paddr.s_addr = INADDR_ANY;
458 		if (str2portrange(*av, protoName, &portRange) != 0)
459 			errx(EX_DATAERR, "redirect_port:"
460 				"invalid public port range");
461 	}
462 
463 	r->pport = GETLOPORT(portRange);
464 	r->pport_cnt = GETNUMPORTS(portRange);
465 	INC_ARGCV();
466 
467 	/*
468 	 * Extract remote address and optionally port.
469 	 */
470 	/*
471 	 * NB: isalpha(**av) => we've to check that next parameter is really an
472 	 * option for this redirect entry, else stop here processing arg[cv].
473 	 */
474 	if (ac != 0 && !isalpha(**av)) {
475 		sep = strchr (*av, ':');
476 		if (sep) {
477 			if (str2addr_portrange (*av, &r->raddr,
478 						protoName, &portRange) != 0) {
479 				errx(EX_DATAERR, "redirect_port:"
480 					"invalid remote port range");
481 			}
482 		} else {
483 			SETLOPORT(portRange, 0);
484 			SETNUMPORTS(portRange, 1);
485 			str2addr (*av, &r->raddr);
486 		}
487 		INC_ARGCV();
488 	} else {
489 		SETLOPORT(portRange, 0);
490 		SETNUMPORTS(portRange, 1);
491 		r->raddr.s_addr = INADDR_ANY;
492 	}
493 	r->rport = GETLOPORT(portRange);
494 	r->rport_cnt = GETNUMPORTS(portRange);
495 
496 	/*
497 	 * Make sure port ranges match up, then add the redirect ports.
498 	 */
499 	if (numLocalPorts != r->pport_cnt) {
500 		errx(EX_DATAERR, "redirect_port:"
501 			"port ranges must be equal in size");
502 	}
503 
504 	/* Remote port range is allowed to be '0' which means all ports. */
505 	if (r->rport_cnt != numLocalPorts &&
506 			(r->rport_cnt != 1 || r->rport != 0)) {
507 		errx(EX_DATAERR, "redirect_port: remote port must"
508 				"be 0 or equal to local port range in size");
509 	}
510 
511 	/*
512 	 * Setup LSNAT server pool.
513 	 */
514 	if (lsnat) {
515 		sep = strtok(tmp_spool_buf, ", ");
516 		while (sep != NULL) {
517 			tmp = (struct cfg_spool *)spool_buf;
518 			if (len < SOF_SPOOL)
519 				goto nospace;
520 
521 			len -= SOF_SPOOL;
522 			space += SOF_SPOOL;
523 			if (str2addr_portrange(sep,
524 				&tmp->addr, protoName, &portRange) != 0)
525 				errx(EX_DATAERR, "redirect_port:"
526 					"invalid local port range");
527 			if (GETNUMPORTS(portRange) != 1)
528 				errx(EX_DATAERR, "redirect_port: local port"
529 					"must be single in this context");
530 			tmp->port = GETLOPORT(portRange);
531 			r->spool_cnt++;
532 			/* Point to the next possible cfg_spool. */
533 			spool_buf = &spool_buf[SOF_SPOOL];
534 			sep = strtok(NULL, ", ");
535 		}
536 	}
537 	return (space);
538 
539 nospace:
540 	errx(EX_DATAERR, "redirect_port: buf is too small\n");
541 }
542 
543 int
544 setup_redir_proto(char *spool_buf, int len, int *_ac, char ***_av)
545 {
546 	struct protoent *protoent;
547 	struct cfg_redir *r;
548 	int ac, i, space;
549 	char **av;
550 
551 	i=0;
552 	av = *_av;
553 	ac = *_ac;
554 	if (len >= SOF_REDIR) {
555 		r = (struct cfg_redir *)spool_buf;
556 		/* Skip cfg_redir at beginning of buf. */
557 		spool_buf = &spool_buf[SOF_REDIR];
558 		space = SOF_REDIR;
559 		len -= SOF_REDIR;
560 	} else {
561 		goto nospace;
562 	}
563 	r->mode = REDIR_PROTO;
564 	/*
565 	 * Extract protocol.
566 	 */
567 	if (ac == 0)
568 		errx(EX_DATAERR, "redirect_proto: missing protocol");
569 
570 	protoent = getprotobyname(*av);
571 	if (protoent == NULL)
572 		errx(EX_DATAERR, "redirect_proto: unknown protocol %s", *av);
573 	else
574 		r->proto = protoent->p_proto;
575 
576 	INC_ARGCV();
577 
578 	/*
579 	 * Extract local address.
580 	 */
581 	if (ac == 0)
582 		errx(EX_DATAERR, "redirect_proto: missing local address");
583 	else
584 		str2addr(*av, &r->laddr);
585 	INC_ARGCV();
586 
587 	/*
588 	 * Extract optional public address.
589 	 */
590 	if (ac == 0) {
591 		r->paddr.s_addr = INADDR_ANY;
592 		r->raddr.s_addr = INADDR_ANY;
593 	} else {
594 		/* see above in setup_redir_port() */
595 		if (!isalpha(**av)) {
596 			str2addr(*av, &r->paddr);
597 			INC_ARGCV();
598 
599 			/*
600 			 * Extract optional remote address.
601 			 */
602 			/* see above in setup_redir_port() */
603 			if (ac != 0 && !isalpha(**av)) {
604 				str2addr(*av, &r->raddr);
605 				INC_ARGCV();
606 			}
607 		}
608 	}
609 	return (space);
610 
611 nospace:
612 	errx(EX_DATAERR, "redirect_proto: buf is too small\n");
613 }
614 
615 int
616 str2proto(const char* str)
617 {
618 	if (!strcmp (str, "tcp"))
619 		return IPPROTO_TCP;
620 	if (!strcmp (str, "udp"))
621 		return IPPROTO_UDP;
622 	errx (EX_DATAERR, "unknown protocol %s. Expected tcp or udp", str);
623 }
624 
625 int
626 str2addr_portrange (const char* str, struct in_addr* addr,
627 	char* proto, port_range *portRange)
628 {
629 	char*	ptr;
630 
631 	ptr = strchr (str, ':');
632 	if (!ptr)
633 		errx (EX_DATAERR, "%s is missing port number", str);
634 
635 	*ptr = '\0';
636 	++ptr;
637 
638 	str2addr (str, addr);
639 	return str2portrange (ptr, proto, portRange);
640 }
641 
642 /*
643  * Search for interface with name "ifn", and fill n accordingly:
644  *
645  * n->ip		ip address of interface "ifn"
646  * n->if_name copy of interface name "ifn"
647  */
648 void
649 set_addr_dynamic(const char *ifn, struct cfg_nat *n)
650 {
651 	struct if_msghdr *ifm;
652 	struct ifa_msghdr *ifam;
653 	struct sockaddr_dl *sdl;
654 	struct sockaddr_in *sin;
655 	char *buf, *lim, *next;
656 	size_t needed;
657 	int mib[6];
658 	int ifIndex, ifMTU;
659 
660 	mib[0] = CTL_NET;
661 	mib[1] = PF_ROUTE;
662 	mib[2] = 0;
663 	mib[3] = AF_INET;
664 	mib[4] = NET_RT_IFLIST;
665 	mib[5] = 0;
666 
667 	/*
668 	 * Get interface data.
669 	 */
670 	if (sysctl(mib, 6, NULL, &needed, NULL, 0) == -1)
671 		err(1, "iflist-sysctl-estimate");
672 	if ((buf = malloc(needed)) == NULL)
673 		errx(1, "malloc failed");
674 	if (sysctl(mib, 6, buf, &needed, NULL, 0) == -1)
675 		err(1, "iflist-sysctl-get");
676 	lim = buf + needed;
677 	/*
678 	 * Loop through interfaces until one with
679 	 * given name is found. This is done to
680 	 * find correct interface index for routing
681 	 * message processing.
682 	 */
683 	ifIndex	= 0;
684 	next = buf;
685 	while (next < lim) {
686 		ifm = (struct if_msghdr *)next;
687 		next += ifm->ifm_msglen;
688 		if (ifm->ifm_version != RTM_VERSION) {
689 			if (verbose)
690 				warnx("routing message version %d "
691 					"not understood", ifm->ifm_version);
692 			continue;
693 		}
694 		if (ifm->ifm_type == RTM_IFINFO) {
695 			sdl = (struct sockaddr_dl *)(ifm + 1);
696 			if (strlen(ifn) == sdl->sdl_nlen &&
697 				strncmp(ifn, sdl->sdl_data,
698 					sdl->sdl_nlen) == 0) {
699 				ifIndex = ifm->ifm_index;
700 				ifMTU = ifm->ifm_data.ifi_mtu;
701 				break;
702 			}
703 		}
704 	}
705 	if (!ifIndex)
706 		errx(1, "unknown interface name %s", ifn);
707 	/*
708 	 * Get interface address.
709 	 */
710 	sin = NULL;
711 	while (next < lim) {
712 		ifam = (struct ifa_msghdr *)next;
713 		next += ifam->ifam_msglen;
714 		if (ifam->ifam_version != RTM_VERSION) {
715 			if (verbose)
716 				warnx("routing message version %d "
717 					"not understood", ifam->ifam_version);
718 			continue;
719 		}
720 		if (ifam->ifam_type != RTM_NEWADDR)
721 			break;
722 		if (ifam->ifam_addrs & RTA_IFA) {
723 			int i;
724 			char *cp = (char *)(ifam + 1);
725 
726 			for (i = 1; i < RTA_IFA; i <<= 1) {
727 				if (ifam->ifam_addrs & i)
728 					cp += SA_SIZE((struct sockaddr *)cp);
729 			}
730 			if (((struct sockaddr *)cp)->sa_family == AF_INET) {
731 				sin = (struct sockaddr_in *)cp;
732 				break;
733 			}
734 		}
735 	}
736 	if (sin == NULL)
737 		errx(1, "%s: cannot get interface address", ifn);
738 
739 	n->ip = sin->sin_addr;
740 	strncpy(n->if_name, ifn, IF_NAMESIZE);
741 
742 	free(buf);
743 }
744 
745 int
746 setup_redir_addr(char *spool_buf, int len, int *_ac, char ***_av)
747 {
748 	struct cfg_redir *r;
749 	struct cfg_spool *tmp;
750 	char **av, *sep;
751 	char tmp_spool_buf[NAT_BUF_LEN];
752 	int ac, i, space, lsnat;
753 
754 	i=0;
755 	av = *_av;
756 	ac = *_ac;
757 	space = 0;
758 	lsnat = 0;
759 	if (len >= SOF_REDIR) {
760 		r = (struct cfg_redir *)spool_buf;
761 		/* Skip cfg_redir at beginning of buf. */
762 		spool_buf = &spool_buf[SOF_REDIR];
763 		space = SOF_REDIR;
764 		len -= SOF_REDIR;
765 	} else {
766 		goto nospace;
767 	}
768 
769 	r->mode = REDIR_ADDR;
770 	/* Extract local address. */
771 	if (ac == 0)
772 		errx(EX_DATAERR, "redirect_addr: missing local address");
773 
774 	sep = strchr(*av, ',');
775 	if (sep) {		/* LSNAT redirection syntax. */
776 		r->laddr.s_addr = INADDR_NONE;
777 		/* Preserve av, copy spool servers to tmp_spool_buf. */
778 		strncpy(tmp_spool_buf, *av, strlen(*av)+1);
779 		lsnat = 1;
780 	} else {
781 		str2addr(*av, &r->laddr);
782 	}
783 	INC_ARGCV();
784 
785 	/* Extract public address. */
786 	if (ac == 0)
787 		errx(EX_DATAERR, "redirect_addr: missing public address");
788 
789 	str2addr(*av, &r->paddr);
790 	INC_ARGCV();
791 
792 	/* Setup LSNAT server pool. */
793 	if (sep) {
794 		sep = strtok(tmp_spool_buf, ", ");
795 		while (sep != NULL) {
796 			tmp = (struct cfg_spool *)spool_buf;
797 			if (len < SOF_SPOOL)
798 				goto nospace;
799 
800 			len -= SOF_SPOOL;
801 			space += SOF_SPOOL;
802 			str2addr(sep, &tmp->addr);
803 			tmp->port = ~0;
804 			r->spool_cnt++;
805 			/* Point to the next possible cfg_spool. */
806 			spool_buf = &spool_buf[SOF_SPOOL];
807 			sep = strtok(NULL, ", ");
808 		}
809 	}
810 	return(space);
811 
812 nospace:
813 	errx(EX_DATAERR, "redirect_addr: buf is too small\n");
814 }
815 
816 void
817 str2addr(const char* str, struct in_addr* addr)
818 {
819 	struct hostent* hp;
820 
821 	if (inet_aton (str, addr))
822 		return;
823 
824 	hp = gethostbyname (str);
825 	if (!hp)
826 		errx (1, "unknown host %s", str);
827 
828 	memcpy (addr, hp->h_addr, sizeof (struct in_addr));
829 }
830 
831 int
832 str2portrange(const char* str, const char* proto, port_range *portRange)
833 {
834 	struct servent*	sp;
835 	char*	sep;
836 	char*	end;
837 	u_short	loPort, hiPort;
838 
839 	/* First see if this is a service, return corresponding port if so. */
840 	sp = getservbyname (str, proto);
841 	if (sp) {
842 		SETLOPORT(*portRange, ntohs(sp->s_port));
843 		SETNUMPORTS(*portRange, 1);
844 		return 0;
845 	}
846 
847 	/* Not a service, see if it's a single port or port range. */
848 	sep = strchr (str, '-');
849 	if (sep == NULL) {
850 		SETLOPORT(*portRange, strtol(str, &end, 10));
851 		if (end != str) {
852 			/* Single port. */
853 			SETNUMPORTS(*portRange, 1);
854 			return 0;
855 		}
856 
857 		/* Error in port range field. */
858 		errx (EX_DATAERR, "%s/%s: unknown service", str, proto);
859 	}
860 
861 	/* Port range, get the values and sanity check. */
862 	sscanf (str, "%hu-%hu", &loPort, &hiPort);
863 	SETLOPORT(*portRange, loPort);
864 	SETNUMPORTS(*portRange, 0); 	/* Error by default */
865 	if (loPort <= hiPort)
866 		SETNUMPORTS(*portRange, hiPort - loPort + 1);
867 
868 	if (GETNUMPORTS(*portRange) == 0)
869 		errx (EX_DATAERR, "invalid port range %s", str);
870 
871 	return 0;
872 }
873 
874 void
875 nat_delete_config(int ac, char *av[])
876 {
877 	NEXT_ARG;
878 	int i = 0;
879 	if (ac > 0) {
880 		i = atoi(*av);
881 	}
882 	if (do_set_x(IP_FW_NAT_DEL, &i, sizeof(i)) == -1)
883 		errx(EX_USAGE, "NAT %d in use or not exists", i);
884 }
885 
886 void
887 nat_show_state(int ac, char **av)
888 {
889 	int nbytes, nalloc;
890 	int nat_id;
891 	uint8_t *data;
892 
893 	nalloc = 1024;
894 	data = NULL;
895 
896 	NEXT_ARG;
897 	if (ac == 0)
898 		nat_id = 0;
899 	else
900 		nat_id = strtoul(*av, NULL, 10);
901 
902 	nbytes = nalloc;
903 	while (nbytes >= nalloc) {
904 		nalloc = nalloc * 2;
905 		nbytes = nalloc;
906 		if ((data = realloc(data, nbytes)) == NULL) {
907 			err(EX_OSERR, "realloc");
908 		}
909 		memcpy(data, &nat_id, sizeof(int));
910 		if (do_get_x(IP_FW_NAT_GET_RECORD, data, &nbytes) < 0) {
911 			err(EX_OSERR, "do_get_x(IP_FW_NAT_GET_RECORD)");
912 		}
913 	}
914 	if (nbytes == 0)
915 		exit(EX_OK);
916 	struct ipfw_ioc_nat_state *nat_state;
917 	nat_state =(struct ipfw_ioc_nat_state *)data;
918 	int count = nbytes / sizeof( struct ipfw_ioc_nat_state);
919 	int i, uptime_sec;
920 	uptime_sec = get_kern_boottime();
921 	for (i = 0; i < count; i ++) {
922 		printf("#%d ", nat_state->cpuid);
923 		printf("%s:%hu => ",inet_ntoa(nat_state->src_addr),
924 				htons(nat_state->src_port));
925 		printf("%s:%hu",inet_ntoa(nat_state->alias_addr),
926 				htons(nat_state->alias_port));
927 		printf(" -> %s:%hu ",inet_ntoa(nat_state->dst_addr),
928 				htons(nat_state->dst_port));
929 		if (do_time == 1) {
930 			char timestr[30];
931 			time_t t = _long_to_time(uptime_sec +
932 					nat_state->timestamp);
933 			strcpy(timestr, ctime(&t));
934 			*strchr(timestr, '\n') = '\0';
935 			printf("%s ", timestr);
936 		} else if (do_time == 2) {
937 			printf( "%10u ", uptime_sec + nat_state->timestamp);
938 		}
939 		struct protoent *pe = getprotobynumber(nat_state->link_type);
940 		printf("%s ", pe->p_name);
941 		printf(" %s", nat_state->is_outgoing? "out": "in");
942 		printf("\n");
943 		nat_state++;
944 	}
945 }
946 
947 int
948 get_kern_boottime(void)
949 {
950 	struct timeval boottime;
951 	size_t size;
952 	int mib[2];
953 	mib[0] = CTL_KERN;
954 	mib[1] = KERN_BOOTTIME;
955 	size = sizeof(boottime);
956 	if (sysctl(mib, 2, &boottime, &size, NULL, 0) != -1 &&
957 			boottime.tv_sec != 0) {
958 		return boottime.tv_sec;
959 	}
960 	return -1;
961 }
962 
963 void
964 nat_flush()
965 {
966 	int cmd = IP_FW_NAT_FLUSH;
967 	if (!do_force) {
968 		int c;
969 
970 		printf("Are you sure? [yn] ");
971 		fflush(stdout);
972 		do {
973 			c = toupper(getc(stdin));
974 			while (c != '\n' && getc(stdin) != '\n')
975 				if (feof(stdin))
976 					return; /* and do not flush */
977 		} while (c != 'Y' && c != 'N');
978 		if (c == 'N')	/* user said no */
979 			return;
980 	}
981 	if (do_set_x(cmd, NULL, 0) < 0 ) {
982 		errx(EX_USAGE, "NAT configuration in use");
983 	}
984 	if (!do_quiet) {
985 		printf("Flushed all nat configurations");
986 	}
987 }
988 
989 void
990 nat_main(int ac, char **av)
991 {
992 	if (!strncmp(*av, "config", strlen(*av))) {
993 		nat_config(ac, av);
994 	} else if (!strncmp(*av, "flush", strlen(*av))) {
995 		nat_flush();
996 	} else if (!strncmp(*av, "show", strlen(*av)) ||
997 			!strncmp(*av, "list", strlen(*av))) {
998 		if (ac > 2 && isdigit(*(av[1]))) {
999 			char *p = av[1];
1000 			av[1] = av[2];
1001 			av[2] = p;
1002 		}
1003 		NEXT_ARG;
1004 		if (!strncmp(*av, "config", strlen(*av))) {
1005 			nat_show(ac, av);
1006 		} else if (!strncmp(*av, "state", strlen(*av))) {
1007 			nat_show_state(ac,av);
1008 		} else {
1009 			errx(EX_USAGE,
1010 					"bad nat show command `%s'", *av);
1011 		}
1012 	} else if (!strncmp(*av, "delete", strlen(*av))) {
1013 		nat_delete_config(ac, av);
1014 	} else {
1015 		errx(EX_USAGE, "bad ipfw nat command `%s'", *av);
1016 	}
1017 }
1018