Revision b03c720211ea5ec027ad85f1e147e3d8122429ba authored by Pyun YongHyeon on 14 January 2010, 21:54:20 UTC, committed by Pyun YongHyeon on 14 January 2010, 21:54:20 UTC
  Add bus_dma(9) and endianness support to ste(4).
   o Sorted includes and added missing header files.
   o Added basic endianness support. In theory ste(4) should work on
     any architectures.
   o Remove the use of contigmalloc(9), contigfree(9) and vtophys(9).
   o Added 8 byte alignment limitation of TX/RX descriptor.
   o Added 1 byte alignment requirement for TX/RX buffers.
   o ste(4) controllers does not support DAC. Limit DMA address space
     to be within 32bit address.
   o Added spare DMA map to gracefully recover from DMA map failure.
   o Removed dead code for checking STE_RXSTAT_DMADONE bit. The bit
     was already checked in each iteration of loop so it can't be true.
   o Added second argument count to ste_rxeof(). It is used to limit
     number of iterations done in RX handler. ATM polling is the only
     consumer.
   o Removed ste_rxeoc() which was added to address RX stuck issue
     (cvs rev 1.66). Unlike TX descriptors, ST201 supports chaining
     descriptors to form a ring for RX descriptors. If RX descriptor
     chaining is not supported it's possible for controller to stop
     receiving incoming frames once controller pass the end of RX
     descriptor which in turn requires driver post new RX
     descriptors to receive more frames. For TX descriptors which
     does not support chaning, we exactly do manual chaining in
     driver by concatenating new descriptors to the end of previous
     TX chain.
     Maybe the workaround was borrowed from other drivers that does
     not support RX descriptor chaining, which is not valid for ST201
     controllers. I still have no idea how this address RX stuck
     issue and I can't reproduce the RX stuck issue on DFE-550TX
     controller.
   o Removed hw.ste_rxsyncs sysctl as the workaround was removed.
   o TX/RX side bus_dmamap_load_mbuf_sg(9) support.
   o Reimplemented optimized ste_encap().
   o Simplified TX logic of ste_start_locked().
   o Added comments for TFD/RFD requirements.
   o Increased number of RX descriptors to 128 from 64. 128 gave much
     better performance than 64 under high network loads.
1 parent d982c88
Raw File
nat.c
/*
 * Copyright (c) 2002-2003 Luigi Rizzo
 * Copyright (c) 1996 Alex Nash, Paul Traina, Poul-Henning Kamp
 * Copyright (c) 1994 Ugen J.S.Antsilevich
 *
 * Idea and grammar partially left from:
 * Copyright (c) 1993 Daniel Boulet
 *
 * Redistribution and use in source forms, with and without modification,
 * are permitted provided that this entire comment appears intact.
 *
 * Redistribution in binary form may occur without any restrictions.
 * Obviously, it would be nice if you gave credit where credit is due
 * but requiring it would be too onerous.
 *
 * This software is provided ``AS IS'' without any warranties of any kind.
 *
 * NEW command line interface for IP firewall facility
 *
 * $FreeBSD$
 *
 * In-kernel nat support
 */

#include <sys/types.h>
#include <sys/socket.h>
#include <sys/sysctl.h>

#include "ipfw2.h"

#include <ctype.h>
#include <err.h>
#include <netdb.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sysexits.h>

#define IPFW_INTERNAL	/* Access to protected structures in ip_fw.h. */

#include <net/if.h>
#include <net/if_dl.h>
#include <net/route.h> /* def. of struct route */
#include <netinet/in.h>
#include <netinet/ip_fw.h>
#include <arpa/inet.h>
#include <alias.h>

static struct _s_x nat_params[] = {
	{ "ip",	                TOK_IP },
	{ "if",	                TOK_IF },
 	{ "log",                TOK_ALOG },
 	{ "deny_in",	        TOK_DENY_INC },
 	{ "same_ports",	        TOK_SAME_PORTS },
 	{ "unreg_only",	        TOK_UNREG_ONLY },
 	{ "reset",	        TOK_RESET_ADDR },
 	{ "reverse",	        TOK_ALIAS_REV },	
 	{ "proxy_only",	        TOK_PROXY_ONLY },
	{ "redirect_addr",	TOK_REDIR_ADDR },
	{ "redirect_port",	TOK_REDIR_PORT },
	{ "redirect_proto",	TOK_REDIR_PROTO },
 	{ NULL, 0 }	/* terminator */
};


