nat.c revision 188294
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 188294 2009-02-07 18:49:42Z piso $
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[] = {
50187770Sluigi	{ "ip",	                TOK_IP },
51187770Sluigi	{ "if",	                TOK_IF },
52187770Sluigi 	{ "log",                TOK_ALOG },
53187770Sluigi 	{ "deny_in",	        TOK_DENY_INC },
54187770Sluigi 	{ "same_ports",	        TOK_SAME_PORTS },
55187770Sluigi 	{ "unreg_only",	        TOK_UNREG_ONLY },
56187770Sluigi 	{ "reset",	        TOK_RESET_ADDR },
57187770Sluigi 	{ "reverse",	        TOK_ALIAS_REV },
58187770Sluigi 	{ "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
66187770Sluigi/*
67187770Sluigi * Search for interface with name "ifn", and fill n accordingly:
68187770Sluigi *
69187770Sluigi * 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;
87187770Sluigi	mib[3] = AF_INET;
88187770Sluigi	mib[4] = NET_RT_IFLIST;
89187770Sluigi	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
166187770Sluigi/*
167187770Sluigi * XXX - The following functions, macros and definitions come from natd.c:
168187770Sluigi * it would be better to move them outside natd.c, in a file
169187770Sluigi * (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.
176187770Sluigi *          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
182187770Sluigi#define GETLOPORT(x)     ((x) >> 0x10)
183187770Sluigi#define GETNUMPORTS(x)   ((x) & 0x0000ffff)
184187770Sluigi#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
192187770Sluigistatic 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
207187770Sluigistatic int
208187770SluigiStrToPortRange (const char* str, const char* proto, port_range *portRange)
209187770Sluigi{
210187770Sluigi	char*           sep;
211187770Sluigi	struct servent*	sp;
212187770Sluigi	char*		end;
213187770Sluigi	u_short         loPort;
214187770Sluigi	u_short         hiPort;
215187770Sluigi
216187770Sluigi	/* First see if this is a service, return corresponding port if so. */
217187770Sluigi	sp = getservbyname (str,proto);
218187770Sluigi	if (sp) {
219187770Sluigi	        SETLOPORT(*portRange, ntohs(sp->s_port));
220187770Sluigi		SETNUMPORTS(*portRange, 1);
221187770Sluigi		return 0;
222187770Sluigi	}
223187770Sluigi
224187770Sluigi	/* Not a service, see if it's a single port or port range. */
225187770Sluigi	sep = strchr (str, '-');
226187770Sluigi	if (sep == NULL) {
227187770Sluigi	        SETLOPORT(*portRange, strtol(str, &end, 10));
228187770Sluigi		if (end != str) {
229187770Sluigi		        /* Single port. */
230187770Sluigi		        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)
243187770Sluigi	        SETNUMPORTS(*portRange, hiPort - loPort + 1);
244187770Sluigi
245187770Sluigi	if (GETNUMPORTS(*portRange) == 0)
246187770Sluigi	        errx (EX_DATAERR, "invalid port range %s", str);
247187770Sluigi
248187770Sluigi	return 0;
249187770Sluigi}
250187770Sluigi
251187770Sluigistatic 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
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 {
438188294Spiso		/*
439188294Spiso		 * The sctp nat does not allow the port numbers to be mapped to
440188294Spiso		 * new port numbers. Therefore, no ports are to be specified
441188294Spiso		 * in the target port field.
442188294Spiso		 */
443188294Spiso		if (r->proto == IPPROTO_SCTP) {
444188294Spiso			if (strchr (*av, ':'))
445188294Spiso				errx(EX_DATAERR, "redirect_port:"
446188294Spiso				    "port numbers do not change in sctp, so do not "
447188294Spiso				    "specify them as part of the target");
448188294Spiso			else
449188294Spiso				StrToAddr(*av, &r->laddr);
450188294Spiso		} else {
451188294Spiso			if (StrToAddrAndPortRange (*av, &r->laddr, protoName,
452188294Spiso				&portRange) != 0)
453188294Spiso				errx(EX_DATAERR, "redirect_port:"
454188294Spiso				    "invalid local port range");
455187770Sluigi
456188294Spiso			r->lport = GETLOPORT(portRange);
457188294Spiso			numLocalPorts = GETNUMPORTS(portRange);
458188294Spiso		}
459187770Sluigi	}
460187770Sluigi	INC_ARGCV();
461187770Sluigi
462187770Sluigi	/*
463187770Sluigi	 * Extract public port and optionally address.
464187770Sluigi	 */
465187770Sluigi	if (ac == 0)
466187770Sluigi		errx (EX_DATAERR, "redirect_port: missing public port");
467187770Sluigi
468187770Sluigi	sep = strchr (*av, ':');
469187770Sluigi	if (sep) {
470187770Sluigi	        if (StrToAddrAndPortRange (*av, &r->paddr, protoName,
471187770Sluigi		    &portRange) != 0)
472187770Sluigi		        errx(EX_DATAERR, "redirect_port:"
473187770Sluigi			    "invalid public port range");
474187770Sluigi	} else {
475187770Sluigi		r->paddr.s_addr = INADDR_ANY;
476187770Sluigi		if (StrToPortRange (*av, protoName, &portRange) != 0)
477187770Sluigi		        errx(EX_DATAERR, "redirect_port:"
478187770Sluigi			    "invalid public port range");
479187770Sluigi	}
480187770Sluigi
481187770Sluigi	r->pport = GETLOPORT(portRange);
482188294Spiso	if (r->proto == IPPROTO_SCTP) { /* so the logic below still works */
483188294Spiso		numLocalPorts = GETNUMPORTS(portRange);
484188294Spiso		r->lport = r->pport;
485188294Spiso	}
486187770Sluigi	r->pport_cnt = GETNUMPORTS(portRange);
487187770Sluigi	INC_ARGCV();
488187770Sluigi
489187770Sluigi	/*
490187770Sluigi	 * Extract remote address and optionally port.
491187770Sluigi	 */
492187770Sluigi	/*
493187770Sluigi	 * NB: isalpha(**av) => we've to check that next parameter is really an
494187770Sluigi	 * option for this redirect entry, else stop here processing arg[cv].
495187770Sluigi	 */
496187770Sluigi	if (ac != 0 && !isalpha(**av)) {
497187770Sluigi		sep = strchr (*av, ':');
498187770Sluigi		if (sep) {
499187770Sluigi		        if (StrToAddrAndPortRange (*av, &r->raddr, protoName,
500187770Sluigi			    &portRange) != 0)
501187770Sluigi				errx(EX_DATAERR, "redirect_port:"
502187770Sluigi				    "invalid remote port range");
503187770Sluigi		} else {
504187770Sluigi		        SETLOPORT(portRange, 0);
505187770Sluigi			SETNUMPORTS(portRange, 1);
506187770Sluigi			StrToAddr (*av, &r->raddr);
507187770Sluigi		}
508187770Sluigi		INC_ARGCV();
509187770Sluigi	} else {
510187770Sluigi		SETLOPORT(portRange, 0);
511187770Sluigi		SETNUMPORTS(portRange, 1);
512187770Sluigi		r->raddr.s_addr = INADDR_ANY;
513187770Sluigi	}
514187770Sluigi	r->rport = GETLOPORT(portRange);
515187770Sluigi	r->rport_cnt = GETNUMPORTS(portRange);
516187770Sluigi
517187770Sluigi	/*
518187770Sluigi	 * Make sure port ranges match up, then add the redirect ports.
519187770Sluigi	 */
520187770Sluigi	if (numLocalPorts != r->pport_cnt)
521187770Sluigi	        errx(EX_DATAERR, "redirect_port:"
522187770Sluigi		    "port ranges must be equal in size");
523187770Sluigi
524187770Sluigi	/* Remote port range is allowed to be '0' which means all ports. */
525187770Sluigi	if (r->rport_cnt != numLocalPorts &&
526187770Sluigi	    (r->rport_cnt != 1 || r->rport != 0))
527187770Sluigi	        errx(EX_DATAERR, "redirect_port: remote port must"
528187770Sluigi		    "be 0 or equal to local port range in size");
529187770Sluigi
530187770Sluigi	/*
531187770Sluigi	 * Setup LSNAT server pool.
532187770Sluigi	 */
533187770Sluigi	if (lsnat) {
534187770Sluigi		sep = strtok(tmp_spool_buf, ",");
535187770Sluigi		while (sep != NULL) {
536187770Sluigi			tmp = (struct cfg_spool *)spool_buf;
537187770Sluigi			if (len < SOF_SPOOL)
538187770Sluigi				goto nospace;
539187770Sluigi			len -= SOF_SPOOL;
540187770Sluigi			space += SOF_SPOOL;
541188294Spiso			/*
542188294Spiso			 * The sctp nat does not allow the port numbers to be mapped to new port numbers
543188294Spiso			 * Therefore, no ports are to be specified in the target port field
544188294Spiso			 */
545188294Spiso			if (r->proto == IPPROTO_SCTP) {
546188294Spiso				if (strchr (sep, ':')) {
547188294Spiso					errx(EX_DATAERR, "redirect_port:"
548188294Spiso					    "port numbers do not change in "
549188294Spiso					    "sctp, so do not specify them as "
550188294Spiso					    "part of the target");
551188294Spiso				} else {
552188294Spiso					StrToAddr(sep, &tmp->addr);
553188294Spiso					tmp->port = r->pport;
554188294Spiso				}
555188294Spiso			} else {
556188294Spiso				if (StrToAddrAndPortRange(sep, &tmp->addr,
557188294Spiso					protoName, &portRange) != 0)
558188294Spiso					errx(EX_DATAERR, "redirect_port:"
559188294Spiso					    "invalid local port range");
560188294Spiso				if (GETNUMPORTS(portRange) != 1)
561188294Spiso					errx(EX_DATAERR, "redirect_port: "
562188294Spiso					    "local port must be single in "
563188294Spiso					    "this context");
564188294Spiso				tmp->port = GETLOPORT(portRange);
565188294Spiso			}
566187770Sluigi			r->spool_cnt++;
567187770Sluigi			/* Point to the next possible cfg_spool. */
568187770Sluigi			spool_buf = &spool_buf[SOF_SPOOL];
569187770Sluigi			sep = strtok(NULL, ",");
570187770Sluigi		}
571187770Sluigi	}
572187770Sluigi	return (space);
573187770Sluiginospace:
574187770Sluigi	errx(EX_DATAERR, "redirect_port: buf is too small\n");
575187770Sluigi}
576187770Sluigi
577187770Sluigistatic int
578187770Sluigisetup_redir_proto(char *spool_buf, int len,
579187770Sluigi		 int *_ac, char ***_av)
580187770Sluigi{
581187770Sluigi	char **av;
582187770Sluigi	int ac, space;
583187770Sluigi	struct protoent *protoent;
584187770Sluigi	struct cfg_redir *r;
585187770Sluigi
586187770Sluigi	av = *_av;
587187770Sluigi	ac = *_ac;
588187770Sluigi	if (len >= SOF_REDIR) {
589187770Sluigi		r = (struct cfg_redir *)spool_buf;
590187770Sluigi		/* Skip cfg_redir at beginning of buf. */
591187770Sluigi		spool_buf = &spool_buf[SOF_REDIR];
592187770Sluigi		space = SOF_REDIR;
593187770Sluigi		len -= SOF_REDIR;
594187770Sluigi	} else
595187770Sluigi		goto nospace;
596187770Sluigi	r->mode = REDIR_PROTO;
597187770Sluigi	/*
598187770Sluigi	 * Extract protocol.
599187770Sluigi	 */
600187770Sluigi	if (ac == 0)
601187770Sluigi		errx(EX_DATAERR, "redirect_proto: missing protocol");
602187770Sluigi
603187770Sluigi	protoent = getprotobyname(*av);
604187770Sluigi	if (protoent == NULL)
605187770Sluigi		errx(EX_DATAERR, "redirect_proto: unknown protocol %s", *av);
606187770Sluigi	else
607187770Sluigi		r->proto = protoent->p_proto;
608187770Sluigi
609187770Sluigi	INC_ARGCV();
610187770Sluigi
611187770Sluigi	/*
612187770Sluigi	 * Extract local address.
613187770Sluigi	 */
614187770Sluigi	if (ac == 0)
615187770Sluigi		errx(EX_DATAERR, "redirect_proto: missing local address");
616187770Sluigi	else
617187770Sluigi		StrToAddr(*av, &r->laddr);
618187770Sluigi
619187770Sluigi	INC_ARGCV();
620187770Sluigi
621187770Sluigi	/*
622187770Sluigi	 * Extract optional public address.
623187770Sluigi	 */
624187770Sluigi	if (ac == 0) {
625187770Sluigi		r->paddr.s_addr = INADDR_ANY;
626187770Sluigi		r->raddr.s_addr = INADDR_ANY;
627187770Sluigi	} else {
628187770Sluigi		/* see above in setup_redir_port() */
629187770Sluigi		if (!isalpha(**av)) {
630187770Sluigi			StrToAddr(*av, &r->paddr);
631187770Sluigi			INC_ARGCV();
632187770Sluigi
633187770Sluigi			/*
634187770Sluigi			 * Extract optional remote address.
635187770Sluigi			 */
636187770Sluigi			/* see above in setup_redir_port() */
637187770Sluigi			if (ac!=0 && !isalpha(**av)) {
638187770Sluigi				StrToAddr(*av, &r->raddr);
639187770Sluigi				INC_ARGCV();
640187770Sluigi			}
641187770Sluigi		}
642187770Sluigi	}
643187770Sluigi	return (space);
644187770Sluiginospace:
645187770Sluigi	errx(EX_DATAERR, "redirect_proto: buf is too small\n");
646187770Sluigi}
647187770Sluigi
648187770Sluigistatic void
649187770Sluigiprint_nat_config(unsigned char *buf)
650187770Sluigi{
651187770Sluigi	struct cfg_nat *n;
652187770Sluigi	int i, cnt, flag, off;
653187770Sluigi	struct cfg_redir *t;
654187770Sluigi	struct cfg_spool *s;
655187770Sluigi	struct protoent *p;
656187770Sluigi
657187770Sluigi	n = (struct cfg_nat *)buf;
658187770Sluigi	flag = 1;
659187770Sluigi	off  = sizeof(*n);
660187770Sluigi	printf("ipfw nat %u config", n->id);
661187770Sluigi	if (strlen(n->if_name) != 0)
662187770Sluigi		printf(" if %s", n->if_name);
663187770Sluigi	else if (n->ip.s_addr != 0)
664187770Sluigi		printf(" ip %s", inet_ntoa(n->ip));
665187770Sluigi	while (n->mode != 0) {
666187770Sluigi		if (n->mode & PKT_ALIAS_LOG) {
667187770Sluigi			printf(" log");
668187770Sluigi			n->mode &= ~PKT_ALIAS_LOG;
669187770Sluigi		} else if (n->mode & PKT_ALIAS_DENY_INCOMING) {
670187770Sluigi			printf(" deny_in");
671187770Sluigi			n->mode &= ~PKT_ALIAS_DENY_INCOMING;
672187770Sluigi		} else if (n->mode & PKT_ALIAS_SAME_PORTS) {
673187770Sluigi			printf(" same_ports");
674187770Sluigi			n->mode &= ~PKT_ALIAS_SAME_PORTS;
675187770Sluigi		} else if (n->mode & PKT_ALIAS_UNREGISTERED_ONLY) {
676187770Sluigi			printf(" unreg_only");
677187770Sluigi			n->mode &= ~PKT_ALIAS_UNREGISTERED_ONLY;
678187770Sluigi		} else if (n->mode & PKT_ALIAS_RESET_ON_ADDR_CHANGE) {
679187770Sluigi			printf(" reset");
680187770Sluigi			n->mode &= ~PKT_ALIAS_RESET_ON_ADDR_CHANGE;
681187770Sluigi		} else if (n->mode & PKT_ALIAS_REVERSE) {
682187770Sluigi			printf(" reverse");
683187770Sluigi			n->mode &= ~PKT_ALIAS_REVERSE;
684187770Sluigi		} else if (n->mode & PKT_ALIAS_PROXY_ONLY) {
685187770Sluigi			printf(" proxy_only");
686187770Sluigi			n->mode &= ~PKT_ALIAS_PROXY_ONLY;
687187770Sluigi		}
688187770Sluigi	}
689187770Sluigi	/* Print all the redirect's data configuration. */
690187770Sluigi	for (cnt = 0; cnt < n->redir_cnt; cnt++) {
691187770Sluigi		t = (struct cfg_redir *)&buf[off];
692187770Sluigi		off += SOF_REDIR;
693187770Sluigi		switch (t->mode) {
694187770Sluigi		case REDIR_ADDR:
695187770Sluigi			printf(" redirect_addr");
696187770Sluigi			if (t->spool_cnt == 0)
697187770Sluigi				printf(" %s", inet_ntoa(t->laddr));
698187770Sluigi			else
699187770Sluigi				for (i = 0; i < t->spool_cnt; i++) {
700187770Sluigi					s = (struct cfg_spool *)&buf[off];
701187770Sluigi					if (i)
702187770Sluigi						printf(",");
703187770Sluigi					else
704187770Sluigi						printf(" ");
705187770Sluigi					printf("%s", inet_ntoa(s->addr));
706187770Sluigi					off += SOF_SPOOL;
707187770Sluigi				}
708187770Sluigi			printf(" %s", inet_ntoa(t->paddr));
709187770Sluigi			break;
710187770Sluigi		case REDIR_PORT:
711187770Sluigi			p = getprotobynumber(t->proto);
712187770Sluigi			printf(" redirect_port %s ", p->p_name);
713187770Sluigi			if (!t->spool_cnt) {
714187770Sluigi				printf("%s:%u", inet_ntoa(t->laddr), t->lport);
715187770Sluigi				if (t->pport_cnt > 1)
716187770Sluigi					printf("-%u", t->lport +
717187770Sluigi					    t->pport_cnt - 1);
718187770Sluigi			} else
719187770Sluigi				for (i=0; i < t->spool_cnt; i++) {
720187770Sluigi					s = (struct cfg_spool *)&buf[off];
721187770Sluigi					if (i)
722187770Sluigi						printf(",");
723187770Sluigi					printf("%s:%u", inet_ntoa(s->addr),
724187770Sluigi					    s->port);
725187770Sluigi					off += SOF_SPOOL;
726187770Sluigi				}
727187770Sluigi
728187770Sluigi			printf(" ");
729187770Sluigi			if (t->paddr.s_addr)
730187770Sluigi				printf("%s:", inet_ntoa(t->paddr));
731187770Sluigi			printf("%u", t->pport);
732187770Sluigi			if (!t->spool_cnt && t->pport_cnt > 1)
733187770Sluigi				printf("-%u", t->pport + t->pport_cnt - 1);
734187770Sluigi
735187770Sluigi			if (t->raddr.s_addr) {
736187770Sluigi				printf(" %s", inet_ntoa(t->raddr));
737187770Sluigi				if (t->rport) {
738187770Sluigi					printf(":%u", t->rport);
739187770Sluigi					if (!t->spool_cnt && t->rport_cnt > 1)
740187770Sluigi						printf("-%u", t->rport +
741187770Sluigi						    t->rport_cnt - 1);
742187770Sluigi				}
743187770Sluigi			}
744187770Sluigi			break;
745187770Sluigi		case REDIR_PROTO:
746187770Sluigi			p = getprotobynumber(t->proto);
747187770Sluigi			printf(" redirect_proto %s %s", p->p_name,
748187770Sluigi			    inet_ntoa(t->laddr));
749187770Sluigi			if (t->paddr.s_addr != 0) {
750187770Sluigi				printf(" %s", inet_ntoa(t->paddr));
751187770Sluigi				if (t->raddr.s_addr)
752187770Sluigi					printf(" %s", inet_ntoa(t->raddr));
753187770Sluigi			}
754187770Sluigi			break;
755187770Sluigi		default:
756187770Sluigi			errx(EX_DATAERR, "unknown redir mode");
757187770Sluigi			break;
758187770Sluigi		}
759187770Sluigi	}
760187770Sluigi	printf("\n");
761187770Sluigi}
762187770Sluigi
763187770Sluigivoid
764187770Sluigiipfw_config_nat(int ac, char **av)
765187770Sluigi{
766187770Sluigi	struct cfg_nat *n;              /* Nat instance configuration. */
767187770Sluigi	int i, len, off, tok;
768187770Sluigi	char *id, buf[NAT_BUF_LEN]; 	/* Buffer for serialized data. */
769187770Sluigi
770187770Sluigi	len = NAT_BUF_LEN;
771187770Sluigi	/* Offset in buf: save space for n at the beginning. */
772187770Sluigi	off = sizeof(*n);
773187770Sluigi	memset(buf, 0, sizeof(buf));
774187770Sluigi	n = (struct cfg_nat *)buf;
775187770Sluigi
776187770Sluigi	av++; ac--;
777187770Sluigi	/* Nat id. */
778187770Sluigi	if (ac && isdigit(**av)) {
779187770Sluigi		id = *av;
780187770Sluigi		i = atoi(*av);
781187770Sluigi		ac--; av++;
782187770Sluigi		n->id = i;
783187770Sluigi	} else
784187770Sluigi		errx(EX_DATAERR, "missing nat id");
785187770Sluigi	if (ac == 0)
786187770Sluigi		errx(EX_DATAERR, "missing option");
787187770Sluigi
788187770Sluigi	while (ac > 0) {
789187770Sluigi		tok = match_token(nat_params, *av);
790187770Sluigi		ac--; av++;
791187770Sluigi		switch (tok) {
792187770Sluigi		case TOK_IP:
793187770Sluigi			if (ac == 0)
794187770Sluigi				errx(EX_DATAERR, "missing option");
795187770Sluigi			if (!inet_aton(av[0], &(n->ip)))
796187770Sluigi				errx(EX_DATAERR, "bad ip address ``%s''",
797187770Sluigi				    av[0]);
798187770Sluigi			ac--; av++;
799187770Sluigi			break;
800187770Sluigi		case TOK_IF:
801187770Sluigi			if (ac == 0)
802187770Sluigi				errx(EX_DATAERR, "missing option");
803187770Sluigi			set_addr_dynamic(av[0], n);
804187770Sluigi			ac--; av++;
805187770Sluigi			break;
806187770Sluigi		case TOK_ALOG:
807187770Sluigi			n->mode |= PKT_ALIAS_LOG;
808187770Sluigi			break;
809187770Sluigi		case TOK_DENY_INC:
810187770Sluigi			n->mode |= PKT_ALIAS_DENY_INCOMING;
811187770Sluigi			break;
812187770Sluigi		case TOK_SAME_PORTS:
813187770Sluigi			n->mode |= PKT_ALIAS_SAME_PORTS;
814187770Sluigi			break;
815187770Sluigi		case TOK_UNREG_ONLY:
816187770Sluigi			n->mode |= PKT_ALIAS_UNREGISTERED_ONLY;
817187770Sluigi			break;
818187770Sluigi		case TOK_RESET_ADDR:
819187770Sluigi			n->mode |= PKT_ALIAS_RESET_ON_ADDR_CHANGE;
820187770Sluigi			break;
821187770Sluigi		case TOK_ALIAS_REV:
822187770Sluigi			n->mode |= PKT_ALIAS_REVERSE;
823187770Sluigi			break;
824187770Sluigi		case TOK_PROXY_ONLY:
825187770Sluigi			n->mode |= PKT_ALIAS_PROXY_ONLY;
826187770Sluigi			break;
827187770Sluigi			/*
828187770Sluigi			 * All the setup_redir_* functions work directly in the final
829187770Sluigi			 * buffer, see above for details.
830187770Sluigi			 */
831187770Sluigi		case TOK_REDIR_ADDR:
832187770Sluigi		case TOK_REDIR_PORT:
833187770Sluigi		case TOK_REDIR_PROTO:
834187770Sluigi			switch (tok) {
835187770Sluigi			case TOK_REDIR_ADDR:
836187770Sluigi				i = setup_redir_addr(&buf[off], len, &ac, &av);
837187770Sluigi				break;
838187770Sluigi			case TOK_REDIR_PORT:
839187770Sluigi				i = setup_redir_port(&buf[off], len, &ac, &av);
840187770Sluigi				break;
841187770Sluigi			case TOK_REDIR_PROTO:
842187770Sluigi				i = setup_redir_proto(&buf[off], len, &ac, &av);
843187770Sluigi				break;
844187770Sluigi			}
845187770Sluigi			n->redir_cnt++;
846187770Sluigi			off += i;
847187770Sluigi			len -= i;
848187770Sluigi			break;
849187770Sluigi		default:
850187770Sluigi			errx(EX_DATAERR, "unrecognised option ``%s''", av[-1]);
851187770Sluigi		}
852187770Sluigi	}
853187770Sluigi
854187770Sluigi	i = do_cmd(IP_FW_NAT_CFG, buf, off);
855187770Sluigi	if (i)
856187770Sluigi		err(1, "setsockopt(%s)", "IP_FW_NAT_CFG");
857187770Sluigi
858187770Sluigi	if (!co.do_quiet) {
859187770Sluigi		/* After every modification, we show the resultant rule. */
860187770Sluigi		int _ac = 3;
861187770Sluigi		char *_av[] = {"show", "config", id};
862187770Sluigi		ipfw_show_nat(_ac, _av);
863187770Sluigi	}
864187770Sluigi}
865187770Sluigi
866187770Sluigi
867187770Sluigivoid
868187770Sluigiipfw_show_nat(int ac, char **av)
869187770Sluigi{
870187770Sluigi	struct cfg_nat *n;
871187770Sluigi	struct cfg_redir *e;
872187770Sluigi	int cmd, i, nbytes, do_cfg, do_rule, frule, lrule, nalloc, size;
873187770Sluigi	int nat_cnt, redir_cnt, r;
874187770Sluigi	uint8_t *data, *p;
875187770Sluigi	char *endptr;
876187770Sluigi
877187770Sluigi	do_rule = 0;
878187770Sluigi	nalloc = 1024;
879187770Sluigi	size = 0;
880187770Sluigi	data = NULL;
881187770Sluigi	frule = 0;
882187770Sluigi	lrule = IPFW_DEFAULT_RULE; /* max ipfw rule number */
883187770Sluigi	ac--; av++;
884187770Sluigi
885187770Sluigi	if (co.test_only)
886187770Sluigi		return;
887187770Sluigi
888187770Sluigi	/* Parse parameters. */
889187770Sluigi	for (cmd = IP_FW_NAT_GET_LOG, do_cfg = 0; ac != 0; ac--, av++) {
890187770Sluigi		if (!strncmp(av[0], "config", strlen(av[0]))) {
891187770Sluigi			cmd = IP_FW_NAT_GET_CONFIG, do_cfg = 1;
892187770Sluigi			continue;
893187770Sluigi		}
894187770Sluigi		/* Convert command line rule #. */
895187770Sluigi		frule = lrule = strtoul(av[0], &endptr, 10);
896187770Sluigi		if (*endptr == '-')
897187770Sluigi			lrule = strtoul(endptr+1, &endptr, 10);
898187770Sluigi		if (lrule == 0)
899187770Sluigi			err(EX_USAGE, "invalid rule number: %s", av[0]);
900187770Sluigi		do_rule = 1;
901187770Sluigi	}
902187770Sluigi
903187770Sluigi	nbytes = nalloc;
904187770Sluigi	while (nbytes >= nalloc) {
905187770Sluigi		nalloc = nalloc * 2;
906187770Sluigi		nbytes = nalloc;
907187770Sluigi		data = safe_realloc(data, nbytes);
908187770Sluigi		if (do_cmd(cmd, data, (uintptr_t)&nbytes) < 0)
909187770Sluigi			err(EX_OSERR, "getsockopt(IP_FW_GET_%s)",
910187770Sluigi			    (cmd == IP_FW_NAT_GET_LOG) ? "LOG" : "CONFIG");
911187770Sluigi	}
912187770Sluigi	if (nbytes == 0)
913187770Sluigi		exit(0);
914187770Sluigi	if (do_cfg) {
915187770Sluigi		nat_cnt = *((int *)data);
916187770Sluigi		for (i = sizeof(nat_cnt); nat_cnt; nat_cnt--) {
917187770Sluigi			n = (struct cfg_nat *)&data[i];
918187770Sluigi			if (frule <= n->id && lrule >= n->id)
919187770Sluigi				print_nat_config(&data[i]);
920187770Sluigi			i += sizeof(struct cfg_nat);
921187770Sluigi			for (redir_cnt = 0; redir_cnt < n->redir_cnt; redir_cnt++) {
922187770Sluigi				e = (struct cfg_redir *)&data[i];
923187770Sluigi				i += sizeof(struct cfg_redir) + e->spool_cnt *
924187770Sluigi				    sizeof(struct cfg_spool);
925187770Sluigi			}
926187770Sluigi		}
927187770Sluigi	} else {
928187770Sluigi		for (i = 0; 1; i += LIBALIAS_BUF_SIZE + sizeof(int)) {
929187770Sluigi			p = &data[i];
930187770Sluigi			if (p == data + nbytes)
931187770Sluigi				break;
932187770Sluigi			bcopy(p, &r, sizeof(int));
933187770Sluigi			if (do_rule) {
934187770Sluigi				if (!(frule <= r && lrule >= r))
935187770Sluigi					continue;
936187770Sluigi			}
937187770Sluigi			printf("nat %u: %s\n", r, p+sizeof(int));
938187770Sluigi		}
939187770Sluigi	}
940187770Sluigi}
941