nat.c revision 220835
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 220835 2011-04-19 15:03:12Z glebius $
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/*
285 * The next 3 functions add support for the addr, port and proto redirect and
286 * their logic is loosely based on SetupAddressRedirect(), SetupPortRedirect()
287 * and SetupProtoRedirect() from natd.c.
288 *
289 * Every setup_* function fills at least one redirect entry
290 * (struct cfg_redir) and zero or more server pool entry (struct cfg_spool)
291 * in buf.
292 *
293 * The format of data in buf is:
294 *
295 *     cfg_nat    cfg_redir    cfg_spool    ......  cfg_spool
296 *
297 *    -------------------------------------        ------------
298 *   |          | .....X ... |          |         |           |  .....
299 *    ------------------------------------- ...... ------------
300 *                     ^
301 *                spool_cnt       n=0       ......   n=(X-1)
302 *
303 * len points to the amount of available space in buf
304 * space counts the memory consumed by every function
305 *
306 * XXX - Every function get all the argv params so it
307 * has to check, in optional parameters, that the next
308 * args is a valid option for the redir entry and not
309 * another token. Only redir_port and redir_proto are
310 * affected by this.
311 */
312
313static int
314estimate_redir_addr(int *ac, char ***av)
315{
316	size_t space = sizeof(struct cfg_redir);
317	char *sep;
318
319	if ((sep = strtok(**av, ",")) != NULL) {
320		space += sizeof(struct cfg_spool);
321		while ((sep = strtok(NULL, ",")) != NULL)
322			space += sizeof(struct cfg_spool);
323	}
324
325	return (space);
326}
327
328static int
329setup_redir_addr(char *buf, int *ac, char ***av)
330{
331	struct cfg_redir *r;
332	char *sep;
333	size_t space;
334
335	r = (struct cfg_redir *)buf;
336	r->mode = REDIR_ADDR;
337	/* Skip cfg_redir at beginning of buf. */
338	buf = &buf[sizeof(struct cfg_redir)];
339	space = sizeof(struct cfg_redir);
340
341	/* Extract local address. */
342	if ((sep = strtok(**av, ",")) != NULL) {
343		struct cfg_spool *spool;
344
345		/* Setup LSNAT server pool. */
346		r->laddr.s_addr = INADDR_NONE;
347		while (sep != NULL) {
348			spool = (struct cfg_spool *)buf;
349			space += sizeof(struct cfg_spool);
350			StrToAddr(sep, &spool->addr);
351			spool->port = ~0;
352			r->spool_cnt++;
353			/* Point to the next possible cfg_spool. */
354			buf = &buf[sizeof(struct cfg_spool)];
355			sep = strtok(NULL, ",");
356		}
357	} else
358		StrToAddr(**av, &r->laddr);
359	(*av)++; (*ac)--;
360
361	/* Extract public address. */
362	StrToAddr(**av, &r->paddr);
363	(*av)++; (*ac)--;
364
365	return (space);
366}
367
368static int
369estimate_redir_port(int *ac, char ***av)
370{
371	size_t space = sizeof(struct cfg_redir);
372	char *sep;
373
374	if ((sep = strtok(**av, ",")) != NULL) {
375		space += sizeof(struct cfg_spool);
376		while ((sep = strtok(NULL, ",")) != NULL)
377			space += sizeof(struct cfg_spool);
378	}
379
380	return (space);
381}
382
383static int
384setup_redir_port(char *buf, int *ac, char ***av)
385{
386	struct cfg_redir *r;
387	char *sep, *protoName, *lsnat = NULL;
388	size_t space;
389	u_short numLocalPorts;
390	port_range portRange;
391
392	numLocalPorts = 0;
393
394	r = (struct cfg_redir *)buf;
395	r->mode = REDIR_PORT;
396	/* Skip cfg_redir at beginning of buf. */
397	buf = &buf[sizeof(struct cfg_redir)];
398	space = sizeof(struct cfg_redir);
399
400	/*
401	 * Extract protocol.
402	 */
403	r->proto = StrToProto(**av);
404	protoName = **av;
405	(*av)++; (*ac)--;
406
407	/*
408	 * Extract local address.
409	 */
410	if ((sep = strchr(**av, ',')) != NULL) {
411		r->laddr.s_addr = INADDR_NONE;
412		r->lport = ~0;
413		numLocalPorts = 1;
414		lsnat = **av;
415	} else {
416		/*
417		 * The sctp nat does not allow the port numbers to be mapped to
418		 * new port numbers. Therefore, no ports are to be specified
419		 * in the target port field.
420		 */
421		if (r->proto == IPPROTO_SCTP) {
422			if (strchr(**av, ':'))
423				errx(EX_DATAERR, "redirect_port:"
424				    "port numbers do not change in sctp, so do "
425				    "not specify them as part of the target");
426			else
427				StrToAddr(**av, &r->laddr);
428		} else {
429			if (StrToAddrAndPortRange(**av, &r->laddr, protoName,
430			    &portRange) != 0)
431				errx(EX_DATAERR, "redirect_port: "
432				    "invalid local port range");
433
434			r->lport = GETLOPORT(portRange);
435			numLocalPorts = GETNUMPORTS(portRange);
436		}
437	}
438	(*av)++; (*ac)--;
439
440	/*
441	 * Extract public port and optionally address.
442	 */
443	if ((sep = strchr(**av, ':')) != NULL) {
444		if (StrToAddrAndPortRange(**av, &r->paddr, protoName,
445		    &portRange) != 0)
446			errx(EX_DATAERR, "redirect_port: "
447			    "invalid public port range");
448	} else {
449		r->paddr.s_addr = INADDR_ANY;
450		if (StrToPortRange(**av, protoName, &portRange) != 0)
451			errx(EX_DATAERR, "redirect_port: "
452			    "invalid public port range");
453	}
454
455	r->pport = GETLOPORT(portRange);
456	if (r->proto == IPPROTO_SCTP) { /* so the logic below still works */
457		numLocalPorts = GETNUMPORTS(portRange);
458		r->lport = r->pport;
459	}
460	r->pport_cnt = GETNUMPORTS(portRange);
461	(*av)++; (*ac)--;
462
463	/*
464	 * Extract remote address and optionally port.
465	 */
466	/*
467	 * NB: isalpha(**av) => we've to check that next parameter is really an
468	 * option for this redirect entry, else stop here processing arg[cv].
469	 */
470	if (*ac != 0 && !isalpha(***av)) {
471		if ((sep = strchr(**av, ':')) != NULL) {
472			if (StrToAddrAndPortRange(**av, &r->raddr, protoName,
473			    &portRange) != 0)
474				errx(EX_DATAERR, "redirect_port: "
475				    "invalid remote port range");
476		} else {
477			SETLOPORT(portRange, 0);
478			SETNUMPORTS(portRange, 1);
479			StrToAddr(**av, &r->raddr);
480		}
481		(*av)++; (*ac)--;
482	} else {
483		SETLOPORT(portRange, 0);
484		SETNUMPORTS(portRange, 1);
485		r->raddr.s_addr = INADDR_ANY;
486	}
487	r->rport = GETLOPORT(portRange);
488	r->rport_cnt = GETNUMPORTS(portRange);
489
490	/*
491	 * Make sure port ranges match up, then add the redirect ports.
492	 */
493	if (numLocalPorts != r->pport_cnt)
494		errx(EX_DATAERR, "redirect_port: "
495		    "port ranges must be equal in size");
496
497	/* Remote port range is allowed to be '0' which means all ports. */
498	if (r->rport_cnt != numLocalPorts &&
499	    (r->rport_cnt != 1 || r->rport != 0))
500		errx(EX_DATAERR, "redirect_port: remote port must"
501		    "be 0 or equal to local port range in size");
502
503	/* Setup LSNAT server pool. */
504	if (lsnat != NULL) {
505		struct cfg_spool *spool;
506
507		sep = strtok(lsnat, ",");
508		while (sep != NULL) {
509			spool = (struct cfg_spool *)buf;
510			space += sizeof(struct cfg_spool);
511			/*
512			 * The sctp nat does not allow the port numbers to
513			 * be mapped to new port numbers. Therefore, no ports
514			 * are to be specified in the target port field.
515			 */
516			if (r->proto == IPPROTO_SCTP) {
517				if (strchr (sep, ':')) {
518					errx(EX_DATAERR, "redirect_port:"
519					    "port numbers do not change in "
520					    "sctp, so do not specify them as "
521					    "part of the target");
522				} else {
523					StrToAddr(sep, &spool->addr);
524					spool->port = r->pport;
525				}
526			} else {
527				if (StrToAddrAndPortRange(sep, &spool->addr,
528					protoName, &portRange) != 0)
529					errx(EX_DATAERR, "redirect_port:"
530					    "invalid local port range");
531				if (GETNUMPORTS(portRange) != 1)
532					errx(EX_DATAERR, "redirect_port: "
533					    "local port must be single in "
534					    "this context");
535				spool->port = GETLOPORT(portRange);
536			}
537			r->spool_cnt++;
538			/* Point to the next possible cfg_spool. */
539			buf = &buf[sizeof(struct cfg_spool)];
540			sep = strtok(NULL, ",");
541		}
542	}
543
544	return (space);
545}
546
547static int
548setup_redir_proto(char *buf, int *ac, char ***av)
549{
550	struct cfg_redir *r;
551	struct protoent *protoent;
552	size_t space;
553
554	r = (struct cfg_redir *)buf;
555	r->mode = REDIR_PROTO;
556	/* Skip cfg_redir at beginning of buf. */
557	buf = &buf[sizeof(struct cfg_redir)];
558	space = sizeof(struct cfg_redir);
559
560	/*
561	 * Extract protocol.
562	 */
563	protoent = getprotobyname(**av);
564	if (protoent == NULL)
565		errx(EX_DATAERR, "redirect_proto: unknown protocol %s", **av);
566	else
567		r->proto = protoent->p_proto;
568
569	(*av)++; (*ac)--;
570
571	/*
572	 * Extract local address.
573	 */
574	StrToAddr(**av, &r->laddr);
575
576	(*av)++; (*ac)--;
577
578	/*
579	 * Extract optional public address.
580	 */
581	if (*ac == 0) {
582		r->paddr.s_addr = INADDR_ANY;
583		r->raddr.s_addr = INADDR_ANY;
584	} else {
585		/* see above in setup_redir_port() */
586		if (!isalpha(***av)) {
587			StrToAddr(**av, &r->paddr);
588			(*av)++; (*ac)--;
589
590			/*
591			 * Extract optional remote address.
592			 */
593			/* see above in setup_redir_port() */
594			if (*ac != 0 && !isalpha(***av)) {
595				StrToAddr(**av, &r->raddr);
596				(*av)++; (*ac)--;
597			}
598		}
599	}
600
601	return (space);
602}
603
604static void
605print_nat_config(unsigned char *buf)
606{
607	struct cfg_nat *n;
608	int i, cnt, flag, off;
609	struct cfg_redir *t;
610	struct cfg_spool *s;
611	struct protoent *p;
612
613	n = (struct cfg_nat *)buf;
614	flag = 1;
615	off  = sizeof(*n);
616	printf("ipfw nat %u config", n->id);
617	if (strlen(n->if_name) != 0)
618		printf(" if %s", n->if_name);
619	else if (n->ip.s_addr != 0)
620		printf(" ip %s", inet_ntoa(n->ip));
621	while (n->mode != 0) {
622		if (n->mode & PKT_ALIAS_LOG) {
623			printf(" log");
624			n->mode &= ~PKT_ALIAS_LOG;
625		} else if (n->mode & PKT_ALIAS_DENY_INCOMING) {
626			printf(" deny_in");
627			n->mode &= ~PKT_ALIAS_DENY_INCOMING;
628		} else if (n->mode & PKT_ALIAS_SAME_PORTS) {
629			printf(" same_ports");
630			n->mode &= ~PKT_ALIAS_SAME_PORTS;
631		} else if (n->mode & PKT_ALIAS_UNREGISTERED_ONLY) {
632			printf(" unreg_only");
633			n->mode &= ~PKT_ALIAS_UNREGISTERED_ONLY;
634		} else if (n->mode & PKT_ALIAS_RESET_ON_ADDR_CHANGE) {
635			printf(" reset");
636			n->mode &= ~PKT_ALIAS_RESET_ON_ADDR_CHANGE;
637		} else if (n->mode & PKT_ALIAS_REVERSE) {
638			printf(" reverse");
639			n->mode &= ~PKT_ALIAS_REVERSE;
640		} else if (n->mode & PKT_ALIAS_PROXY_ONLY) {
641			printf(" proxy_only");
642			n->mode &= ~PKT_ALIAS_PROXY_ONLY;
643		}
644	}
645	/* Print all the redirect's data configuration. */
646	for (cnt = 0; cnt < n->redir_cnt; cnt++) {
647		t = (struct cfg_redir *)&buf[off];
648		off += SOF_REDIR;
649		switch (t->mode) {
650		case REDIR_ADDR:
651			printf(" redirect_addr");
652			if (t->spool_cnt == 0)
653				printf(" %s", inet_ntoa(t->laddr));
654			else
655				for (i = 0; i < t->spool_cnt; i++) {
656					s = (struct cfg_spool *)&buf[off];
657					if (i)
658						printf(",");
659					else
660						printf(" ");
661					printf("%s", inet_ntoa(s->addr));
662					off += SOF_SPOOL;
663				}
664			printf(" %s", inet_ntoa(t->paddr));
665			break;
666		case REDIR_PORT:
667			p = getprotobynumber(t->proto);
668			printf(" redirect_port %s ", p->p_name);
669			if (!t->spool_cnt) {
670				printf("%s:%u", inet_ntoa(t->laddr), t->lport);
671				if (t->pport_cnt > 1)
672					printf("-%u", t->lport +
673					    t->pport_cnt - 1);
674			} else
675				for (i=0; i < t->spool_cnt; i++) {
676					s = (struct cfg_spool *)&buf[off];
677					if (i)
678						printf(",");
679					printf("%s:%u", inet_ntoa(s->addr),
680					    s->port);
681					off += SOF_SPOOL;
682				}
683
684			printf(" ");
685			if (t->paddr.s_addr)
686				printf("%s:", inet_ntoa(t->paddr));
687			printf("%u", t->pport);
688			if (!t->spool_cnt && t->pport_cnt > 1)
689				printf("-%u", t->pport + t->pport_cnt - 1);
690
691			if (t->raddr.s_addr) {
692				printf(" %s", inet_ntoa(t->raddr));
693				if (t->rport) {
694					printf(":%u", t->rport);
695					if (!t->spool_cnt && t->rport_cnt > 1)
696						printf("-%u", t->rport +
697						    t->rport_cnt - 1);
698				}
699			}
700			break;
701		case REDIR_PROTO:
702			p = getprotobynumber(t->proto);
703			printf(" redirect_proto %s %s", p->p_name,
704			    inet_ntoa(t->laddr));
705			if (t->paddr.s_addr != 0) {
706				printf(" %s", inet_ntoa(t->paddr));
707				if (t->raddr.s_addr)
708					printf(" %s", inet_ntoa(t->raddr));
709			}
710			break;
711		default:
712			errx(EX_DATAERR, "unknown redir mode");
713			break;
714		}
715	}
716	printf("\n");
717}
718
719void
720ipfw_config_nat(int ac, char **av)
721{
722	struct cfg_nat *n;		/* Nat instance configuration. */
723	int i, off, tok, ac1;
724	char *id, *buf, **av1;
725	size_t len;
726
727	av++; ac--;
728	/* Nat id. */
729	if (ac && isdigit(**av)) {
730		id = *av;
731		ac--; av++;
732	} else
733		errx(EX_DATAERR, "missing nat id");
734	if (ac == 0)
735		errx(EX_DATAERR, "missing option");
736
737	len = sizeof(struct cfg_nat);
738	ac1 = ac;
739	av1 = av;
740	while (ac1 > 0) {
741		tok = match_token(nat_params, *av1);
742		ac1--; av1++;
743		switch (tok) {
744		case TOK_IP:
745		case TOK_IF:
746			ac1--; av1++;
747			break;
748		case TOK_ALOG:
749		case TOK_DENY_INC:
750		case TOK_SAME_PORTS:
751		case TOK_UNREG_ONLY:
752		case TOK_RESET_ADDR:
753		case TOK_ALIAS_REV:
754		case TOK_PROXY_ONLY:
755			break;
756		case TOK_REDIR_ADDR:
757			if (ac1 < 2)
758				errx(EX_DATAERR, "redirect_addr: "
759				    "not enough arguments");
760			len += estimate_redir_addr(&ac1, &av1);
761			av1 += 2; ac1 -= 2;
762			break;
763		case TOK_REDIR_PORT:
764			if (ac1 < 3)
765				errx(EX_DATAERR, "redirect_port: "
766				    "not enough arguments");
767			av1++; ac1--;
768			len += estimate_redir_port(&ac1, &av1);
769			av1 += 2; ac1 -= 2;
770			break;
771		case TOK_REDIR_PROTO:
772			if (ac1 < 2)
773				errx(EX_DATAERR, "redirect_proto: "
774				    "not enough arguments");
775			len += sizeof(struct cfg_redir);
776			av1 += 2; ac1 -= 2;
777			break;
778		default:
779			errx(EX_DATAERR, "unrecognised option ``%s''", av1[-1]);
780		}
781	}
782
783	if ((buf = malloc(len)) == NULL)
784		errx(EX_OSERR, "malloc failed");
785
786	/* Offset in buf: save space for n at the beginning. */
787	off = sizeof(*n);
788	memset(buf, 0, len);
789	n = (struct cfg_nat *)buf;
790	i = atoi(id);
791	n->id = i;
792
793	while (ac > 0) {
794		tok = match_token(nat_params, *av);
795		ac--; av++;
796		switch (tok) {
797		case TOK_IP:
798			if (ac == 0)
799				errx(EX_DATAERR, "missing option");
800			if (!inet_aton(av[0], &(n->ip)))
801				errx(EX_DATAERR, "bad ip address ``%s''",
802				    av[0]);
803			ac--; av++;
804			break;
805		case TOK_IF:
806			if (ac == 0)
807				errx(EX_DATAERR, "missing option");
808			set_addr_dynamic(av[0], n);
809			ac--; av++;
810			break;
811		case TOK_ALOG:
812			n->mode |= PKT_ALIAS_LOG;
813			break;
814		case TOK_DENY_INC:
815			n->mode |= PKT_ALIAS_DENY_INCOMING;
816			break;
817		case TOK_SAME_PORTS:
818			n->mode |= PKT_ALIAS_SAME_PORTS;
819			break;
820		case TOK_UNREG_ONLY:
821			n->mode |= PKT_ALIAS_UNREGISTERED_ONLY;
822			break;
823		case TOK_RESET_ADDR:
824			n->mode |= PKT_ALIAS_RESET_ON_ADDR_CHANGE;
825			break;
826		case TOK_ALIAS_REV:
827			n->mode |= PKT_ALIAS_REVERSE;
828			break;
829		case TOK_PROXY_ONLY:
830			n->mode |= PKT_ALIAS_PROXY_ONLY;
831			break;
832			/*
833			 * All the setup_redir_* functions work directly in
834			 * the final buffer, see above for details.
835			 */
836		case TOK_REDIR_ADDR:
837		case TOK_REDIR_PORT:
838		case TOK_REDIR_PROTO:
839			switch (tok) {
840			case TOK_REDIR_ADDR:
841				i = setup_redir_addr(&buf[off], &ac, &av);
842				break;
843			case TOK_REDIR_PORT:
844				i = setup_redir_port(&buf[off], &ac, &av);
845				break;
846			case TOK_REDIR_PROTO:
847				i = setup_redir_proto(&buf[off], &ac, &av);
848				break;
849			}
850			n->redir_cnt++;
851			off += i;
852			break;
853		}
854	}
855
856	i = do_cmd(IP_FW_NAT_CFG, buf, off);
857	if (i)
858		err(1, "setsockopt(%s)", "IP_FW_NAT_CFG");
859
860	if (!co.do_quiet) {
861		/* After every modification, we show the resultant rule. */
862		int _ac = 3;
863		const char *_av[] = {"show", "config", id};
864		ipfw_show_nat(_ac, (char **)(void *)_av);
865	}
866}
867
868
869void
870ipfw_show_nat(int ac, char **av)
871{
872	struct cfg_nat *n;
873	struct cfg_redir *e;
874	int cmd, i, nbytes, do_cfg, do_rule, frule, lrule, nalloc, size;
875	int nat_cnt, redir_cnt, r;
876	uint8_t *data, *p;
877	char *endptr;
878
879	do_rule = 0;
880	nalloc = 1024;
881	size = 0;
882	data = NULL;
883	frule = 0;
884	lrule = IPFW_DEFAULT_RULE; /* max ipfw rule number */
885	ac--; av++;
886
887	if (co.test_only)
888		return;
889
890	/* Parse parameters. */
891	for (cmd = IP_FW_NAT_GET_LOG, do_cfg = 0; ac != 0; ac--, av++) {
892		if (!strncmp(av[0], "config", strlen(av[0]))) {
893			cmd = IP_FW_NAT_GET_CONFIG, do_cfg = 1;
894			continue;
895		}
896		/* Convert command line rule #. */
897		frule = lrule = strtoul(av[0], &endptr, 10);
898		if (*endptr == '-')
899			lrule = strtoul(endptr+1, &endptr, 10);
900		if (lrule == 0)
901			err(EX_USAGE, "invalid rule number: %s", av[0]);
902		do_rule = 1;
903	}
904
905	nbytes = nalloc;
906	while (nbytes >= nalloc) {
907		nalloc = nalloc * 2;
908		nbytes = nalloc;
909		data = safe_realloc(data, nbytes);
910		if (do_cmd(cmd, data, (uintptr_t)&nbytes) < 0)
911			err(EX_OSERR, "getsockopt(IP_FW_GET_%s)",
912			    (cmd == IP_FW_NAT_GET_LOG) ? "LOG" : "CONFIG");
913	}
914	if (nbytes == 0)
915		exit(0);
916	if (do_cfg) {
917		nat_cnt = *((int *)data);
918		for (i = sizeof(nat_cnt); nat_cnt; nat_cnt--) {
919			n = (struct cfg_nat *)&data[i];
920			if (frule <= n->id && lrule >= n->id)
921				print_nat_config(&data[i]);
922			i += sizeof(struct cfg_nat);
923			for (redir_cnt = 0; redir_cnt < n->redir_cnt; redir_cnt++) {
924				e = (struct cfg_redir *)&data[i];
925				i += sizeof(struct cfg_redir) + e->spool_cnt *
926				    sizeof(struct cfg_spool);
927			}
928		}
929	} else {
930		for (i = 0; 1; i += LIBALIAS_BUF_SIZE + sizeof(int)) {
931			p = &data[i];
932			if (p == data + nbytes)
933				break;
934			bcopy(p, &r, sizeof(int));
935			if (do_rule) {
936				if (!(frule <= r && lrule >= r))
937					continue;
938			}
939			printf("nat %u: %s\n", r, p+sizeof(int));
940		}
941	}
942}
943