nat.c revision 220804
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 220804 2011-04-18 22:09:03Z glebius $
21187770Sluigi *
22187770Sluigi * In-kernel nat support
23187770Sluigi */
24187770Sluigi
25187770Sluigi#include <sys/types.h>
26187770Sluigi#include <sys/socket.h>
27187770Sluigi#include <sys/sysctl.h>
28187770Sluigi
29187770Sluigi#include "ipfw2.h"
30187770Sluigi
31187770Sluigi#include <ctype.h>
32187770Sluigi#include <err.h>
33187770Sluigi#include <netdb.h>
34187770Sluigi#include <stdio.h>
35187770Sluigi#include <stdlib.h>
36187770Sluigi#include <string.h>
37187770Sluigi#include <sysexits.h>
38187770Sluigi
39187770Sluigi#define IPFW_INTERNAL	/* Access to protected structures in ip_fw.h. */
40187770Sluigi
41187770Sluigi#include <net/if.h>
42187770Sluigi#include <net/if_dl.h>
43187770Sluigi#include <net/route.h> /* def. of struct route */
44187770Sluigi#include <netinet/in.h>
45187770Sluigi#include <netinet/ip_fw.h>
46187770Sluigi#include <arpa/inet.h>
47187770Sluigi#include <alias.h>
48187770Sluigi
49187770Sluigistatic struct _s_x nat_params[] = {
50220802Sglebius	{ "ip",			TOK_IP },
51220802Sglebius	{ "if",			TOK_IF },
52220802Sglebius 	{ "log",		TOK_ALOG },
53220802Sglebius 	{ "deny_in",		TOK_DENY_INC },
54220802Sglebius 	{ "same_ports",		TOK_SAME_PORTS },
55220802Sglebius 	{ "unreg_only",		TOK_UNREG_ONLY },
56220802Sglebius 	{ "reset",		TOK_RESET_ADDR },
57220804Sglebius 	{ "reverse",		TOK_ALIAS_REV },
58220802Sglebius 	{ "proxy_only",		TOK_PROXY_ONLY },
59187770Sluigi	{ "redirect_addr",	TOK_REDIR_ADDR },
60187770Sluigi	{ "redirect_port",	TOK_REDIR_PORT },
61187770Sluigi	{ "redirect_proto",	TOK_REDIR_PROTO },
62187770Sluigi 	{ NULL, 0 }	/* terminator */
63187770Sluigi};
64187770Sluigi
65187770Sluigi
66220802Sglebius/*
67187770Sluigi * Search for interface with name "ifn", and fill n accordingly:
68187770Sluigi *
69220802Sglebius * n->ip	ip address of interface "ifn"
70187770Sluigi * n->if_name   copy of interface name "ifn"
71187770Sluigi */
72187770Sluigistatic void
73187770Sluigiset_addr_dynamic(const char *ifn, struct cfg_nat *n)
74187770Sluigi{
75187770Sluigi	size_t needed;
76187770Sluigi	int mib[6];
77187770Sluigi	char *buf, *lim, *next;
78187770Sluigi	struct if_msghdr *ifm;
79187770Sluigi	struct ifa_msghdr *ifam;
80187770Sluigi	struct sockaddr_dl *sdl;
81187770Sluigi	struct sockaddr_in *sin;
82187770Sluigi	int ifIndex, ifMTU;
83187770Sluigi
84187770Sluigi	mib[0] = CTL_NET;
85187770Sluigi	mib[1] = PF_ROUTE;
86187770Sluigi	mib[2] = 0;
87220804Sglebius	mib[3] = AF_INET;
88187770Sluigi	mib[4] = NET_RT_IFLIST;
89220804Sglebius	mib[5] = 0;
90187770Sluigi/*
91187770Sluigi * Get interface data.
92187770Sluigi */
93187770Sluigi	if (sysctl(mib, 6, NULL, &needed, NULL, 0) == -1)
94187770Sluigi		err(1, "iflist-sysctl-estimate");
95187770Sluigi	buf = safe_calloc(1, needed);
96187770Sluigi	if (sysctl(mib, 6, buf, &needed, NULL, 0) == -1)
97187770Sluigi		err(1, "iflist-sysctl-get");
98187770Sluigi	lim = buf + needed;
99187770Sluigi/*
100187770Sluigi * Loop through interfaces until one with
101187770Sluigi * given name is found. This is done to
102187770Sluigi * find correct interface index for routing
103187770Sluigi * message processing.
104187770Sluigi */
105187770Sluigi	ifIndex	= 0;
106187770Sluigi	next = buf;
107187770Sluigi	while (next < lim) {
108187770Sluigi		ifm = (struct if_msghdr *)next;
109187770Sluigi		next += ifm->ifm_msglen;
110187770Sluigi		if (ifm->ifm_version != RTM_VERSION) {
111187770Sluigi			if (co.verbose)
112187770Sluigi				warnx("routing message version %d "
113187770Sluigi				    "not understood", ifm->ifm_version);
114187770Sluigi			continue;
115187770Sluigi		}
116187770Sluigi		if (ifm->ifm_type == RTM_IFINFO) {
117187770Sluigi			sdl = (struct sockaddr_dl *)(ifm + 1);
118187770Sluigi			if (strlen(ifn) == sdl->sdl_nlen &&
119187770Sluigi			    strncmp(ifn, sdl->sdl_data, sdl->sdl_nlen) == 0) {
120187770Sluigi				ifIndex = ifm->ifm_index;
121187770Sluigi				ifMTU = ifm->ifm_data.ifi_mtu;
122187770Sluigi				break;
123187770Sluigi			}
124187770Sluigi		}
125187770Sluigi	}
126187770Sluigi	if (!ifIndex)
127187770Sluigi		errx(1, "unknown interface name %s", ifn);
128187770Sluigi/*
129187770Sluigi * Get interface address.
130187770Sluigi */
131187770Sluigi	sin = NULL;
132187770Sluigi	while (next < lim) {
133187770Sluigi		ifam = (struct ifa_msghdr *)next;
134187770Sluigi		next += ifam->ifam_msglen;
135187770Sluigi		if (ifam->ifam_version != RTM_VERSION) {
136187770Sluigi			if (co.verbose)
137187770Sluigi				warnx("routing message version %d "
138187770Sluigi				    "not understood", ifam->ifam_version);
139187770Sluigi			continue;
140187770Sluigi		}
141187770Sluigi		if (ifam->ifam_type != RTM_NEWADDR)
142187770Sluigi			break;
143187770Sluigi		if (ifam->ifam_addrs & RTA_IFA) {
144187770Sluigi			int i;
145187770Sluigi			char *cp = (char *)(ifam + 1);
146187770Sluigi
147187770Sluigi			for (i = 1; i < RTA_IFA; i <<= 1) {
148187770Sluigi				if (ifam->ifam_addrs & i)
149187770Sluigi					cp += SA_SIZE((struct sockaddr *)cp);
150187770Sluigi			}
151187770Sluigi			if (((struct sockaddr *)cp)->sa_family == AF_INET) {
152187770Sluigi				sin = (struct sockaddr_in *)cp;
153187770Sluigi				break;
154187770Sluigi			}
155187770Sluigi		}
156187770Sluigi	}
157187770Sluigi	if (sin == NULL)
158187770Sluigi		errx(1, "%s: cannot get interface address", ifn);
159187770Sluigi
160187770Sluigi	n->ip = sin->sin_addr;
161187770Sluigi	strncpy(n->if_name, ifn, IF_NAMESIZE);
162187770Sluigi
163187770Sluigi	free(buf);
164187770Sluigi}
165187770Sluigi
166220802Sglebius/*
167187770Sluigi * XXX - The following functions, macros and definitions come from natd.c:
168220802Sglebius * it would be better to move them outside natd.c, in a file
169220802Sglebius * (redirect_support.[ch]?) shared by ipfw and natd, but for now i can live
170187770Sluigi * with it.
171187770Sluigi */
172187770Sluigi
173187770Sluigi/*
174187770Sluigi * Definition of a port range, and macros to deal with values.
175187770Sluigi * FORMAT:  HI 16-bits == first port in range, 0 == all ports.
176220802Sglebius *	  LO 16-bits == number of ports in range
177187770Sluigi * NOTES:   - Port values are not stored in network byte order.
178187770Sluigi */
179187770Sluigi
180187770Sluigi#define port_range u_long
181187770Sluigi
182220802Sglebius#define GETLOPORT(x)	((x) >> 0x10)
183220802Sglebius#define GETNUMPORTS(x)	((x) & 0x0000ffff)
184220802Sglebius#define GETHIPORT(x)	(GETLOPORT((x)) + GETNUMPORTS((x)))
185187770Sluigi
186187770Sluigi/* Set y to be the low-port value in port_range variable x. */
187187770Sluigi#define SETLOPORT(x,y)   ((x) = ((x) & 0x0000ffff) | ((y) << 0x10))
188187770Sluigi
189187770Sluigi/* Set y to be the number of ports in port_range variable x. */
190187770Sluigi#define SETNUMPORTS(x,y) ((x) = ((x) & 0xffff0000) | (y))
191187770Sluigi
192220802Sglebiusstatic void
193187770SluigiStrToAddr (const char* str, struct in_addr* addr)
194187770Sluigi{
195187770Sluigi	struct hostent* hp;
196187770Sluigi
197187770Sluigi	if (inet_aton (str, addr))
198187770Sluigi		return;
199187770Sluigi
200187770Sluigi	hp = gethostbyname (str);
201187770Sluigi	if (!hp)
202187770Sluigi		errx (1, "unknown host %s", str);
203187770Sluigi
204187770Sluigi	memcpy (addr, hp->h_addr, sizeof (struct in_addr));
205187770Sluigi}
206187770Sluigi
207220802Sglebiusstatic int
208187770SluigiStrToPortRange (const char* str, const char* proto, port_range *portRange)
209187770Sluigi{
210220802Sglebius	char*	   sep;
211187770Sluigi	struct servent*	sp;
212187770Sluigi	char*		end;
213220802Sglebius	u_short	 loPort;
214220802Sglebius	u_short	 hiPort;
215220804Sglebius
216187770Sluigi	/* First see if this is a service, return corresponding port if so. */
217187770Sluigi	sp = getservbyname (str,proto);
218187770Sluigi	if (sp) {
219220802Sglebius		SETLOPORT(*portRange, ntohs(sp->s_port));
220187770Sluigi		SETNUMPORTS(*portRange, 1);
221187770Sluigi		return 0;
222187770Sluigi	}
223220802Sglebius
224187770Sluigi	/* Not a service, see if it's a single port or port range. */
225187770Sluigi	sep = strchr (str, '-');
226187770Sluigi	if (sep == NULL) {
227220802Sglebius		SETLOPORT(*portRange, strtol(str, &end, 10));
228187770Sluigi		if (end != str) {
229220802Sglebius			/* Single port. */
230220802Sglebius			SETNUMPORTS(*portRange, 1);
231187770Sluigi			return 0;
232187770Sluigi		}
233187770Sluigi
234187770Sluigi		/* Error in port range field. */
235187770Sluigi		errx (EX_DATAERR, "%s/%s: unknown service", str, proto);
236187770Sluigi	}
237187770Sluigi
238187770Sluigi	/* Port range, get the values and sanity check. */
239187770Sluigi	sscanf (str, "%hu-%hu", &loPort, &hiPort);
240187770Sluigi	SETLOPORT(*portRange, loPort);
241187770Sluigi	SETNUMPORTS(*portRange, 0);	/* Error by default */
242187770Sluigi	if (loPort <= hiPort)
243220802Sglebius		SETNUMPORTS(*portRange, hiPort - loPort + 1);
244187770Sluigi
245187770Sluigi	if (GETNUMPORTS(*portRange) == 0)
246220802Sglebius		errx (EX_DATAERR, "invalid port range %s", str);
247187770Sluigi
248187770Sluigi	return 0;
249187770Sluigi}
250187770Sluigi
251220802Sglebiusstatic int
252187770SluigiStrToProto (const char* str)
253187770Sluigi{
254187770Sluigi	if (!strcmp (str, "tcp"))
255187770Sluigi		return IPPROTO_TCP;
256187770Sluigi
257187770Sluigi	if (!strcmp (str, "udp"))
258187770Sluigi		return IPPROTO_UDP;
259187770Sluigi
260188294Spiso	if (!strcmp (str, "sctp"))
261188294Spiso		return IPPROTO_SCTP;
262188294Spiso	errx (EX_DATAERR, "unknown protocol %s. Expected sctp, tcp or udp", str);
263187770Sluigi}
264187770Sluigi
265220802Sglebiusstatic int
266220802SglebiusStrToAddrAndPortRange (const char* str, struct in_addr* addr, char* proto,
267220802Sglebius			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
284220802Sglebius#define INC_ARGCV() do {	\
285220802Sglebius	(*_av)++;		\
286220802Sglebius	(*_ac)--;		\
287220802Sglebius	av = *_av;		\
288220802Sglebius	ac = *_ac;		\
289187770Sluigi} while(0)
290187770Sluigi
291220802Sglebius/*
292220802Sglebius * The next 3 functions add support for the addr, port and proto redirect and
293220802Sglebius * their logic is loosely based on SetupAddressRedirect(), SetupPortRedirect()
294187770Sluigi * and SetupProtoRedirect() from natd.c.
295187770Sluigi *
296220802Sglebius * Every setup_* function fills at least one redirect entry
297220802Sglebius * (struct cfg_redir) and zero or more server pool entry (struct cfg_spool)
298187770Sluigi * in buf.
299220802Sglebius *
300187770Sluigi * The format of data in buf is:
301187770Sluigi *
302220802Sglebius *     cfg_nat    cfg_redir    cfg_spool    ......  cfg_spool
303187770Sluigi *
304187770Sluigi *    -------------------------------------        ------------
305187770Sluigi *   |          | .....X ... |          |         |           |  .....
306187770Sluigi *    ------------------------------------- ...... ------------
307220802Sglebius *                     ^
308187770Sluigi *                spool_cnt       n=0       ......   n=(X-1)
309187770Sluigi *
310187770Sluigi * len points to the amount of available space in buf
311187770Sluigi * space counts the memory consumed by every function
312187770Sluigi *
313220802Sglebius * XXX - Every function get all the argv params so it
314187770Sluigi * has to check, in optional parameters, that the next
315220802Sglebius * args is a valid option for the redir entry and not
316220802Sglebius * another token. Only redir_port and redir_proto are
317187770Sluigi * affected by this.
318187770Sluigi */
319187770Sluigi
320187770Sluigistatic int
321189395Sluigisetup_redir_addr(char *spool_buf, unsigned int len,
322220802Sglebius		 int *_ac, char ***_av)
323187770Sluigi{
324187770Sluigi	char **av, *sep; /* Token separator. */
325187770Sluigi	/* Temporary buffer used to hold server pool ip's. */
326220802Sglebius	char tmp_spool_buf[NAT_BUF_LEN];
327187770Sluigi	int ac, space, lsnat;
328220804Sglebius	struct cfg_redir *r;
329220804Sglebius	struct cfg_spool *tmp;
330187770Sluigi
331187770Sluigi	av = *_av;
332187770Sluigi	ac = *_ac;
333187770Sluigi	space = 0;
334187770Sluigi	lsnat = 0;
335187770Sluigi	if (len >= SOF_REDIR) {
336187770Sluigi		r = (struct cfg_redir *)spool_buf;
337187770Sluigi		/* Skip cfg_redir at beginning of buf. */
338187770Sluigi		spool_buf = &spool_buf[SOF_REDIR];
339187770Sluigi		space = SOF_REDIR;
340187770Sluigi		len -= SOF_REDIR;
341220802Sglebius	} else
342220802Sglebius		goto nospace;
343187770Sluigi	r->mode = REDIR_ADDR;
344187770Sluigi	/* Extract local address. */
345220802Sglebius	if (ac == 0)
346187770Sluigi		errx(EX_DATAERR, "redirect_addr: missing local address");
347187770Sluigi	sep = strchr(*av, ',');
348187770Sluigi	if (sep) {		/* LSNAT redirection syntax. */
349187770Sluigi		r->laddr.s_addr = INADDR_NONE;
350187770Sluigi		/* Preserve av, copy spool servers to tmp_spool_buf. */
351187770Sluigi		strncpy(tmp_spool_buf, *av, strlen(*av)+1);
352187770Sluigi		lsnat = 1;
353220802Sglebius	} else
354220804Sglebius		StrToAddr(*av, &r->laddr);
355187770Sluigi	INC_ARGCV();
356187770Sluigi
357187770Sluigi	/* Extract public address. */
358220802Sglebius	if (ac == 0)
359187770Sluigi		errx(EX_DATAERR, "redirect_addr: missing public address");
360187770Sluigi	StrToAddr(*av, &r->paddr);
361187770Sluigi	INC_ARGCV();
362187770Sluigi
363187770Sluigi	/* Setup LSNAT server pool. */
364187770Sluigi	if (sep) {
365220804Sglebius		sep = strtok(tmp_spool_buf, ",");
366187770Sluigi		while (sep != NULL) {
367220804Sglebius			tmp = (struct cfg_spool *)spool_buf;
368187770Sluigi			if (len < SOF_SPOOL)
369187770Sluigi				goto nospace;
370187770Sluigi			len -= SOF_SPOOL;
371220804Sglebius			space += SOF_SPOOL;
372187770Sluigi			StrToAddr(sep, &tmp->addr);
373187770Sluigi			tmp->port = ~0;
374187770Sluigi			r->spool_cnt++;
375187770Sluigi			/* Point to the next possible cfg_spool. */
376187770Sluigi			spool_buf = &spool_buf[SOF_SPOOL];
377187770Sluigi			sep = strtok(NULL, ",");
378187770Sluigi		}
379187770Sluigi	}
380187770Sluigi	return(space);
381187770Sluiginospace:
382187770Sluigi	errx(EX_DATAERR, "redirect_addr: buf is too small\n");
383187770Sluigi}
384187770Sluigi
385187770Sluigistatic int
386189395Sluigisetup_redir_port(char *spool_buf, unsigned int len,
387220802Sglebius		 int *_ac, char ***_av)
388187770Sluigi{
389187770Sluigi	char **av, *sep, *protoName;
390187770Sluigi	char tmp_spool_buf[NAT_BUF_LEN];
391187770Sluigi	int ac, space, lsnat;
392187770Sluigi	struct cfg_redir *r;
393187770Sluigi	struct cfg_spool *tmp;
394187770Sluigi	u_short numLocalPorts;
395220804Sglebius	port_range portRange;
396187770Sluigi
397187770Sluigi	av = *_av;
398187770Sluigi	ac = *_ac;
399187770Sluigi	space = 0;
400187770Sluigi	lsnat = 0;
401220804Sglebius	numLocalPorts = 0;
402187770Sluigi
403187770Sluigi	if (len >= SOF_REDIR) {
404187770Sluigi		r = (struct cfg_redir *)spool_buf;
405187770Sluigi		/* Skip cfg_redir at beginning of buf. */
406187770Sluigi		spool_buf = &spool_buf[SOF_REDIR];
407187770Sluigi		space = SOF_REDIR;
408187770Sluigi		len -= SOF_REDIR;
409220802Sglebius	} else
410220802Sglebius		goto nospace;
411187770Sluigi	r->mode = REDIR_PORT;
412187770Sluigi	/*
413187770Sluigi	 * Extract protocol.
414187770Sluigi	 */
415187770Sluigi	if (ac == 0)
416187770Sluigi		errx (EX_DATAERR, "redirect_port: missing protocol");
417187770Sluigi	r->proto = StrToProto(*av);
418220804Sglebius	protoName = *av;
419187770Sluigi	INC_ARGCV();
420187770Sluigi
421187770Sluigi	/*
422187770Sluigi	 * Extract local address.
423187770Sluigi	 */
424187770Sluigi	if (ac == 0)
425187770Sluigi		errx (EX_DATAERR, "redirect_port: missing local address");
426187770Sluigi
427187770Sluigi	sep = strchr(*av, ',');
428187770Sluigi	/* LSNAT redirection syntax. */
429187770Sluigi	if (sep) {
430187770Sluigi		r->laddr.s_addr = INADDR_NONE;
431187770Sluigi		r->lport = ~0;
432187770Sluigi		numLocalPorts = 1;
433187770Sluigi		/* Preserve av, copy spool servers to tmp_spool_buf. */
434187770Sluigi		strncpy(tmp_spool_buf, *av, strlen(*av)+1);
435187770Sluigi		lsnat = 1;
436187770Sluigi	} else {
437188294Spiso		/*
438220802Sglebius		 * The sctp nat does not allow the port numbers to be mapped to
439220802Sglebius		 * new port numbers. Therefore, no ports are to be specified
440188294Spiso		 * in the target port field.
441188294Spiso		 */
442188294Spiso		if (r->proto == IPPROTO_SCTP) {
443188294Spiso			if (strchr (*av, ':'))
444188294Spiso				errx(EX_DATAERR, "redirect_port:"
445188294Spiso				    "port numbers do not change in sctp, so do not "
446188294Spiso				    "specify them as part of the target");
447188294Spiso			else
448188294Spiso				StrToAddr(*av, &r->laddr);
449188294Spiso		} else {
450220802Sglebius			if (StrToAddrAndPortRange (*av, &r->laddr, protoName,
451188294Spiso				&portRange) != 0)
452188294Spiso				errx(EX_DATAERR, "redirect_port:"
453188294Spiso				    "invalid local port range");
454187770Sluigi
455188294Spiso			r->lport = GETLOPORT(portRange);
456188294Spiso			numLocalPorts = GETNUMPORTS(portRange);
457188294Spiso		}
458187770Sluigi	}
459220804Sglebius	INC_ARGCV();
460187770Sluigi
461187770Sluigi	/*
462187770Sluigi	 * Extract public port and optionally address.
463187770Sluigi	 */
464187770Sluigi	if (ac == 0)
465187770Sluigi		errx (EX_DATAERR, "redirect_port: missing public port");
466187770Sluigi
467187770Sluigi	sep = strchr (*av, ':');
468187770Sluigi	if (sep) {
469220802Sglebius		if (StrToAddrAndPortRange (*av, &r->paddr, protoName,
470187770Sluigi		    &portRange) != 0)
471220802Sglebius			errx(EX_DATAERR, "redirect_port:"
472187770Sluigi			    "invalid public port range");
473187770Sluigi	} else {
474187770Sluigi		r->paddr.s_addr = INADDR_ANY;
475187770Sluigi		if (StrToPortRange (*av, protoName, &portRange) != 0)
476220802Sglebius			errx(EX_DATAERR, "redirect_port:"
477187770Sluigi			    "invalid public port range");
478187770Sluigi	}
479187770Sluigi
480187770Sluigi	r->pport = GETLOPORT(portRange);
481188294Spiso	if (r->proto == IPPROTO_SCTP) { /* so the logic below still works */
482188294Spiso		numLocalPorts = GETNUMPORTS(portRange);
483188294Spiso		r->lport = r->pport;
484188294Spiso	}
485187770Sluigi	r->pport_cnt = GETNUMPORTS(portRange);
486187770Sluigi	INC_ARGCV();
487187770Sluigi
488187770Sluigi	/*
489187770Sluigi	 * Extract remote address and optionally port.
490220804Sglebius	 */
491220802Sglebius	/*
492187770Sluigi	 * NB: isalpha(**av) => we've to check that next parameter is really an
493187770Sluigi	 * option for this redirect entry, else stop here processing arg[cv].
494187770Sluigi	 */
495220802Sglebius	if (ac != 0 && !isalpha(**av)) {
496187770Sluigi		sep = strchr (*av, ':');
497187770Sluigi		if (sep) {
498220802Sglebius			if (StrToAddrAndPortRange (*av, &r->raddr, protoName,
499187770Sluigi			    &portRange) != 0)
500187770Sluigi				errx(EX_DATAERR, "redirect_port:"
501187770Sluigi				    "invalid remote port range");
502187770Sluigi		} else {
503220802Sglebius			SETLOPORT(portRange, 0);
504187770Sluigi			SETNUMPORTS(portRange, 1);
505187770Sluigi			StrToAddr (*av, &r->raddr);
506187770Sluigi		}
507187770Sluigi		INC_ARGCV();
508187770Sluigi	} else {
509187770Sluigi		SETLOPORT(portRange, 0);
510187770Sluigi		SETNUMPORTS(portRange, 1);
511187770Sluigi		r->raddr.s_addr = INADDR_ANY;
512187770Sluigi	}
513187770Sluigi	r->rport = GETLOPORT(portRange);
514187770Sluigi	r->rport_cnt = GETNUMPORTS(portRange);
515187770Sluigi
516220802Sglebius	/*
517187770Sluigi	 * Make sure port ranges match up, then add the redirect ports.
518187770Sluigi	 */
519187770Sluigi	if (numLocalPorts != r->pport_cnt)
520220802Sglebius		errx(EX_DATAERR, "redirect_port:"
521187770Sluigi		    "port ranges must be equal in size");
522187770Sluigi
523187770Sluigi	/* Remote port range is allowed to be '0' which means all ports. */
524220802Sglebius	if (r->rport_cnt != numLocalPorts &&
525187770Sluigi	    (r->rport_cnt != 1 || r->rport != 0))
526220802Sglebius		errx(EX_DATAERR, "redirect_port: remote port must"
527187770Sluigi		    "be 0 or equal to local port range in size");
528187770Sluigi
529187770Sluigi	/*
530187770Sluigi	 * Setup LSNAT server pool.
531187770Sluigi	 */
532187770Sluigi	if (lsnat) {
533187770Sluigi		sep = strtok(tmp_spool_buf, ",");
534187770Sluigi		while (sep != NULL) {
535187770Sluigi			tmp = (struct cfg_spool *)spool_buf;
536187770Sluigi			if (len < SOF_SPOOL)
537187770Sluigi				goto nospace;
538187770Sluigi			len -= SOF_SPOOL;
539187770Sluigi			space += SOF_SPOOL;
540188294Spiso			/*
541188294Spiso			 * The sctp nat does not allow the port numbers to be mapped to new port numbers
542188294Spiso			 * Therefore, no ports are to be specified in the target port field
543188294Spiso			 */
544188294Spiso			if (r->proto == IPPROTO_SCTP) {
545188294Spiso				if (strchr (sep, ':')) {
546188294Spiso					errx(EX_DATAERR, "redirect_port:"
547188294Spiso					    "port numbers do not change in "
548188294Spiso					    "sctp, so do not specify them as "
549188294Spiso					    "part of the target");
550188294Spiso				} else {
551188294Spiso					StrToAddr(sep, &tmp->addr);
552188294Spiso					tmp->port = r->pport;
553188294Spiso				}
554188294Spiso			} else {
555220802Sglebius				if (StrToAddrAndPortRange(sep, &tmp->addr,
556188294Spiso					protoName, &portRange) != 0)
557188294Spiso					errx(EX_DATAERR, "redirect_port:"
558188294Spiso					    "invalid local port range");
559188294Spiso				if (GETNUMPORTS(portRange) != 1)
560188294Spiso					errx(EX_DATAERR, "redirect_port: "
561188294Spiso					    "local port must be single in "
562188294Spiso					    "this context");
563188294Spiso				tmp->port = GETLOPORT(portRange);
564188294Spiso			}
565220804Sglebius			r->spool_cnt++;
566187770Sluigi			/* Point to the next possible cfg_spool. */
567187770Sluigi			spool_buf = &spool_buf[SOF_SPOOL];
568187770Sluigi			sep = strtok(NULL, ",");
569187770Sluigi		}
570187770Sluigi	}
571187770Sluigi	return (space);
572187770Sluiginospace:
573187770Sluigi	errx(EX_DATAERR, "redirect_port: buf is too small\n");
574187770Sluigi}
575187770Sluigi
576187770Sluigistatic int
577189395Sluigisetup_redir_proto(char *spool_buf, unsigned int len,
578220802Sglebius		 int *_ac, char ***_av)
579187770Sluigi{
580187770Sluigi	char **av;
581187770Sluigi	int ac, space;
582187770Sluigi	struct protoent *protoent;
583187770Sluigi	struct cfg_redir *r;
584220804Sglebius
585187770Sluigi	av = *_av;
586187770Sluigi	ac = *_ac;
587187770Sluigi	if (len >= SOF_REDIR) {
588187770Sluigi		r = (struct cfg_redir *)spool_buf;
589187770Sluigi		/* Skip cfg_redir at beginning of buf. */
590187770Sluigi		spool_buf = &spool_buf[SOF_REDIR];
591187770Sluigi		space = SOF_REDIR;
592187770Sluigi		len -= SOF_REDIR;
593220802Sglebius	} else
594187770Sluigi		goto nospace;
595187770Sluigi	r->mode = REDIR_PROTO;
596187770Sluigi	/*
597187770Sluigi	 * Extract protocol.
598220804Sglebius	 */
599187770Sluigi	if (ac == 0)
600187770Sluigi		errx(EX_DATAERR, "redirect_proto: missing protocol");
601187770Sluigi
602187770Sluigi	protoent = getprotobyname(*av);
603187770Sluigi	if (protoent == NULL)
604187770Sluigi		errx(EX_DATAERR, "redirect_proto: unknown protocol %s", *av);
605187770Sluigi	else
606187770Sluigi		r->proto = protoent->p_proto;
607187770Sluigi
608187770Sluigi	INC_ARGCV();
609220804Sglebius
610187770Sluigi	/*
611187770Sluigi	 * Extract local address.
612187770Sluigi	 */
613187770Sluigi	if (ac == 0)
614187770Sluigi		errx(EX_DATAERR, "redirect_proto: missing local address");
615187770Sluigi	else
616187770Sluigi		StrToAddr(*av, &r->laddr);
617187770Sluigi
618187770Sluigi	INC_ARGCV();
619220804Sglebius
620187770Sluigi	/*
621187770Sluigi	 * Extract optional public address.
622187770Sluigi	 */
623187770Sluigi	if (ac == 0) {
624220804Sglebius		r->paddr.s_addr = INADDR_ANY;
625220804Sglebius		r->raddr.s_addr = INADDR_ANY;
626187770Sluigi	} else {
627187770Sluigi		/* see above in setup_redir_port() */
628187770Sluigi		if (!isalpha(**av)) {
629220804Sglebius			StrToAddr(*av, &r->paddr);
630187770Sluigi			INC_ARGCV();
631220804Sglebius
632187770Sluigi			/*
633187770Sluigi			 * Extract optional remote address.
634220804Sglebius			 */
635187770Sluigi			/* see above in setup_redir_port() */
636187770Sluigi			if (ac!=0 && !isalpha(**av)) {
637187770Sluigi				StrToAddr(*av, &r->raddr);
638187770Sluigi				INC_ARGCV();
639187770Sluigi			}
640220804Sglebius		}
641187770Sluigi	}
642187770Sluigi	return (space);
643187770Sluiginospace:
644187770Sluigi	errx(EX_DATAERR, "redirect_proto: buf is too small\n");
645187770Sluigi}
646187770Sluigi
647187770Sluigistatic void
648187770Sluigiprint_nat_config(unsigned char *buf)
649187770Sluigi{
650187770Sluigi	struct cfg_nat *n;
651187770Sluigi	int i, cnt, flag, off;
652187770Sluigi	struct cfg_redir *t;
653187770Sluigi	struct cfg_spool *s;
654187770Sluigi	struct protoent *p;
655187770Sluigi
656187770Sluigi	n = (struct cfg_nat *)buf;
657187770Sluigi	flag = 1;
658187770Sluigi	off  = sizeof(*n);
659187770Sluigi	printf("ipfw nat %u config", n->id);
660187770Sluigi	if (strlen(n->if_name) != 0)
661187770Sluigi		printf(" if %s", n->if_name);
662187770Sluigi	else if (n->ip.s_addr != 0)
663187770Sluigi		printf(" ip %s", inet_ntoa(n->ip));
664187770Sluigi	while (n->mode != 0) {
665187770Sluigi		if (n->mode & PKT_ALIAS_LOG) {
666187770Sluigi			printf(" log");
667187770Sluigi			n->mode &= ~PKT_ALIAS_LOG;
668187770Sluigi		} else if (n->mode & PKT_ALIAS_DENY_INCOMING) {
669187770Sluigi			printf(" deny_in");
670187770Sluigi			n->mode &= ~PKT_ALIAS_DENY_INCOMING;
671187770Sluigi		} else if (n->mode & PKT_ALIAS_SAME_PORTS) {
672187770Sluigi			printf(" same_ports");
673187770Sluigi			n->mode &= ~PKT_ALIAS_SAME_PORTS;
674187770Sluigi		} else if (n->mode & PKT_ALIAS_UNREGISTERED_ONLY) {
675187770Sluigi			printf(" unreg_only");
676187770Sluigi			n->mode &= ~PKT_ALIAS_UNREGISTERED_ONLY;
677187770Sluigi		} else if (n->mode & PKT_ALIAS_RESET_ON_ADDR_CHANGE) {
678187770Sluigi			printf(" reset");
679187770Sluigi			n->mode &= ~PKT_ALIAS_RESET_ON_ADDR_CHANGE;
680187770Sluigi		} else if (n->mode & PKT_ALIAS_REVERSE) {
681187770Sluigi			printf(" reverse");
682187770Sluigi			n->mode &= ~PKT_ALIAS_REVERSE;
683187770Sluigi		} else if (n->mode & PKT_ALIAS_PROXY_ONLY) {
684187770Sluigi			printf(" proxy_only");
685187770Sluigi			n->mode &= ~PKT_ALIAS_PROXY_ONLY;
686187770Sluigi		}
687187770Sluigi	}
688187770Sluigi	/* Print all the redirect's data configuration. */
689187770Sluigi	for (cnt = 0; cnt < n->redir_cnt; cnt++) {
690187770Sluigi		t = (struct cfg_redir *)&buf[off];
691187770Sluigi		off += SOF_REDIR;
692187770Sluigi		switch (t->mode) {
693187770Sluigi		case REDIR_ADDR:
694187770Sluigi			printf(" redirect_addr");
695187770Sluigi			if (t->spool_cnt == 0)
696187770Sluigi				printf(" %s", inet_ntoa(t->laddr));
697187770Sluigi			else
698187770Sluigi				for (i = 0; i < t->spool_cnt; i++) {
699187770Sluigi					s = (struct cfg_spool *)&buf[off];
700187770Sluigi					if (i)
701187770Sluigi						printf(",");
702220802Sglebius					else
703187770Sluigi						printf(" ");
704187770Sluigi					printf("%s", inet_ntoa(s->addr));
705187770Sluigi					off += SOF_SPOOL;
706187770Sluigi				}
707187770Sluigi			printf(" %s", inet_ntoa(t->paddr));
708187770Sluigi			break;
709187770Sluigi		case REDIR_PORT:
710187770Sluigi			p = getprotobynumber(t->proto);
711187770Sluigi			printf(" redirect_port %s ", p->p_name);
712187770Sluigi			if (!t->spool_cnt) {
713187770Sluigi				printf("%s:%u", inet_ntoa(t->laddr), t->lport);
714187770Sluigi				if (t->pport_cnt > 1)
715220802Sglebius					printf("-%u", t->lport +
716187770Sluigi					    t->pport_cnt - 1);
717187770Sluigi			} else
718187770Sluigi				for (i=0; i < t->spool_cnt; i++) {
719187770Sluigi					s = (struct cfg_spool *)&buf[off];
720187770Sluigi					if (i)
721187770Sluigi						printf(",");
722220802Sglebius					printf("%s:%u", inet_ntoa(s->addr),
723187770Sluigi					    s->port);
724187770Sluigi					off += SOF_SPOOL;
725187770Sluigi				}
726187770Sluigi
727187770Sluigi			printf(" ");
728187770Sluigi			if (t->paddr.s_addr)
729220802Sglebius				printf("%s:", inet_ntoa(t->paddr));
730187770Sluigi			printf("%u", t->pport);
731187770Sluigi			if (!t->spool_cnt && t->pport_cnt > 1)
732187770Sluigi				printf("-%u", t->pport + t->pport_cnt - 1);
733187770Sluigi
734187770Sluigi			if (t->raddr.s_addr) {
735187770Sluigi				printf(" %s", inet_ntoa(t->raddr));
736187770Sluigi				if (t->rport) {
737187770Sluigi					printf(":%u", t->rport);
738187770Sluigi					if (!t->spool_cnt && t->rport_cnt > 1)
739220802Sglebius						printf("-%u", t->rport +
740187770Sluigi						    t->rport_cnt - 1);
741187770Sluigi				}
742187770Sluigi			}
743187770Sluigi			break;
744187770Sluigi		case REDIR_PROTO:
745187770Sluigi			p = getprotobynumber(t->proto);
746220802Sglebius			printf(" redirect_proto %s %s", p->p_name,
747187770Sluigi			    inet_ntoa(t->laddr));
748187770Sluigi			if (t->paddr.s_addr != 0) {
749187770Sluigi				printf(" %s", inet_ntoa(t->paddr));
750187770Sluigi				if (t->raddr.s_addr)
751187770Sluigi					printf(" %s", inet_ntoa(t->raddr));
752187770Sluigi			}
753187770Sluigi			break;
754187770Sluigi		default:
755187770Sluigi			errx(EX_DATAERR, "unknown redir mode");
756187770Sluigi			break;
757187770Sluigi		}
758187770Sluigi	}
759187770Sluigi	printf("\n");
760187770Sluigi}
761187770Sluigi
762187770Sluigivoid
763187770Sluigiipfw_config_nat(int ac, char **av)
764187770Sluigi{
765220802Sglebius	struct cfg_nat *n;		/* Nat instance configuration. */
766187770Sluigi	int i, len, off, tok;
767187770Sluigi	char *id, buf[NAT_BUF_LEN]; 	/* Buffer for serialized data. */
768220804Sglebius
769187770Sluigi	len = NAT_BUF_LEN;
770187770Sluigi	/* Offset in buf: save space for n at the beginning. */
771187770Sluigi	off = sizeof(*n);
772187770Sluigi	memset(buf, 0, sizeof(buf));
773187770Sluigi	n = (struct cfg_nat *)buf;
774187770Sluigi
775187770Sluigi	av++; ac--;
776187770Sluigi	/* Nat id. */
777187770Sluigi	if (ac && isdigit(**av)) {
778187770Sluigi		id = *av;
779220802Sglebius		i = atoi(*av);
780220804Sglebius		ac--; av++;
781187770Sluigi		n->id = i;
782220802Sglebius	} else
783187770Sluigi		errx(EX_DATAERR, "missing nat id");
784220802Sglebius	if (ac == 0)
785187770Sluigi		errx(EX_DATAERR, "missing option");
786187770Sluigi
787187770Sluigi	while (ac > 0) {
788187770Sluigi		tok = match_token(nat_params, *av);
789187770Sluigi		ac--; av++;
790187770Sluigi		switch (tok) {
791187770Sluigi		case TOK_IP:
792220802Sglebius			if (ac == 0)
793187770Sluigi				errx(EX_DATAERR, "missing option");
794187770Sluigi			if (!inet_aton(av[0], &(n->ip)))
795220802Sglebius				errx(EX_DATAERR, "bad ip address ``%s''",
796187770Sluigi				    av[0]);
797187770Sluigi			ac--; av++;
798220802Sglebius			break;
799187770Sluigi		case TOK_IF:
800220802Sglebius			if (ac == 0)
801187770Sluigi				errx(EX_DATAERR, "missing option");
802187770Sluigi			set_addr_dynamic(av[0], n);
803187770Sluigi			ac--; av++;
804187770Sluigi			break;
805187770Sluigi		case TOK_ALOG:
806187770Sluigi			n->mode |= PKT_ALIAS_LOG;
807187770Sluigi			break;
808187770Sluigi		case TOK_DENY_INC:
809187770Sluigi			n->mode |= PKT_ALIAS_DENY_INCOMING;
810187770Sluigi			break;
811187770Sluigi		case TOK_SAME_PORTS:
812187770Sluigi			n->mode |= PKT_ALIAS_SAME_PORTS;
813187770Sluigi			break;
814187770Sluigi		case TOK_UNREG_ONLY:
815187770Sluigi			n->mode |= PKT_ALIAS_UNREGISTERED_ONLY;
816187770Sluigi			break;
817187770Sluigi		case TOK_RESET_ADDR:
818187770Sluigi			n->mode |= PKT_ALIAS_RESET_ON_ADDR_CHANGE;
819187770Sluigi			break;
820187770Sluigi		case TOK_ALIAS_REV:
821187770Sluigi			n->mode |= PKT_ALIAS_REVERSE;
822187770Sluigi			break;
823187770Sluigi		case TOK_PROXY_ONLY:
824187770Sluigi			n->mode |= PKT_ALIAS_PROXY_ONLY;
825187770Sluigi			break;
826220802Sglebius			/*
827220802Sglebius			 * All the setup_redir_* functions work directly in
828220802Sglebius			 * the final buffer, see above for details.
829187770Sluigi			 */
830187770Sluigi		case TOK_REDIR_ADDR:
831187770Sluigi		case TOK_REDIR_PORT:
832187770Sluigi		case TOK_REDIR_PROTO:
833187770Sluigi			switch (tok) {
834187770Sluigi			case TOK_REDIR_ADDR:
835187770Sluigi				i = setup_redir_addr(&buf[off], len, &ac, &av);
836220802Sglebius				break;
837187770Sluigi			case TOK_REDIR_PORT:
838187770Sluigi				i = setup_redir_port(&buf[off], len, &ac, &av);
839220802Sglebius				break;
840187770Sluigi			case TOK_REDIR_PROTO:
841187770Sluigi				i = setup_redir_proto(&buf[off], len, &ac, &av);
842187770Sluigi				break;
843187770Sluigi			}
844187770Sluigi			n->redir_cnt++;
845187770Sluigi			off += i;
846187770Sluigi			len -= i;
847187770Sluigi			break;
848187770Sluigi		default:
849187770Sluigi			errx(EX_DATAERR, "unrecognised option ``%s''", av[-1]);
850187770Sluigi		}
851187770Sluigi	}
852187770Sluigi
853187770Sluigi	i = do_cmd(IP_FW_NAT_CFG, buf, off);
854187770Sluigi	if (i)
855187770Sluigi		err(1, "setsockopt(%s)", "IP_FW_NAT_CFG");
856187770Sluigi
857187770Sluigi	if (!co.do_quiet) {
858187770Sluigi		/* After every modification, we show the resultant rule. */
859187770Sluigi		int _ac = 3;
860189395Sluigi		const char *_av[] = {"show", "config", id};
861189395Sluigi		ipfw_show_nat(_ac, (char **)(void *)_av);
862187770Sluigi	}
863187770Sluigi}
864187770Sluigi
865187770Sluigi
866187770Sluigivoid
867187770Sluigiipfw_show_nat(int ac, char **av)
868187770Sluigi{
869187770Sluigi	struct cfg_nat *n;
870187770Sluigi	struct cfg_redir *e;
871187770Sluigi	int cmd, i, nbytes, do_cfg, do_rule, frule, lrule, nalloc, size;
872187770Sluigi	int nat_cnt, redir_cnt, r;
873187770Sluigi	uint8_t *data, *p;
874187770Sluigi	char *endptr;
875187770Sluigi
876187770Sluigi	do_rule = 0;
877187770Sluigi	nalloc = 1024;
878187770Sluigi	size = 0;
879187770Sluigi	data = NULL;
880187770Sluigi	frule = 0;
881187770Sluigi	lrule = IPFW_DEFAULT_RULE; /* max ipfw rule number */
882187770Sluigi	ac--; av++;
883187770Sluigi
884187770Sluigi	if (co.test_only)
885187770Sluigi		return;
886187770Sluigi
887187770Sluigi	/* Parse parameters. */
888187770Sluigi	for (cmd = IP_FW_NAT_GET_LOG, do_cfg = 0; ac != 0; ac--, av++) {
889187770Sluigi		if (!strncmp(av[0], "config", strlen(av[0]))) {
890220802Sglebius			cmd = IP_FW_NAT_GET_CONFIG, do_cfg = 1;
891187770Sluigi			continue;
892187770Sluigi		}
893187770Sluigi		/* Convert command line rule #. */
894187770Sluigi		frule = lrule = strtoul(av[0], &endptr, 10);
895187770Sluigi		if (*endptr == '-')
896187770Sluigi			lrule = strtoul(endptr+1, &endptr, 10);
897220804Sglebius		if (lrule == 0)
898187770Sluigi			err(EX_USAGE, "invalid rule number: %s", av[0]);
899187770Sluigi		do_rule = 1;
900187770Sluigi	}
901187770Sluigi
902187770Sluigi	nbytes = nalloc;
903187770Sluigi	while (nbytes >= nalloc) {
904187770Sluigi		nalloc = nalloc * 2;
905187770Sluigi		nbytes = nalloc;
906187770Sluigi		data = safe_realloc(data, nbytes);
907187770Sluigi		if (do_cmd(cmd, data, (uintptr_t)&nbytes) < 0)
908187770Sluigi			err(EX_OSERR, "getsockopt(IP_FW_GET_%s)",
909187770Sluigi			    (cmd == IP_FW_NAT_GET_LOG) ? "LOG" : "CONFIG");
910187770Sluigi	}
911187770Sluigi	if (nbytes == 0)
912187770Sluigi		exit(0);
913187770Sluigi	if (do_cfg) {
914187770Sluigi		nat_cnt = *((int *)data);
915187770Sluigi		for (i = sizeof(nat_cnt); nat_cnt; nat_cnt--) {
916187770Sluigi			n = (struct cfg_nat *)&data[i];
917187770Sluigi			if (frule <= n->id && lrule >= n->id)
918187770Sluigi				print_nat_config(&data[i]);
919187770Sluigi			i += sizeof(struct cfg_nat);
920187770Sluigi			for (redir_cnt = 0; redir_cnt < n->redir_cnt; redir_cnt++) {
921187770Sluigi				e = (struct cfg_redir *)&data[i];
922220802Sglebius				i += sizeof(struct cfg_redir) + e->spool_cnt *
923187770Sluigi				    sizeof(struct cfg_spool);
924187770Sluigi			}
925187770Sluigi		}
926187770Sluigi	} else {
927187770Sluigi		for (i = 0; 1; i += LIBALIAS_BUF_SIZE + sizeof(int)) {
928187770Sluigi			p = &data[i];
929187770Sluigi			if (p == data + nbytes)
930187770Sluigi				break;
931187770Sluigi			bcopy(p, &r, sizeof(int));
932187770Sluigi			if (do_rule) {
933187770Sluigi				if (!(frule <= r && lrule >= r))
934187770Sluigi					continue;
935187770Sluigi			}
936187770Sluigi			printf("nat %u: %s\n", r, p+sizeof(int));
937187770Sluigi		}
938187770Sluigi	}
939187770Sluigi}
940