nat.c revision 187983
1226298Sedwin/*
2192886Sedwin * Copyright (c) 2002-2003 Luigi Rizzo
3192886Sedwin * Copyright (c) 1996 Alex Nash, Paul Traina, Poul-Henning Kamp
464499Swollman * Copyright (c) 1994 Ugen J.S.Antsilevich
52742Swollman *
62742Swollman * Idea and grammar partially left from:
72742Swollman * Copyright (c) 1993 Daniel Boulet
82742Swollman *
9158421Swollman * Redistribution and use in source forms, with and without modification,
102742Swollman * are permitted provided that this entire comment appears intact.
112742Swollman *
12158421Swollman * Redistribution in binary form may occur without any restrictions.
13158421Swollman * Obviously, it would be nice if you gave credit where credit is due
142742Swollman * but requiring it would be too onerous.
1586222Swollman *
1620094Swollman * This software is provided ``AS IS'' without any warranties of any kind.
1720094Swollman *
1820094Swollman * NEW command line interface for IP firewall facility
1920094Swollman *
2020094Swollman * $FreeBSD: head/sbin/ipfw/nat.c 187983 2009-02-01 16:00:49Z luigi $
21158421Swollman *
22158421Swollman * In-kernel nat support
2320094Swollman */
242742Swollman
252742Swollman#include <sys/types.h>
262742Swollman#include <sys/socket.h>
272742Swollman#include <sys/sysctl.h>
282742Swollman
2958787Sru#include "ipfw2.h"
302742Swollman
312742Swollman#include <ctype.h>
322742Swollman#include <err.h>
332742Swollman#include <netdb.h>
34114173Swollman#include <stdio.h>
35114173Swollman#include <stdlib.h>
36114173Swollman#include <string.h>
37114173Swollman#include <sysexits.h>
38114173Swollman
39114173Swollman#define IPFW_INTERNAL	/* Access to protected structures in ip_fw.h. */
40114173Swollman
41114173Swollman#include <net/if.h>
42114173Swollman#include <net/if_dl.h>
43114173Swollman#include <net/route.h> /* def. of struct route */
44114173Swollman#include <netinet/in.h>
45114173Swollman#include <netinet/ip_fw.h>
46114173Swollman#include <arpa/inet.h>
47114173Swollman#include <alias.h>
48149590Swollman
49149590Swollmanstatic struct _s_x nat_params[] = {
50114173Swollman	{ "ip",	                TOK_IP },
512742Swollman	{ "if",	                TOK_IF },
529908Swollman 	{ "log",                TOK_ALOG },
532742Swollman 	{ "deny_in",	        TOK_DENY_INC },
542742Swollman 	{ "same_ports",	        TOK_SAME_PORTS },
552742Swollman 	{ "unreg_only",	        TOK_UNREG_ONLY },
562742Swollman 	{ "reset",	        TOK_RESET_ADDR },
572742Swollman 	{ "reverse",	        TOK_ALIAS_REV },
582742Swollman 	{ "proxy_only",	        TOK_PROXY_ONLY },
592742Swollman	{ "redirect_addr",	TOK_REDIR_ADDR },
602742Swollman	{ "redirect_port",	TOK_REDIR_PORT },
612742Swollman	{ "redirect_proto",	TOK_REDIR_PROTO },
6220094Swollman 	{ NULL, 0 }	/* terminator */
632742Swollman};
6420094Swollman
65158421Swollman
6620094Swollman/*
6720094Swollman * Search for interface with name "ifn", and fill n accordingly:
6820094Swollman *
6920094Swollman * n->ip        ip address of interface "ifn"
7020094Swollman * n->if_name   copy of interface name "ifn"
7120094Swollman */
7220094Swollmanstatic void
7320094Swollmanset_addr_dynamic(const char *ifn, struct cfg_nat *n)
7420094Swollman{
7520094Swollman	size_t needed;
7620094Swollman	int mib[6];
7720094Swollman	char *buf, *lim, *next;
7820094Swollman	struct if_msghdr *ifm;
792742Swollman	struct ifa_msghdr *ifam;
80223629Sedwin	struct sockaddr_dl *sdl;
81223629Sedwin	struct sockaddr_in *sin;
82223629Sedwin	int ifIndex, ifMTU;
83223629Sedwin
842742Swollman	mib[0] = CTL_NET;
852742Swollman	mib[1] = PF_ROUTE;
862742Swollman	mib[2] = 0;
8719878Swollman	mib[3] = AF_INET;
882742Swollman	mib[4] = NET_RT_IFLIST;
892742Swollman	mib[5] = 0;
902742Swollman/*
91158421Swollman * Get interface data.
92158421Swollman */
93158421Swollman	if (sysctl(mib, 6, NULL, &needed, NULL, 0) == -1)
94158421Swollman		err(1, "iflist-sysctl-estimate");
95158421Swollman	buf = safe_calloc(1, needed);
96153670Swollman	if (sysctl(mib, 6, buf, &needed, NULL, 0) == -1)
9743014Swollman		err(1, "iflist-sysctl-get");
9843014Swollman	lim = buf + needed;
9943014Swollman/*
1002742Swollman * Loop through interfaces until one with
1012742Swollman * given name is found. This is done to
10219878Swollman * find correct interface index for routing
10319878Swollman * message processing.
10419878Swollman */
10558787Sru	ifIndex	= 0;
10643014Swollman	next = buf;
10775267Swollman	while (next < lim) {
1082742Swollman		ifm = (struct if_msghdr *)next;
1092742Swollman		next += ifm->ifm_msglen;
110153670Swollman		if (ifm->ifm_version != RTM_VERSION) {
111153670Swollman			if (co.verbose)
112153670Swollman				warnx("routing message version %d "
11343014Swollman				    "not understood", ifm->ifm_version);
114153670Swollman			continue;
115153670Swollman		}
1162742Swollman		if (ifm->ifm_type == RTM_IFINFO) {
1172742Swollman			sdl = (struct sockaddr_dl *)(ifm + 1);
11819878Swollman			if (strlen(ifn) == sdl->sdl_nlen &&
11919878Swollman			    strncmp(ifn, sdl->sdl_data, sdl->sdl_nlen) == 0) {
12019878Swollman				ifIndex = ifm->ifm_index;
121149514Swollman				ifMTU = ifm->ifm_data.ifi_mtu;
12220094Swollman				break;
12343014Swollman			}
12443014Swollman		}
1252742Swollman	}
1262742Swollman	if (!ifIndex)
1272742Swollman		errx(1, "unknown interface name %s", ifn);
12867578Swollman/*
1292742Swollman * Get interface address.
1302742Swollman */
1312742Swollman	sin = NULL;
1322742Swollman	while (next < lim) {
133193785Sedwin		ifam = (struct ifa_msghdr *)next;
134193785Sedwin		next += ifam->ifam_msglen;
135193785Sedwin		if (ifam->ifam_version != RTM_VERSION) {
136193785Sedwin			if (co.verbose)
137193785Sedwin				warnx("routing message version %d "
138193785Sedwin				    "not understood", ifam->ifam_version);
139193785Sedwin			continue;
140193785Sedwin		}
141193785Sedwin		if (ifam->ifam_type != RTM_NEWADDR)
142193785Sedwin			break;
143193785Sedwin		if (ifam->ifam_addrs & RTA_IFA) {
144193785Sedwin			int i;
145193785Sedwin			char *cp = (char *)(ifam + 1);
146193785Sedwin
147193785Sedwin			for (i = 1; i < RTA_IFA; i <<= 1) {
148193785Sedwin				if (ifam->ifam_addrs & i)
149193785Sedwin					cp += SA_SIZE((struct sockaddr *)cp);
150193785Sedwin			}
151193785Sedwin			if (((struct sockaddr *)cp)->sa_family == AF_INET) {
152193785Sedwin				sin = (struct sockaddr_in *)cp;
153193785Sedwin				break;
154193785Sedwin			}
155193785Sedwin		}
156193785Sedwin	}
157193785Sedwin	if (sin == NULL)
158193785Sedwin		errx(1, "%s: cannot get interface address", ifn);
159193785Sedwin
160193785Sedwin	n->ip = sin->sin_addr;
161193785Sedwin	strncpy(n->if_name, ifn, IF_NAMESIZE);
162193785Sedwin
163193785Sedwin	free(buf);
164193785Sedwin}
165193785Sedwin
166193785Sedwin/*
167193785Sedwin * XXX - The following functions, macros and definitions come from natd.c:
168193785Sedwin * it would be better to move them outside natd.c, in a file
169193785Sedwin * (redirect_support.[ch]?) shared by ipfw and natd, but for now i can live
170193785Sedwin * with it.
171194485Sedwin */
172194485Sedwin
173194485Sedwin/*
174194485Sedwin * Definition of a port range, and macros to deal with values.
175194485Sedwin * FORMAT:  HI 16-bits == first port in range, 0 == all ports.
176194485Sedwin *          LO 16-bits == number of ports in range
177193785Sedwin * NOTES:   - Port values are not stored in network byte order.
178198270Sedwin */
179198270Sedwin
180198270Sedwin#define port_range u_long
181198270Sedwin
182198270Sedwin#define GETLOPORT(x)     ((x) >> 0x10)
183198270Sedwin#define GETNUMPORTS(x)   ((x) & 0x0000ffff)
184198270Sedwin#define GETHIPORT(x)     (GETLOPORT((x)) + GETNUMPORTS((x)))
185198270Sedwin
186198270Sedwin/* Set y to be the low-port value in port_range variable x. */
187198270Sedwin#define SETLOPORT(x,y)   ((x) = ((x) & 0x0000ffff) | ((y) << 0x10))
188198270Sedwin
189198270Sedwin/* Set y to be the number of ports in port_range variable x. */
190198270Sedwin#define SETNUMPORTS(x,y) ((x) = ((x) & 0xffff0000) | (y))
191196581Sedwin
192198270Sedwinstatic void
193198270SedwinStrToAddr (const char* str, struct in_addr* addr)
194198270Sedwin{
195198270Sedwin	struct hostent* hp;
196198270Sedwin
197198270Sedwin	if (inet_aton (str, addr))
198198270Sedwin		return;
199198270Sedwin
200198270Sedwin	hp = gethostbyname (str);
201198270Sedwin	if (!hp)
202198270Sedwin		errx (1, "unknown host %s", str);
203201189Sedwin
204201189Sedwin	memcpy (addr, hp->h_addr, sizeof (struct in_addr));
205201189Sedwin}
206201189Sedwin
207201189Sedwinstatic int
208201189SedwinStrToPortRange (const char* str, const char* proto, port_range *portRange)
209201189Sedwin{
210201189Sedwin	char*           sep;
211201189Sedwin	struct servent*	sp;
212201189Sedwin	char*		end;
213201189Sedwin	u_short         loPort;
214201189Sedwin	u_short         hiPort;
215201189Sedwin
216201189Sedwin	/* First see if this is a service, return corresponding port if so. */
217201189Sedwin	sp = getservbyname (str,proto);
218201189Sedwin	if (sp) {
219201189Sedwin	        SETLOPORT(*portRange, ntohs(sp->s_port));
220201189Sedwin		SETNUMPORTS(*portRange, 1);
221206219Sedwin		return 0;
222206219Sedwin	}
223206219Sedwin
224206219Sedwin	/* Not a service, see if it's a single port or port range. */
225206219Sedwin	sep = strchr (str, '-');
226204887Sedwin	if (sep == NULL) {
227206219Sedwin	        SETLOPORT(*portRange, strtol(str, &end, 10));
228206219Sedwin		if (end != str) {
229206219Sedwin		        /* Single port. */
230206219Sedwin		        SETNUMPORTS(*portRange, 1);
231204887Sedwin			return 0;
232201189Sedwin		}
233202606Sedwin
234204887Sedwin		/* Error in port range field. */
235201189Sedwin		errx (EX_DATAERR, "%s/%s: unknown service", str, proto);
2362742Swollman	}
23775267Swollman
23819878Swollman	/* Port range, get the values and sanity check. */
23919878Swollman	sscanf (str, "%hu-%hu", &loPort, &hiPort);
2402742Swollman	SETLOPORT(*portRange, loPort);
24119878Swollman	SETNUMPORTS(*portRange, 0);	/* Error by default */
24219878Swollman	if (loPort <= hiPort)
243201189Sedwin	        SETNUMPORTS(*portRange, hiPort - loPort + 1);
244201189Sedwin
2452742Swollman	if (GETNUMPORTS(*portRange) == 0)
2462742Swollman	        errx (EX_DATAERR, "invalid port range %s", str);
2472742Swollman
24867578Swollman	return 0;
2492742Swollman}
25019878Swollman
2512742Swollmanstatic int
2522742SwollmanStrToProto (const char* str)
25386222Swollman{
25486222Swollman	if (!strcmp (str, "tcp"))
255149514Swollman		return IPPROTO_TCP;
256149514Swollman
257149514Swollman	if (!strcmp (str, "udp"))
2582742Swollman		return IPPROTO_UDP;
259149514Swollman
260149514Swollman	errx (EX_DATAERR, "unknown protocol %s. Expected tcp or udp", str);
26186222Swollman}
2622742Swollman
2632742Swollmanstatic int
2642742SwollmanStrToAddrAndPortRange (const char* str, struct in_addr* addr, char* proto,
2652742Swollman		       port_range *portRange)
2662742Swollman{
2672742Swollman	char*	ptr;
2682742Swollman
26914343Swollman	ptr = strchr (str, ':');
2702742Swollman	if (!ptr)
27117200Swollman		errx (EX_DATAERR, "%s is missing port number", str);
27219878Swollman
27319878Swollman	*ptr = '\0';
2742742Swollman	++ptr;
27519878Swollman
2762742Swollman	StrToAddr (str, addr);
2772742Swollman	return StrToPortRange (ptr, proto, portRange);
2782742Swollman}
2792742Swollman
28019878Swollman/* End of stuff taken from natd.c. */
2812742Swollman
2822742Swollman#define INC_ARGCV() do {        \
2832742Swollman	(*_av)++;               \
2842742Swollman	(*_ac)--;               \
28543014Swollman	av = *_av;              \
2862742Swollman	ac = *_ac;              \
2872742Swollman} while(0)
2882742Swollman
2892742Swollman/*
29019878Swollman * The next 3 functions add support for the addr, port and proto redirect and
29119878Swollman * their logic is loosely based on SetupAddressRedirect(), SetupPortRedirect()
2922742Swollman * and SetupProtoRedirect() from natd.c.
2932742Swollman *
2942742Swollman * Every setup_* function fills at least one redirect entry
29593799Swollman * (struct cfg_redir) and zero or more server pool entry (struct cfg_spool)
2962742Swollman * in buf.
2972742Swollman *
2982742Swollman * The format of data in buf is:
2992742Swollman *
3002742Swollman *
3012742Swollman *     cfg_nat    cfg_redir    cfg_spool    ......  cfg_spool
3022742Swollman *
3032742Swollman *    -------------------------------------        ------------
30419878Swollman *   |          | .....X ... |          |         |           |  .....
3052742Swollman *    ------------------------------------- ...... ------------
3062742Swollman *                     ^
3072742Swollman *                spool_cnt       n=0       ......   n=(X-1)
308158421Swollman *
309158421Swollman * len points to the amount of available space in buf
310158421Swollman * space counts the memory consumed by every function
311158421Swollman *
3122742Swollman * XXX - Every function get all the argv params so it
313158421Swollman * has to check, in optional parameters, that the next
314158421Swollman * args is a valid option for the redir entry and not
3152742Swollman * another token. Only redir_port and redir_proto are
316158421Swollman * affected by this.
3172742Swollman */
3182742Swollman
3192742Swollmanstatic int
3202742Swollmansetup_redir_addr(char *spool_buf, int len,
3212742Swollman		 int *_ac, char ***_av)
32214343Swollman{
32314343Swollman	char **av, *sep; /* Token separator. */
324163302Sru	/* Temporary buffer used to hold server pool ip's. */
32593799Swollman	char tmp_spool_buf[NAT_BUF_LEN];
32693799Swollman	int ac, space, lsnat;
32793799Swollman	struct cfg_redir *r;
328163302Sru	struct cfg_spool *tmp;
329169811Swollman
330163302Sru	av = *_av;
331163302Sru	ac = *_ac;
332163302Sru	space = 0;
333163302Sru	lsnat = 0;
334163302Sru	if (len >= SOF_REDIR) {
335163302Sru		r = (struct cfg_redir *)spool_buf;
336163302Sru		/* Skip cfg_redir at beginning of buf. */
337163302Sru		spool_buf = &spool_buf[SOF_REDIR];
338163302Sru		space = SOF_REDIR;
339163302Sru		len -= SOF_REDIR;
340163302Sru	} else
341181421Sedwin		goto nospace;
342181421Sedwin	r->mode = REDIR_ADDR;
343181421Sedwin	/* Extract local address. */
344181421Sedwin	if (ac == 0)
345181421Sedwin		errx(EX_DATAERR, "redirect_addr: missing local address");
346181421Sedwin	sep = strchr(*av, ',');
347181421Sedwin	if (sep) {		/* LSNAT redirection syntax. */
348181421Sedwin		r->laddr.s_addr = INADDR_NONE;
349181421Sedwin		/* Preserve av, copy spool servers to tmp_spool_buf. */
350181421Sedwin		strncpy(tmp_spool_buf, *av, strlen(*av)+1);
351181421Sedwin		lsnat = 1;
352181421Sedwin	} else
353181421Sedwin		StrToAddr(*av, &r->laddr);
354181421Sedwin	INC_ARGCV();
355181421Sedwin
356181421Sedwin	/* Extract public address. */
357181421Sedwin	if (ac == 0)
358181421Sedwin		errx(EX_DATAERR, "redirect_addr: missing public address");
359181421Sedwin	StrToAddr(*av, &r->paddr);
360181421Sedwin	INC_ARGCV();
361181421Sedwin
362181421Sedwin	/* Setup LSNAT server pool. */
363163302Sru	if (sep) {
364163302Sru		sep = strtok(tmp_spool_buf, ",");
36593799Swollman		while (sep != NULL) {
366163302Sru			tmp = (struct cfg_spool *)spool_buf;
36767578Swollman			if (len < SOF_SPOOL)
36893799Swollman				goto nospace;
36914343Swollman			len -= SOF_SPOOL;
37093799Swollman			space += SOF_SPOOL;
37193799Swollman			StrToAddr(sep, &tmp->addr);
37214343Swollman			tmp->port = ~0;
37393799Swollman			r->spool_cnt++;
374163302Sru			/* Point to the next possible cfg_spool. */
3752742Swollman			spool_buf = &spool_buf[SOF_SPOOL];
3762742Swollman			sep = strtok(NULL, ",");
3772742Swollman		}
37893799Swollman	}
379163302Sru	return(space);
380163302Srunospace:
381163302Sru	errx(EX_DATAERR, "redirect_addr: buf is too small\n");
382163302Sru}
38386222Swollman
38493799Swollmanstatic int
38514343Swollmansetup_redir_port(char *spool_buf, int len,
38693799Swollman		 int *_ac, char ***_av)
387163302Sru{
388163302Sru	char **av, *sep, *protoName;
389163302Sru	char tmp_spool_buf[NAT_BUF_LEN];
390163302Sru	int ac, space, lsnat;
391163302Sru	struct cfg_redir *r;
392163302Sru	struct cfg_spool *tmp;
393163302Sru	u_short numLocalPorts;
394163302Sru	port_range portRange;
39567578Swollman
39619878Swollman	av = *_av;
39714343Swollman	ac = *_ac;
39893799Swollman	space = 0;
399163302Sru	lsnat = 0;
400163302Sru	numLocalPorts = 0;
401163302Sru
402163302Sru	if (len >= SOF_REDIR) {
403200835Sedwin		r = (struct cfg_redir *)spool_buf;
404200835Sedwin		/* Skip cfg_redir at beginning of buf. */
405200835Sedwin		spool_buf = &spool_buf[SOF_REDIR];
406200835Sedwin		space = SOF_REDIR;
407200835Sedwin		len -= SOF_REDIR;
408200835Sedwin	} else
409200835Sedwin		goto nospace;
410200835Sedwin	r->mode = REDIR_PORT;
411200835Sedwin	/*
412200835Sedwin	 * Extract protocol.
413200835Sedwin	 */
414200835Sedwin	if (ac == 0)
415200835Sedwin		errx (EX_DATAERR, "redirect_port: missing protocol");
416200835Sedwin	r->proto = StrToProto(*av);
417200835Sedwin	protoName = *av;
418200835Sedwin	INC_ARGCV();
419200835Sedwin
420200835Sedwin	/*
421200835Sedwin	 * Extract local address.
422200835Sedwin	 */
423200835Sedwin	if (ac == 0)
424200835Sedwin		errx (EX_DATAERR, "redirect_port: missing local address");
425200835Sedwin
426200835Sedwin	sep = strchr(*av, ',');
427200835Sedwin	/* LSNAT redirection syntax. */
428200835Sedwin	if (sep) {
429200835Sedwin		r->laddr.s_addr = INADDR_NONE;
430200835Sedwin		r->lport = ~0;
431200835Sedwin		numLocalPorts = 1;
432200835Sedwin		/* Preserve av, copy spool servers to tmp_spool_buf. */
433200835Sedwin		strncpy(tmp_spool_buf, *av, strlen(*av)+1);
434200835Sedwin		lsnat = 1;
435200835Sedwin	} else {
436200835Sedwin		if (StrToAddrAndPortRange (*av, &r->laddr, protoName,
437200835Sedwin		    &portRange) != 0)
438200835Sedwin			errx(EX_DATAERR, "redirect_port:"
439200835Sedwin			    "invalid local port range");
440200835Sedwin
441200835Sedwin		r->lport = GETLOPORT(portRange);
442200835Sedwin		numLocalPorts = GETNUMPORTS(portRange);
443200835Sedwin	}
444200835Sedwin	INC_ARGCV();
445200835Sedwin
446200835Sedwin	/*
447200835Sedwin	 * Extract public port and optionally address.
448200835Sedwin	 */
449200835Sedwin	if (ac == 0)
450200835Sedwin		errx (EX_DATAERR, "redirect_port: missing public port");
451200835Sedwin
452200835Sedwin	sep = strchr (*av, ':');
453200835Sedwin	if (sep) {
454200835Sedwin	        if (StrToAddrAndPortRange (*av, &r->paddr, protoName,
455200835Sedwin		    &portRange) != 0)
456200835Sedwin		        errx(EX_DATAERR, "redirect_port:"
457200835Sedwin			    "invalid public port range");
458200835Sedwin	} else {
459200835Sedwin		r->paddr.s_addr = INADDR_ANY;
460200835Sedwin		if (StrToPortRange (*av, protoName, &portRange) != 0)
461200835Sedwin		        errx(EX_DATAERR, "redirect_port:"
462200835Sedwin			    "invalid public port range");
463200835Sedwin	}
464200835Sedwin
465200835Sedwin	r->pport = GETLOPORT(portRange);
46667578Swollman	r->pport_cnt = GETNUMPORTS(portRange);
46719878Swollman	INC_ARGCV();
46819878Swollman
46914343Swollman	/*
47093799Swollman	 * Extract remote address and optionally port.
471198825Sedwin	 */
472198825Sedwin	/*
473200835Sedwin	 * NB: isalpha(**av) => we've to check that next parameter is really an
474200835Sedwin	 * option for this redirect entry, else stop here processing arg[cv].
475198825Sedwin	 */
476198825Sedwin	if (ac != 0 && !isalpha(**av)) {
477198825Sedwin		sep = strchr (*av, ':');
478198825Sedwin		if (sep) {
479198825Sedwin		        if (StrToAddrAndPortRange (*av, &r->raddr, protoName,
480198825Sedwin			    &portRange) != 0)
481198825Sedwin				errx(EX_DATAERR, "redirect_port:"
482198825Sedwin				    "invalid remote port range");
483198825Sedwin		} else {
484198825Sedwin		        SETLOPORT(portRange, 0);
485198825Sedwin			SETNUMPORTS(portRange, 1);
486198825Sedwin			StrToAddr (*av, &r->raddr);
487198825Sedwin		}
488198825Sedwin		INC_ARGCV();
489198825Sedwin	} else {
490198825Sedwin		SETLOPORT(portRange, 0);
491198825Sedwin		SETNUMPORTS(portRange, 1);
492198825Sedwin		r->raddr.s_addr = INADDR_ANY;
493198825Sedwin	}
494198825Sedwin	r->rport = GETLOPORT(portRange);
495198825Sedwin	r->rport_cnt = GETNUMPORTS(portRange);
496198825Sedwin
497198825Sedwin	/*
498198825Sedwin	 * Make sure port ranges match up, then add the redirect ports.
499198825Sedwin	 */
500198825Sedwin	if (numLocalPorts != r->pport_cnt)
501198825Sedwin	        errx(EX_DATAERR, "redirect_port:"
502198825Sedwin		    "port ranges must be equal in size");
503198825Sedwin
504198825Sedwin	/* Remote port range is allowed to be '0' which means all ports. */
505198825Sedwin	if (r->rport_cnt != numLocalPorts &&
506198825Sedwin	    (r->rport_cnt != 1 || r->rport != 0))
507198825Sedwin	        errx(EX_DATAERR, "redirect_port: remote port must"
508198825Sedwin		    "be 0 or equal to local port range in size");
509198825Sedwin
510198825Sedwin	/*
511198825Sedwin	 * Setup LSNAT server pool.
512198825Sedwin	 */
513198825Sedwin	if (lsnat) {
514198825Sedwin		sep = strtok(tmp_spool_buf, ",");
515198825Sedwin		while (sep != NULL) {
516198825Sedwin			tmp = (struct cfg_spool *)spool_buf;
517198825Sedwin			if (len < SOF_SPOOL)
518198825Sedwin				goto nospace;
519198825Sedwin			len -= SOF_SPOOL;
520198825Sedwin			space += SOF_SPOOL;
521198825Sedwin			if (StrToAddrAndPortRange(sep, &tmp->addr, protoName,
522198825Sedwin			    &portRange) != 0)
523198825Sedwin				errx(EX_DATAERR, "redirect_port:"
524198825Sedwin				    "invalid local port range");
525198825Sedwin			if (GETNUMPORTS(portRange) != 1)
526198825Sedwin				errx(EX_DATAERR, "redirect_port: local port"
527198825Sedwin				    "must be single in this context");
528198825Sedwin			tmp->port = GETLOPORT(portRange);
529198825Sedwin			r->spool_cnt++;
530198825Sedwin			/* Point to the next possible cfg_spool. */
531198825Sedwin			spool_buf = &spool_buf[SOF_SPOOL];
532198825Sedwin			sep = strtok(NULL, ",");
533198825Sedwin		}
534198825Sedwin	}
535198825Sedwin	return (space);
536198825Sedwinnospace:
537198825Sedwin	errx(EX_DATAERR, "redirect_port: buf is too small\n");
53867578Swollman}
5392742Swollman
540198825Sedwinstatic int
541198825Sedwinsetup_redir_proto(char *spool_buf, int len,
5422742Swollman		 int *_ac, char ***_av)
5432742Swollman{
5442742Swollman	char **av;
5452742Swollman	int ac, space;
5462742Swollman	struct protoent *protoent;
547198825Sedwin	struct cfg_redir *r;
548198825Sedwin
5492742Swollman	av = *_av;
5502742Swollman	ac = *_ac;
5512742Swollman	if (len >= SOF_REDIR) {
5522742Swollman		r = (struct cfg_redir *)spool_buf;
5532742Swollman		/* Skip cfg_redir at beginning of buf. */
554213312Sedwin		spool_buf = &spool_buf[SOF_REDIR];
555213312Sedwin		space = SOF_REDIR;
556198825Sedwin		len -= SOF_REDIR;
557198825Sedwin	} else
558198825Sedwin		goto nospace;
5592742Swollman	r->mode = REDIR_PROTO;
5602742Swollman	/*
561198825Sedwin	 * Extract protocol.
562198825Sedwin	 */
56358787Sru	if (ac == 0)
5642742Swollman		errx(EX_DATAERR, "redirect_proto: missing protocol");
56530711Swollman
56630711Swollman	protoent = getprotobyname(*av);
56743014Swollman	if (protoent == NULL)
56830711Swollman		errx(EX_DATAERR, "redirect_proto: unknown protocol %s", *av);
569158421Swollman	else
57043543Swollman		r->proto = protoent->p_proto;
57143543Swollman
57243543Swollman	INC_ARGCV();
573206868Sedwin
574206868Sedwin	/*
575206868Sedwin	 * Extract local address.
576206868Sedwin	 */
577206868Sedwin	if (ac == 0)
578206868Sedwin		errx(EX_DATAERR, "redirect_proto: missing local address");
579206868Sedwin	else
580206868Sedwin		StrToAddr(*av, &r->laddr);
581206868Sedwin
582206868Sedwin	INC_ARGCV();
583206868Sedwin
584206868Sedwin	/*
585206868Sedwin	 * Extract optional public address.
586206868Sedwin	 */
587206868Sedwin	if (ac == 0) {
588206868Sedwin		r->paddr.s_addr = INADDR_ANY;
589206868Sedwin		r->raddr.s_addr = INADDR_ANY;
590206868Sedwin	} else {
591206868Sedwin		/* see above in setup_redir_port() */
592206868Sedwin		if (!isalpha(**av)) {
593206868Sedwin			StrToAddr(*av, &r->paddr);
594206868Sedwin			INC_ARGCV();
59530711Swollman
59630711Swollman			/*
59730711Swollman			 * Extract optional remote address.
59830711Swollman			 */
59930711Swollman			/* see above in setup_redir_port() */
60030711Swollman			if (ac!=0 && !isalpha(**av)) {
60130711Swollman				StrToAddr(*av, &r->raddr);
60230711Swollman				INC_ARGCV();
60330711Swollman			}
60430711Swollman		}
605206868Sedwin	}
606206868Sedwin	return (space);
607206868Sedwinnospace:
60830711Swollman	errx(EX_DATAERR, "redirect_proto: buf is too small\n");
60986222Swollman}
61030711Swollman
61130711Swollmanstatic void
61293799Swollmanprint_nat_config(unsigned char *buf)
6132742Swollman{
61493799Swollman	struct cfg_nat *n;
61593799Swollman	int i, cnt, flag, off;
61693799Swollman	struct cfg_redir *t;
61793799Swollman	struct cfg_spool *s;
61893799Swollman	struct protoent *p;
61993799Swollman
62093799Swollman	n = (struct cfg_nat *)buf;
62193799Swollman	flag = 1;
62293799Swollman	off  = sizeof(*n);
62393799Swollman	printf("ipfw nat %u config", n->id);
62493799Swollman	if (strlen(n->if_name) != 0)
62593799Swollman		printf(" if %s", n->if_name);
62693799Swollman	else if (n->ip.s_addr != 0)
62793799Swollman		printf(" ip %s", inet_ntoa(n->ip));
6282742Swollman	while (n->mode != 0) {
62993799Swollman		if (n->mode & PKT_ALIAS_LOG) {
63093799Swollman			printf(" log");
63119878Swollman			n->mode &= ~PKT_ALIAS_LOG;
6322742Swollman		} else if (n->mode & PKT_ALIAS_DENY_INCOMING) {
6332742Swollman			printf(" deny_in");
6342742Swollman			n->mode &= ~PKT_ALIAS_DENY_INCOMING;
6352742Swollman		} else if (n->mode & PKT_ALIAS_SAME_PORTS) {
6362742Swollman			printf(" same_ports");
6372742Swollman			n->mode &= ~PKT_ALIAS_SAME_PORTS;
63819878Swollman		} else if (n->mode & PKT_ALIAS_UNREGISTERED_ONLY) {
6392742Swollman			printf(" unreg_only");
64019878Swollman			n->mode &= ~PKT_ALIAS_UNREGISTERED_ONLY;
6412742Swollman		} else if (n->mode & PKT_ALIAS_RESET_ON_ADDR_CHANGE) {
64219878Swollman			printf(" reset");
6432742Swollman			n->mode &= ~PKT_ALIAS_RESET_ON_ADDR_CHANGE;
6442742Swollman		} else if (n->mode & PKT_ALIAS_REVERSE) {
64543543Swollman			printf(" reverse");
64643543Swollman			n->mode &= ~PKT_ALIAS_REVERSE;
6472742Swollman		} else if (n->mode & PKT_ALIAS_PROXY_ONLY) {
6482742Swollman			printf(" proxy_only");
64943543Swollman			n->mode &= ~PKT_ALIAS_PROXY_ONLY;
65058787Sru		}
65143543Swollman	}
6522742Swollman	/* Print all the redirect's data configuration. */
65367578Swollman	for (cnt = 0; cnt < n->redir_cnt; cnt++) {
65467578Swollman		t = (struct cfg_redir *)&buf[off];
65567578Swollman		off += SOF_REDIR;
65667578Swollman		switch (t->mode) {
6572742Swollman		case REDIR_ADDR:
658149514Swollman			printf(" redirect_addr");
6599908Swollman			if (t->spool_cnt == 0)
6609908Swollman				printf(" %s", inet_ntoa(t->laddr));
6619908Swollman			else
66214343Swollman				for (i = 0; i < t->spool_cnt; i++) {
66314343Swollman					s = (struct cfg_spool *)&buf[off];
664149514Swollman					if (i)
66520094Swollman						printf(",");
66620094Swollman					else
66720094Swollman						printf(" ");
668136638Swollman					printf("%s", inet_ntoa(s->addr));
669136638Swollman					off += SOF_SPOOL;
670149514Swollman				}
671136638Swollman			printf(" %s", inet_ntoa(t->paddr));
672136638Swollman			break;
673136638Swollman		case REDIR_PORT:
674136638Swollman			p = getprotobynumber(t->proto);
675136638Swollman			printf(" redirect_port %s ", p->p_name);
676136638Swollman			if (!t->spool_cnt) {
677136638Swollman				printf("%s:%u", inet_ntoa(t->laddr), t->lport);
678153670Swollman				if (t->pport_cnt > 1)
679153670Swollman					printf("-%u", t->lport +
680153670Swollman					    t->pport_cnt - 1);
681153670Swollman			} else
682153670Swollman				for (i=0; i < t->spool_cnt; i++) {
683153670Swollman					s = (struct cfg_spool *)&buf[off];
684153670Swollman					if (i)
685153670Swollman						printf(",");
686153670Swollman					printf("%s:%u", inet_ntoa(s->addr),
687153670Swollman					    s->port);
688153670Swollman					off += SOF_SPOOL;
6892742Swollman				}
6902742Swollman
69119878Swollman			printf(" ");
69219878Swollman			if (t->paddr.s_addr)
69319878Swollman				printf("%s:", inet_ntoa(t->paddr));
69419878Swollman			printf("%u", t->pport);
69520094Swollman			if (!t->spool_cnt && t->pport_cnt > 1)
69620094Swollman				printf("-%u", t->pport + t->pport_cnt - 1);
69720094Swollman
69843543Swollman			if (t->raddr.s_addr) {
699136638Swollman				printf(" %s", inet_ntoa(t->raddr));
700153670Swollman				if (t->rport) {
701153670Swollman					printf(":%u", t->rport);
7022742Swollman					if (!t->spool_cnt && t->rport_cnt > 1)
70358787Sru						printf("-%u", t->rport +
70475267Swollman						    t->rport_cnt - 1);
705169811Swollman				}
706169811Swollman			}
70775267Swollman			break;
70875267Swollman		case REDIR_PROTO:
70975267Swollman			p = getprotobynumber(t->proto);
71075267Swollman			printf(" redirect_proto %s %s", p->p_name,
71175267Swollman			    inet_ntoa(t->laddr));
71275267Swollman			if (t->paddr.s_addr != 0) {
71375267Swollman				printf(" %s", inet_ntoa(t->paddr));
71475267Swollman				if (t->raddr.s_addr)
71575267Swollman					printf(" %s", inet_ntoa(t->raddr));
71675267Swollman			}
71775267Swollman			break;
71875267Swollman		default:
71975267Swollman			errx(EX_DATAERR, "unknown redir mode");
72075267Swollman			break;
72175267Swollman		}
72275267Swollman	}
72375267Swollman	printf("\n");
72475267Swollman}
72575267Swollman
72675267Swollmanvoid
72775267Swollmanipfw_config_nat(int ac, char **av)
72858787Sru{
72958787Sru	struct cfg_nat *n;              /* Nat instance configuration. */
730149514Swollman	int i, len, off, tok;
731169811Swollman	char *id, buf[NAT_BUF_LEN]; 	/* Buffer for serialized data. */
732149514Swollman
73386222Swollman	len = NAT_BUF_LEN;
734149514Swollman	/* Offset in buf: save space for n at the beginning. */
73558787Sru	off = sizeof(*n);
7362742Swollman	memset(buf, 0, sizeof(buf));
7372742Swollman	n = (struct cfg_nat *)buf;
738177591Sedwin
73919878Swollman	av++; ac--;
74019878Swollman	/* Nat id. */
7412742Swollman	if (ac && isdigit(**av)) {
7422742Swollman		id = *av;
7432742Swollman		i = atoi(*av);
744177591Sedwin		ac--; av++;
7452742Swollman		n->id = i;
7462742Swollman	} else
7472742Swollman		errx(EX_DATAERR, "missing nat id");
7482742Swollman	if (ac == 0)
7492742Swollman		errx(EX_DATAERR, "missing option");
75086222Swollman
751158421Swollman	while (ac > 0) {
75286222Swollman		tok = match_token(nat_params, *av);
75386222Swollman		ac--; av++;
75486222Swollman		switch (tok) {
75586222Swollman		case TOK_IP:
75686222Swollman			if (ac == 0)
757169811Swollman				errx(EX_DATAERR, "missing option");
758169811Swollman			if (!inet_aton(av[0], &(n->ip)))
759169811Swollman				errx(EX_DATAERR, "bad ip address ``%s''",
760169811Swollman				    av[0]);
761169811Swollman			ac--; av++;
762169811Swollman			break;
763169811Swollman		case TOK_IF:
764169811Swollman			if (ac == 0)
765169811Swollman				errx(EX_DATAERR, "missing option");
766169811Swollman			set_addr_dynamic(av[0], n);
767169811Swollman			ac--; av++;
768169811Swollman			break;
769169811Swollman		case TOK_ALOG:
7702742Swollman			n->mode |= PKT_ALIAS_LOG;
7712742Swollman			break;
772158421Swollman		case TOK_DENY_INC:
77358787Sru			n->mode |= PKT_ALIAS_DENY_INCOMING;
77458787Sru			break;
77519878Swollman		case TOK_SAME_PORTS:
77686222Swollman			n->mode |= PKT_ALIAS_SAME_PORTS;
777169811Swollman			break;
77886222Swollman		case TOK_UNREG_ONLY:
77986222Swollman			n->mode |= PKT_ALIAS_UNREGISTERED_ONLY;
78086222Swollman			break;
78186222Swollman		case TOK_RESET_ADDR:
78286222Swollman			n->mode |= PKT_ALIAS_RESET_ON_ADDR_CHANGE;
78386222Swollman			break;
78486222Swollman		case TOK_ALIAS_REV:
785169811Swollman			n->mode |= PKT_ALIAS_REVERSE;
78686222Swollman			break;
78786222Swollman		case TOK_PROXY_ONLY:
78886222Swollman			n->mode |= PKT_ALIAS_PROXY_ONLY;
78986222Swollman			break;
79086222Swollman			/*
79193799Swollman			 * All the setup_redir_* functions work directly in the final
79219878Swollman			 * buffer, see above for details.
79386222Swollman			 */
794169811Swollman		case TOK_REDIR_ADDR:
79586222Swollman		case TOK_REDIR_PORT:
7962742Swollman		case TOK_REDIR_PROTO:
797169811Swollman			switch (tok) {
7982742Swollman			case TOK_REDIR_ADDR:
79986222Swollman				i = setup_redir_addr(&buf[off], len, &ac, &av);
8002742Swollman				break;
8012742Swollman			case TOK_REDIR_PORT:
802114173Swollman				i = setup_redir_port(&buf[off], len, &ac, &av);
803114173Swollman				break;
804114173Swollman			case TOK_REDIR_PROTO:
805114173Swollman				i = setup_redir_proto(&buf[off], len, &ac, &av);
80621217Swollman				break;
807114173Swollman			}
808114173Swollman			n->redir_cnt++;
80921217Swollman			off += i;
810114173Swollman			len -= i;
811114173Swollman			break;
812114173Swollman		default:
813114173Swollman			errx(EX_DATAERR, "unrecognised option ``%s''", av[-1]);
814114173Swollman		}
815114173Swollman	}
816114173Swollman
817114173Swollman	i = do_cmd(IP_FW_NAT_CFG, buf, off);
818114173Swollman	if (i)
819114173Swollman		err(1, "setsockopt(%s)", "IP_FW_NAT_CFG");
820114173Swollman
821114173Swollman	if (!co.do_quiet) {
822114173Swollman		/* After every modification, we show the resultant rule. */
823114173Swollman		int _ac = 3;
824114173Swollman		char *_av[] = {"show", "config", id};
825114173Swollman		ipfw_show_nat(_ac, _av);
826114173Swollman	}
827114173Swollman}
828114173Swollman
829114173Swollman
830114173Swollmanvoid
831114173Swollmanipfw_show_nat(int ac, char **av)
832149514Swollman{
833149514Swollman	struct cfg_nat *n;
834149514Swollman	struct cfg_redir *e;
835149514Swollman	int cmd, i, nbytes, do_cfg, do_rule, frule, lrule, nalloc, size;
836149514Swollman	int nat_cnt, redir_cnt, r;
837149514Swollman	uint8_t *data, *p;
838149514Swollman	char *endptr;
839158421Swollman
840158421Swollman	do_rule = 0;
841149514Swollman	nalloc = 1024;
842149514Swollman	size = 0;
843149514Swollman	data = NULL;
844149514Swollman	frule = 0;
84521217Swollman	lrule = IPFW_DEFAULT_RULE; /* max ipfw rule number */
846149514Swollman	ac--; av++;
847149514Swollman
848149514Swollman	if (co.test_only)
849149514Swollman		return;
850149514Swollman
851149514Swollman	/* Parse parameters. */
852149514Swollman	for (cmd = IP_FW_NAT_GET_LOG, do_cfg = 0; ac != 0; ac--, av++) {
853149514Swollman		if (!strncmp(av[0], "config", strlen(av[0]))) {
854149514Swollman			cmd = IP_FW_NAT_GET_CONFIG, do_cfg = 1;
855149514Swollman			continue;
856149514Swollman		}
857149514Swollman		/* Convert command line rule #. */
858149514Swollman		frule = lrule = strtoul(av[0], &endptr, 10);
859149514Swollman		if (*endptr == '-')
860158421Swollman			lrule = strtoul(endptr+1, &endptr, 10);
861158421Swollman		if (lrule == 0)
862158421Swollman			err(EX_USAGE, "invalid rule number: %s", av[0]);
863158421Swollman		do_rule = 1;
864172479Sedwin	}
865172479Sedwin
866172479Sedwin	nbytes = nalloc;
867172479Sedwin	while (nbytes >= nalloc) {
868172479Sedwin		nalloc = nalloc * 2;
869174242Sedwin		nbytes = nalloc;
870174242Sedwin		data = safe_realloc(data, nbytes);
871174242Sedwin		if (do_cmd(cmd, data, (uintptr_t)&nbytes) < 0)
872174242Sedwin			err(EX_OSERR, "getsockopt(IP_FW_GET_%s)",
873174242Sedwin			    (cmd == IP_FW_NAT_GET_LOG) ? "LOG" : "CONFIG");
874174242Sedwin	}
875174242Sedwin	if (nbytes == 0)
876174242Sedwin		exit(0);
877174242Sedwin	if (do_cfg) {
8782742Swollman		nat_cnt = *((int *)data);
879114173Swollman		for (i = sizeof(nat_cnt); nat_cnt; nat_cnt--) {
880114173Swollman			n = (struct cfg_nat *)&data[i];
881114173Swollman			if (frule <= n->id && lrule >= n->id)
882114173Swollman				print_nat_config(&data[i]);
883114173Swollman			i += sizeof(struct cfg_nat);
884114173Swollman			for (redir_cnt = 0; redir_cnt < n->redir_cnt; redir_cnt++) {
885114173Swollman				e = (struct cfg_redir *)&data[i];
886114173Swollman				i += sizeof(struct cfg_redir) + e->spool_cnt *
887114173Swollman				    sizeof(struct cfg_spool);
888114173Swollman			}
889114173Swollman		}
890114173Swollman	} else {
891114173Swollman		for (i = 0; 1; i += LIBALIAS_BUF_SIZE + sizeof(int)) {
892114173Swollman			p = &data[i];
893114173Swollman			if (p == data + nbytes)
894114173Swollman				break;
895114173Swollman			bcopy(p, &r, sizeof(int));
896158421Swollman			if (do_rule) {
897158421Swollman				if (!(frule <= r && lrule >= r))
898172479Sedwin					continue;
899172479Sedwin			}
900172479Sedwin			printf("nat %u: %s\n", r, p+sizeof(int));
901172479Sedwin		}
902172479Sedwin	}
903172479Sedwin}
904172479Sedwin