PERFORCE change 99995 for review

Paolo Pisati piso at FreeBSD.org
Sun Jun 25 18:39:26 UTC 2006


http://perforce.freebsd.org/chv.cgi?CH=99995

Change 99995 by piso at piso_newluxor on 2006/06/25 18:38:49

	Apply my libalias+ipfw patch

Affected files ...

.. //depot/projects/soc2005/libalias/etc/Makefile#2 edit
.. //depot/projects/soc2005/libalias/etc/libalias.conf#1 add
.. //depot/projects/soc2005/libalias/lib/libalias/Makefile#2 edit
.. //depot/projects/soc2005/libalias/lib/libalias/lib-cuseeme/Makefile#1 add
.. //depot/projects/soc2005/libalias/lib/libalias/lib-dummy/Makefile#1 add
.. //depot/projects/soc2005/libalias/lib/libalias/lib-ftp/Makefile#1 add
.. //depot/projects/soc2005/libalias/lib/libalias/lib-irc/Makefile#1 add
.. //depot/projects/soc2005/libalias/lib/libalias/lib-libalias/Makefile#1 add
.. //depot/projects/soc2005/libalias/lib/libalias/lib-nbt/Makefile#1 add
.. //depot/projects/soc2005/libalias/lib/libalias/lib-pptp/Makefile#1 add
.. //depot/projects/soc2005/libalias/lib/libalias/lib-skinny/Makefile#1 add
.. //depot/projects/soc2005/libalias/lib/libalias/lib-smedia/Makefile#1 add
.. //depot/projects/soc2005/libalias/sbin/ipfw/ipfw.8#2 edit
.. //depot/projects/soc2005/libalias/sbin/ipfw/ipfw2.c#2 edit
.. //depot/projects/soc2005/libalias/sbin/natd/natd.c#2 edit
.. //depot/projects/soc2005/libalias/sys/conf/files#2 edit
.. //depot/projects/soc2005/libalias/sys/modules/libalias/Makefile#2 edit
.. //depot/projects/soc2005/libalias/sys/modules/libalias/kld-cuseeme/Makefile#1 add
.. //depot/projects/soc2005/libalias/sys/modules/libalias/kld-dummy/Makefile#1 add
.. //depot/projects/soc2005/libalias/sys/modules/libalias/kld-ftp/Makefile#1 add
.. //depot/projects/soc2005/libalias/sys/modules/libalias/kld-irc/Makefile#1 add
.. //depot/projects/soc2005/libalias/sys/modules/libalias/kld-libalias/Makefile#1 add
.. //depot/projects/soc2005/libalias/sys/modules/libalias/kld-nbt/Makefile#1 add
.. //depot/projects/soc2005/libalias/sys/modules/libalias/kld-pptp/Makefile#1 add
.. //depot/projects/soc2005/libalias/sys/modules/libalias/kld-skinny/Makefile#1 add
.. //depot/projects/soc2005/libalias/sys/modules/libalias/kld-smedia/Makefile#1 add
.. //depot/projects/soc2005/libalias/sys/netinet/in.h#2 edit
.. //depot/projects/soc2005/libalias/sys/netinet/ip_fw.h#2 edit
.. //depot/projects/soc2005/libalias/sys/netinet/ip_fw2.c#2 edit
.. //depot/projects/soc2005/libalias/sys/netinet/ip_fw_pfil.c#2 edit
.. //depot/projects/soc2005/libalias/sys/netinet/libalias/alias.c#2 edit
.. //depot/projects/soc2005/libalias/sys/netinet/libalias/alias.h#2 edit
.. //depot/projects/soc2005/libalias/sys/netinet/libalias/alias_cuseeme.c#2 edit
.. //depot/projects/soc2005/libalias/sys/netinet/libalias/alias_db.c#2 edit
.. //depot/projects/soc2005/libalias/sys/netinet/libalias/alias_dummy.c#1 add
.. //depot/projects/soc2005/libalias/sys/netinet/libalias/alias_ftp.c#2 edit
.. //depot/projects/soc2005/libalias/sys/netinet/libalias/alias_irc.c#2 edit
.. //depot/projects/soc2005/libalias/sys/netinet/libalias/alias_local.h#2 edit
.. //depot/projects/soc2005/libalias/sys/netinet/libalias/alias_mod.c#1 add
.. //depot/projects/soc2005/libalias/sys/netinet/libalias/alias_mod.h#1 add
.. //depot/projects/soc2005/libalias/sys/netinet/libalias/alias_nbt.c#2 edit
.. //depot/projects/soc2005/libalias/sys/netinet/libalias/alias_old.c#2 edit
.. //depot/projects/soc2005/libalias/sys/netinet/libalias/alias_pptp.c#2 edit
.. //depot/projects/soc2005/libalias/sys/netinet/libalias/alias_proxy.c#2 edit
.. //depot/projects/soc2005/libalias/sys/netinet/libalias/alias_skinny.c#2 edit
.. //depot/projects/soc2005/libalias/sys/netinet/libalias/alias_smedia.c#2 edit
.. //depot/projects/soc2005/libalias/sys/netinet/libalias/alias_util.c#2 edit
.. //depot/projects/soc2005/libalias/sys/netinet/libalias/libalias.3#2 edit
.. //depot/projects/soc2005/libalias/sys/netinet/raw_ip.c#2 edit
.. //depot/projects/soc2005/libalias/usr.sbin/ppp/main.c#2 edit