/* 
 * Search for interface with name "ifn", and fill n accordingly:
 *
 * n->ip        ip address of interface "ifn"
 * n->if_name   copy of interface name "ifn"
 */
static void
set_addr_dynamic(const char *ifn, struct cfg_nat *n)
{
	size_t needed;
	int mib[6];
	char *buf, *lim, *next;
	struct if_msghdr *ifm;
	struct ifa_msghdr *ifam;
	struct sockaddr_dl *sdl;
	struct sockaddr_in *sin;
	int ifIndex, ifMTU;

	mib[0] = CTL_NET;
	mib[1] = PF_ROUTE;
	mib[2] = 0;
	mib[3] = AF_INET;	
	mib[4] = NET_RT_IFLIST;
	mib[5] = 0;		
/*
 * Get interface data.
 */
	if (sysctl(mib, 6, NULL, &needed, NULL, 0) == -1)
		err(1, "iflist-sysctl-estimate");
	buf = safe_calloc(1, needed);
	if (sysctl(mib, 6, buf, &needed, NULL, 0) == -1)
		err(1, "iflist-sysctl-get");
	lim = buf + needed;
/*
 * Loop through interfaces until one with
 * given name is found. This is done to
 * find correct interface index for routing
 * message processing.
 */
	ifIndex	= 0;
	next = buf;
	while (next < lim) {
		ifm = (struct if_msghdr *)next;
		next += ifm->ifm_msglen;
		if (ifm->ifm_version != RTM_VERSION) {
			if (co.verbose)
				warnx("routing message version %d "
				    "not understood", ifm->ifm_version);
			continue;
		}
		if (ifm->ifm_type == RTM_IFINFO) {
			sdl = (struct sockaddr_dl *)(ifm + 1);
			if (strlen(ifn) == sdl->sdl_nlen &&
			    strncmp(ifn, sdl->sdl_data, sdl->sdl_nlen) == 0) {
				ifIndex = ifm->ifm_index;
				ifMTU = ifm->ifm_data.ifi_mtu;
				break;
			}
		}
	}
	if (!ifIndex)
		errx(1, "unknown interface name %s", ifn);
/*
 * Get interface address.
 */
	sin = NULL;
	while (next < lim) {
		ifam = (struct ifa_msghdr *)next;
		next += ifam->ifam_msglen;
		if (ifam->ifam_version != RTM_VERSION) {
			if (co.verbose)
				warnx("routing message version %d "
				    "not understood", ifam->ifam_version);
			continue;
		}
		if (ifam->ifam_type != RTM_NEWADDR)
			break;
		if (ifam->ifam_addrs & RTA_IFA) {
			int i;
			char *cp = (char *)(ifam + 1);

			for (i = 1; i < RTA_IFA; i <<= 1) {
				if (ifam->ifam_addrs & i)
					cp += SA_SIZE((struct sockaddr *)cp);
			}
			if (((struct sockaddr *)cp)->sa_family == AF_INET) {
				sin = (struct sockaddr_in *)cp;
				break;
			}
		}
	}
	if (sin == NULL)
		errx(1, "%s: cannot get interface address", ifn);

	n->ip = sin->sin_addr;
	strncpy(n->if_name, ifn, IF_NAMESIZE);

	free(buf);
}

/* 
 * XXX - The following functions, macros and definitions come from natd.c:
 * it would be better to move them outside natd.c, in a file 
 * (redirect_support.[ch]?) shared by ipfw and natd, but for now i can live 
 * with it.
 */

/*
 * Definition of a port range, and macros to deal with values.
 * FORMAT:  HI 16-bits == first port in range, 0 == all ports.
 *          LO 16-bits == number of ports in range
 * NOTES:   - Port values are not stored in network byte order.
 */

#define port_range u_long

#define GETLOPORT(x)     ((x) >> 0x10)
#define GETNUMPORTS(x)   ((x) & 0x0000ffff)
#define GETHIPORT(x)     (GETLOPORT((x)) + GETNUMPORTS((x)))

