nat.c revision 188294
1/*
2 * Copyright (c) 2002-2003 Luigi Rizzo
3 * Copyright (c) 1996 Alex Nash, Paul Traina, Poul-Henning Kamp
4 * Copyright (c) 1994 Ugen J.S.Antsilevich
5 *
6 * Idea and grammar partially left from:
7 * Copyright (c) 1993 Daniel Boulet
8 *
9 * Redistribution and use in source forms, with and without modification,
10 * are permitted provided that this entire comment appears intact.
11 *
12 * Redistribution in binary form may occur without any restrictions.
13 * Obviously, it would be nice if you gave credit where credit is due
14 * but requiring it would be too onerous.
15 *
16 * This software is provided ``AS IS'' without any warranties of any kind.
17 *
18 * NEW command line interface for IP firewall facility
19 *
20 * $FreeBSD: head/sbin/ipfw/nat.c 188294 2009-02-07 18:49:42Z piso $
21 *
22 * In-kernel nat support
23 */
24
25#include <sys/types.h>
26#include <sys/socket.h>
27#include <sys/sysctl.h>
28
29#include "ipfw2.h"
30
31#include <ctype.h>
32#include <err.h>
33#include <netdb.h>
34#include <stdio.h>
35#include <stdlib.h>
36#include <string.h>
37#include <sysexits.h>
38
39#define IPFW_INTERNAL	/* Access to protected structures in ip_fw.h. */
40
41#include <net/if.h>
42#include <net/if_dl.h>
43#include <net/route.h> /* def. of struct route */
44#include <netinet/in.h>
45#include <netinet/ip_fw.h>
46#include <arpa/inet.h>
47#include <alias.h>
48
49static struct _s_x nat_params[] = {
50	{ "ip",	                TOK_IP },
51	{ "if",	                TOK_IF },
52 	{ "log",                TOK_ALOG },
53 	{ "deny_in",	        TOK_DENY_INC },
54 	{ "same_ports",	        TOK_SAME_PORTS },
55 	{ "unreg_only",	        TOK_UNREG_ONLY },
56 	{ "reset",	        TOK_RESET_ADDR },
57 	{ "reverse",	        TOK_ALIAS_REV },
58 	{ "proxy_only",	        TOK_PROXY_ONLY },
59	{ "redirect_addr",	TOK_REDIR_ADDR },
60	{ "redirect_port",	TOK_REDIR_PORT },
61	{ "redirect_proto",	TOK_REDIR_PROTO },
62 	{ NULL, 0 }	/* terminator */
63};
64
65
66/*
67 * Search for interface with name "ifn", and fill n accordingly:
68 *
69 * n->ip        ip address of interface "ifn"
70 * n->if_name   copy of interface name "ifn"
71 */
72static void
73set_addr_dynamic(const char *ifn, struct cfg_nat *n)
74{
75	size_t needed;
76	int mib[6];
77	char *buf, *lim, *next;
78	struct if_msghdr *ifm;
79	struct ifa_msghdr *ifam;
80	struct sockaddr_dl *sdl;
81	struct sockaddr_in *sin;
82	int ifIndex, ifMTU;
83
84	mib[0] = CTL_NET;
85	mib[1] = PF_ROUTE;
86	mib[2] = 0;
87	mib[3] = AF_INET;
88	mib[4] = NET_RT_IFLIST;
89	mib[5] = 0;
90/*
91 * Get interface data.
92 */
93	if (sysctl(mib, 6, NULL, &needed, NULL, 0) == -1)
94		err(1, "iflist-sysctl-estimate");
95	buf = safe_calloc(1, needed);
96	if (sysctl(mib, 6, buf, &needed, NULL, 0) == -1)
97		err(1, "iflist-sysctl-get");
98	lim = buf + needed;
99/*
100 * Loop through interfaces until one with
101 * given name is found. This is done to
102 * find correct interface index for routing
103 * message processing.
104 */
105	ifIndex	= 0;
106	next = buf;
107	while (next < lim) {
108		ifm = (struct if_msghdr *)next;
109		next += ifm->ifm_msglen;
110		if (ifm->ifm_version != RTM_VERSION) {
111			if (co.verbose)
112				warnx("routing message version %d "
113				    "not understood", ifm->ifm_version);
114			continue;
115		}
116		if (ifm->ifm_type == RTM_IFINFO) {
117			sdl = (struct sockaddr_dl *)(ifm + 1);
118			if (strlen(ifn) == sdl->sdl_nlen &&
119			    strncmp(ifn, sdl->sdl_data, sdl->sdl_nlen) == 0) {
120				ifIndex = ifm->ifm_index;
121				ifMTU = ifm->ifm_data.ifi_mtu;
122				break;
123			}
124		}
125	}
126	if (!ifIndex)
127		errx(1, "unknown interface name %s", ifn);
128/*
129 * Get interface address.
130 */
131	sin = NULL;
132	while (next < lim) {
133		ifam = (struct ifa_msghdr *)next;
134		next += ifam->ifam_msglen;
135		if (ifam->ifam_version != RTM_VERSION) {
136			if (co.verbose)
137				warnx("routing message version %d "
138				    "not understood", ifam->ifam_version);
139			continue;
140		}
141		if (ifam->ifam_type != RTM_NEWADDR)
142			break;
143		if (ifam->ifam_addrs & RTA_IFA) {
144			int i;
145			char *cp = (char *)(ifam + 1);
146
147			for (i = 1; i < RTA_IFA; i <<= 1) {
148				if (ifam->ifam_addrs & i)
149					cp += SA_SIZE((struct sockaddr *)cp);
150			}
151			if (((struct sockaddr *)cp)->sa_family == AF_INET) {
152				sin = (struct sockaddr_in *)cp;
153				break;
154			}
155		}
156	}
157	if (sin == NULL)
158		errx(1, "%s: cannot get interface address", ifn);
159
160	n->ip = sin->sin_addr;
161	strncpy(n->if_name, ifn, IF_NAMESIZE);
162
163	free(buf);
164}
165
166/*
167 * XXX - The following functions, macros and definitions come from natd.c:
168 * it would be better to move them outside natd.c, in a file
169 * (redirect_support.[ch]?) shared by ipfw and natd, but for now i can live
170 * with it.
171 */
172
173/*
174 * Definition of a port range, and macros to deal with values.
175 * FORMAT:  HI 16-bits == first port in range, 0 == all ports.
176 *          LO 16-bits == number of ports in range
177 * NOTES:   - Port values are not stored in network byte order.
178 */
179
180#define port_range u_long
181
182#define GETLOPORT(x)     ((x) >> 0x10)
183#define GETNUMPORTS(x)   ((x) & 0x0000ffff)
184#define GETHIPORT(x)     (GETLOPORT((x)) + GETNUMPORTS((x)))
185
186/* Set y to be the low-port value in port_range variable x. */
187#define SETLOPORT(x,y)   ((x) = ((x) & 0x0000ffff) | ((y) << 0x10))
188
189/* Set y to be the number of ports in port_range variable x. */
190#define SETNUMPORTS(x,y) ((x) = ((x) & 0xffff0000) | (y))
191
192static void
193StrToAddr (const char* str, struct in_addr* addr)
194{
195	struct hostent* hp;
196
197	if (inet_aton (str, addr))
198		return;
199
200	hp = gethostbyname (str);
201	if (!hp)
202		errx (1, "unknown host %s", str);
203
204	memcpy (addr, hp->h_addr, sizeof (struct in_addr));
205}
206
207static int
208StrToPortRange (const char* str, const char* proto, port_range *portRange)
209{
210	char*           sep;
211	struct servent*	sp;
212	char*		end;
213	u_short         loPort;
214	u_short         hiPort;
215
216	/* First see if this is a service, return corresponding port if so. */
217	sp = getservbyname (str,proto);
218	if (sp) {
219	        SETLOPORT(*portRange, ntohs(sp->s_port));
220		SETNUMPORTS(*portRange, 1);
221		return 0;
222	}
223
224	/* Not a service, see if it's a single port or port range. */
225	sep = strchr (str, '-');
226	if (sep == NULL) {
227	        SETLOPORT(*portRange, strtol(str, &end, 10));
228		if (end != str) {
229		        /* Single port. */
230		        SETNUMPORTS(*portRange, 1);
231			return 0;
232		}
233
234		/* Error in port range field. */
235		errx (EX_DATAERR, "%s/%s: unknown service", str, proto);
236	}
237
238	/* Port range, get the values and sanity check. */
239	sscanf (str, "%hu-%hu", &loPort, &hiPort);
240	SETLOPORT(*portRange, loPort);
241	SETNUMPORTS(*portRange, 0);	/* Error by default */
242	if (loPort <= hiPort)
243	        SETNUMPORTS(*portRange, hiPort - loPort + 1);
244
245	if (GETNUMPORTS(*portRange) == 0)
246	        errx (EX_DATAERR, "invalid port range %s", str);
247
248	return 0;
249}
250
251static int
252StrToProto (const char* str)
253{
254	if (!strcmp (str, "tcp"))
255		return IPPROTO_TCP;
256
257	if (!strcmp (str, "udp"))
258		return IPPROTO_UDP;
259
260	if (!strcmp (str, "sctp"))
261		return IPPROTO_SCTP;
262	errx (EX_DATAERR, "unknown protocol %s. Expected sctp, tcp or udp", str);
263}
264
265static int
266StrToAddrAndPortRange (const char* str, struct in_addr* addr, char* proto,
267		       port_range *portRange)
268{
269	char*	ptr;
270
271	ptr = strchr (str, ':');
272	if (!ptr)
273		errx (EX_DATAERR, "%s is missing port number", str);
274
275	*ptr = '\0';
276	++ptr;
277
278	StrToAddr (str, addr);
279	return StrToPortRange (ptr, proto, portRange);
280}
281
282/* End of stuff taken from natd.c. */
283
284#define INC_ARGCV() do {        \
285	(*_av)++;               \
286	(*_ac)--;               \
287	av = *_av;              \
288	ac = *_ac;              \
289} while(0)
290
291/*
292 * The next 3 functions add support for the addr, port and proto redirect and
293 * their logic is loosely based on SetupAddressRedirect(), SetupPortRedirect()
294 * and SetupProtoRedirect() from natd.c.
295 *
296 * Every setup_* function fills at least one redirect entry
297 * (struct cfg_redir) and zero or more server pool entry (struct cfg_spool)
298 * in buf.
299 *
300 * The format of data in buf is:
301 *
302 *
303 *     cfg_nat    cfg_redir    cfg_spool    ......  cfg_spool
304 *
305 *    -------------------------------------        ------------
306 *   |          | .....X ... |          |         |           |  .....
307 *    ------------------------------------- ...... ------------
308 *                     ^
309 *                spool_cnt       n=0       ......   n=(X-1)
310 *
311 * len points to the amount of available space in buf
312 * space counts the memory consumed by every function
313 *
314 * XXX - Every function get all the argv params so it
315 * has to check, in optional parameters, that the next
316 * args is a valid option for the redir entry and not
317 * another token. Only redir_port and redir_proto are
318 * affected by this.
319 */
320
321static int
322setup_redir_addr(char *spool_buf, int len,
323		 int *_ac, char ***_av)
324{
325	char **av, *sep; /* Token separator. */
326	/* Temporary buffer used to hold server pool ip's. */
327	char tmp_spool_buf[NAT_BUF_LEN];
328	int ac, space, lsnat;
329	struct cfg_redir *r;
330	struct cfg_spool *tmp;
331
332	av = *_av;
333	ac = *_ac;
334	space = 0;
335	lsnat = 0;
336	if (len >= SOF_REDIR) {
337		r = (struct cfg_redir *)spool_buf;
338		/* Skip cfg_redir at beginning of buf. */
339		spool_buf = &spool_buf[SOF_REDIR];
340		space = SOF_REDIR;
341		len -= SOF_REDIR;
342	} else
343		goto nospace;
344	r->mode = REDIR_ADDR;
345	/* Extract local address. */
346	if (ac == 0)
347		errx(EX_DATAERR, "redirect_addr: missing local address");
348	sep = strchr(*av, ',');
349	if (sep) {		/* LSNAT redirection syntax. */
350		r->laddr.s_addr = INADDR_NONE;
351		/* Preserve av, copy spool servers to tmp_spool_buf. */
352		strncpy(tmp_spool_buf, *av, strlen(*av)+1);
353		lsnat = 1;
354	} else
355		StrToAddr(*av, &r->laddr);
356	INC_ARGCV();
357
358	/* Extract public address. */
359	if (ac == 0)
360		errx(EX_DATAERR, "redirect_addr: missing public address");
361	StrToAddr(*av, &r->paddr);
362	INC_ARGCV();
363
364	/* Setup LSNAT server pool. */
365	if (sep) {
366		sep = strtok(tmp_spool_buf, ",");
367		while (sep != NULL) {
368			tmp = (struct cfg_spool *)spool_buf;
369			if (len < SOF_SPOOL)
370				goto nospace;
371			len -= SOF_SPOOL;
372			space += SOF_SPOOL;
373			StrToAddr(sep, &tmp->addr);
374			tmp->port = ~0;
375			r->spool_cnt++;
376			/* Point to the next possible cfg_spool. */
377			spool_buf = &spool_buf[SOF_SPOOL];
378			sep = strtok(NULL, ",");
379		}
380	}
381	return(space);
382nospace:
383	errx(EX_DATAERR, "redirect_addr: buf is too small\n");
384}
385
386static int
387setup_redir_port(char *spool_buf, int len,
388		 int *_ac, char ***_av)
389{
390	char **av, *sep, *protoName;
391	char tmp_spool_buf[NAT_BUF_LEN];
392	int ac, space, lsnat;
393	struct cfg_redir *r;
394	struct cfg_spool *tmp;
395	u_short numLocalPorts;
396	port_range portRange;
397
398	av = *_av;
399	ac = *_ac;
400	space = 0;
401	lsnat = 0;
402	numLocalPorts = 0;
403
404	if (len >= SOF_REDIR) {
405		r = (struct cfg_redir *)spool_buf;
406		/* Skip cfg_redir at beginning of buf. */
407		spool_buf = &spool_buf[SOF_REDIR];
408		space = SOF_REDIR;
409		len -= SOF_REDIR;
410	} else
411		goto nospace;
412	r->mode = REDIR_PORT;
413	/*
414	 * Extract protocol.
415	 */
416	if (ac == 0)
417		errx (EX_DATAERR, "redirect_port: missing protocol");
418	r->proto = StrToProto(*av);
419	protoName = *av;
420	INC_ARGCV();
421
422	/*
423	 * Extract local address.
424	 */
425	if (ac == 0)
426		errx (EX_DATAERR, "redirect_port: missing local address");
427
428	sep = strchr(*av, ',');
429	/* LSNAT redirection syntax. */
430	if (sep) {
431		r->laddr.s_addr = INADDR_NONE;
432		r->lport = ~0;
433		numLocalPorts = 1;
434		/* Preserve av, copy spool servers to tmp_spool_buf. */
435		strncpy(tmp_spool_buf, *av, strlen(*av)+1);
436		lsnat = 1;
437	} else {
438		/*
439		 * The sctp nat does not allow the port numbers to be mapped to
440		 * new port numbers. Therefore, no ports are to be specified
441		 * in the target port field.
442		 */
443		if (r->proto == IPPROTO_SCTP) {
444			if (strchr (*av, ':'))
445				errx(EX_DATAERR, "redirect_port:"
446				    "port numbers do not change in sctp, so do not "
447				    "specify them as part of the target");
448			else
449				StrToAddr(*av, &r->laddr);
450		} else {
451			if (StrToAddrAndPortRange (*av, &r->laddr, protoName,
452				&portRange) != 0)
453				errx(EX_DATAERR, "redirect_port:"
454				    "invalid local port range");
455
456			r->lport = GETLOPORT(portRange);
457			numLocalPorts = GETNUMPORTS(portRange);
458		}
459	}
460	INC_ARGCV();
461
462	/*
463	 * Extract public port and optionally address.
464	 */
465	if (ac == 0)
466		errx (EX_DATAERR, "redirect_port: missing public port");
467
468	sep = strchr (*av, ':');
469	if (sep) {
470	        if (StrToAddrAndPortRange (*av, &r->paddr, protoName,
471		    &portRange) != 0)
472		        errx(EX_DATAERR, "redirect_port:"
473			    "invalid public port range");
474	} else {
475		r->paddr.s_addr = INADDR_ANY;
476		if (StrToPortRange (*av, protoName, &portRange) != 0)
477		        errx(EX_DATAERR, "redirect_port:"
478			    "invalid public port range");
479	}
480
481	r->pport = GETLOPORT(portRange);
482	if (r->proto == IPPROTO_SCTP) { /* so the logic below still works */
483		numLocalPorts = GETNUMPORTS(portRange);
484		r->lport = r->pport;
485	}
486	r->pport_cnt = GETNUMPORTS(portRange);
487	INC_ARGCV();
488
489	/*
490	 * Extract remote address and optionally port.
491	 */
492	/*
493	 * NB: isalpha(**av) => we've to check that next parameter is really an
494	 * option for this redirect entry, else stop here processing arg[cv].
495	 */
496	if (ac != 0 && !isalpha(**av)) {
497		sep = strchr (*av, ':');
498		if (sep) {
499		        if (StrToAddrAndPortRange (*av, &r->raddr, protoName,
500			    &portRange) != 0)
501				errx(EX_DATAERR, "redirect_port:"
502				    "invalid remote port range");
503		} else {
504		        SETLOPORT(portRange, 0);
505			SETNUMPORTS(portRange, 1);
506			StrToAddr (*av, &r->raddr);
507		}
508		INC_ARGCV();
509	} else {
510		SETLOPORT(portRange, 0);
511		SETNUMPORTS(portRange, 1);
512		r->raddr.s_addr = INADDR_ANY;
513	}
514	r->rport = GETLOPORT(portRange);
515	r->rport_cnt = GETNUMPORTS(portRange);
516
517	/*
518	 * Make sure port ranges match up, then add the redirect ports.
519	 */
520	if (numLocalPorts != r->pport_cnt)
521	        errx(EX_DATAERR, "redirect_port:"
522		    "port ranges must be equal in size");
523
524	/* Remote port range is allowed to be '0' which means all ports. */
525	if (r->rport_cnt != numLocalPorts &&
526	    (r->rport_cnt != 1 || r->rport != 0))
527	        errx(EX_DATAERR, "redirect_port: remote port must"
528		    "be 0 or equal to local port range in size");
529
530	/*
531	 * Setup LSNAT server pool.
532	 */
533	if (lsnat) {
534		sep = strtok(tmp_spool_buf, ",");
535		while (sep != NULL) {
536			tmp = (struct cfg_spool *)spool_buf;
537			if (len < SOF_SPOOL)
538				goto nospace;
539			len -= SOF_SPOOL;
540			space += SOF_SPOOL;
541			/*
542			 * The sctp nat does not allow the port numbers to be mapped to new port numbers
543			 * Therefore, no ports are to be specified in the target port field
544			 */
545			if (r->proto == IPPROTO_SCTP) {
546				if (strchr (sep, ':')) {
547					errx(EX_DATAERR, "redirect_port:"
548					    "port numbers do not change in "
549					    "sctp, so do not specify them as "
550					    "part of the target");
551				} else {
552					StrToAddr(sep, &tmp->addr);
553					tmp->port = r->pport;
554				}
555			} else {
556				if (StrToAddrAndPortRange(sep, &tmp->addr,
557					protoName, &portRange) != 0)
558					errx(EX_DATAERR, "redirect_port:"
559					    "invalid local port range");
560				if (GETNUMPORTS(portRange) != 1)
561					errx(EX_DATAERR, "redirect_port: "
562					    "local port must be single in "
563					    "this context");
564				tmp->port = GETLOPORT(portRange);
565			}
566			r->spool_cnt++;
567			/* Point to the next possible cfg_spool. */
568			spool_buf = &spool_buf[SOF_SPOOL];
569			sep = strtok(NULL, ",");
570		}
571	}
572	return (space);
573nospace:
574	errx(EX_DATAERR, "redirect_port: buf is too small\n");
575}
576
577static int
578setup_redir_proto(char *spool_buf, int len,
579		 int *_ac, char ***_av)
580{
581	char **av;
582	int ac, space;
583	struct protoent *protoent;
584	struct cfg_redir *r;
585
586	av = *_av;
587	ac = *_ac;
588	if (len >= SOF_REDIR) {
589		r = (struct cfg_redir *)spool_buf;
590		/* Skip cfg_redir at beginning of buf. */
591		spool_buf = &spool_buf[SOF_REDIR];
592		space = SOF_REDIR;
593		len -= SOF_REDIR;
594	} else
595		goto nospace;
596	r->mode = REDIR_PROTO;
597	/*
598	 * Extract protocol.
599	 */
600	if (ac == 0)
601		errx(EX_DATAERR, "redirect_proto: missing protocol");
602
603	protoent = getprotobyname(*av);
604	if (protoent == NULL)
605		errx(EX_DATAERR, "redirect_proto: unknown protocol %s", *av);
606	else
607		r->proto = protoent->p_proto;
608
609	INC_ARGCV();
610
611	/*
612	 * Extract local address.
613	 */
614	if (ac == 0)
615		errx(EX_DATAERR, "redirect_proto: missing local address");
616	else
617		StrToAddr(*av, &r->laddr);
618
619	INC_ARGCV();
620
621	/*
622	 * Extract optional public address.
623	 */
624	if (ac == 0) {
625		r->paddr.s_addr = INADDR_ANY;
626		r->raddr.s_addr = INADDR_ANY;
627	} else {
628		/* see above in setup_redir_port() */
629		if (!isalpha(**av)) {
630			StrToAddr(*av, &r->paddr);
631			INC_ARGCV();
632
633			/*
634			 * Extract optional remote address.
635			 */
636			/* see above in setup_redir_port() */
637			if (ac!=0 && !isalpha(**av)) {
638				StrToAddr(*av, &r->raddr);
639				INC_ARGCV();
640			}
641		}
642	}
643	return (space);
644nospace:
645	errx(EX_DATAERR, "redirect_proto: buf is too small\n");
646}
647
648static void
649print_nat_config(unsigned char *buf)
650{
651	struct cfg_nat *n;
652	int i, cnt, flag, off;
653	struct cfg_redir *t;
654	struct cfg_spool *s;
655	struct protoent *p;
656
657	n = (struct cfg_nat *)buf;
658	flag = 1;
659	off  = sizeof(*n);
660	printf("ipfw nat %u config", n->id);
661	if (strlen(n->if_name) != 0)
662		printf(" if %s", n->if_name);
663	else if (n->ip.s_addr != 0)
664		printf(" ip %s", inet_ntoa(n->ip));
665	while (n->mode != 0) {
666		if (n->mode & PKT_ALIAS_LOG) {
667			printf(" log");
668			n->mode &= ~PKT_ALIAS_LOG;
669		} else if (n->mode & PKT_ALIAS_DENY_INCOMING) {
670			printf(" deny_in");
671			n->mode &= ~PKT_ALIAS_DENY_INCOMING;
672		} else if (n->mode & PKT_ALIAS_SAME_PORTS) {
673			printf(" same_ports");
674			n->mode &= ~PKT_ALIAS_SAME_PORTS;
675		} else if (n->mode & PKT_ALIAS_UNREGISTERED_ONLY) {
676			printf(" unreg_only");
677			n->mode &= ~PKT_ALIAS_UNREGISTERED_ONLY;
678		} else if (n->mode & PKT_ALIAS_RESET_ON_ADDR_CHANGE) {
679			printf(" reset");
680			n->mode &= ~PKT_ALIAS_RESET_ON_ADDR_CHANGE;
681		} else if (n->mode & PKT_ALIAS_REVERSE) {
682			printf(" reverse");
683			n->mode &= ~PKT_ALIAS_REVERSE;
684		} else if (n->mode & PKT_ALIAS_PROXY_ONLY) {
685			printf(" proxy_only");
686			n->mode &= ~PKT_ALIAS_PROXY_ONLY;
687		}
688	}
689	/* Print all the redirect's data configuration. */
690	for (cnt = 0; cnt < n->redir_cnt; cnt++) {
691		t = (struct cfg_redir *)&buf[off];
692		off += SOF_REDIR;
693		switch (t->mode) {
694		case REDIR_ADDR:
695			printf(" redirect_addr");
696			if (t->spool_cnt == 0)
697				printf(" %s", inet_ntoa(t->laddr));
698			else
699				for (i = 0; i < t->spool_cnt; i++) {
700					s = (struct cfg_spool *)&buf[off];
701					if (i)
702						printf(",");
703					else
704						printf(" ");
705					printf("%s", inet_ntoa(s->addr));
706					off += SOF_SPOOL;
707				}
708			printf(" %s", inet_ntoa(t->paddr));
709			break;
710		case REDIR_PORT:
711			p = getprotobynumber(t->proto);
712			printf(" redirect_port %s ", p->p_name);
713			if (!t->spool_cnt) {
714				printf("%s:%u", inet_ntoa(t->laddr), t->lport);
715				if (t->pport_cnt > 1)
716					printf("-%u", t->lport +
717					    t->pport_cnt - 1);
718			} else
719				for (i=0; i < t->spool_cnt; i++) {
720					s = (struct cfg_spool *)&buf[off];
721					if (i)
722						printf(",");
723					printf("%s:%u", inet_ntoa(s->addr),
724					    s->port);
725					off += SOF_SPOOL;
726				}
727
728			printf(" ");
729			if (t->paddr.s_addr)
730				printf("%s:", inet_ntoa(t->paddr));
731			printf("%u", t->pport);
732			if (!t->spool_cnt && t->pport_cnt > 1)
733				printf("-%u", t->pport + t->pport_cnt - 1);
734
735			if (t->raddr.s_addr) {
736				printf(" %s", inet_ntoa(t->raddr));
737				if (t->rport) {
738					printf(":%u", t->rport);
739					if (!t->spool_cnt && t->rport_cnt > 1)
740						printf("-%u", t->rport +
741						    t->rport_cnt - 1);
742				}
743			}
744			break;
745		case REDIR_PROTO:
746			p = getprotobynumber(t->proto);
747			printf(" redirect_proto %s %s", p->p_name,
748			    inet_ntoa(t->laddr));
749			if (t->paddr.s_addr != 0) {
750				printf(" %s", inet_ntoa(t->paddr));
751				if (t->raddr.s_addr)
752					printf(" %s", inet_ntoa(t->raddr));
753			}
754			break;
755		default:
756			errx(EX_DATAERR, "unknown redir mode");
757			break;
758		}
759	}
760	printf("\n");
761}
762
763void
764ipfw_config_nat(int ac, char **av)
765{
766	struct cfg_nat *n;              /* Nat instance configuration. */
767	int i, len, off, tok;
768	char *id, buf[NAT_BUF_LEN]; 	/* Buffer for serialized data. */
769
770	len = NAT_BUF_LEN;
771	/* Offset in buf: save space for n at the beginning. */
772	off = sizeof(*n);
773	memset(buf, 0, sizeof(buf));
774	n = (struct cfg_nat *)buf;
775
776	av++; ac--;
777	/* Nat id. */
778	if (ac && isdigit(**av)) {
779		id = *av;
780		i = atoi(*av);
781		ac--; av++;
782		n->id = i;
783	} else
784		errx(EX_DATAERR, "missing nat id");
785	if (ac == 0)
786		errx(EX_DATAERR, "missing option");
787
788	while (ac > 0) {
789		tok = match_token(nat_params, *av);
790		ac--; av++;
791		switch (tok) {
792		case TOK_IP:
793			if (ac == 0)
794				errx(EX_DATAERR, "missing option");
795			if (!inet_aton(av[0], &(n->ip)))
796				errx(EX_DATAERR, "bad ip address ``%s''",
797				    av[0]);
798			ac--; av++;
799			break;
800		case TOK_IF:
801			if (ac == 0)
802				errx(EX_DATAERR, "missing option");
803			set_addr_dynamic(av[0], n);
804			ac--; av++;
805			break;
806		case TOK_ALOG:
807			n->mode |= PKT_ALIAS_LOG;
808			break;
809		case TOK_DENY_INC:
810			n->mode |= PKT_ALIAS_DENY_INCOMING;
811			break;
812		case TOK_SAME_PORTS:
813			n->mode |= PKT_ALIAS_SAME_PORTS;
814			break;
815		case TOK_UNREG_ONLY:
816			n->mode |= PKT_ALIAS_UNREGISTERED_ONLY;
817			break;
818		case TOK_RESET_ADDR:
819			n->mode |= PKT_ALIAS_RESET_ON_ADDR_CHANGE;
820			break;
821		case TOK_ALIAS_REV:
822			n->mode |= PKT_ALIAS_REVERSE;
823			break;
824		case TOK_PROXY_ONLY:
825			n->mode |= PKT_ALIAS_PROXY_ONLY;
826			break;
827			/*
828			 * All the setup_redir_* functions work directly in the final
829			 * buffer, see above for details.
830			 */
831		case TOK_REDIR_ADDR:
832		case TOK_REDIR_PORT:
833		case TOK_REDIR_PROTO:
834			switch (tok) {
835			case TOK_REDIR_ADDR:
836				i = setup_redir_addr(&buf[off], len, &ac, &av);
837				break;
838			case TOK_REDIR_PORT:
839				i = setup_redir_port(&buf[off], len, &ac, &av);
840				break;
841			case TOK_REDIR_PROTO:
842				i = setup_redir_proto(&buf[off], len, &ac, &av);
843				break;
844			}
845			n->redir_cnt++;
846			off += i;
847			len -= i;
848			break;
849		default:
850			errx(EX_DATAERR, "unrecognised option ``%s''", av[-1]);
851		}
852	}
853
854	i = do_cmd(IP_FW_NAT_CFG, buf, off);
855	if (i)
856		err(1, "setsockopt(%s)", "IP_FW_NAT_CFG");
857
858	if (!co.do_quiet) {
859		/* After every modification, we show the resultant rule. */
860		int _ac = 3;
861		char *_av[] = {"show", "config", id};
862		ipfw_show_nat(_ac, _av);
863	}
864}
865
866
867void
868ipfw_show_nat(int ac, char **av)
869{
870	struct cfg_nat *n;
871	struct cfg_redir *e;
872	int cmd, i, nbytes, do_cfg, do_rule, frule, lrule, nalloc, size;
873	int nat_cnt, redir_cnt, r;
874	uint8_t *data, *p;
875	char *endptr;
876
877	do_rule = 0;
878	nalloc = 1024;
879	size = 0;
880	data = NULL;
881	frule = 0;
882	lrule = IPFW_DEFAULT_RULE; /* max ipfw rule number */
883	ac--; av++;
884
885	if (co.test_only)
886		return;
887
888	/* Parse parameters. */
889	for (cmd = IP_FW_NAT_GET_LOG, do_cfg = 0; ac != 0; ac--, av++) {
890		if (!strncmp(av[0], "config", strlen(av[0]))) {
891			cmd = IP_FW_NAT_GET_CONFIG, do_cfg = 1;
892			continue;
893		}
894		/* Convert command line rule #. */
895		frule = lrule = strtoul(av[0], &endptr, 10);
896		if (*endptr == '-')
897			lrule = strtoul(endptr+1, &endptr, 10);
898		if (lrule == 0)
899			err(EX_USAGE, "invalid rule number: %s", av[0]);
900		do_rule = 1;
901	}
902
903	nbytes = nalloc;
904	while (nbytes >= nalloc) {
905		nalloc = nalloc * 2;
906		nbytes = nalloc;
907		data = safe_realloc(data, nbytes);
908		if (do_cmd(cmd, data, (uintptr_t)&nbytes) < 0)
909			err(EX_OSERR, "getsockopt(IP_FW_GET_%s)",
910			    (cmd == IP_FW_NAT_GET_LOG) ? "LOG" : "CONFIG");
911	}
912	if (nbytes == 0)
913		exit(0);
914	if (do_cfg) {
915		nat_cnt = *((int *)data);
916		for (i = sizeof(nat_cnt); nat_cnt; nat_cnt--) {
917			n = (struct cfg_nat *)&data[i];
918			if (frule <= n->id && lrule >= n->id)
919				print_nat_config(&data[i]);
920			i += sizeof(struct cfg_nat);
921			for (redir_cnt = 0; redir_cnt < n->redir_cnt; redir_cnt++) {
922				e = (struct cfg_redir *)&data[i];
923				i += sizeof(struct cfg_redir) + e->spool_cnt *
924				    sizeof(struct cfg_spool);
925			}
926		}
927	} else {
928		for (i = 0; 1; i += LIBALIAS_BUF_SIZE + sizeof(int)) {
929			p = &data[i];
930			if (p == data + nbytes)
931				break;
932			bcopy(p, &r, sizeof(int));
933			if (do_rule) {
934				if (!(frule <= r && lrule >= r))
935					continue;
936			}
937			printf("nat %u: %s\n", r, p+sizeof(int));
938		}
939	}
940}
941