Differences ...

==== //depot/projects/soc2005/libalias/etc/Makefile#2 (text+ko) ====

@@ -11,7 +11,7 @@
 	crontab csh.cshrc csh.login csh.logout devd.conf devfs.conf \
 	dhclient.conf disktab fbtab ftpusers gettytab group \
 	hosts hosts.allow hosts.equiv hosts.lpd \
-	inetd.conf login.access login.conf mac.conf motd \
+	inetd.conf libalias.conf login.access login.conf mac.conf motd \
 	netconfig network.subr networks newsyslog.conf nsswitch.conf \
 	portsnap.conf pf.conf pf.os phones profile protocols \
 	rc rc.bsdextended rc.firewall rc.firewall6 rc.initdiskless \

==== //depot/projects/soc2005/libalias/lib/libalias/Makefile#2 (text+ko) ====

@@ -1,16 +1,12 @@
 # $FreeBSD: src/lib/libalias/Makefile,v 1.31 2005/07/22 17:18:59 kensmith Exp $
-
-.PATH:	${.CURDIR}/../../sys/netinet/libalias
-
-LIB=	alias
-SHLIBDIR?= /lib
-SHLIB_MAJOR=	5
-MAN=	libalias.3
-SRCS=	alias.c alias_cuseeme.c alias_db.c alias_ftp.c alias_irc.c \
-	alias_nbt.c alias_pptp.c alias_proxy.c alias_skinny.c alias_smedia.c \
-	alias_util.c alias_old.c
-INCS=	alias.h
-WARNS?=	6
-NO_WERROR=
-
-.include <bsd.lib.mk>
+SUBDIR=	lib-cuseeme \
+      	lib-dummy \
+      	lib-ftp \
+      	lib-irc \
+      	lib-libalias \
+      	lib-nbt \
+      	lib-pptp \
+      	lib-skinny \
+      	lib-smedia
+  
+.include <bsd.subdir.mk>

==== //depot/projects/soc2005/libalias/sbin/ipfw/ipfw.8#2 (text+ko) ====

@@ -2031,6 +2031,98 @@
 If no socket is bound to the destination port, or if the divert module is
 not loaded, or if the kernel was not compiled with divert socket support,
 the packets are dropped.