/* Set y to be the low-port value in port_range variable x. */
#define SETLOPORT(x,y)   ((x) = ((x) & 0x0000ffff) | ((y) << 0x10))

/* Set y to be the number of ports in port_range variable x. */
#define SETNUMPORTS(x,y) ((x) = ((x) & 0xffff0000) | (y))

static void 
StrToAddr (const char* str, struct in_addr* addr)
{
	struct hostent* hp;

	if (inet_aton (str, addr))
		return;

	hp = gethostbyname (str);
	if (!hp)
		errx (1, "unknown host %s", str);

	memcpy (addr, hp->h_addr, sizeof (struct in_addr));
}

static int 
StrToPortRange (const char* str, const char* proto, port_range *portRange)
{
	char*           sep;
	struct servent*	sp;
	char*		end;
	u_short         loPort;
	u_short         hiPort;
	
	/* First see if this is a service, return corresponding port if so. */
	sp = getservbyname (str,proto);
	if (sp) {
	        SETLOPORT(*portRange, ntohs(sp->s_port));
		SETNUMPORTS(*portRange, 1);
		return 0;
	}
	        
	/* Not a service, see if it's a single port or port range. */
	sep = strchr (str, '-');
	if (sep == NULL) {
	        SETLOPORT(*portRange, strtol(str, &end, 10));
		if (end != str) {
		        /* Single port. */
		        SETNUMPORTS(*portRange, 1);
			return 0;
		}

		/* Error in port range field. */
		errx (EX_DATAERR, "%s/%s: unknown service", str, proto);
	}

	/* Port range, get the values and sanity check. */
	sscanf (str, "%hu-%hu", &loPort, &hiPort);
	SETLOPORT(*portRange, loPort);
	SETNUMPORTS(*portRange, 0);	/* Error by default */
	if (loPort <= hiPort)
	        SETNUMPORTS(*portRange, hiPort - loPort + 1);

	if (GETNUMPORTS(*portRange) == 0)
	        errx (EX_DATAERR, "invalid port range %s", str);

	return 0;
}

static int 
StrToProto (const char* str)
{
	if (!strcmp (str, "tcp"))
		return IPPROTO_TCP;

	if (!strcmp (str, "udp"))
		return IPPROTO_UDP;

	if (!strcmp (str, "sctp"))
		return IPPROTO_SCTP;
	errx (EX_DATAERR, "unknown protocol %s. Expected sctp, tcp or udp", str);
}

static int 
StrToAddrAndPortRange (const char* str, struct in_addr* addr, char* proto, 
		       port_range *portRange)
{
	char*	ptr;

	ptr = strchr (str, ':');
	if (!ptr)
		errx (EX_DATAERR, "%s is missing port number", str);

	*ptr = '\0';
	++ptr;

	StrToAddr (str, addr);
	return StrToPortRange (ptr, proto, portRange);
}

/* End of stuff taken from natd.c. */

#define INC_ARGCV() do {        \
	(*_av)++;               \
	(*_ac)--;               \
	av = *_av;              \
	ac = *_ac;              \
} while(0)

/* 
 * The next 3 functions add support for the addr, port and proto redirect and 
 * their logic is loosely based on SetupAddressRedirect(), SetupPortRedirect() 
 * and SetupProtoRedirect() from natd.c.
 *
 * Every setup_* function fills at least one redirect entry 
 * (struct cfg_redir) and zero or more server pool entry (struct cfg_spool) 
 * in buf.
 * 
 * The format of data in buf is:
 * 
 *
 *     cfg_nat    cfg_redir    cfg_spool    ......  cfg_spool 
 *
 *    -------------------------------------        ------------
 *   |          | .....X ... |          |         |           |  .....
 *    ------------------------------------- ...... ------------
 *                     ^          
 *                spool_cnt       n=0       ......   n=(X-1)
 *
 * len points to the amount of available space in buf
 * space counts the memory consumed by every function
 *
 * XXX - Every function get all the argv params so it 
 * has to check, in optional parameters, that the next
 * args is a valid option for the redir entry and not 
 * another token. Only redir_port and redir_proto are 
 * affected by this.
 */

