nat.c revision 187770
1187770Sluigi/*
2187770Sluigi * Copyright (c) 2002-2003 Luigi Rizzo
3187770Sluigi * Copyright (c) 1996 Alex Nash, Paul Traina, Poul-Henning Kamp
4187770Sluigi * Copyright (c) 1994 Ugen J.S.Antsilevich
5187770Sluigi *
6187770Sluigi * Idea and grammar partially left from:
7187770Sluigi * Copyright (c) 1993 Daniel Boulet
8187770Sluigi *
9187770Sluigi * Redistribution and use in source forms, with and without modification,
10187770Sluigi * are permitted provided that this entire comment appears intact.
11187770Sluigi *
12187770Sluigi * Redistribution in binary form may occur without any restrictions.
13187770Sluigi * Obviously, it would be nice if you gave credit where credit is due
14187770Sluigi * but requiring it would be too onerous.
15187770Sluigi *
16187770Sluigi * This software is provided ``AS IS'' without any warranties of any kind.
17187770Sluigi *
18187770Sluigi * NEW command line interface for IP firewall facility
19187770Sluigi *
20187770Sluigi * $FreeBSD: head/sbin/ipfw/nat.c 187770 2009-01-27 12:01:30Z luigi $
21187770Sluigi *
22187770Sluigi * In-kernel nat support
23187770Sluigi */
24187770Sluigi
25187770Sluigi#include <sys/types.h>
26187770Sluigi#include <sys/socket.h>
27187770Sluigi#include <sys/sockio.h>
28187770Sluigi#include <sys/sysctl.h>
29187770Sluigi
30187770Sluigi#include "ipfw2.h"
31187770Sluigi
32187770Sluigi#include <ctype.h>
33187770Sluigi#include <err.h>
34187770Sluigi#include <netdb.h>
35187770Sluigi#include <stdio.h>
36187770Sluigi#include <stdlib.h>
37187770Sluigi#include <string.h>
38187770Sluigi#include <sysexits.h>
39187770Sluigi
40187770Sluigi#define IPFW_INTERNAL	/* Access to protected structures in ip_fw.h. */
41187770Sluigi
42187770Sluigi#include <net/if.h>
43187770Sluigi#include <net/if_dl.h>
44187770Sluigi#include <net/route.h> /* def. of struct route */
45187770Sluigi#include <netinet/in.h>
46187770Sluigi#include <netinet/in_systm.h>
47187770Sluigi#include <netinet/ip_fw.h>
48187770Sluigi#include <arpa/inet.h>
49187770Sluigi#include <alias.h>
50187770Sluigi
51187770Sluigistatic struct _s_x nat_params[] = {
52187770Sluigi	{ "ip",	                TOK_IP },
53187770Sluigi	{ "if",	                TOK_IF },
54187770Sluigi 	{ "log",                TOK_ALOG },
55187770Sluigi 	{ "deny_in",	        TOK_DENY_INC },
56187770Sluigi 	{ "same_ports",	        TOK_SAME_PORTS },
57187770Sluigi 	{ "unreg_only",	        TOK_UNREG_ONLY },
58187770Sluigi 	{ "reset",	        TOK_RESET_ADDR },
59187770Sluigi 	{ "reverse",	        TOK_ALIAS_REV },
60187770Sluigi 	{ "proxy_only",	        TOK_PROXY_ONLY },
61187770Sluigi	{ "redirect_addr",	TOK_REDIR_ADDR },
62187770Sluigi	{ "redirect_port",	TOK_REDIR_PORT },
63187770Sluigi	{ "redirect_proto",	TOK_REDIR_PROTO },
64187770Sluigi 	{ NULL, 0 }	/* terminator */
65187770Sluigi};
66187770Sluigi
67187770Sluigi
68187770Sluigi/*
69187770Sluigi * Search for interface with name "ifn", and fill n accordingly:
70187770Sluigi *
71187770Sluigi * n->ip        ip address of interface "ifn"
72187770Sluigi * n->if_name   copy of interface name "ifn"
73187770Sluigi */
74187770Sluigistatic void
75187770Sluigiset_addr_dynamic(const char *ifn, struct cfg_nat *n)
76187770Sluigi{
77187770Sluigi	size_t needed;
78187770Sluigi	int mib[6];
79187770Sluigi	char *buf, *lim, *next;
80187770Sluigi	struct if_msghdr *ifm;
81187770Sluigi	struct ifa_msghdr *ifam;
82187770Sluigi	struct sockaddr_dl *sdl;
83187770Sluigi	struct sockaddr_in *sin;
84187770Sluigi	int ifIndex, ifMTU;
85187770Sluigi
86187770Sluigi	mib[0] = CTL_NET;
87187770Sluigi	mib[1] = PF_ROUTE;
88187770Sluigi	mib[2] = 0;
89187770Sluigi	mib[3] = AF_INET;
90187770Sluigi	mib[4] = NET_RT_IFLIST;
91187770Sluigi	mib[5] = 0;
92187770Sluigi/*
93187770Sluigi * Get interface data.
94187770Sluigi */
95187770Sluigi	if (sysctl(mib, 6, NULL, &needed, NULL, 0) == -1)
96187770Sluigi		err(1, "iflist-sysctl-estimate");
97187770Sluigi	buf = safe_calloc(1, needed);
98187770Sluigi	if (sysctl(mib, 6, buf, &needed, NULL, 0) == -1)
99187770Sluigi		err(1, "iflist-sysctl-get");
100187770Sluigi	lim = buf + needed;
101187770Sluigi/*
102187770Sluigi * Loop through interfaces until one with
103187770Sluigi * given name is found. This is done to
104187770Sluigi * find correct interface index for routing
105187770Sluigi * message processing.
106187770Sluigi */
107187770Sluigi	ifIndex	= 0;
108187770Sluigi	next = buf;
109187770Sluigi	while (next < lim) {
110187770Sluigi		ifm = (struct if_msghdr *)next;
111187770Sluigi		next += ifm->ifm_msglen;
112187770Sluigi		if (ifm->ifm_version != RTM_VERSION) {
113187770Sluigi			if (co.verbose)
114187770Sluigi				warnx("routing message version %d "
115187770Sluigi				    "not understood", ifm->ifm_version);
116187770Sluigi			continue;
117187770Sluigi		}
118187770Sluigi		if (ifm->ifm_type == RTM_IFINFO) {
119187770Sluigi			sdl = (struct sockaddr_dl *)(ifm + 1);
120187770Sluigi			if (strlen(ifn) == sdl->sdl_nlen &&
121187770Sluigi			    strncmp(ifn, sdl->sdl_data, sdl->sdl_nlen) == 0) {
122187770Sluigi				ifIndex = ifm->ifm_index;
123187770Sluigi				ifMTU = ifm->ifm_data.ifi_mtu;
124187770Sluigi				break;
125187770Sluigi			}
126187770Sluigi		}
127187770Sluigi	}
128187770Sluigi	if (!ifIndex)
129187770Sluigi		errx(1, "unknown interface name %s", ifn);
130187770Sluigi/*
131187770Sluigi * Get interface address.
132187770Sluigi */
133187770Sluigi	sin = NULL;
134187770Sluigi	while (next < lim) {
135187770Sluigi		ifam = (struct ifa_msghdr *)next;
136187770Sluigi		next += ifam->ifam_msglen;
137187770Sluigi		if (ifam->ifam_version != RTM_VERSION) {
138187770Sluigi			if (co.verbose)
139187770Sluigi				warnx("routing message version %d "
140187770Sluigi				    "not understood", ifam->ifam_version);
141187770Sluigi			continue;
142187770Sluigi		}
143187770Sluigi		if (ifam->ifam_type != RTM_NEWADDR)
144187770Sluigi			break;
145187770Sluigi		if (ifam->ifam_addrs & RTA_IFA) {
146187770Sluigi			int i;
147187770Sluigi			char *cp = (char *)(ifam + 1);
148187770Sluigi
149187770Sluigi			for (i = 1; i < RTA_IFA; i <<= 1) {
150187770Sluigi				if (ifam->ifam_addrs & i)
151187770Sluigi					cp += SA_SIZE((struct sockaddr *)cp);
152187770Sluigi			}
153187770Sluigi			if (((struct sockaddr *)cp)->sa_family == AF_INET) {
154187770Sluigi				sin = (struct sockaddr_in *)cp;
155187770Sluigi				break;
156187770Sluigi			}
157187770Sluigi		}
158187770Sluigi	}
159187770Sluigi	if (sin == NULL)
160187770Sluigi		errx(1, "%s: cannot get interface address", ifn);
161187770Sluigi
162187770Sluigi	n->ip = sin->sin_addr;
163187770Sluigi	strncpy(n->if_name, ifn, IF_NAMESIZE);
164187770Sluigi
165187770Sluigi	free(buf);
166187770Sluigi}
167187770Sluigi
168187770Sluigi/*
169187770Sluigi * XXX - The following functions, macros and definitions come from natd.c:
170187770Sluigi * it would be better to move them outside natd.c, in a file
171187770Sluigi * (redirect_support.[ch]?) shared by ipfw and natd, but for now i can live
172187770Sluigi * with it.
173187770Sluigi */
174187770Sluigi
175187770Sluigi/*
176187770Sluigi * Definition of a port range, and macros to deal with values.
177187770Sluigi * FORMAT:  HI 16-bits == first port in range, 0 == all ports.
178187770Sluigi *          LO 16-bits == number of ports in range
179187770Sluigi * NOTES:   - Port values are not stored in network byte order.
180187770Sluigi */
181187770Sluigi
182187770Sluigi#define port_range u_long
183187770Sluigi
184187770Sluigi#define GETLOPORT(x)     ((x) >> 0x10)
185187770Sluigi#define GETNUMPORTS(x)   ((x) & 0x0000ffff)
186187770Sluigi#define GETHIPORT(x)     (GETLOPORT((x)) + GETNUMPORTS((x)))
187187770Sluigi
188187770Sluigi/* Set y to be the low-port value in port_range variable x. */
189187770Sluigi#define SETLOPORT(x,y)   ((x) = ((x) & 0x0000ffff) | ((y) << 0x10))
190187770Sluigi
191187770Sluigi/* Set y to be the number of ports in port_range variable x. */
192187770Sluigi#define SETNUMPORTS(x,y) ((x) = ((x) & 0xffff0000) | (y))
193187770Sluigi
194187770Sluigistatic void
195187770SluigiStrToAddr (const char* str, struct in_addr* addr)
196187770Sluigi{
197187770Sluigi	struct hostent* hp;
198187770Sluigi
199187770Sluigi	if (inet_aton (str, addr))
200187770Sluigi		return;
201187770Sluigi
202187770Sluigi	hp = gethostbyname (str);
203187770Sluigi	if (!hp)
204187770Sluigi		errx (1, "unknown host %s", str);
205187770Sluigi
206187770Sluigi	memcpy (addr, hp->h_addr, sizeof (struct in_addr));
207187770Sluigi}
208187770Sluigi
209187770Sluigistatic int
210187770SluigiStrToPortRange (const char* str, const char* proto, port_range *portRange)
211187770Sluigi{
212187770Sluigi	char*           sep;
213187770Sluigi	struct servent*	sp;
214187770Sluigi	char*		end;
215187770Sluigi	u_short         loPort;
216187770Sluigi	u_short         hiPort;
217187770Sluigi
218187770Sluigi	/* First see if this is a service, return corresponding port if so. */
219187770Sluigi	sp = getservbyname (str,proto);
220187770Sluigi	if (sp) {
221187770Sluigi	        SETLOPORT(*portRange, ntohs(sp->s_port));
222187770Sluigi		SETNUMPORTS(*portRange, 1);
223187770Sluigi		return 0;
224187770Sluigi	}
225187770Sluigi
226187770Sluigi	/* Not a service, see if it's a single port or port range. */
227187770Sluigi	sep = strchr (str, '-');
228187770Sluigi	if (sep == NULL) {
229187770Sluigi	        SETLOPORT(*portRange, strtol(str, &end, 10));
230187770Sluigi		if (end != str) {
231187770Sluigi		        /* Single port. */
232187770Sluigi		        SETNUMPORTS(*portRange, 1);
233187770Sluigi			return 0;
234187770Sluigi		}
235187770Sluigi
236187770Sluigi		/* Error in port range field. */
237187770Sluigi		errx (EX_DATAERR, "%s/%s: unknown service", str, proto);
238187770Sluigi	}
239187770Sluigi
240187770Sluigi	/* Port range, get the values and sanity check. */
241187770Sluigi	sscanf (str, "%hu-%hu", &loPort, &hiPort);
242187770Sluigi	SETLOPORT(*portRange, loPort);
243187770Sluigi	SETNUMPORTS(*portRange, 0);	/* Error by default */
244187770Sluigi	if (loPort <= hiPort)
245187770Sluigi	        SETNUMPORTS(*portRange, hiPort - loPort + 1);
246187770Sluigi
247187770Sluigi	if (GETNUMPORTS(*portRange) == 0)
248187770Sluigi	        errx (EX_DATAERR, "invalid port range %s", str);
249187770Sluigi
250187770Sluigi	return 0;
251187770Sluigi}
252187770Sluigi
253187770Sluigistatic int
254187770SluigiStrToProto (const char* str)
255187770Sluigi{
256187770Sluigi	if (!strcmp (str, "tcp"))
257187770Sluigi		return IPPROTO_TCP;
258187770Sluigi
259187770Sluigi	if (!strcmp (str, "udp"))
260187770Sluigi		return IPPROTO_UDP;
261187770Sluigi
262187770Sluigi	errx (EX_DATAERR, "unknown protocol %s. Expected tcp or udp", str);
263187770Sluigi}
264187770Sluigi
265187770Sluigistatic int
266187770SluigiStrToAddrAndPortRange (const char* str, struct in_addr* addr, char* proto,
267187770Sluigi		       port_range *portRange)
268187770Sluigi{
269187770Sluigi	char*	ptr;
270187770Sluigi
271187770Sluigi	ptr = strchr (str, ':');
272187770Sluigi	if (!ptr)
273187770Sluigi		errx (EX_DATAERR, "%s is missing port number", str);
274187770Sluigi
275187770Sluigi	*ptr = '\0';
276187770Sluigi	++ptr;
277187770Sluigi
278187770Sluigi	StrToAddr (str, addr);
279187770Sluigi	return StrToPortRange (ptr, proto, portRange);
280187770Sluigi}
281187770Sluigi
282187770Sluigi/* End of stuff taken from natd.c. */
283187770Sluigi
284187770Sluigi#define INC_ARGCV() do {        \
285187770Sluigi	(*_av)++;               \
286187770Sluigi	(*_ac)--;               \
287187770Sluigi	av = *_av;              \
288187770Sluigi	ac = *_ac;              \
289187770Sluigi} while(0)
290187770Sluigi
291187770Sluigi/*
292187770Sluigi * The next 3 functions add support for the addr, port and proto redirect and
293187770Sluigi * their logic is loosely based on SetupAddressRedirect(), SetupPortRedirect()
294187770Sluigi * and SetupProtoRedirect() from natd.c.
295187770Sluigi *
296187770Sluigi * Every setup_* function fills at least one redirect entry
297187770Sluigi * (struct cfg_redir) and zero or more server pool entry (struct cfg_spool)
298187770Sluigi * in buf.
299187770Sluigi *
300187770Sluigi * The format of data in buf is:
301187770Sluigi *
302187770Sluigi *
303187770Sluigi *     cfg_nat    cfg_redir    cfg_spool    ......  cfg_spool
304187770Sluigi *
305187770Sluigi *    -------------------------------------        ------------
306187770Sluigi *   |          | .....X ... |          |         |           |  .....
307187770Sluigi *    ------------------------------------- ...... ------------
308187770Sluigi *                     ^
309187770Sluigi *                spool_cnt       n=0       ......   n=(X-1)
310187770Sluigi *
311187770Sluigi * len points to the amount of available space in buf
312187770Sluigi * space counts the memory consumed by every function
313187770Sluigi *
314187770Sluigi * XXX - Every function get all the argv params so it
315187770Sluigi * has to check, in optional parameters, that the next
316187770Sluigi * args is a valid option for the redir entry and not
317187770Sluigi * another token. Only redir_port and redir_proto are
318187770Sluigi * affected by this.
319187770Sluigi */
320187770Sluigi
321187770Sluigistatic int
322187770Sluigisetup_redir_addr(char *spool_buf, int len,
323187770Sluigi		 int *_ac, char ***_av)
324187770Sluigi{
325187770Sluigi	char **av, *sep; /* Token separator. */
326187770Sluigi	/* Temporary buffer used to hold server pool ip's. */
327187770Sluigi	char tmp_spool_buf[NAT_BUF_LEN];
328187770Sluigi	int ac, space, lsnat;
329187770Sluigi	struct cfg_redir *r;
330187770Sluigi	struct cfg_spool *tmp;
331187770Sluigi
332187770Sluigi	av = *_av;
333187770Sluigi	ac = *_ac;
334187770Sluigi	space = 0;
335187770Sluigi	lsnat = 0;
336187770Sluigi	if (len >= SOF_REDIR) {
337187770Sluigi		r = (struct cfg_redir *)spool_buf;
338187770Sluigi		/* Skip cfg_redir at beginning of buf. */
339187770Sluigi		spool_buf = &spool_buf[SOF_REDIR];
340187770Sluigi		space = SOF_REDIR;
341187770Sluigi		len -= SOF_REDIR;
342187770Sluigi	} else
343187770Sluigi		goto nospace;
344187770Sluigi	r->mode = REDIR_ADDR;
345187770Sluigi	/* Extract local address. */
346187770Sluigi	if (ac == 0)
347187770Sluigi		errx(EX_DATAERR, "redirect_addr: missing local address");
348187770Sluigi	sep = strchr(*av, ',');
349187770Sluigi	if (sep) {		/* LSNAT redirection syntax. */
350187770Sluigi		r->laddr.s_addr = INADDR_NONE;
351187770Sluigi		/* Preserve av, copy spool servers to tmp_spool_buf. */
352187770Sluigi		strncpy(tmp_spool_buf, *av, strlen(*av)+1);
353187770Sluigi		lsnat = 1;
354187770Sluigi	} else
355187770Sluigi		StrToAddr(*av, &r->laddr);
356187770Sluigi	INC_ARGCV();
357187770Sluigi
358187770Sluigi	/* Extract public address. */
359187770Sluigi	if (ac == 0)
360187770Sluigi		errx(EX_DATAERR, "redirect_addr: missing public address");
361187770Sluigi	StrToAddr(*av, &r->paddr);
362187770Sluigi	INC_ARGCV();
363187770Sluigi
364187770Sluigi	/* Setup LSNAT server pool. */
365187770Sluigi	if (sep) {
366187770Sluigi		sep = strtok(tmp_spool_buf, ",");
367187770Sluigi		while (sep != NULL) {
368187770Sluigi			tmp = (struct cfg_spool *)spool_buf;
369187770Sluigi			if (len < SOF_SPOOL)
370187770Sluigi				goto nospace;
371187770Sluigi			len -= SOF_SPOOL;
372187770Sluigi			space += SOF_SPOOL;
373187770Sluigi			StrToAddr(sep, &tmp->addr);
374187770Sluigi			tmp->port = ~0;
375187770Sluigi			r->spool_cnt++;
376187770Sluigi			/* Point to the next possible cfg_spool. */
377187770Sluigi			spool_buf = &spool_buf[SOF_SPOOL];
378187770Sluigi			sep = strtok(NULL, ",");
379187770Sluigi		}
380187770Sluigi	}
381187770Sluigi	return(space);
382187770Sluiginospace:
383187770Sluigi	errx(EX_DATAERR, "redirect_addr: buf is too small\n");
384187770Sluigi}
385187770Sluigi
386187770Sluigistatic int
387187770Sluigisetup_redir_port(char *spool_buf, int len,
388187770Sluigi		 int *_ac, char ***_av)
389187770Sluigi{
390187770Sluigi	char **av, *sep, *protoName;
391187770Sluigi	char tmp_spool_buf[NAT_BUF_LEN];
392187770Sluigi	int ac, space, lsnat;
393187770Sluigi	struct cfg_redir *r;
394187770Sluigi	struct cfg_spool *tmp;
395187770Sluigi	u_short numLocalPorts;
396187770Sluigi	port_range portRange;
397187770Sluigi
398187770Sluigi	av = *_av;
399187770Sluigi	ac = *_ac;
400187770Sluigi	space = 0;
401187770Sluigi	lsnat = 0;
402187770Sluigi	numLocalPorts = 0;
403187770Sluigi
404187770Sluigi	if (len >= SOF_REDIR) {
405187770Sluigi		r = (struct cfg_redir *)spool_buf;
406187770Sluigi		/* Skip cfg_redir at beginning of buf. */
407187770Sluigi		spool_buf = &spool_buf[SOF_REDIR];
408187770Sluigi		space = SOF_REDIR;
409187770Sluigi		len -= SOF_REDIR;
410187770Sluigi	} else
411187770Sluigi		goto nospace;
412187770Sluigi	r->mode = REDIR_PORT;
413187770Sluigi	/*
414187770Sluigi	 * Extract protocol.
415187770Sluigi	 */
416187770Sluigi	if (ac == 0)
417187770Sluigi		errx (EX_DATAERR, "redirect_port: missing protocol");
418187770Sluigi	r->proto = StrToProto(*av);
419187770Sluigi	protoName = *av;
420187770Sluigi	INC_ARGCV();
421187770Sluigi
422187770Sluigi	/*
423187770Sluigi	 * Extract local address.
424187770Sluigi	 */
425187770Sluigi	if (ac == 0)
426187770Sluigi		errx (EX_DATAERR, "redirect_port: missing local address");
427187770Sluigi
428187770Sluigi	sep = strchr(*av, ',');
429187770Sluigi	/* LSNAT redirection syntax. */
430187770Sluigi	if (sep) {
431187770Sluigi		r->laddr.s_addr = INADDR_NONE;
432187770Sluigi		r->lport = ~0;
433187770Sluigi		numLocalPorts = 1;
434187770Sluigi		/* Preserve av, copy spool servers to tmp_spool_buf. */
435187770Sluigi		strncpy(tmp_spool_buf, *av, strlen(*av)+1);
436187770Sluigi		lsnat = 1;
437187770Sluigi	} else {
438187770Sluigi		if (StrToAddrAndPortRange (*av, &r->laddr, protoName,
439187770Sluigi		    &portRange) != 0)
440187770Sluigi			errx(EX_DATAERR, "redirect_port:"
441187770Sluigi			    "invalid local port range");
442187770Sluigi
443187770Sluigi		r->lport = GETLOPORT(portRange);
444187770Sluigi		numLocalPorts = GETNUMPORTS(portRange);
445187770Sluigi	}
446187770Sluigi	INC_ARGCV();
447187770Sluigi
448187770Sluigi	/*
449187770Sluigi	 * Extract public port and optionally address.
450187770Sluigi	 */
451187770Sluigi	if (ac == 0)
452187770Sluigi		errx (EX_DATAERR, "redirect_port: missing public port");
453187770Sluigi
454187770Sluigi	sep = strchr (*av, ':');
455187770Sluigi	if (sep) {
456187770Sluigi	        if (StrToAddrAndPortRange (*av, &r->paddr, protoName,
457187770Sluigi		    &portRange) != 0)
458187770Sluigi		        errx(EX_DATAERR, "redirect_port:"
459187770Sluigi			    "invalid public port range");
460187770Sluigi	} else {
461187770Sluigi		r->paddr.s_addr = INADDR_ANY;
462187770Sluigi		if (StrToPortRange (*av, protoName, &portRange) != 0)
463187770Sluigi		        errx(EX_DATAERR, "redirect_port:"
464187770Sluigi			    "invalid public port range");
465187770Sluigi	}
466187770Sluigi
467187770Sluigi	r->pport = GETLOPORT(portRange);
468187770Sluigi	r->pport_cnt = GETNUMPORTS(portRange);
469187770Sluigi	INC_ARGCV();
470187770Sluigi
471187770Sluigi	/*
472187770Sluigi	 * Extract remote address and optionally port.
473187770Sluigi	 */
474187770Sluigi	/*
475187770Sluigi	 * NB: isalpha(**av) => we've to check that next parameter is really an
476187770Sluigi	 * option for this redirect entry, else stop here processing arg[cv].
477187770Sluigi	 */
478187770Sluigi	if (ac != 0 && !isalpha(**av)) {
479187770Sluigi		sep = strchr (*av, ':');
480187770Sluigi		if (sep) {
481187770Sluigi		        if (StrToAddrAndPortRange (*av, &r->raddr, protoName,
482187770Sluigi			    &portRange) != 0)
483187770Sluigi				errx(EX_DATAERR, "redirect_port:"
484187770Sluigi				    "invalid remote port range");
485187770Sluigi		} else {
486187770Sluigi		        SETLOPORT(portRange, 0);
487187770Sluigi			SETNUMPORTS(portRange, 1);
488187770Sluigi			StrToAddr (*av, &r->raddr);
489187770Sluigi		}
490187770Sluigi		INC_ARGCV();
491187770Sluigi	} else {
492187770Sluigi		SETLOPORT(portRange, 0);
493187770Sluigi		SETNUMPORTS(portRange, 1);
494187770Sluigi		r->raddr.s_addr = INADDR_ANY;
495187770Sluigi	}
496187770Sluigi	r->rport = GETLOPORT(portRange);
497187770Sluigi	r->rport_cnt = GETNUMPORTS(portRange);
498187770Sluigi
499187770Sluigi	/*
500187770Sluigi	 * Make sure port ranges match up, then add the redirect ports.
501187770Sluigi	 */
502187770Sluigi	if (numLocalPorts != r->pport_cnt)
503187770Sluigi	        errx(EX_DATAERR, "redirect_port:"
504187770Sluigi		    "port ranges must be equal in size");
505187770Sluigi
506187770Sluigi	/* Remote port range is allowed to be '0' which means all ports. */
507187770Sluigi	if (r->rport_cnt != numLocalPorts &&
508187770Sluigi	    (r->rport_cnt != 1 || r->rport != 0))
509187770Sluigi	        errx(EX_DATAERR, "redirect_port: remote port must"
510187770Sluigi		    "be 0 or equal to local port range in size");
511187770Sluigi
512187770Sluigi	/*
513187770Sluigi	 * Setup LSNAT server pool.
514187770Sluigi	 */
515187770Sluigi	if (lsnat) {
516187770Sluigi		sep = strtok(tmp_spool_buf, ",");
517187770Sluigi		while (sep != NULL) {
518187770Sluigi			tmp = (struct cfg_spool *)spool_buf;
519187770Sluigi			if (len < SOF_SPOOL)
520187770Sluigi				goto nospace;
521187770Sluigi			len -= SOF_SPOOL;
522187770Sluigi			space += SOF_SPOOL;
523187770Sluigi			if (StrToAddrAndPortRange(sep, &tmp->addr, protoName,
524187770Sluigi			    &portRange) != 0)
525187770Sluigi				errx(EX_DATAERR, "redirect_port:"
526187770Sluigi				    "invalid local port range");
527187770Sluigi			if (GETNUMPORTS(portRange) != 1)
528187770Sluigi				errx(EX_DATAERR, "redirect_port: local port"
529187770Sluigi				    "must be single in this context");
530187770Sluigi			tmp->port = GETLOPORT(portRange);
531187770Sluigi			r->spool_cnt++;
532187770Sluigi			/* Point to the next possible cfg_spool. */
533187770Sluigi			spool_buf = &spool_buf[SOF_SPOOL];
534187770Sluigi			sep = strtok(NULL, ",");
535187770Sluigi		}
536187770Sluigi	}
537187770Sluigi	return (space);
538187770Sluiginospace:
539187770Sluigi	errx(EX_DATAERR, "redirect_port: buf is too small\n");
540187770Sluigi}
541187770Sluigi
542187770Sluigistatic int
543187770Sluigisetup_redir_proto(char *spool_buf, int len,
544187770Sluigi		 int *_ac, char ***_av)
545187770Sluigi{
546187770Sluigi	char **av;
547187770Sluigi	int ac, space;
548187770Sluigi	struct protoent *protoent;
549187770Sluigi	struct cfg_redir *r;
550187770Sluigi
551187770Sluigi	av = *_av;
552187770Sluigi	ac = *_ac;
553187770Sluigi	if (len >= SOF_REDIR) {
554187770Sluigi		r = (struct cfg_redir *)spool_buf;
555187770Sluigi		/* Skip cfg_redir at beginning of buf. */
556187770Sluigi		spool_buf = &spool_buf[SOF_REDIR];
557187770Sluigi		space = SOF_REDIR;
558187770Sluigi		len -= SOF_REDIR;
559187770Sluigi	} else
560187770Sluigi		goto nospace;
561187770Sluigi	r->mode = REDIR_PROTO;
562187770Sluigi	/*
563187770Sluigi	 * Extract protocol.
564187770Sluigi	 */
565187770Sluigi	if (ac == 0)
566187770Sluigi		errx(EX_DATAERR, "redirect_proto: missing protocol");
567187770Sluigi
568187770Sluigi	protoent = getprotobyname(*av);
569187770Sluigi	if (protoent == NULL)
570187770Sluigi		errx(EX_DATAERR, "redirect_proto: unknown protocol %s", *av);
571187770Sluigi	else
572187770Sluigi		r->proto = protoent->p_proto;
573187770Sluigi
574187770Sluigi	INC_ARGCV();
575187770Sluigi
576187770Sluigi	/*
577187770Sluigi	 * Extract local address.
578187770Sluigi	 */
579187770Sluigi	if (ac == 0)
580187770Sluigi		errx(EX_DATAERR, "redirect_proto: missing local address");
581187770Sluigi	else
582187770Sluigi		StrToAddr(*av, &r->laddr);
583187770Sluigi
584187770Sluigi	INC_ARGCV();
585187770Sluigi
586187770Sluigi	/*
587187770Sluigi	 * Extract optional public address.
588187770Sluigi	 */
589187770Sluigi	if (ac == 0) {
590187770Sluigi		r->paddr.s_addr = INADDR_ANY;
591187770Sluigi		r->raddr.s_addr = INADDR_ANY;
592187770Sluigi	} else {
593187770Sluigi		/* see above in setup_redir_port() */
594187770Sluigi		if (!isalpha(**av)) {
595187770Sluigi			StrToAddr(*av, &r->paddr);
596187770Sluigi			INC_ARGCV();
597187770Sluigi
598187770Sluigi			/*
599187770Sluigi			 * Extract optional remote address.
600187770Sluigi			 */
601187770Sluigi			/* see above in setup_redir_port() */
602187770Sluigi			if (ac!=0 && !isalpha(**av)) {
603187770Sluigi				StrToAddr(*av, &r->raddr);
604187770Sluigi				INC_ARGCV();
605187770Sluigi			}
606187770Sluigi		}
607187770Sluigi	}
608187770Sluigi	return (space);
609187770Sluiginospace:
610187770Sluigi	errx(EX_DATAERR, "redirect_proto: buf is too small\n");
611187770Sluigi}
612187770Sluigi
613187770Sluigistatic void
614187770Sluigiprint_nat_config(unsigned char *buf)
615187770Sluigi{
616187770Sluigi	struct cfg_nat *n;
617187770Sluigi	int i, cnt, flag, off;
618187770Sluigi	struct cfg_redir *t;
619187770Sluigi	struct cfg_spool *s;
620187770Sluigi	struct protoent *p;
621187770Sluigi
622187770Sluigi	n = (struct cfg_nat *)buf;
623187770Sluigi	flag = 1;
624187770Sluigi	off  = sizeof(*n);
625187770Sluigi	printf("ipfw nat %u config", n->id);
626187770Sluigi	if (strlen(n->if_name) != 0)
627187770Sluigi		printf(" if %s", n->if_name);
628187770Sluigi	else if (n->ip.s_addr != 0)
629187770Sluigi		printf(" ip %s", inet_ntoa(n->ip));
630187770Sluigi	while (n->mode != 0) {
631187770Sluigi		if (n->mode & PKT_ALIAS_LOG) {
632187770Sluigi			printf(" log");
633187770Sluigi			n->mode &= ~PKT_ALIAS_LOG;
634187770Sluigi		} else if (n->mode & PKT_ALIAS_DENY_INCOMING) {
635187770Sluigi			printf(" deny_in");
636187770Sluigi			n->mode &= ~PKT_ALIAS_DENY_INCOMING;
637187770Sluigi		} else if (n->mode & PKT_ALIAS_SAME_PORTS) {
638187770Sluigi			printf(" same_ports");
639187770Sluigi			n->mode &= ~PKT_ALIAS_SAME_PORTS;
640187770Sluigi		} else if (n->mode & PKT_ALIAS_UNREGISTERED_ONLY) {
641187770Sluigi			printf(" unreg_only");
642187770Sluigi			n->mode &= ~PKT_ALIAS_UNREGISTERED_ONLY;
643187770Sluigi		} else if (n->mode & PKT_ALIAS_RESET_ON_ADDR_CHANGE) {
644187770Sluigi			printf(" reset");
645187770Sluigi			n->mode &= ~PKT_ALIAS_RESET_ON_ADDR_CHANGE;
646187770Sluigi		} else if (n->mode & PKT_ALIAS_REVERSE) {
647187770Sluigi			printf(" reverse");
648187770Sluigi			n->mode &= ~PKT_ALIAS_REVERSE;
649187770Sluigi		} else if (n->mode & PKT_ALIAS_PROXY_ONLY) {
650187770Sluigi			printf(" proxy_only");
651187770Sluigi			n->mode &= ~PKT_ALIAS_PROXY_ONLY;
652187770Sluigi		}
653187770Sluigi	}
654187770Sluigi	/* Print all the redirect's data configuration. */
655187770Sluigi	for (cnt = 0; cnt < n->redir_cnt; cnt++) {
656187770Sluigi		t = (struct cfg_redir *)&buf[off];
657187770Sluigi		off += SOF_REDIR;
658187770Sluigi		switch (t->mode) {
659187770Sluigi		case REDIR_ADDR:
660187770Sluigi			printf(" redirect_addr");
661187770Sluigi			if (t->spool_cnt == 0)
662187770Sluigi				printf(" %s", inet_ntoa(t->laddr));
663187770Sluigi			else
664187770Sluigi				for (i = 0; i < t->spool_cnt; i++) {
665187770Sluigi					s = (struct cfg_spool *)&buf[off];
666187770Sluigi					if (i)
667187770Sluigi						printf(",");
668187770Sluigi					else
669187770Sluigi						printf(" ");
670187770Sluigi					printf("%s", inet_ntoa(s->addr));
671187770Sluigi					off += SOF_SPOOL;
672187770Sluigi				}
673187770Sluigi			printf(" %s", inet_ntoa(t->paddr));
674187770Sluigi			break;
675187770Sluigi		case REDIR_PORT:
676187770Sluigi			p = getprotobynumber(t->proto);
677187770Sluigi			printf(" redirect_port %s ", p->p_name);
678187770Sluigi			if (!t->spool_cnt) {
679187770Sluigi				printf("%s:%u", inet_ntoa(t->laddr), t->lport);
680187770Sluigi				if (t->pport_cnt > 1)
681187770Sluigi					printf("-%u", t->lport +
682187770Sluigi					    t->pport_cnt - 1);
683187770Sluigi			} else
684187770Sluigi				for (i=0; i < t->spool_cnt; i++) {
685187770Sluigi					s = (struct cfg_spool *)&buf[off];
686187770Sluigi					if (i)
687187770Sluigi						printf(",");
688187770Sluigi					printf("%s:%u", inet_ntoa(s->addr),
689187770Sluigi					    s->port);
690187770Sluigi					off += SOF_SPOOL;
691187770Sluigi				}
692187770Sluigi
693187770Sluigi			printf(" ");
694187770Sluigi			if (t->paddr.s_addr)
695187770Sluigi				printf("%s:", inet_ntoa(t->paddr));
696187770Sluigi			printf("%u", t->pport);
697187770Sluigi			if (!t->spool_cnt && t->pport_cnt > 1)
698187770Sluigi				printf("-%u", t->pport + t->pport_cnt - 1);
699187770Sluigi
700187770Sluigi			if (t->raddr.s_addr) {
701187770Sluigi				printf(" %s", inet_ntoa(t->raddr));
702187770Sluigi				if (t->rport) {
703187770Sluigi					printf(":%u", t->rport);
704187770Sluigi					if (!t->spool_cnt && t->rport_cnt > 1)
705187770Sluigi						printf("-%u", t->rport +
706187770Sluigi						    t->rport_cnt - 1);
707187770Sluigi				}
708187770Sluigi			}
709187770Sluigi			break;
710187770Sluigi		case REDIR_PROTO:
711187770Sluigi			p = getprotobynumber(t->proto);
712187770Sluigi			printf(" redirect_proto %s %s", p->p_name,
713187770Sluigi			    inet_ntoa(t->laddr));
714187770Sluigi			if (t->paddr.s_addr != 0) {
715187770Sluigi				printf(" %s", inet_ntoa(t->paddr));
716187770Sluigi				if (t->raddr.s_addr)
717187770Sluigi					printf(" %s", inet_ntoa(t->raddr));
718187770Sluigi			}
719187770Sluigi			break;
720187770Sluigi		default:
721187770Sluigi			errx(EX_DATAERR, "unknown redir mode");
722187770Sluigi			break;
723187770Sluigi		}
724187770Sluigi	}
725187770Sluigi	printf("\n");
726187770Sluigi}
727187770Sluigi
728187770Sluigivoid
729187770Sluigiipfw_config_nat(int ac, char **av)
730187770Sluigi{
731187770Sluigi	struct cfg_nat *n;              /* Nat instance configuration. */
732187770Sluigi	int i, len, off, tok;
733187770Sluigi	char *id, buf[NAT_BUF_LEN]; 	/* Buffer for serialized data. */
734187770Sluigi
735187770Sluigi	len = NAT_BUF_LEN;
736187770Sluigi	/* Offset in buf: save space for n at the beginning. */
737187770Sluigi	off = sizeof(*n);
738187770Sluigi	memset(buf, 0, sizeof(buf));
739187770Sluigi	n = (struct cfg_nat *)buf;
740187770Sluigi
741187770Sluigi	av++; ac--;
742187770Sluigi	/* Nat id. */
743187770Sluigi	if (ac && isdigit(**av)) {
744187770Sluigi		id = *av;
745187770Sluigi		i = atoi(*av);
746187770Sluigi		ac--; av++;
747187770Sluigi		n->id = i;
748187770Sluigi	} else
749187770Sluigi		errx(EX_DATAERR, "missing nat id");
750187770Sluigi	if (ac == 0)
751187770Sluigi		errx(EX_DATAERR, "missing option");
752187770Sluigi
753187770Sluigi	while (ac > 0) {
754187770Sluigi		tok = match_token(nat_params, *av);
755187770Sluigi		ac--; av++;
756187770Sluigi		switch (tok) {
757187770Sluigi		case TOK_IP:
758187770Sluigi			if (ac == 0)
759187770Sluigi				errx(EX_DATAERR, "missing option");
760187770Sluigi			if (!inet_aton(av[0], &(n->ip)))
761187770Sluigi				errx(EX_DATAERR, "bad ip address ``%s''",
762187770Sluigi				    av[0]);
763187770Sluigi			ac--; av++;
764187770Sluigi			break;
765187770Sluigi		case TOK_IF:
766187770Sluigi			if (ac == 0)
767187770Sluigi				errx(EX_DATAERR, "missing option");
768187770Sluigi			set_addr_dynamic(av[0], n);
769187770Sluigi			ac--; av++;
770187770Sluigi			break;
771187770Sluigi		case TOK_ALOG:
772187770Sluigi			n->mode |= PKT_ALIAS_LOG;
773187770Sluigi			break;
774187770Sluigi		case TOK_DENY_INC:
775187770Sluigi			n->mode |= PKT_ALIAS_DENY_INCOMING;
776187770Sluigi			break;
777187770Sluigi		case TOK_SAME_PORTS:
778187770Sluigi			n->mode |= PKT_ALIAS_SAME_PORTS;
779187770Sluigi			break;
780187770Sluigi		case TOK_UNREG_ONLY:
781187770Sluigi			n->mode |= PKT_ALIAS_UNREGISTERED_ONLY;
782187770Sluigi			break;
783187770Sluigi		case TOK_RESET_ADDR:
784187770Sluigi			n->mode |= PKT_ALIAS_RESET_ON_ADDR_CHANGE;
785187770Sluigi			break;
786187770Sluigi		case TOK_ALIAS_REV:
787187770Sluigi			n->mode |= PKT_ALIAS_REVERSE;
788187770Sluigi			break;
789187770Sluigi		case TOK_PROXY_ONLY:
790187770Sluigi			n->mode |= PKT_ALIAS_PROXY_ONLY;
791187770Sluigi			break;
792187770Sluigi			/*
793187770Sluigi			 * All the setup_redir_* functions work directly in the final
794187770Sluigi			 * buffer, see above for details.
795187770Sluigi			 */
796187770Sluigi		case TOK_REDIR_ADDR:
797187770Sluigi		case TOK_REDIR_PORT:
798187770Sluigi		case TOK_REDIR_PROTO:
799187770Sluigi			switch (tok) {
800187770Sluigi			case TOK_REDIR_ADDR:
801187770Sluigi				i = setup_redir_addr(&buf[off], len, &ac, &av);
802187770Sluigi				break;
803187770Sluigi			case TOK_REDIR_PORT:
804187770Sluigi				i = setup_redir_port(&buf[off], len, &ac, &av);
805187770Sluigi				break;
806187770Sluigi			case TOK_REDIR_PROTO:
807187770Sluigi				i = setup_redir_proto(&buf[off], len, &ac, &av);
808187770Sluigi				break;
809187770Sluigi			}
810187770Sluigi			n->redir_cnt++;
811187770Sluigi			off += i;
812187770Sluigi			len -= i;
813187770Sluigi			break;
814187770Sluigi		default:
815187770Sluigi			errx(EX_DATAERR, "unrecognised option ``%s''", av[-1]);
816187770Sluigi		}
817187770Sluigi	}
818187770Sluigi
819187770Sluigi	i = do_cmd(IP_FW_NAT_CFG, buf, off);
820187770Sluigi	if (i)
821187770Sluigi		err(1, "setsockopt(%s)", "IP_FW_NAT_CFG");
822187770Sluigi
823187770Sluigi	if (!co.do_quiet) {
824187770Sluigi		/* After every modification, we show the resultant rule. */
825187770Sluigi		int _ac = 3;
826187770Sluigi		char *_av[] = {"show", "config", id};
827187770Sluigi		ipfw_show_nat(_ac, _av);
828187770Sluigi	}
829187770Sluigi}
830187770Sluigi
831187770Sluigi
832187770Sluigivoid
833187770Sluigiipfw_show_nat(int ac, char **av)
834187770Sluigi{
835187770Sluigi	struct cfg_nat *n;
836187770Sluigi	struct cfg_redir *e;
837187770Sluigi	int cmd, i, nbytes, do_cfg, do_rule, frule, lrule, nalloc, size;
838187770Sluigi	int nat_cnt, redir_cnt, r;
839187770Sluigi	uint8_t *data, *p;
840187770Sluigi	char *endptr;
841187770Sluigi
842187770Sluigi	do_rule = 0;
843187770Sluigi	nalloc = 1024;
844187770Sluigi	size = 0;
845187770Sluigi	data = NULL;
846187770Sluigi	frule = 0;
847187770Sluigi	lrule = IPFW_DEFAULT_RULE; /* max ipfw rule number */
848187770Sluigi	ac--; av++;
849187770Sluigi
850187770Sluigi	if (co.test_only)
851187770Sluigi		return;
852187770Sluigi
853187770Sluigi	/* Parse parameters. */
854187770Sluigi	for (cmd = IP_FW_NAT_GET_LOG, do_cfg = 0; ac != 0; ac--, av++) {
855187770Sluigi		if (!strncmp(av[0], "config", strlen(av[0]))) {
856187770Sluigi			cmd = IP_FW_NAT_GET_CONFIG, do_cfg = 1;
857187770Sluigi			continue;
858187770Sluigi		}
859187770Sluigi		/* Convert command line rule #. */
860187770Sluigi		frule = lrule = strtoul(av[0], &endptr, 10);
861187770Sluigi		if (*endptr == '-')
862187770Sluigi			lrule = strtoul(endptr+1, &endptr, 10);
863187770Sluigi		if (lrule == 0)
864187770Sluigi			err(EX_USAGE, "invalid rule number: %s", av[0]);
865187770Sluigi		do_rule = 1;
866187770Sluigi	}
867187770Sluigi
868187770Sluigi	nbytes = nalloc;
869187770Sluigi	while (nbytes >= nalloc) {
870187770Sluigi		nalloc = nalloc * 2;
871187770Sluigi		nbytes = nalloc;
872187770Sluigi		data = safe_realloc(data, nbytes);
873187770Sluigi		if (do_cmd(cmd, data, (uintptr_t)&nbytes) < 0)
874187770Sluigi			err(EX_OSERR, "getsockopt(IP_FW_GET_%s)",
875187770Sluigi			    (cmd == IP_FW_NAT_GET_LOG) ? "LOG" : "CONFIG");
876187770Sluigi	}
877187770Sluigi	if (nbytes == 0)
878187770Sluigi		exit(0);
879187770Sluigi	if (do_cfg) {
880187770Sluigi		nat_cnt = *((int *)data);
881187770Sluigi		for (i = sizeof(nat_cnt); nat_cnt; nat_cnt--) {
882187770Sluigi			n = (struct cfg_nat *)&data[i];
883187770Sluigi			if (frule <= n->id && lrule >= n->id)
884187770Sluigi				print_nat_config(&data[i]);
885187770Sluigi			i += sizeof(struct cfg_nat);
886187770Sluigi			for (redir_cnt = 0; redir_cnt < n->redir_cnt; redir_cnt++) {
887187770Sluigi				e = (struct cfg_redir *)&data[i];
888187770Sluigi				i += sizeof(struct cfg_redir) + e->spool_cnt *
889187770Sluigi				    sizeof(struct cfg_spool);
890187770Sluigi			}
891187770Sluigi		}
892187770Sluigi	} else {
893187770Sluigi		for (i = 0; 1; i += LIBALIAS_BUF_SIZE + sizeof(int)) {
894187770Sluigi			p = &data[i];
895187770Sluigi			if (p == data + nbytes)
896187770Sluigi				break;
897187770Sluigi			bcopy(p, &r, sizeof(int));
898187770Sluigi			if (do_rule) {
899187770Sluigi				if (!(frule <= r && lrule >= r))
900187770Sluigi					continue;
901187770Sluigi			}
902187770Sluigi			printf("nat %u: %s\n", r, p+sizeof(int));
903187770Sluigi		}
904187770Sluigi	}
905187770Sluigi}
906