+.Sh IPFW NAT
+To support nat operations inside ipfw, the syntax was extended with a
+new action: nat.
+Then, to configure/handle nat instances the following syntax was
+added (trying to follow closely pipe|queue options):
+.Bd -ragged -offset indent
+.Bk -words
+.Cm nat 
+.Ar nat_number 
+.Cm config 
+.Ar options
+.Ek
+.Ed
+.Pp
+where 
+.Ar options 
+is one or more mandatory fields that can assume the 
+following values:
+.Bl -tag -width indent
+.It Cm ip Ar ip_address
+Define an ip address to use for aliasing
+.It Cm if Ar nic
+Use ip addres of NIC for aliasing, dynamically changing
+it if NIC's ip address change
+.It Cm log
+Enable logging on this nat instance
+.It Cm deny_in
+Deny any incoming connection from outside world
+.It Cm same_ports
+Try to leave the alias port numbers unchanged from
+the actual local port numbers
+.It Cm unreg_only
+Traffic on the local network not originating from an
+unregistered address spaces will be ignored                   
+.It Cm reset
+Reset table of the packet aliasing engine on address change
+.It Cm reverse
+Reverse the way libalias handles aliasing
+.It Cm proxy_only
+Obey transparent proxy rules only, packet aliasing is not performed
+.El
+.Pp
+For more information about aliasing modes, take a look 
+at libalias(
+.Xr libalias 3
+).
+.Pp
+Other commands to manipulate nats are:
+.Bd -ragged -offset indent
+.Bk -words
+.Cm nat 
+.Ar nat_number 
+.Cm show
+.Cm config 
+.Ek
+.Ed
+.Pp
+to see nat configuration of
+.Ar nat_number
+.
+.Pp
+.Bd -ragged -offset indent
+.Bk -words
+.Cm nat 
+.Ar nat_number 
+.Cm show
+.Ek
+.Ed
+.Pp
+to see the logs of
+.Ar nat_number
+(if any)
+.Pp
+In these two previous examples
+.Ar nat_number 
+could be a single number to see the configuration of that
+instance (i.e. 123, a range of numbers (i.e 333-555) to see the
+configurations all the instances in that range or nothing, to see all
+the configured instances.
+.Pp
+See Section
+.Sx EXAMPLES
+for some examples on how to use nat.
+.Sh REDIRECT AND LSNAT SUPPORT IN IPFW
+Redirect and LSNAT support follow closely the syntax used in natd: refer to natd's man page 
+for syntax details.
+The only difference between natd's redirect and ipfw redirect is:
+instead of redirect_[addr|port|prot] i chose redir_[addr|port|proto].
+.Pp
+See Section
+.Sx EXAMPLES
+for some examples on how to do redirect and lsnat.
 .Sh SYSCTL VARIABLES
 A set of
 .Xr sysctl 8
@@ -2410,6 +2502,55 @@
 Otherwise, e.g.\& if
 you cannot access your box, the ruleset will be disabled after
 the sleep terminates thus restoring the previous situation.
+.Ss NAT, REDIRECT AND LSNAT
+First redirect all the traffic to nat instance 123:
+.Pp
+.Dl "ipfw add nat 123 all from any to any"
+.Pp
+Then to configure nat instance 123 to alias all the outgoing traffic with ip
+192.168.0.123, blocking all incoming connections, trying to keep
+same ports on both sides, clearing aliasing table on address change 
+and keeping a log of traffic/link statistics:
+.Pp
+.Dl "ipfw nat 123 config ip 192.168.0.123 log deny_in reset same_ports"
+.Pp
+Or to change address of instance 123, aliasing table will be cleared (see
+reset option):
+.Pp
+.Dl "ipfw nat 123 config ip 10.0.0.1"
+.Pp
+To see configuration of nat instance 123:
+.Pp
+.Dl "ipfw nat 123 show config"
+.Pp
+To show logs of all the instances in range 111-999:
+.Pp
+.Dl "ipfw nat 111-999 show"
+.Pp
+To see configurations of all instances:
+.Pp
+.Dl "ipfw nat show config"
+.Pp
+Or a redirect rule with mixed modes could looks like:
+.Pp
+.Dl "ipfw nat 123 config redir_addr 10.0.0.1 10.0.0.66"
+.Dl "				redir_port tcp 192.168.0.1:80 500"
+.Dl "				redir_proto udp 192.168.1.43 192.168.1.1"
+.Dl "				redir_addr 192.168.0.10,192.168.0.11"
+.Dl "					    10.0.0.100	# LSNAT"
+.Dl "				redir_port tcp 192.168.0.1:80,192.168.0.10:22" 
+.Dl "					    500		# LSNAT"
+.Pp
+or it could be splitted in:
+.Pp
+.Dl "ipfw nat 1 config redir_addr 10.0.0.1 10.0.0.66"
+.Dl "ipfw nat 2 config redir_port tcp 192.168.0.1:80 500"
+.Dl "ipfw nat 3 config redir_proto udp 192.168.1.43 192.168.1.1"
+.Dl "ipfw nat 4 config redir_addr 192.168.0.10,192.168.0.11,192.168.0.12" 
+.Dl "				         10.0.0.100"
+.Dl "ipfw nat 5 config redir_port tcp"
+.Dl "			192.168.0.1:80,192.168.0.10:22,192.168.0.20:25 500"
+.Pp
 .Sh SEE ALSO
 .Xr cpp 1 ,
 .Xr m4 1 ,
@@ -2451,6 +2592,11 @@
 .An Daniel Boulet
 for BSDI.
 .Pp
+.An -nosplit
+In-kernel NAT support written by
+.An Paolo Pisati Aq piso at FreeBSD.org
+as part of a Summer of Code 2005 project.
+.Pp
 Work on
 .Xr dummynet 4
 traffic shaper supported by Akamba Corp.

==== //depot/projects/soc2005/libalias/sbin/ipfw/ipfw2.c#2 (text+ko) ====

@@ -48,6 +48,7 @@
 #include <fcntl.h>
 
 #include <net/if.h>
+#include <net/if_dl.h>
 #include <net/pfvar.h>
 #include <net/route.h> /* def. of struct route */
 #include <netinet/in.h>
@@ -59,12 +60,14 @@
 #include <netinet/ip_dummynet.h>
 #include <netinet/tcp.h>
 #include <arpa/inet.h>
+#include <alias.h>
 
 int
 		do_resolv,		/* Would try to resolve all */
 		do_time,		/* Show time stamps */
 		do_quiet,		/* Be quiet in add and flush */
 		do_pipe,		/* this cmd refers to a pipe */
+	        do_nat, 		/* nat configuration */
 		do_sort,		/* field to sort results (0 = no) */
 		do_dynamic,		/* display dynamic rules */
 		do_expired,		/* display expired dynamic rules */
@@ -247,6 +250,7 @@
 	TOK_RESET,
 	TOK_UNREACH,
 	TOK_CHECKSTATE,
+	TOK_NAT,
 
 	TOK_ALTQ,
 	TOK_LOG,
@@ -309,6 +313,18 @@
 	TOK_DROPTAIL,
 	TOK_PROTO,
 	TOK_WEIGHT,
+	TOK_IP,
+	TOK_IF,
+ 	TOK_LOG,
+ 	TOK_DENY_INC,
+ 	TOK_SAME_PORTS,
+ 	TOK_UNREG_ONLY,
+ 	TOK_RESET_ADDR,
+ 	TOK_ALIAS_REV,
+ 	TOK_PROXY_ONLY,
+	TOK_REDIR_ADDR,
+	TOK_REDIR_PORT,
+	TOK_REDIR_PROTO,	
 
 	TOK_IPV6,
 	TOK_FLOWID,
@@ -351,6 +367,22 @@
 	{ NULL, 0 }	/* terminator */
 };
 