static int
setup_redir_addr(char *spool_buf, unsigned int len,
		 int *_ac, char ***_av) 
{
	char **av, *sep; /* Token separator. */
	/* Temporary buffer used to hold server pool ip's. */
	char tmp_spool_buf[NAT_BUF_LEN]; 
	int ac, space, lsnat;
	struct cfg_redir *r;	
	struct cfg_spool *tmp;		

	av = *_av;
	ac = *_ac;
	space = 0;
	lsnat = 0;
	if (len >= SOF_REDIR) {
		r = (struct cfg_redir *)spool_buf;
		/* Skip cfg_redir at beginning of buf. */
		spool_buf = &spool_buf[SOF_REDIR];
		space = SOF_REDIR;
		len -= SOF_REDIR;
	} else 
		goto nospace; 
	r->mode = REDIR_ADDR;
	/* Extract local address. */
	if (ac == 0) 
		errx(EX_DATAERR, "redirect_addr: missing local address");
	sep = strchr(*av, ',');
	if (sep) {		/* LSNAT redirection syntax. */
		r->laddr.s_addr = INADDR_NONE;
		/* Preserve av, copy spool servers to tmp_spool_buf. */
		strncpy(tmp_spool_buf, *av, strlen(*av)+1);
		lsnat = 1;
	} else 
		StrToAddr(*av, &r->laddr);		
	INC_ARGCV();

	/* Extract public address. */
	if (ac == 0) 
		errx(EX_DATAERR, "redirect_addr: missing public address");
	StrToAddr(*av, &r->paddr);
	INC_ARGCV();

	/* Setup LSNAT server pool. */
	if (sep) {
		sep = strtok(tmp_spool_buf, ",");		
		while (sep != NULL) {
			tmp = (struct cfg_spool *)spool_buf;		
			if (len < SOF_SPOOL)
				goto nospace;
			len -= SOF_SPOOL;
			space += SOF_SPOOL;			
			StrToAddr(sep, &tmp->addr);
			tmp->port = ~0;
			r->spool_cnt++;
			/* Point to the next possible cfg_spool. */
			spool_buf = &spool_buf[SOF_SPOOL];
			sep = strtok(NULL, ",");
		}
	}
	return(space);
nospace:
	errx(EX_DATAERR, "redirect_addr: buf is too small\n");
}

