nat.c revision 220835
160786Sps/*
2240121Sdelphij * Copyright (c) 2002-2003 Luigi Rizzo
360786Sps * Copyright (c) 1996 Alex Nash, Paul Traina, Poul-Henning Kamp
460786Sps * Copyright (c) 1994 Ugen J.S.Antsilevich
560786Sps *
660786Sps * Idea and grammar partially left from:
7240121Sdelphij * Copyright (c) 1993 Daniel Boulet
860786Sps *
960786Sps * Redistribution and use in source forms, with and without modification,
1060786Sps * are permitted provided that this entire comment appears intact.
1160786Sps *
1260786Sps * Redistribution in binary form may occur without any restrictions.
1360786Sps * Obviously, it would be nice if you gave credit where credit is due
1460786Sps * but requiring it would be too onerous.
1560786Sps *
1660786Sps * This software is provided ``AS IS'' without any warranties of any kind.
1760786Sps *
1860786Sps * NEW command line interface for IP firewall facility
1960786Sps *
2060786Sps * $FreeBSD: head/sbin/ipfw/nat.c 220835 2011-04-19 15:03:12Z glebius $
2160786Sps *
2260786Sps * In-kernel nat support
2360786Sps */
2460786Sps
2563128Sps#include <sys/types.h>
2660786Sps#include <sys/socket.h>
2760786Sps#include <sys/sysctl.h>
2860786Sps
29161475Sdelphij#include "ipfw2.h"
3060786Sps
3160786Sps#include <ctype.h>
3260786Sps#include <err.h>
3360786Sps#include <netdb.h>
3460786Sps#include <stdio.h>
3560786Sps#include <stdlib.h>
3660786Sps#include <string.h>
3760786Sps#include <sysexits.h>
3860786Sps
3960786Sps#define IPFW_INTERNAL	/* Access to protected structures in ip_fw.h. */
4060786Sps
4160786Sps#include <net/if.h>
4260786Sps#include <net/if_dl.h>
4360786Sps#include <net/route.h> /* def. of struct route */
4460786Sps#include <netinet/in.h>
4560786Sps#include <netinet/ip_fw.h>
4660786Sps#include <arpa/inet.h>
4760786Sps#include <alias.h>
48161475Sdelphij
4960786Spsstatic struct _s_x nat_params[] = {
5060786Sps	{ "ip",			TOK_IP },
5160786Sps	{ "if",			TOK_IF },
5260786Sps 	{ "log",		TOK_ALOG },
53161475Sdelphij 	{ "deny_in",		TOK_DENY_INC },
5460786Sps 	{ "same_ports",		TOK_SAME_PORTS },
55191930Sdelphij 	{ "unreg_only",		TOK_UNREG_ONLY },
5660786Sps 	{ "reset",		TOK_RESET_ADDR },
5760786Sps 	{ "reverse",		TOK_ALIAS_REV },
5860786Sps 	{ "proxy_only",		TOK_PROXY_ONLY },
5960786Sps	{ "redirect_addr",	TOK_REDIR_ADDR },
6060786Sps	{ "redirect_port",	TOK_REDIR_PORT },
6160786Sps	{ "redirect_proto",	TOK_REDIR_PROTO },
62191930Sdelphij 	{ NULL, 0 }	/* terminator */
6360786Sps};
6460786Sps
6560786Sps
6660786Sps/*
6760786Sps * Search for interface with name "ifn", and fill n accordingly:
6860786Sps *
6960786Sps * n->ip	ip address of interface "ifn"
7060786Sps * n->if_name   copy of interface name "ifn"
7160786Sps */
7260786Spsstatic void
7360786Spsset_addr_dynamic(const char *ifn, struct cfg_nat *n)
7460786Sps{
7560786Sps	size_t needed;
7660786Sps	int mib[6];
7760786Sps	char *buf, *lim, *next;
7860786Sps	struct if_msghdr *ifm;
79191930Sdelphij	struct ifa_msghdr *ifam;
80191930Sdelphij	struct sockaddr_dl *sdl;
81191930Sdelphij	struct sockaddr_in *sin;
82161475Sdelphij	int ifIndex, ifMTU;
83161475Sdelphij
84161475Sdelphij	mib[0] = CTL_NET;
85161475Sdelphij	mib[1] = PF_ROUTE;
86161475Sdelphij	mib[2] = 0;
87161475Sdelphij	mib[3] = AF_INET;
88161475Sdelphij	mib[4] = NET_RT_IFLIST;
89161475Sdelphij	mib[5] = 0;
90161475Sdelphij/*
91161475Sdelphij * Get interface data.
92161475Sdelphij */
93161475Sdelphij	if (sysctl(mib, 6, NULL, &needed, NULL, 0) == -1)
94161475Sdelphij		err(1, "iflist-sysctl-estimate");
95161475Sdelphij	buf = safe_calloc(1, needed);
96161475Sdelphij	if (sysctl(mib, 6, buf, &needed, NULL, 0) == -1)
97161475Sdelphij		err(1, "iflist-sysctl-get");
98161475Sdelphij	lim = buf + needed;
99161475Sdelphij/*
10060786Sps * Loop through interfaces until one with
101191930Sdelphij * given name is found. This is done to
102191930Sdelphij * find correct interface index for routing
103191930Sdelphij * message processing.
104161475Sdelphij */
105161475Sdelphij	ifIndex	= 0;
106161475Sdelphij	next = buf;
107191930Sdelphij	while (next < lim) {
108191930Sdelphij		ifm = (struct if_msghdr *)next;
109161475Sdelphij		next += ifm->ifm_msglen;
110161475Sdelphij		if (ifm->ifm_version != RTM_VERSION) {
111161475Sdelphij			if (co.verbose)
112161475Sdelphij				warnx("routing message version %d "
113161475Sdelphij				    "not understood", ifm->ifm_version);
114161475Sdelphij			continue;
115161475Sdelphij		}
116191930Sdelphij		if (ifm->ifm_type == RTM_IFINFO) {
117191930Sdelphij			sdl = (struct sockaddr_dl *)(ifm + 1);
118161475Sdelphij			if (strlen(ifn) == sdl->sdl_nlen &&
119161475Sdelphij			    strncmp(ifn, sdl->sdl_data, sdl->sdl_nlen) == 0) {
120161475Sdelphij				ifIndex = ifm->ifm_index;
121191930Sdelphij				ifMTU = ifm->ifm_data.ifi_mtu;
122161475Sdelphij				break;
123161475Sdelphij			}
124161475Sdelphij		}
125161475Sdelphij	}
126161475Sdelphij	if (!ifIndex)
127161475Sdelphij		errx(1, "unknown interface name %s", ifn);
128161475Sdelphij/*
129191930Sdelphij * Get interface address.
130191930Sdelphij */
131191930Sdelphij	sin = NULL;
13260786Sps	while (next < lim) {
13360786Sps		ifam = (struct ifa_msghdr *)next;
13460786Sps		next += ifam->ifam_msglen;
13560786Sps		if (ifam->ifam_version != RTM_VERSION) {
13660786Sps			if (co.verbose)
13760786Sps				warnx("routing message version %d "
13860786Sps				    "not understood", ifam->ifam_version);
13960786Sps			continue;
140191930Sdelphij		}
141191930Sdelphij		if (ifam->ifam_type != RTM_NEWADDR)
142191930Sdelphij			break;
14360786Sps		if (ifam->ifam_addrs & RTA_IFA) {
14460786Sps			int i;
14560786Sps			char *cp = (char *)(ifam + 1);
14660786Sps
14760786Sps			for (i = 1; i < RTA_IFA; i <<= 1) {
14860786Sps				if (ifam->ifam_addrs & i)
14960786Sps					cp += SA_SIZE((struct sockaddr *)cp);
15060786Sps			}
15160786Sps			if (((struct sockaddr *)cp)->sa_family == AF_INET) {
15260786Sps				sin = (struct sockaddr_in *)cp;
15360786Sps				break;
15460786Sps			}
155161475Sdelphij		}
15660786Sps	}
157161475Sdelphij	if (sin == NULL)
158161475Sdelphij		errx(1, "%s: cannot get interface address", ifn);
159161475Sdelphij
160161475Sdelphij	n->ip = sin->sin_addr;
161161475Sdelphij	strncpy(n->if_name, ifn, IF_NAMESIZE);
162161475Sdelphij
16360786Sps	free(buf);
16460786Sps}
165161475Sdelphij
166161475Sdelphij/*
16760786Sps * XXX - The following functions, macros and definitions come from natd.c:
16860786Sps * it would be better to move them outside natd.c, in a file
16960786Sps * (redirect_support.[ch]?) shared by ipfw and natd, but for now i can live
17060786Sps * with it.
171161475Sdelphij */
172161475Sdelphij
17360786Sps/*
17460786Sps * Definition of a port range, and macros to deal with values.
17560786Sps * FORMAT:  HI 16-bits == first port in range, 0 == all ports.
17660786Sps *	  LO 16-bits == number of ports in range
17760786Sps * NOTES:   - Port values are not stored in network byte order.
17860786Sps */
17963128Sps
18060786Sps#define port_range u_long
18160786Sps
18260786Sps#define GETLOPORT(x)	((x) >> 0x10)
183221715Sdelphij#define GETNUMPORTS(x)	((x) & 0x0000ffff)
184221715Sdelphij#define GETHIPORT(x)	(GETLOPORT((x)) + GETNUMPORTS((x)))
185221715Sdelphij
186221715Sdelphij/* Set y to be the low-port value in port_range variable x. */
187221715Sdelphij#define SETLOPORT(x,y)   ((x) = ((x) & 0x0000ffff) | ((y) << 0x10))
18860786Sps
18960786Sps/* Set y to be the number of ports in port_range variable x. */
19060786Sps#define SETNUMPORTS(x,y) ((x) = ((x) & 0xffff0000) | (y))
19160786Sps
19260786Spsstatic void
19360786SpsStrToAddr (const char* str, struct in_addr* addr)
19460786Sps{
195161475Sdelphij	struct hostent* hp;
19660786Sps
19760786Sps	if (inet_aton (str, addr))
19860786Sps		return;
19960786Sps
20060786Sps	hp = gethostbyname (str);
20160786Sps	if (!hp)
20260786Sps		errx (1, "unknown host %s", str);
203195941Sdelphij
204191930Sdelphij	memcpy (addr, hp->h_addr, sizeof (struct in_addr));
205191930Sdelphij}
206191930Sdelphij
207191930Sdelphijstatic int
208191930SdelphijStrToPortRange (const char* str, const char* proto, port_range *portRange)
209191930Sdelphij{
210191930Sdelphij	char*	   sep;
211191930Sdelphij	struct servent*	sp;
212191930Sdelphij	char*		end;
213191930Sdelphij	u_short	 loPort;
214191930Sdelphij	u_short	 hiPort;
215191930Sdelphij
216191930Sdelphij	/* First see if this is a service, return corresponding port if so. */
217191930Sdelphij	sp = getservbyname (str,proto);
218191930Sdelphij	if (sp) {
219191930Sdelphij		SETLOPORT(*portRange, ntohs(sp->s_port));
22060786Sps		SETNUMPORTS(*portRange, 1);
22160786Sps		return 0;
22260786Sps	}
22360786Sps
22460786Sps	/* Not a service, see if it's a single port or port range. */
22560786Sps	sep = strchr (str, '-');
22660786Sps	if (sep == NULL) {
22760786Sps		SETLOPORT(*portRange, strtol(str, &end, 10));
22860786Sps		if (end != str) {
22960786Sps			/* Single port. */
23060786Sps			SETNUMPORTS(*portRange, 1);
23160786Sps			return 0;
23260786Sps		}
23360786Sps
23460786Sps		/* Error in port range field. */
23560786Sps		errx (EX_DATAERR, "%s/%s: unknown service", str, proto);
23660786Sps	}
23760786Sps
23860786Sps	/* Port range, get the values and sanity check. */
23960786Sps	sscanf (str, "%hu-%hu", &loPort, &hiPort);
24060786Sps	SETLOPORT(*portRange, loPort);
24160786Sps	SETNUMPORTS(*portRange, 0);	/* Error by default */
24260786Sps	if (loPort <= hiPort)
24360786Sps		SETNUMPORTS(*portRange, hiPort - loPort + 1);
24460786Sps
24560786Sps	if (GETNUMPORTS(*portRange) == 0)
24660786Sps		errx (EX_DATAERR, "invalid port range %s", str);
24760786Sps
24860786Sps	return 0;
24960786Sps}
25060786Sps
25160786Spsstatic int
252191930SdelphijStrToProto (const char* str)
25360786Sps{
25460786Sps	if (!strcmp (str, "tcp"))
255161475Sdelphij		return IPPROTO_TCP;
25660786Sps
257191930Sdelphij	if (!strcmp (str, "udp"))
25860786Sps		return IPPROTO_UDP;
25960786Sps
26060786Sps	if (!strcmp (str, "sctp"))
26160786Sps		return IPPROTO_SCTP;
26260786Sps	errx (EX_DATAERR, "unknown protocol %s. Expected sctp, tcp or udp", str);
26360786Sps}
264191930Sdelphij
26560786Spsstatic int
26660786SpsStrToAddrAndPortRange (const char* str, struct in_addr* addr, char* proto,
26760786Sps			port_range *portRange)
26860786Sps{
26960786Sps	char*	ptr;
27060786Sps
27160786Sps	ptr = strchr (str, ':');
27260786Sps	if (!ptr)
27360786Sps		errx (EX_DATAERR, "%s is missing port number", str);
27460786Sps
27560786Sps	*ptr = '\0';
27660786Sps	++ptr;
27760786Sps
27860786Sps	StrToAddr (str, addr);
279191930Sdelphij	return StrToPortRange (ptr, proto, portRange);
280191930Sdelphij}
281191930Sdelphij
28260786Sps/* End of stuff taken from natd.c. */
28360786Sps
28460786Sps/*
28560786Sps * The next 3 functions add support for the addr, port and proto redirect and
28660786Sps * their logic is loosely based on SetupAddressRedirect(), SetupPortRedirect()
28760786Sps * and SetupProtoRedirect() from natd.c.
28860786Sps *
28960786Sps * Every setup_* function fills at least one redirect entry
29060786Sps * (struct cfg_redir) and zero or more server pool entry (struct cfg_spool)
29160786Sps * in buf.
29260786Sps *
29360786Sps * The format of data in buf is:
29460786Sps *
29560786Sps *     cfg_nat    cfg_redir    cfg_spool    ......  cfg_spool
29660786Sps *
29760786Sps *    -------------------------------------        ------------
29860786Sps *   |          | .....X ... |          |         |           |  .....
29960786Sps *    ------------------------------------- ...... ------------
30060786Sps *                     ^
30160786Sps *                spool_cnt       n=0       ......   n=(X-1)
30260786Sps *
30360786Sps * len points to the amount of available space in buf
30460786Sps * space counts the memory consumed by every function
30560786Sps *
30660786Sps * XXX - Every function get all the argv params so it
30760786Sps * has to check, in optional parameters, that the next
30860786Sps * args is a valid option for the redir entry and not
30960786Sps * another token. Only redir_port and redir_proto are
31060786Sps * affected by this.
31160786Sps */
31260786Sps
31360786Spsstatic int
31460786Spsestimate_redir_addr(int *ac, char ***av)
31560786Sps{
31660786Sps	size_t space = sizeof(struct cfg_redir);
31760786Sps	char *sep;
31860786Sps
31960786Sps	if ((sep = strtok(**av, ",")) != NULL) {
32060786Sps		space += sizeof(struct cfg_spool);
32160786Sps		while ((sep = strtok(NULL, ",")) != NULL)
32260786Sps			space += sizeof(struct cfg_spool);
323191930Sdelphij	}
32460786Sps
32560786Sps	return (space);
32660786Sps}
32760786Sps
32860786Spsstatic int
32960786Spssetup_redir_addr(char *buf, int *ac, char ***av)
33060786Sps{
33160786Sps	struct cfg_redir *r;
33260786Sps	char *sep;
333191930Sdelphij	size_t space;
33460786Sps
33560786Sps	r = (struct cfg_redir *)buf;
33660786Sps	r->mode = REDIR_ADDR;
33760786Sps	/* Skip cfg_redir at beginning of buf. */
33860786Sps	buf = &buf[sizeof(struct cfg_redir)];
33960786Sps	space = sizeof(struct cfg_redir);
34060786Sps
34160786Sps	/* Extract local address. */
34260786Sps	if ((sep = strtok(**av, ",")) != NULL) {
34360786Sps		struct cfg_spool *spool;
34460786Sps
34560786Sps		/* Setup LSNAT server pool. */
34660786Sps		r->laddr.s_addr = INADDR_NONE;
347191930Sdelphij		while (sep != NULL) {
34860786Sps			spool = (struct cfg_spool *)buf;
34960786Sps			space += sizeof(struct cfg_spool);
35060786Sps			StrToAddr(sep, &spool->addr);
35160786Sps			spool->port = ~0;
35260786Sps			r->spool_cnt++;
35360786Sps			/* Point to the next possible cfg_spool. */
354161475Sdelphij			buf = &buf[sizeof(struct cfg_spool)];
355161475Sdelphij			sep = strtok(NULL, ",");
35660786Sps		}
35760786Sps	} else
35860786Sps		StrToAddr(**av, &r->laddr);
35960786Sps	(*av)++; (*ac)--;
36060786Sps
36160786Sps	/* Extract public address. */
36260786Sps	StrToAddr(**av, &r->paddr);
36360786Sps	(*av)++; (*ac)--;
36460786Sps
36560786Sps	return (space);
36660786Sps}
36760786Sps
36860786Spsstatic int
36960786Spsestimate_redir_port(int *ac, char ***av)
37060786Sps{
371161475Sdelphij	size_t space = sizeof(struct cfg_redir);
372161475Sdelphij	char *sep;
373161475Sdelphij
374161475Sdelphij	if ((sep = strtok(**av, ",")) != NULL) {
375161475Sdelphij		space += sizeof(struct cfg_spool);
376161475Sdelphij		while ((sep = strtok(NULL, ",")) != NULL)
37760786Sps			space += sizeof(struct cfg_spool);
37860786Sps	}
37960786Sps
380161475Sdelphij	return (space);
381161475Sdelphij}
38260786Sps
38360786Spsstatic int
38460786Spssetup_redir_port(char *buf, int *ac, char ***av)
38560786Sps{
38660786Sps	struct cfg_redir *r;
38760786Sps	char *sep, *protoName, *lsnat = NULL;
38863128Sps	size_t space;
38960786Sps	u_short numLocalPorts;
39060786Sps	port_range portRange;
39160786Sps
39260786Sps	numLocalPorts = 0;
39360786Sps
394161475Sdelphij	r = (struct cfg_redir *)buf;
395161475Sdelphij	r->mode = REDIR_PORT;
396161475Sdelphij	/* Skip cfg_redir at beginning of buf. */
397161475Sdelphij	buf = &buf[sizeof(struct cfg_redir)];
398161475Sdelphij	space = sizeof(struct cfg_redir);
399161475Sdelphij
400161475Sdelphij	/*
40160786Sps	 * Extract protocol.
40260786Sps	 */
40360786Sps	r->proto = StrToProto(**av);
40460786Sps	protoName = **av;
405195941Sdelphij	(*av)++; (*ac)--;
40660786Sps
407191930Sdelphij	/*
408191930Sdelphij	 * Extract local address.
409191930Sdelphij	 */
410191930Sdelphij	if ((sep = strchr(**av, ',')) != NULL) {
411191930Sdelphij		r->laddr.s_addr = INADDR_NONE;
412191930Sdelphij		r->lport = ~0;
413191930Sdelphij		numLocalPorts = 1;
414191930Sdelphij		lsnat = **av;
415191930Sdelphij	} else {
416191930Sdelphij		/*
417191930Sdelphij		 * The sctp nat does not allow the port numbers to be mapped to
418240121Sdelphij		 * new port numbers. Therefore, no ports are to be specified
419191930Sdelphij		 * in the target port field.
420191930Sdelphij		 */
421191930Sdelphij		if (r->proto == IPPROTO_SCTP) {
42260786Sps			if (strchr(**av, ':'))
42360786Sps				errx(EX_DATAERR, "redirect_port:"
42460786Sps				    "port numbers do not change in sctp, so do "
42560786Sps				    "not specify them as part of the target");
42660786Sps			else
42760786Sps				StrToAddr(**av, &r->laddr);
42860786Sps		} else {
42960786Sps			if (StrToAddrAndPortRange(**av, &r->laddr, protoName,
43060786Sps			    &portRange) != 0)
43160786Sps				errx(EX_DATAERR, "redirect_port: "
43260786Sps				    "invalid local port range");
43360786Sps
43460786Sps			r->lport = GETLOPORT(portRange);
43560786Sps			numLocalPorts = GETNUMPORTS(portRange);
43660786Sps		}
43760786Sps	}
43860786Sps	(*av)++; (*ac)--;
43960786Sps
44060786Sps	/*
44160786Sps	 * Extract public port and optionally address.
44260786Sps	 */
44360786Sps	if ((sep = strchr(**av, ':')) != NULL) {
44460786Sps		if (StrToAddrAndPortRange(**av, &r->paddr, protoName,
44560786Sps		    &portRange) != 0)
44660786Sps			errx(EX_DATAERR, "redirect_port: "
44760786Sps			    "invalid public port range");
44860786Sps	} else {
44960786Sps		r->paddr.s_addr = INADDR_ANY;
45060786Sps		if (StrToPortRange(**av, protoName, &portRange) != 0)
45160786Sps			errx(EX_DATAERR, "redirect_port: "
45260786Sps			    "invalid public port range");
45360786Sps	}
45460786Sps
45560786Sps	r->pport = GETLOPORT(portRange);
45660786Sps	if (r->proto == IPPROTO_SCTP) { /* so the logic below still works */
45760786Sps		numLocalPorts = GETNUMPORTS(portRange);
458		r->lport = r->pport;
459	}
460	r->pport_cnt = GETNUMPORTS(portRange);
461	(*av)++; (*ac)--;
462
463	/*
464	 * Extract remote address and optionally port.
465	 */
466	/*
467	 * NB: isalpha(**av) => we've to check that next parameter is really an
468	 * option for this redirect entry, else stop here processing arg[cv].
469	 */
470	if (*ac != 0 && !isalpha(***av)) {
471		if ((sep = strchr(**av, ':')) != NULL) {
472			if (StrToAddrAndPortRange(**av, &r->raddr, protoName,
473			    &portRange) != 0)
474				errx(EX_DATAERR, "redirect_port: "
475				    "invalid remote port range");
476		} else {
477			SETLOPORT(portRange, 0);
478			SETNUMPORTS(portRange, 1);
479			StrToAddr(**av, &r->raddr);
480		}
481		(*av)++; (*ac)--;
482	} else {
483		SETLOPORT(portRange, 0);
484		SETNUMPORTS(portRange, 1);
485		r->raddr.s_addr = INADDR_ANY;
486	}
487	r->rport = GETLOPORT(portRange);
488	r->rport_cnt = GETNUMPORTS(portRange);
489
490	/*
491	 * Make sure port ranges match up, then add the redirect ports.
492	 */
493	if (numLocalPorts != r->pport_cnt)
494		errx(EX_DATAERR, "redirect_port: "
495		    "port ranges must be equal in size");
496
497	/* Remote port range is allowed to be '0' which means all ports. */
498	if (r->rport_cnt != numLocalPorts &&
499	    (r->rport_cnt != 1 || r->rport != 0))
500		errx(EX_DATAERR, "redirect_port: remote port must"
501		    "be 0 or equal to local port range in size");
502
503	/* Setup LSNAT server pool. */
504	if (lsnat != NULL) {
505		struct cfg_spool *spool;
506
507		sep = strtok(lsnat, ",");
508		while (sep != NULL) {
509			spool = (struct cfg_spool *)buf;
510			space += sizeof(struct cfg_spool);
511			/*
512			 * The sctp nat does not allow the port numbers to
513			 * be mapped to new port numbers. Therefore, no ports
514			 * are to be specified in the target port field.
515			 */
516			if (r->proto == IPPROTO_SCTP) {
517				if (strchr (sep, ':')) {
518					errx(EX_DATAERR, "redirect_port:"
519					    "port numbers do not change in "
520					    "sctp, so do not specify them as "
521					    "part of the target");
522				} else {
523					StrToAddr(sep, &spool->addr);
524					spool->port = r->pport;
525				}
526			} else {
527				if (StrToAddrAndPortRange(sep, &spool->addr,
528					protoName, &portRange) != 0)
529					errx(EX_DATAERR, "redirect_port:"
530					    "invalid local port range");
531				if (GETNUMPORTS(portRange) != 1)
532					errx(EX_DATAERR, "redirect_port: "
533					    "local port must be single in "
534					    "this context");
535				spool->port = GETLOPORT(portRange);
536			}
537			r->spool_cnt++;
538			/* Point to the next possible cfg_spool. */
539			buf = &buf[sizeof(struct cfg_spool)];
540			sep = strtok(NULL, ",");
541		}
542	}
543
544	return (space);
545}
546
547static int
548setup_redir_proto(char *buf, int *ac, char ***av)
549{
550	struct cfg_redir *r;
551	struct protoent *protoent;
552	size_t space;
553
554	r = (struct cfg_redir *)buf;
555	r->mode = REDIR_PROTO;
556	/* Skip cfg_redir at beginning of buf. */
557	buf = &buf[sizeof(struct cfg_redir)];
558	space = sizeof(struct cfg_redir);
559
560	/*
561	 * Extract protocol.
562	 */
563	protoent = getprotobyname(**av);
564	if (protoent == NULL)
565		errx(EX_DATAERR, "redirect_proto: unknown protocol %s", **av);
566	else
567		r->proto = protoent->p_proto;
568
569	(*av)++; (*ac)--;
570
571	/*
572	 * Extract local address.
573	 */
574	StrToAddr(**av, &r->laddr);
575
576	(*av)++; (*ac)--;
577
578	/*
579	 * Extract optional public address.
580	 */
581	if (*ac == 0) {
582		r->paddr.s_addr = INADDR_ANY;
583		r->raddr.s_addr = INADDR_ANY;
584	} else {
585		/* see above in setup_redir_port() */
586		if (!isalpha(***av)) {
587			StrToAddr(**av, &r->paddr);
588			(*av)++; (*ac)--;
589
590			/*
591			 * Extract optional remote address.
592			 */
593			/* see above in setup_redir_port() */
594			if (*ac != 0 && !isalpha(***av)) {
595				StrToAddr(**av, &r->raddr);
596				(*av)++; (*ac)--;
597			}
598		}
599	}
600
601	return (space);
602}
603
604static void
605print_nat_config(unsigned char *buf)
606{
607	struct cfg_nat *n;
608	int i, cnt, flag, off;
609	struct cfg_redir *t;
610	struct cfg_spool *s;
611	struct protoent *p;
612
613	n = (struct cfg_nat *)buf;
614	flag = 1;
615	off  = sizeof(*n);
616	printf("ipfw nat %u config", n->id);
617	if (strlen(n->if_name) != 0)
618		printf(" if %s", n->if_name);
619	else if (n->ip.s_addr != 0)
620		printf(" ip %s", inet_ntoa(n->ip));
621	while (n->mode != 0) {
622		if (n->mode & PKT_ALIAS_LOG) {
623			printf(" log");
624			n->mode &= ~PKT_ALIAS_LOG;
625		} else if (n->mode & PKT_ALIAS_DENY_INCOMING) {
626			printf(" deny_in");
627			n->mode &= ~PKT_ALIAS_DENY_INCOMING;
628		} else if (n->mode & PKT_ALIAS_SAME_PORTS) {
629			printf(" same_ports");
630			n->mode &= ~PKT_ALIAS_SAME_PORTS;
631		} else if (n->mode & PKT_ALIAS_UNREGISTERED_ONLY) {
632			printf(" unreg_only");
633			n->mode &= ~PKT_ALIAS_UNREGISTERED_ONLY;
634		} else if (n->mode & PKT_ALIAS_RESET_ON_ADDR_CHANGE) {
635			printf(" reset");
636			n->mode &= ~PKT_ALIAS_RESET_ON_ADDR_CHANGE;
637		} else if (n->mode & PKT_ALIAS_REVERSE) {
638			printf(" reverse");
639			n->mode &= ~PKT_ALIAS_REVERSE;
640		} else if (n->mode & PKT_ALIAS_PROXY_ONLY) {
641			printf(" proxy_only");
642			n->mode &= ~PKT_ALIAS_PROXY_ONLY;
643		}
644	}
645	/* Print all the redirect's data configuration. */
646	for (cnt = 0; cnt < n->redir_cnt; cnt++) {
647		t = (struct cfg_redir *)&buf[off];
648		off += SOF_REDIR;
649		switch (t->mode) {
650		case REDIR_ADDR:
651			printf(" redirect_addr");
652			if (t->spool_cnt == 0)
653				printf(" %s", inet_ntoa(t->laddr));
654			else
655				for (i = 0; i < t->spool_cnt; i++) {
656					s = (struct cfg_spool *)&buf[off];
657					if (i)
658						printf(",");
659					else
660						printf(" ");
661					printf("%s", inet_ntoa(s->addr));
662					off += SOF_SPOOL;
663				}
664			printf(" %s", inet_ntoa(t->paddr));
665			break;
666		case REDIR_PORT:
667			p = getprotobynumber(t->proto);
668			printf(" redirect_port %s ", p->p_name);
669			if (!t->spool_cnt) {
670				printf("%s:%u", inet_ntoa(t->laddr), t->lport);
671				if (t->pport_cnt > 1)
672					printf("-%u", t->lport +
673					    t->pport_cnt - 1);
674			} else
675				for (i=0; i < t->spool_cnt; i++) {
676					s = (struct cfg_spool *)&buf[off];
677					if (i)
678						printf(",");
679					printf("%s:%u", inet_ntoa(s->addr),
680					    s->port);
681					off += SOF_SPOOL;
682				}
683
684			printf(" ");
685			if (t->paddr.s_addr)
686				printf("%s:", inet_ntoa(t->paddr));
687			printf("%u", t->pport);
688			if (!t->spool_cnt && t->pport_cnt > 1)
689				printf("-%u", t->pport + t->pport_cnt - 1);
690
691			if (t->raddr.s_addr) {
692				printf(" %s", inet_ntoa(t->raddr));
693				if (t->rport) {
694					printf(":%u", t->rport);
695					if (!t->spool_cnt && t->rport_cnt > 1)
696						printf("-%u", t->rport +
697						    t->rport_cnt - 1);
698				}
699			}
700			break;
701		case REDIR_PROTO:
702			p = getprotobynumber(t->proto);
703			printf(" redirect_proto %s %s", p->p_name,
704			    inet_ntoa(t->laddr));
705			if (t->paddr.s_addr != 0) {
706				printf(" %s", inet_ntoa(t->paddr));
707				if (t->raddr.s_addr)
708					printf(" %s", inet_ntoa(t->raddr));
709			}
710			break;
711		default:
712			errx(EX_DATAERR, "unknown redir mode");
713			break;
714		}
715	}
716	printf("\n");
717}
718
719void
720ipfw_config_nat(int ac, char **av)
721{
722	struct cfg_nat *n;		/* Nat instance configuration. */
723	int i, off, tok, ac1;
724	char *id, *buf, **av1;
725	size_t len;
726
727	av++; ac--;
728	/* Nat id. */
729	if (ac && isdigit(**av)) {
730		id = *av;
731		ac--; av++;
732	} else
733		errx(EX_DATAERR, "missing nat id");
734	if (ac == 0)
735		errx(EX_DATAERR, "missing option");
736
737	len = sizeof(struct cfg_nat);
738	ac1 = ac;
739	av1 = av;
740	while (ac1 > 0) {
741		tok = match_token(nat_params, *av1);
742		ac1--; av1++;
743		switch (tok) {
744		case TOK_IP:
745		case TOK_IF:
746			ac1--; av1++;
747			break;
748		case TOK_ALOG:
749		case TOK_DENY_INC:
750		case TOK_SAME_PORTS:
751		case TOK_UNREG_ONLY:
752		case TOK_RESET_ADDR:
753		case TOK_ALIAS_REV:
754		case TOK_PROXY_ONLY:
755			break;
756		case TOK_REDIR_ADDR:
757			if (ac1 < 2)
758				errx(EX_DATAERR, "redirect_addr: "
759				    "not enough arguments");
760			len += estimate_redir_addr(&ac1, &av1);
761			av1 += 2; ac1 -= 2;
762			break;
763		case TOK_REDIR_PORT:
764			if (ac1 < 3)
765				errx(EX_DATAERR, "redirect_port: "
766				    "not enough arguments");
767			av1++; ac1--;
768			len += estimate_redir_port(&ac1, &av1);
769			av1 += 2; ac1 -= 2;
770			break;
771		case TOK_REDIR_PROTO:
772			if (ac1 < 2)
773				errx(EX_DATAERR, "redirect_proto: "
774				    "not enough arguments");
775			len += sizeof(struct cfg_redir);
776			av1 += 2; ac1 -= 2;
777			break;
778		default:
779			errx(EX_DATAERR, "unrecognised option ``%s''", av1[-1]);
780		}
781	}
782
783	if ((buf = malloc(len)) == NULL)
784		errx(EX_OSERR, "malloc failed");
785
786	/* Offset in buf: save space for n at the beginning. */
787	off = sizeof(*n);
788	memset(buf, 0, len);
789	n = (struct cfg_nat *)buf;
790	i = atoi(id);
791	n->id = i;
792
793	while (ac > 0) {
794		tok = match_token(nat_params, *av);
795		ac--; av++;
796		switch (tok) {
797		case TOK_IP:
798			if (ac == 0)
799				errx(EX_DATAERR, "missing option");
800			if (!inet_aton(av[0], &(n->ip)))
801				errx(EX_DATAERR, "bad ip address ``%s''",
802				    av[0]);
803			ac--; av++;
804			break;
805		case TOK_IF:
806			if (ac == 0)
807				errx(EX_DATAERR, "missing option");
808			set_addr_dynamic(av[0], n);
809			ac--; av++;
810			break;
811		case TOK_ALOG:
812			n->mode |= PKT_ALIAS_LOG;
813			break;
814		case TOK_DENY_INC:
815			n->mode |= PKT_ALIAS_DENY_INCOMING;
816			break;
817		case TOK_SAME_PORTS:
818			n->mode |= PKT_ALIAS_SAME_PORTS;
819			break;
820		case TOK_UNREG_ONLY:
821			n->mode |= PKT_ALIAS_UNREGISTERED_ONLY;
822			break;
823		case TOK_RESET_ADDR:
824			n->mode |= PKT_ALIAS_RESET_ON_ADDR_CHANGE;
825			break;
826		case TOK_ALIAS_REV:
827			n->mode |= PKT_ALIAS_REVERSE;
828			break;
829		case TOK_PROXY_ONLY:
830			n->mode |= PKT_ALIAS_PROXY_ONLY;
831			break;
832			/*
833			 * All the setup_redir_* functions work directly in
834			 * the final buffer, see above for details.
835			 */
836		case TOK_REDIR_ADDR:
837		case TOK_REDIR_PORT:
838		case TOK_REDIR_PROTO:
839			switch (tok) {
840			case TOK_REDIR_ADDR:
841				i = setup_redir_addr(&buf[off], &ac, &av);
842				break;
843			case TOK_REDIR_PORT:
844				i = setup_redir_port(&buf[off], &ac, &av);
845				break;
846			case TOK_REDIR_PROTO:
847				i = setup_redir_proto(&buf[off], &ac, &av);
848				break;
849			}
850			n->redir_cnt++;
851			off += i;
852			break;
853		}
854	}
855
856	i = do_cmd(IP_FW_NAT_CFG, buf, off);
857	if (i)
858		err(1, "setsockopt(%s)", "IP_FW_NAT_CFG");
859
860	if (!co.do_quiet) {
861		/* After every modification, we show the resultant rule. */
862		int _ac = 3;
863		const char *_av[] = {"show", "config", id};
864		ipfw_show_nat(_ac, (char **)(void *)_av);
865	}
866}
867
868
869void
870ipfw_show_nat(int ac, char **av)
871{
872	struct cfg_nat *n;
873	struct cfg_redir *e;
874	int cmd, i, nbytes, do_cfg, do_rule, frule, lrule, nalloc, size;
875	int nat_cnt, redir_cnt, r;
876	uint8_t *data, *p;
877	char *endptr;
878
879	do_rule = 0;
880	nalloc = 1024;
881	size = 0;
882	data = NULL;
883	frule = 0;
884	lrule = IPFW_DEFAULT_RULE; /* max ipfw rule number */
885	ac--; av++;
886
887	if (co.test_only)
888		return;
889
890	/* Parse parameters. */
891	for (cmd = IP_FW_NAT_GET_LOG, do_cfg = 0; ac != 0; ac--, av++) {
892		if (!strncmp(av[0], "config", strlen(av[0]))) {
893			cmd = IP_FW_NAT_GET_CONFIG, do_cfg = 1;
894			continue;
895		}
896		/* Convert command line rule #. */
897		frule = lrule = strtoul(av[0], &endptr, 10);
898		if (*endptr == '-')
899			lrule = strtoul(endptr+1, &endptr, 10);
900		if (lrule == 0)
901			err(EX_USAGE, "invalid rule number: %s", av[0]);
902		do_rule = 1;
903	}
904
905	nbytes = nalloc;
906	while (nbytes >= nalloc) {
907		nalloc = nalloc * 2;
908		nbytes = nalloc;
909		data = safe_realloc(data, nbytes);
910		if (do_cmd(cmd, data, (uintptr_t)&nbytes) < 0)
911			err(EX_OSERR, "getsockopt(IP_FW_GET_%s)",
912			    (cmd == IP_FW_NAT_GET_LOG) ? "LOG" : "CONFIG");
913	}
914	if (nbytes == 0)
915		exit(0);
916	if (do_cfg) {
917		nat_cnt = *((int *)data);
918		for (i = sizeof(nat_cnt); nat_cnt; nat_cnt--) {
919			n = (struct cfg_nat *)&data[i];
920			if (frule <= n->id && lrule >= n->id)
921				print_nat_config(&data[i]);
922			i += sizeof(struct cfg_nat);
923			for (redir_cnt = 0; redir_cnt < n->redir_cnt; redir_cnt++) {
924				e = (struct cfg_redir *)&data[i];
925				i += sizeof(struct cfg_redir) + e->spool_cnt *
926				    sizeof(struct cfg_spool);
927			}
928		}
929	} else {
930		for (i = 0; 1; i += LIBALIAS_BUF_SIZE + sizeof(int)) {
931			p = &data[i];
932			if (p == data + nbytes)
933				break;
934			bcopy(p, &r, sizeof(int));
935			if (do_rule) {
936				if (!(frule <= r && lrule >= r))
937					continue;
938			}
939			printf("nat %u: %s\n", r, p+sizeof(int));
940		}
941	}
942}
943