+struct _s_x nat_params[] = {
+	{ "ip",	                TOK_IP },
+	{ "if",	                TOK_IF },
+ 	{ "log",                TOK_LOG },
+ 	{ "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 },
+	{ "redir_addr",	        TOK_REDIR_ADDR },
+	{ "redir_port",	        TOK_REDIR_PORT },
+	{ "redir_proto",        TOK_REDIR_PROTO },
+ 	{ NULL, 0 }	/* terminator */
+};
+
 struct _s_x rule_actions[] = {
 	{ "accept",		TOK_ACCEPT },
 	{ "pass",		TOK_ACCEPT },
@@ -375,6 +407,7 @@
 	{ "unreach",		TOK_UNREACH },
 	{ "check-state",	TOK_CHECKSTATE },
 	{ "//",			TOK_COMMENT },
+	{ "nat",                TOK_NAT},
 	{ NULL, 0 }	/* terminator */
 };
 
@@ -482,7 +515,7 @@
 {
 	static int s = -1;	/* the socket */
 	int i;
-
+	
 	if (test_only)
 		return 0;
 
@@ -493,7 +526,8 @@
 
 	if (optname == IP_FW_GET || optname == IP_DUMMYNET_GET ||
 	    optname == IP_FW_ADD || optname == IP_FW_TABLE_LIST ||
-	    optname == IP_FW_TABLE_GETSIZE)
+	    optname == IP_FW_TABLE_GETSIZE || optname == IP_FW_NAT_GET_CONFIG ||
+ 	    optname == IP_FW_NAT_GET_LOG)
 		i = getsockopt(s, IPPROTO_IP, optname, optval,
 			(socklen_t *)optlen);
 	else
@@ -1563,6 +1597,10 @@
 			tagptr = cmd;
 			break;
 
+		case O_NAT:
+ 			printf("nat %u", cmd->arg1);
+ 			break;
+			
 		default:
 			printf("** unrecognized action %d len %d ",
 				cmd->opcode, cmd->len);
@@ -2625,13 +2663,16 @@
 "add [num] [set N] [prob x] RULE-BODY\n"
 "{pipe|queue} N config PIPE-BODY\n"
 "[pipe|queue] {zero|delete|show} [N{,N}]\n"
+"nat N config {ip IPADDR|if IFNAME|log|deny_in|same_ports|unreg_only|reset|\n"
+"              reverse|proxy_only|redir_addr linkspec| redir_port linkspec|\n"
+"              redir_proto linkspec}\n"
 "set [disable N... enable N...] | move [rule] X to Y | swap X Y | show\n"
 "table N {add ip[/bits] [value] | delete ip[/bits] | flush | list}\n"
 "\n"
 "RULE-BODY:	check-state [PARAMS] | ACTION [PARAMS] ADDR [OPTION_LIST]\n"
 "ACTION:	check-state | allow | count | deny | unreach{,6} CODE |\n"
 "               skipto N | {divert|tee} PORT | forward ADDR |\n"
-"               pipe N | queue N\n"
+"               pipe N | queue N | nat N\n"
 "PARAMS: 	[log [logamount LOGLIMIT]] [altq QUEUE_NAME]\n"
 "ADDR:		[ MAC dst src ether_type ] \n"
 "		[ ip from IPADDR [ PORT ] to IPADDR [ PORTLIST ] ]\n"
@@ -3140,7 +3181,14 @@
 	/* Rule number */
 	while (ac && isdigit(**av)) {
 		i = atoi(*av); av++; ac--;
-		if (do_pipe) {
+		if (do_nat) {
+			exitval = do_cmd(IP_FW_NAT_DEL, &i, sizeof i);
+			if (exitval) {
+				exitval = EX_UNAVAILABLE;
+				warn("rule %u not available",
+				     i);
+			}
+ 		} else if (do_pipe) {
 			if (do_pipe == 1)
 				p.pipe_nr = i;
 			else
@@ -3165,7 +3213,6 @@
 		exit(exitval);
 }
 
-
 /*
  * fill the interface structure. We do not check the name as we can
  * create interfaces dynamically, so checking them at insert time
@@ -3189,7 +3236,754 @@
 		errx(EX_DATAERR, "bad ip address ``%s''", arg);
 }
 
+/* 
+ * 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;	/* Only IP addresses please */
+	mib[4] = NET_RT_IFLIST;
+	mib[5] = 0;		/* ifIndex??? */
+/*
+ * Get interface data.
+ */
+	if (sysctl(mib, 6, NULL, &needed, NULL, 0) == -1)
+		err(1, "iflist-sysctl-estimate");
+	if ((buf = malloc(needed)) == NULL)
+		errx(1, "malloc failed");
+	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 (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 (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 of 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;
+
+	errx (EX_DATAERR, "unknown protocol %s. Expected 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, int len,
+		 int *_ac, char ***_av) 
+{
+	char **av = *_av, *sep; /* token separator */
+	/* temporary buffer used to hold server pool ip's */
+	char tmp_spool_buf[NAT_BUF_LEN]; 
+	int ac = *_ac, i, space = 0, lsnat = 0;
+	int sof_redir = sizeof(struct cfg_redir);
+	struct cfg_redir *r;	
+
+	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, "redir_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, "redir_addr: missing public address");
+	StrToAddr(*av, &r->paddr);
+	INC_ARGCV();
+
+	/* setup LSNAT server pool */
+	if (sep) {
+		int                     sof_spool = sizeof(struct cfg_spool);
+		struct cfg_spool        *tmp;		
+
+		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, "redir_addr: buf is too small\n");
+}
+
+static int
+setup_redir_port(char *spool_buf, int len,
+		 int *_ac, char ***_av) 
+{
+	char **av = *_av, *sep, *protoName;
+	char tmp_spool_buf[NAT_BUF_LEN];
+	int ac = *_ac, space = 0, lsnat = 0;
+	int sof_redir = sizeof(struct cfg_redir);
+	struct cfg_redir *r;
+	u_short numLocalPorts = 0;
+	port_range      portRange;
+
+	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, ',');
+	if (sep) {		/* LSNAT redirection syntax. */
+		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 {
+		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);
+	r->pport_cnt = GETNUMPORTS(portRange);
+	INC_ARGCV();
+
+	/*
+	 * Extract remote address and optionally port.
+	 */	
+	/* 
+	 * 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) {
+		int                     sof_spool = sizeof(struct cfg_spool);
+		struct cfg_spool        *tmp;
+		
+		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;
+			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, "redir_port: buf is too small\n");
+}
+
+static int
+setup_redir_proto(char *spool_buf, int len,
+		 int *_ac, char ***_av) 
+{
+	char **av = *_av;
+	int ac = *_ac, i, space;
+	struct protoent *protoent;
+	int sof_redir = sizeof(struct cfg_redir);
+	struct cfg_redir *r;
+	
+	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, "redir_proto: buf is too small\n");
+}
+
+static void
+show_nat(int ac, char **av);
+
+static void
+print_nat_config(char *buf) {
+	struct cfg_nat *n = (struct cfg_nat *)buf;
+	int i, cnt, flag = 1, off = sizeof(*n);
+	int sof_redir = sizeof(struct cfg_redir);
+	int sof_spool = sizeof(struct cfg_spool);
+	struct cfg_redir *t;
+	struct cfg_spool *s;
+	struct protoent *p;
+
+	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++) {

>>> TRUNCATED FOR MAIL (1000 lines) <<<


More information about the p4-projects mailing list