static int
setup_redir_port(char *spool_buf, unsigned int len,
		 int *_ac, char ***_av) 
{
	char **av, *sep, *protoName;
	char tmp_spool_buf[NAT_BUF_LEN];
	int ac, space, lsnat;
	struct cfg_redir *r;
	struct cfg_spool *tmp;
	u_short numLocalPorts;
	port_range portRange;	

	av = *_av;
	ac = *_ac;
	space = 0;
	lsnat = 0;
	numLocalPorts = 0;	

	if (len >= SOF_REDIR) {
		r = (struct cfg_redir *)spool_buf;
		/* Skip cfg_redir at beginning of buf. */
		spool_buf = &spool_buf[SOF_REDIR];
		space = SOF_REDIR;
		len -= SOF_REDIR;
	} else 
		goto nospace; 
	r->mode = REDIR_PORT;
	/*
	 * Extract protocol.
	 */
	if (ac == 0)
		errx (EX_DATAERR, "redirect_port: missing protocol");
	r->proto = StrToProto(*av);
	protoName = *av;	
	INC_ARGCV();

	/*
	 * Extract local address.
	 */
	if (ac == 0)
		errx (EX_DATAERR, "redirect_port: missing local address");

	sep = strchr(*av, ',');
	/* LSNAT redirection syntax. */
	if (sep) {
		r->laddr.s_addr = INADDR_NONE;
		r->lport = ~0;
		numLocalPorts = 1;
		/* Preserve av, copy spool servers to tmp_spool_buf. */
		strncpy(tmp_spool_buf, *av, strlen(*av)+1);
		lsnat = 1;
	} else {
		/*
		 * The sctp nat does not allow the port numbers to be mapped to 
		 * new port numbers. Therefore, no ports are to be specified 
		 * in the target port field.
		 */
		if (r->proto == IPPROTO_SCTP) {
			if (strchr (*av, ':'))
				errx(EX_DATAERR, "redirect_port:"
				    "port numbers do not change in sctp, so do not "
				    "specify them as part of the target");
			else
				StrToAddr(*av, &r->laddr);
		} else {
			if (StrToAddrAndPortRange (*av, &r->laddr, protoName, 
				&portRange) != 0)
				errx(EX_DATAERR, "redirect_port:"
				    "invalid local port range");

			r->lport = GETLOPORT(portRange);
			numLocalPorts = GETNUMPORTS(portRange);
		}
	}
	INC_ARGCV();	

	/*
	 * Extract public port and optionally address.
	 */
	if (ac == 0)
		errx (EX_DATAERR, "redirect_port: missing public port");

	sep = strchr (*av, ':');
	if (sep) {
	        if (StrToAddrAndPortRange (*av, &r->paddr, protoName, 
		    &portRange) != 0)
		        errx(EX_DATAERR, "redirect_port:" 
			    "invalid public port range");
	} else {
		r->paddr.s_addr = INADDR_ANY;
		if (StrToPortRange (*av, protoName, &portRange) != 0)
		        errx(EX_DATAERR, "redirect_port:"
			    "invalid public port range");
	}

	r->pport = GETLOPORT(portRange);
	if (r->proto == IPPROTO_SCTP) { /* so the logic below still works */
		numLocalPorts = GETNUMPORTS(portRange);
		r->lport = r->pport;
	}
	r->pport_cnt = GETNUMPORTS(portRange);
	INC_ARGCV();

	/*
	 * Extract remote address and optionally port.
	 */	
	/* 
	 * NB: isalpha(**av) => we've to check that next parameter is really an
	 * option for this redirect entry, else stop here processing arg[cv].
	 */
	if (ac != 0 && !isalpha(**av)) { 
		sep = strchr (*av, ':');
		if (sep) {
		        if (StrToAddrAndPortRange (*av, &r->raddr, protoName, 
			    &portRange) != 0)
				errx(EX_DATAERR, "redirect_port:"
				    "invalid remote port range");
		} else {
		        SETLOPORT(portRange, 0);
			SETNUMPORTS(portRange, 1);
			StrToAddr (*av, &r->raddr);
		}
		INC_ARGCV();
	} else {
		SETLOPORT(portRange, 0);
		SETNUMPORTS(portRange, 1);
		r->raddr.s_addr = INADDR_ANY;
	}
	r->rport = GETLOPORT(portRange);
	r->rport_cnt = GETNUMPORTS(portRange);

	/* 
	 * Make sure port ranges match up, then add the redirect ports.
	 */
	if (numLocalPorts != r->pport_cnt)
	        errx(EX_DATAERR, "redirect_port:"
		    "port ranges must be equal in size");

	/* Remote port range is allowed to be '0' which means all ports. */
	if (r->rport_cnt != numLocalPorts && 
	    (r->rport_cnt != 1 || r->rport != 0))
	        errx(EX_DATAERR, "redirect_port: remote port must"
		    "be 0 or equal to local port range in size");

	/*
	 * Setup LSNAT server pool.
	 */
	if (lsnat) {
		sep = strtok(tmp_spool_buf, ",");
		while (sep != NULL) {
			tmp = (struct cfg_spool *)spool_buf;
			if (len < SOF_SPOOL)
				goto nospace;
			len -= SOF_SPOOL;
			space += SOF_SPOOL;
			/*
			 * The sctp nat does not allow the port numbers to be mapped to new port numbers
			 * Therefore, no ports are to be specified in the target port field
			 */
			if (r->proto == IPPROTO_SCTP) {
				if (strchr (sep, ':')) {
					errx(EX_DATAERR, "redirect_port:"
					    "port numbers do not change in "
					    "sctp, so do not specify them as "
					    "part of the target");
				} else {
					StrToAddr(sep, &tmp->addr);
					tmp->port = r->pport;
				}
			} else {
				if (StrToAddrAndPortRange(sep, &tmp->addr, 
					protoName, &portRange) != 0)
					errx(EX_DATAERR, "redirect_port:"
					    "invalid local port range");
				if (GETNUMPORTS(portRange) != 1)
					errx(EX_DATAERR, "redirect_port: "
					    "local port must be single in "
					    "this context");
				tmp->port = GETLOPORT(portRange);
			}
			r->spool_cnt++;	
			/* Point to the next possible cfg_spool. */
			spool_buf = &spool_buf[SOF_SPOOL];
			sep = strtok(NULL, ",");
		}
	}
	return (space);
nospace:
	errx(EX_DATAERR, "redirect_port: buf is too small\n");
}

static int
setup_redir_proto(char *spool_buf, unsigned int len,
		 int *_ac, char ***_av) 
{
	char **av;
	int ac, space;
	struct protoent *protoent;
	struct cfg_redir *r;
	
	av = *_av;
	ac = *_ac;
	if (len >= SOF_REDIR) {
		r = (struct cfg_redir *)spool_buf;
		/* Skip cfg_redir at beginning of buf. */
		spool_buf = &spool_buf[SOF_REDIR];
		space = SOF_REDIR;
		len -= SOF_REDIR;
	} else 
		goto nospace;
	r->mode = REDIR_PROTO;
	/*
	 * Extract protocol.
	 */	
	if (ac == 0)
		errx(EX_DATAERR, "redirect_proto: missing protocol");

	protoent = getprotobyname(*av);
	if (protoent == NULL)
		errx(EX_DATAERR, "redirect_proto: unknown protocol %s", *av);
	else
		r->proto = protoent->p_proto;

	INC_ARGCV();
	
	/*
	 * Extract local address.
	 */
	if (ac == 0)
		errx(EX_DATAERR, "redirect_proto: missing local address");
	else
		StrToAddr(*av, &r->laddr);

	INC_ARGCV();
	
	/*
	 * Extract optional public address.
	 */
	if (ac == 0) {
		r->paddr.s_addr = INADDR_ANY;		
		r->raddr.s_addr = INADDR_ANY;	
	} else {
		/* see above in setup_redir_port() */
		if (!isalpha(**av)) {
			StrToAddr(*av, &r->paddr);			
			INC_ARGCV();
		
			/*
			 * Extract optional remote address.
			 */	
			/* see above in setup_redir_port() */
			if (ac!=0 && !isalpha(**av)) {
				StrToAddr(*av, &r->raddr);
				INC_ARGCV();
			}
		}		
	}
	return (space);
nospace:
	errx(EX_DATAERR, "redirect_proto: buf is too small\n");
}

static void
print_nat_config(unsigned char *buf)
{
	struct cfg_nat *n;
	int i, cnt, flag, off;
	struct cfg_redir *t;
	struct cfg_spool *s;
	struct protoent *p;

	n = (struct cfg_nat *)buf;
	flag = 1;
	off  = sizeof(*n);
	printf("ipfw nat %u config", n->id);
	if (strlen(n->if_name) != 0)
		printf(" if %s", n->if_name);
	else if (n->ip.s_addr != 0)
		printf(" ip %s", inet_ntoa(n->ip));
	while (n->mode != 0) {
		if (n->mode & PKT_ALIAS_LOG) {
			printf(" log");
			n->mode &= ~PKT_ALIAS_LOG;
		} else if (n->mode & PKT_ALIAS_DENY_INCOMING) {
			printf(" deny_in");
			n->mode &= ~PKT_ALIAS_DENY_INCOMING;
		} else if (n->mode & PKT_ALIAS_SAME_PORTS) {
			printf(" same_ports");
			n->mode &= ~PKT_ALIAS_SAME_PORTS;
		} else if (n->mode & PKT_ALIAS_UNREGISTERED_ONLY) {
			printf(" unreg_only");
			n->mode &= ~PKT_ALIAS_UNREGISTERED_ONLY;
		} else if (n->mode & PKT_ALIAS_RESET_ON_ADDR_CHANGE) {
			printf(" reset");
			n->mode &= ~PKT_ALIAS_RESET_ON_ADDR_CHANGE;
		} else if (n->mode & PKT_ALIAS_REVERSE) {
			printf(" reverse");
			n->mode &= ~PKT_ALIAS_REVERSE;
		} else if (n->mode & PKT_ALIAS_PROXY_ONLY) {
			printf(" proxy_only");
			n->mode &= ~PKT_ALIAS_PROXY_ONLY;
		}
	}
	/* Print all the redirect's data configuration. */
	for (cnt = 0; cnt < n->redir_cnt; cnt++) {
		t = (struct cfg_redir *)&buf[off];
		off += SOF_REDIR;
		switch (t->mode) {
		case REDIR_ADDR:
			printf(" redirect_addr");
			if (t->spool_cnt == 0)
				printf(" %s", inet_ntoa(t->laddr));
			else
				for (i = 0; i < t->spool_cnt; i++) {
					s = (struct cfg_spool *)&buf[off];
					if (i)
						printf(",");
					else 
						printf(" ");
					printf("%s", inet_ntoa(s->addr));
					off += SOF_SPOOL;
				}
			printf(" %s", inet_ntoa(t->paddr));
			break;
		case REDIR_PORT:
			p = getprotobynumber(t->proto);
			printf(" redirect_port %s ", p->p_name);
			if (!t->spool_cnt) {
				printf("%s:%u", inet_ntoa(t->laddr), t->lport);
				if (t->pport_cnt > 1)
					printf("-%u", t->lport + 
					    t->pport_cnt - 1);
			} else
				for (i=0; i < t->spool_cnt; i++) {
					s = (struct cfg_spool *)&buf[off];
					if (i)
						printf(",");
					printf("%s:%u", inet_ntoa(s->addr), 
					    s->port);
					off += SOF_SPOOL;
				}

			printf(" ");
			if (t->paddr.s_addr)
				printf("%s:", inet_ntoa(t->paddr)); 
			printf("%u", t->pport);
			if (!t->spool_cnt && t->pport_cnt > 1)
				printf("-%u", t->pport + t->pport_cnt - 1);

			if (t->raddr.s_addr) {
				printf(" %s", inet_ntoa(t->raddr));
				if (t->rport) {
					printf(":%u", t->rport);
					if (!t->spool_cnt && t->rport_cnt > 1)
						printf("-%u", t->rport + 
						    t->rport_cnt - 1);
				}
			}
			break;
		case REDIR_PROTO:
			p = getprotobynumber(t->proto);
			printf(" redirect_proto %s %s", p->p_name, 
			    inet_ntoa(t->laddr));
			if (t->paddr.s_addr != 0) {
				printf(" %s", inet_ntoa(t->paddr));
				if (t->raddr.s_addr)
					printf(" %s", inet_ntoa(t->raddr));
			}
			break;
		default:
			errx(EX_DATAERR, "unknown redir mode");
			break;
		}
	}
	printf("\n");
}

void
ipfw_config_nat(int ac, char **av)
{
	struct cfg_nat *n;              /* Nat instance configuration. */
	int i, len, off, tok;
	char *id, buf[NAT_BUF_LEN]; 	/* Buffer for serialized data. */
	
	len = NAT_BUF_LEN;
	/* Offset in buf: save space for n at the beginning. */
	off = sizeof(*n);
	memset(buf, 0, sizeof(buf));
	n = (struct cfg_nat *)buf;

	av++; ac--;
	/* Nat id. */
	if (ac && isdigit(**av)) {
		id = *av;
		i = atoi(*av); 
		ac--; av++;		
		n->id = i;
	} else 
		errx(EX_DATAERR, "missing nat id");
	if (ac == 0) 
		errx(EX_DATAERR, "missing option");

	while (ac > 0) {
		tok = match_token(nat_params, *av);
		ac--; av++;
		switch (tok) {
		case TOK_IP:
			if (ac == 0) 
				errx(EX_DATAERR, "missing option");
			if (!inet_aton(av[0], &(n->ip)))
				errx(EX_DATAERR, "bad ip address ``%s''", 
				    av[0]);
			ac--; av++;
			break;	    
		case TOK_IF:
			if (ac == 0) 
				errx(EX_DATAERR, "missing option");
			set_addr_dynamic(av[0], n);
			ac--; av++;
			break;
		case TOK_ALOG:
			n->mode |= PKT_ALIAS_LOG;
			break;
		case TOK_DENY_INC:
			n->mode |= PKT_ALIAS_DENY_INCOMING;
			break;
		case TOK_SAME_PORTS:
			n->mode |= PKT_ALIAS_SAME_PORTS;
			break;
		case TOK_UNREG_ONLY:
			n->mode |= PKT_ALIAS_UNREGISTERED_ONLY;
			break;
		case TOK_RESET_ADDR:
			n->mode |= PKT_ALIAS_RESET_ON_ADDR_CHANGE;
			break;
		case TOK_ALIAS_REV:
			n->mode |= PKT_ALIAS_REVERSE;
			break;
		case TOK_PROXY_ONLY:
			n->mode |= PKT_ALIAS_PROXY_ONLY;
			break;
			/* 
			 * All the setup_redir_* functions work directly in the final 
			 * buffer, see above for details.
			 */
		case TOK_REDIR_ADDR:
		case TOK_REDIR_PORT:
		case TOK_REDIR_PROTO:
			switch (tok) {
			case TOK_REDIR_ADDR:
				i = setup_redir_addr(&buf[off], len, &ac, &av);
				break;			  
			case TOK_REDIR_PORT:
				i = setup_redir_port(&buf[off], len, &ac, &av);
				break;			  
			case TOK_REDIR_PROTO:
				i = setup_redir_proto(&buf[off], len, &ac, &av);
				break;
			}
			n->redir_cnt++;
			off += i;
			len -= i;
			break;
		default:
			errx(EX_DATAERR, "unrecognised option ``%s''", av[-1]);
		}
	}

	i = do_cmd(IP_FW_NAT_CFG, buf, off);
	if (i)
		err(1, "setsockopt(%s)", "IP_FW_NAT_CFG");

	if (!co.do_quiet) {
		/* After every modification, we show the resultant rule. */
		int _ac = 3;
		const char *_av[] = {"show", "config", id};
		ipfw_show_nat(_ac, (char **)(void *)_av);
	}
}


void
ipfw_show_nat(int ac, char **av)
{
	struct cfg_nat *n;
	struct cfg_redir *e;
	int cmd, i, nbytes, do_cfg, do_rule, frule, lrule, nalloc, size;
	int nat_cnt, redir_cnt, r;
	uint8_t *data, *p;
	char *endptr;

	do_rule = 0;
	nalloc = 1024;
	size = 0;
	data = NULL;
	frule = 0;
	lrule = IPFW_DEFAULT_RULE; /* max ipfw rule number */
	ac--; av++;

	if (co.test_only)
		return;

	/* Parse parameters. */
	for (cmd = IP_FW_NAT_GET_LOG, do_cfg = 0; ac != 0; ac--, av++) {
		if (!strncmp(av[0], "config", strlen(av[0]))) {
			cmd = IP_FW_NAT_GET_CONFIG, do_cfg = 1; 
			continue;
		}
		/* Convert command line rule #. */
		frule = lrule = strtoul(av[0], &endptr, 10);
		if (*endptr == '-')
			lrule = strtoul(endptr+1, &endptr, 10);
		if (lrule == 0)			
			err(EX_USAGE, "invalid rule number: %s", av[0]);
		do_rule = 1;
	}

	nbytes = nalloc;
	while (nbytes >= nalloc) {
		nalloc = nalloc * 2;
		nbytes = nalloc;
		data = safe_realloc(data, nbytes);
		if (do_cmd(cmd, data, (uintptr_t)&nbytes) < 0)
			err(EX_OSERR, "getsockopt(IP_FW_GET_%s)",
			    (cmd == IP_FW_NAT_GET_LOG) ? "LOG" : "CONFIG");
	}
	if (nbytes == 0)
		exit(0);
	if (do_cfg) {
		nat_cnt = *((int *)data);
		for (i = sizeof(nat_cnt); nat_cnt; nat_cnt--) {
			n = (struct cfg_nat *)&data[i];
			if (frule <= n->id && lrule >= n->id)
				print_nat_config(&data[i]);
			i += sizeof(struct cfg_nat);
			for (redir_cnt = 0; redir_cnt < n->redir_cnt; redir_cnt++) {
				e = (struct cfg_redir *)&data[i];
				i += sizeof(struct cfg_redir) + e->spool_cnt * 
				    sizeof(struct cfg_spool);
			}
		}
	} else {
		for (i = 0; 1; i += LIBALIAS_BUF_SIZE + sizeof(int)) {
			p = &data[i];
			if (p == data + nbytes)
				break;
			bcopy(p, &r, sizeof(int));
			if (do_rule) {
				if (!(frule <= r && lrule >= r))
					continue;
			}
			printf("nat %u: %s\n", r, p+sizeof(int));
		}
	}
}
back to top