[Midnightbsd-cvs] src: sbin/ipfw: merge changes

laffer1 at midnightbsd.org laffer1 at midnightbsd.org
Fri Nov 21 17:30:56 EST 2008


Log Message:
-----------
merge changes

Modified Files:
--------------
    src/sbin/ipfw:
        ipfw.8 (r1.2 -> r1.3)
        ipfw2.c (r1.2 -> r1.3)

-------------- next part --------------
Index: ipfw2.c
===================================================================
RCS file: /home/cvs/src/sbin/ipfw/ipfw2.c,v
retrieving revision 1.2
retrieving revision 1.3
diff -L sbin/ipfw/ipfw2.c -L sbin/ipfw/ipfw2.c -u -r1.2 -r1.3
--- sbin/ipfw/ipfw2.c
+++ sbin/ipfw/ipfw2.c
@@ -17,7 +17,7 @@
  *
  * NEW command line interface for IP firewall facility
  *
- * $FreeBSD: src/sbin/ipfw/ipfw2.c,v 1.76.2.6 2006/03/09 09:18:37 ume Exp $
+ * $FreeBSD: src/sbin/ipfw/ipfw2.c,v 1.108.4.1 2008/01/28 17:44:30 rwatson Exp $
  */
 
 #include <sys/param.h>
@@ -47,7 +47,11 @@
 #include <unistd.h>
 #include <fcntl.h>
 
+#define IPFW_INTERNAL	/* Access to protected structures in ip_fw.h. */
+
+#include <net/ethernet.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,17 +63,20 @@
 #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 */
 		do_compact,		/* show rules in compact mode */
 		do_force,		/* do not ask for confirmation */
+		use_set,		/* work with specified set number */
 		show_sets,		/* display rule sets */
 		test_only,		/* only check syntax */
 		comment_only,		/* only print action and comment */
@@ -82,6 +89,44 @@
  */
 #define NEED1(msg)      {if (!ac) errx(EX_USAGE, msg);}
 
+#define GET_UINT_ARG(arg, min, max, tok, s_x) do {			\
+	if (!ac)							\
+		errx(EX_USAGE, "%s: missing argument", match_value(s_x, tok)); \
+	if (_substrcmp(*av, "tablearg") == 0) {				\
+		arg = IP_FW_TABLEARG;					\
+		break;							\
+	}								\
+									\
+	{								\
+	long val;							\
+	char *end;							\
+									\
+	val = strtol(*av, &end, 10);					\
+									\
+	if (!isdigit(**av) || *end != '\0' || (val == 0 && errno == EINVAL)) \
+		errx(EX_DATAERR, "%s: invalid argument: %s",		\
+		    match_value(s_x, tok), *av);			\
+									\
+	if (errno == ERANGE || val < min || val > max)			\
+		errx(EX_DATAERR, "%s: argument is out of range (%u..%u): %s", \
+		    match_value(s_x, tok), min, max, *av);		\
+									\
+	if (val == IP_FW_TABLEARG)					\
+		errx(EX_DATAERR, "%s: illegal argument value: %s",	\
+		    match_value(s_x, tok), *av);			\
+	arg = val;							\
+	}								\
+} while (0)
+
+#define PRINT_UINT_ARG(str, arg) do {					\
+	if (str != NULL)						\
+		printf("%s",str);					\
+	if (arg == IP_FW_TABLEARG)					\
+		printf("tablearg");					\
+	else								\
+		printf("%u", (uint32_t)arg);				\
+} while (0)
+
 /*
  * _s_x is a structure that stores a string <-> token pairs, used in
  * various places in the parser. Entries are stored in arrays,
@@ -209,10 +254,14 @@
 	TOK_RESET,
 	TOK_UNREACH,
 	TOK_CHECKSTATE,
+	TOK_NAT,
 
 	TOK_ALTQ,
 	TOK_LOG,
+	TOK_TAG,
+	TOK_UNTAG,
 
+	TOK_TAGGED,
 	TOK_UID,
 	TOK_GID,
 	TOK_JAIL,
@@ -268,6 +317,18 @@
 	TOK_DROPTAIL,
 	TOK_PROTO,
 	TOK_WEIGHT,
+	TOK_IP,
+	TOK_IF,
+ 	TOK_ALOG,
+ 	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,
@@ -310,6 +371,22 @@
 	{ NULL, 0 }	/* terminator */
 };
 
+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 */
+};
+
 struct _s_x rule_actions[] = {
 	{ "accept",		TOK_ACCEPT },
 	{ "pass",		TOK_ACCEPT },
@@ -334,16 +411,20 @@
 	{ "unreach",		TOK_UNREACH },
 	{ "check-state",	TOK_CHECKSTATE },
 	{ "//",			TOK_COMMENT },
+	{ "nat",                TOK_NAT },
 	{ NULL, 0 }	/* terminator */
 };
 
 struct _s_x rule_action_params[] = {
 	{ "altq",		TOK_ALTQ },
 	{ "log",		TOK_LOG },
+	{ "tag",		TOK_TAG },
+	{ "untag",		TOK_UNTAG },
 	{ NULL, 0 }	/* terminator */
 };
 
 struct _s_x rule_options[] = {
+	{ "tagged",		TOK_TAGGED },
 	{ "uid",		TOK_UID },
 	{ "gid",		TOK_GID },
 	{ "jail",		TOK_JAIL },
@@ -449,7 +530,9 @@
 
 	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
@@ -572,6 +655,7 @@
 	{"ipttl",	O_IPTTL},
 	{"mac-type",	O_MAC_TYPE},
 	{"tcpdatalen",	O_TCPDATALEN},
+	{"tagged",	O_TAGGED},
 	{NULL,		0}
 };
 
@@ -586,8 +670,6 @@
 	int i;
 	char const *sep;
 
-	if (cmd->o.len & F_NOT)
-		printf(" not");
 	if (opcode != 0) {
 		sep = match_value(_port_name, opcode);
 		if (sep == NULL)
@@ -785,30 +867,39 @@
 
 	while (*s) {
 		a = strtoport(av, &s, 0, proto);
-		if (s == av) /* no parameter */
-			break;
-		if (*s == '-') { /* a range */
-			av = s+1;
+		if (s == av) 			/* empty or invalid argument */
+			return (0);
+
+		switch (*s) {
+		case '-':			/* a range */
+			av = s + 1;
 			b = strtoport(av, &s, 0, proto);
-			if (s == av) /* no parameter */
-				break;
+			/* Reject expressions like '1-abc' or '1-2-3'. */
+			if (s == av || (*s != ',' && *s != '\0'))
+				return (0);
 			p[0] = a;
 			p[1] = b;
-		} else if (*s == ',' || *s == '\0' )
+			break;
+		case ',':			/* comma separated list */
+		case '\0':
 			p[0] = p[1] = a;
-		else 	/* invalid separator */
-			errx(EX_DATAERR, "invalid separator <%c> in <%s>\n",
+			break;
+		default:
+			warnx("port list: invalid separator <%c> in <%s>",
 				*s, av);
+			return (0);
+		}
+
 		i++;
 		p += 2;
-		av = s+1;
+		av = s + 1;
 	}
 	if (i > 0) {
-		if (i+1 > F_LEN_MASK)
+		if (i + 1 > F_LEN_MASK)
 			errx(EX_DATAERR, "too many ports/ranges\n");
-		cmd->o.len |= i+1; /* leave F_NOT and F_OR untouched */
+		cmd->o.len |= i + 1;	/* leave F_NOT and F_OR untouched */
 	}
-	return i;
+	return (i);
 }
 
 static struct _s_x icmpcodes[] = {
@@ -1152,7 +1243,7 @@
 {
        uint8_t type;
 
-       cmd->d[0] = 0;
+       bzero(cmd, sizeof(*cmd));
        while (*av) {
            if (*av == ',')
                av++;
@@ -1212,6 +1303,8 @@
        { "dstopt",     EXT_DSTOPTS },
        { "ah",         EXT_AH },
        { "esp",        EXT_ESP },
+       { "rthdr0",     EXT_RTHDR0 },
+       { "rthdr2",     EXT_RTHDR2 },
        { NULL,         0 }
 };
 
@@ -1252,6 +1345,14 @@
                cmd->arg1 |= EXT_ESP;
                break;
 
+           case EXT_RTHDR0:
+               cmd->arg1 |= EXT_RTHDR0;
+               break;
+
+           case EXT_RTHDR2:
+               cmd->arg1 |= EXT_RTHDR2;
+               break;
+
            default:
                errx( EX_DATAERR, "invalid option for ipv6 exten header" );
                break;
@@ -1282,6 +1383,14 @@
            printf("%crouting options", sep);
            sep = ',';
        }
+       if (cmd->arg1 & EXT_RTHDR0 ) {
+           printf("%crthdr0", sep);
+           sep = ',';
+       }
+       if (cmd->arg1 & EXT_RTHDR2 ) {
+           printf("%crthdr2", sep);
+           sep = ',';
+       }
        if (cmd->arg1 & EXT_DSTOPTS ) {
            printf("%cdestination options", sep);
            sep = ',';
@@ -1311,10 +1420,8 @@
 #define	HAVE_PROTO	0x0001
 #define	HAVE_SRCIP	0x0002
 #define	HAVE_DSTIP	0x0004
-#define	HAVE_MAC	0x0008
-#define	HAVE_MACTYPE	0x0010
-#define	HAVE_PROTO4	0x0040
-#define	HAVE_PROTO6	0x0080
+#define	HAVE_PROTO4	0x0008
+#define	HAVE_PROTO6	0x0010
 #define	HAVE_OPTIONS	0x8000
 
 #define	HAVE_IP		(HAVE_PROTO | HAVE_SRCIP | HAVE_DSTIP)
@@ -1326,16 +1433,6 @@
 	if ( (*flags & HAVE_IP) == HAVE_IP)
 		*flags |= HAVE_OPTIONS;
 
-	if ( (*flags & (HAVE_MAC|HAVE_MACTYPE|HAVE_OPTIONS)) == HAVE_MAC &&
-	     cmd != O_MAC_TYPE) {
-		/*
-		 * mac-type was optimized out by the compiler,
-		 * restore it
-		 */
-		printf(" any");
-		*flags |= HAVE_MACTYPE | HAVE_OPTIONS;
-		return;
-	}
 	if ( !(*flags & HAVE_OPTIONS)) {
 		if ( !(*flags & HAVE_PROTO) && (want & HAVE_PROTO))
 			if ( (*flags & HAVE_PROTO4))
@@ -1358,7 +1455,7 @@
 {
 	static int twidth = 0;
 	int l;
-	ipfw_insn *cmd;
+	ipfw_insn *cmd, *tagptr = NULL;
 	char *comment = NULL;	/* ptr to comment if we have one */
 	int proto = 0;		/* default */
 	int flags = 0;	/* prerequisites */
@@ -1459,34 +1556,43 @@
 				print_unreach6_code(cmd->arg1);
 			break;
 
-#define	PRINT_WITH_ARG(o)						\
-			if (cmd->arg1 == IP_FW_TABLEARG)		\
-				printf("%s tablearg", (o));		\
-			else						\
-				printf("%s %u", (o), cmd->arg1);	\
+		case O_SKIPTO:
+			PRINT_UINT_ARG("skipto ", cmd->arg1);
 			break;
 
-		case O_SKIPTO:
-			PRINT_WITH_ARG("skipto");
 		case O_PIPE:
-			PRINT_WITH_ARG("pipe");
+			PRINT_UINT_ARG("pipe ", cmd->arg1);
+			break;
+
 		case O_QUEUE:
-			PRINT_WITH_ARG("queue");
+			PRINT_UINT_ARG("queue ", cmd->arg1);
+			break;
+
 		case O_DIVERT:
-			PRINT_WITH_ARG("divert");
+			PRINT_UINT_ARG("divert ", cmd->arg1);
+			break;
+
 		case O_TEE:
-			PRINT_WITH_ARG("tee");
+			PRINT_UINT_ARG("tee ", cmd->arg1);
+			break;
+
 		case O_NETGRAPH:
-			PRINT_WITH_ARG("netgraph");
+			PRINT_UINT_ARG("netgraph ", cmd->arg1);
+			break;
+
 		case O_NGTEE:
-			PRINT_WITH_ARG("ngtee");
-#undef PRINT_WITH_ARG
+			PRINT_UINT_ARG("ngtee ", cmd->arg1);
+			break;
 
 		case O_FORWARD_IP:
 		    {
 			ipfw_insn_sa *s = (ipfw_insn_sa *)cmd;
 
-			printf("fwd %s", inet_ntoa(s->sa.sin_addr));
+			if (s->sa.sin_addr.s_addr == INADDR_ANY) {
+				printf("fwd tablearg");
+			} else {
+				printf("fwd %s", inet_ntoa(s->sa.sin_addr));
+			}
 			if (s->sa.sin_port)
 				printf(",%d", s->sa.sin_port);
 		    }
@@ -1500,6 +1606,14 @@
 			altqptr = (ipfw_insn_altq *)cmd;
 			break;
 
+		case O_TAG:
+			tagptr = cmd;
+			break;
+
+		case O_NAT:
+ 			printf("nat %u", cmd->arg1);
+ 			break;
+			
 		default:
 			printf("** unrecognized action %d len %d ",
 				cmd->opcode, cmd->len);
@@ -1520,6 +1634,12 @@
 		else
 			printf(" altq %s", qname);
 	}
+	if (tagptr) {
+		if (tagptr->len & F_NOT)
+			PRINT_UINT_ARG(" untag ", tagptr->arg1);
+		else
+			PRINT_UINT_ARG(" tag ", tagptr->arg1);
+	}
 
 	/*
 	 * then print the body.
@@ -1568,28 +1688,6 @@
 		case O_PROBE_STATE:
 			break; /* no need to print anything here */
 
-		case O_MACADDR2: {
-			ipfw_insn_mac *m = (ipfw_insn_mac *)cmd;
-
-			if ((cmd->len & F_OR) && !or_block)
-				printf(" {");
-			if (cmd->len & F_NOT)
-				printf(" not");
-			printf(" MAC");
-			flags |= HAVE_MAC;
-			print_mac(m->addr, m->mask);
-			print_mac(m->addr + 6, m->mask + 6);
-			}
-			break;
-
-		case O_MAC_TYPE:
-			if ((cmd->len & F_OR) && !or_block)
-				printf(" {");
-			print_newports((ipfw_insn_u16 *)cmd, IPPROTO_ETHERTYPE,
-				(flags & HAVE_OPTIONS) ? cmd->opcode : 0);
-			flags |= HAVE_MAC | HAVE_MACTYPE | HAVE_OPTIONS;
-			break;
-
 		case O_IP_SRC:
 		case O_IP_SRC_LOOKUP:
 		case O_IP_SRC_MASK:
@@ -1657,6 +1755,8 @@
 			show_prerequisites(&flags, HAVE_PROTO|HAVE_SRCIP, 0);
 			if ((cmd->len & F_OR) && !or_block)
 				printf(" {");
+			if (cmd->len & F_NOT)
+				printf(" not");
 			print_newports((ipfw_insn_u16 *)cmd, proto,
 				(flags & HAVE_OPTIONS) ? cmd->opcode : 0);
 			break;
@@ -1697,6 +1797,21 @@
 			if (cmd->len & F_NOT && cmd->opcode != O_IN)
 				printf(" not");
 			switch(cmd->opcode) {
+			case O_MACADDR2: {
+				ipfw_insn_mac *m = (ipfw_insn_mac *)cmd;
+
+				printf(" MAC");
+				print_mac(m->addr, m->mask);
+				print_mac(m->addr + 6, m->mask + 6);
+				}
+				break;
+
+			case O_MAC_TYPE:
+				print_newports((ipfw_insn_u16 *)cmd,
+						IPPROTO_ETHERTYPE, cmd->opcode);
+				break;
+
+
 			case O_FRAG:
 				printf(" frag");
 				break;
@@ -1872,8 +1987,7 @@
 				printf(" keep-state");
 				break;
 
-			case O_LIMIT:
-			    {
+			case O_LIMIT: {
 				struct _s_x *p = limit_masks;
 				ipfw_insn_limit *c = (ipfw_insn_limit *)cmd;
 				uint8_t x = c->limit_mask;
@@ -1886,9 +2000,9 @@
 						printf("%s%s", comma, p->s);
 						comma = ",";
 					}
-				printf(" %d", c->conn_limit);
-			    }
+				PRINT_UINT_ARG(" ", c->conn_limit);
 				break;
+			}
 
 			case O_IP6:
 				printf(" ip6");
@@ -1906,6 +2020,14 @@
 				print_ext6hdr( (ipfw_insn *) cmd );
 				break;
 
+			case O_TAGGED:
+				if (F_LEN(cmd) == 1)
+					PRINT_UINT_ARG(" tagged ", cmd->arg1);
+				else
+					print_newports((ipfw_insn_u16 *)cmd, 0,
+					    O_TAGGED);
+				break;
+
 			default:
 				printf(" [opcode %d len %d]",
 				    cmd->opcode, cmd->len);
@@ -1931,6 +2053,7 @@
 	struct protoent *pe;
 	struct in_addr a;
 	uint16_t rulenum;
+	char buf[INET6_ADDRSTRLEN];
 
 	if (!do_expired) {
 		if (!d->expire && !(d->dyn_type == O_LIMIT_PARENT))
@@ -1959,11 +2082,20 @@
 	else
 		printf(" proto %u", d->id.proto);
 
-	a.s_addr = htonl(d->id.src_ip);
-	printf(" %s %d", inet_ntoa(a), d->id.src_port);
-
-	a.s_addr = htonl(d->id.dst_ip);
-	printf(" <-> %s %d", inet_ntoa(a), d->id.dst_port);
+	if (d->id.addr_type == 4) {
+		a.s_addr = htonl(d->id.src_ip);
+		printf(" %s %d", inet_ntoa(a), d->id.src_port);
+
+		a.s_addr = htonl(d->id.dst_ip);
+		printf(" <-> %s %d", inet_ntoa(a), d->id.dst_port);
+	} else if (d->id.addr_type == 6) {
+		printf(" %s %d", inet_ntop(AF_INET6, &d->id.src_ip6, buf,
+		    sizeof(buf)), d->id.src_port);
+		printf(" <-> %s %d", inet_ntop(AF_INET6, &d->id.dst_ip6, buf,
+		    sizeof(buf)), d->id.dst_port);
+	} else
+		printf(" UNKNOWN <-> UNKNOWN\n");
+	
 	printf("\n");
 }
 
@@ -2157,7 +2289,7 @@
 		char buf[30];
 		char prefix[80];
 
-		if (p->next.sle_next != (struct dn_pipe *)DN_IS_PIPE)
+		if (SLIST_NEXT(p, next) != (struct dn_pipe *)DN_IS_PIPE)
 			break;	/* done with pipes, now queues */
 
 		/*
@@ -2196,7 +2328,7 @@
 	for (fs = next; nbytes >= sizeof *fs; fs = next) {
 		char prefix[80];
 
-		if (fs->next.sle_next != (struct dn_flow_set *)DN_IS_QUEUE)
+		if (SLIST_NEXT(fs, next) != (struct dn_flow_set *)DN_IS_QUEUE)
 			break;
 		l = sizeof(*fs) + fs->rq_elements * sizeof(*q);
 		next = (char *)fs + l;
@@ -2369,6 +2501,7 @@
 	u_long rnum, last;
 	char *endptr;
 	int seen = 0;
+	uint8_t set;
 
 	const int ocmd = do_pipe ? IP_DUMMYNET_GET : IP_FW_GET;
 	int nalloc = 1024;	/* start somewhere... */
@@ -2423,6 +2556,10 @@
 	bcwidth = pcwidth = 0;
 	if (show_counters) {
 		for (n = 0, r = data; n < nstat; n++, r = NEXT(r)) {
+			/* skip rules from another set */
+			if (use_set && r->set != use_set - 1)
+				continue;
+
 			/* packet counter */
 			width = snprintf(NULL, 0, "%llu",
 			    align_uint64(&r->pcnt));
@@ -2438,6 +2575,13 @@
 	}
 	if (do_dynamic && ndyn) {
 		for (n = 0, d = dynrules; n < ndyn; n++, d++) {
+			if (use_set) {
+				/* skip rules from another set */
+				bcopy((char *)&d->rule + sizeof(uint16_t),
+				      &set, sizeof(uint8_t));
+				if (set != use_set - 1)
+					continue;
+			}
 			width = snprintf(NULL, 0, "%llu",
 			    align_uint64(&d->pcnt));
 			if (width > pcwidth)
@@ -2451,14 +2595,24 @@
 	}
 	/* if no rule numbers were specified, list all rules */
 	if (ac == 0) {
-		for (n = 0, r = data; n < nstat; n++, r = NEXT(r) )
+		for (n = 0, r = data; n < nstat; n++, r = NEXT(r)) {
+			if (use_set && r->set != use_set - 1)
+				continue;
 			show_ipfw(r, pcwidth, bcwidth);
+		}
 
 		if (do_dynamic && ndyn) {
 			printf("## Dynamic rules (%d):\n", ndyn);
-			for (n = 0, d = dynrules; n < ndyn; n++, d++)
+			for (n = 0, d = dynrules; n < ndyn; n++, d++) {
+				if (use_set) {
+					bcopy((char *)&d->rule + sizeof(uint16_t),
+					      &set, sizeof(uint8_t));
+					if (set != use_set - 1)
+						continue;
+				}
 				show_dyn_ipfw(d, pcwidth, bcwidth);
 		}
+		}
 		goto done;
 	}
 
@@ -2477,6 +2631,8 @@
 		for (n = seen = 0, r = data; n < nstat; n++, r = NEXT(r) ) {
 			if (r->rulenum > last)
 				break;
+			if (use_set && r->set != use_set - 1)
+				continue;
 			if (r->rulenum >= rnum && r->rulenum <= last) {
 				show_ipfw(r, pcwidth, bcwidth);
 				seen = 1;
@@ -2505,6 +2661,12 @@
 				bcopy(&d->rule, &rulenum, sizeof(rulenum));
 				if (rulenum > rnum)
 					break;
+				if (use_set) {
+					bcopy((char *)&d->rule + sizeof(uint16_t),
+					      &set, sizeof(uint8_t));
+					if (set != use_set - 1)
+						continue;
+				}
 				if (r->rulenum >= rnum && r->rulenum <= last)
 					show_dyn_ipfw(d, pcwidth, bcwidth);
 			}
@@ -2539,13 +2701,17 @@
 "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|redirect_addr linkspec|\n"
+"		redirect_port linkspec|redirect_proto linkspec}\n"
 "set [disable N... enable N...] | move [rule] X to Y | swap X Y | show\n"
+"set N {show|list|zero|resetlog|delete} [N{,N}] | flush\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"
@@ -2631,13 +2797,17 @@
 	 * ',' indicating another address follows, '{' indicating a
 	 * set of addresses of unspecified size.
 	 */
-	char *p = strpbrk(av, "/:,{");
+	char *t = NULL, *p = strpbrk(av, "/:,{");
 	int masklen;
-	char md;
+	char md, nd;
 
 	if (p) {
 		md = *p;
 		*p++ = '\0';
+		if ((t = strpbrk(p, ",{")) != NULL) {
+			nd = *t;
+			*t = '\0';
+		}
 	} else
 		md = '\0';
 
@@ -2671,6 +2841,8 @@
 		break;
 	}
 	d[0] &= d[1];		/* mask base address with mask */
+	if (t)
+		*t = nd;
 	/* find next separator */
 	if (p)
 		p = strpbrk(p, ",{");
@@ -2769,6 +2941,8 @@
 	len += 2;	/* two words... */
 	d += 2;
     } /* end while */
+    if (len + 1 > F_LEN_MASK)
+	errx(EX_DATAERR, "address list too long");
     cmd->o.len |= len+1;
 }
 
@@ -2920,6 +3094,8 @@
 	 * Total length of the command, remember that 1 is the size of
 	 * the base command.
 	 */
+	if (len + 1 > F_LEN_MASK)
+		errx(EX_DATAERR, "address list too long");
 	cmd->o.len |= len+1;
 	free(av);
 	return (1);
@@ -3047,6 +3223,11 @@
 	av++; ac--;
 	NEED1("missing rule specification");
 	if (ac > 0 && _substrcmp(*av, "set") == 0) {
+		/* Do not allow using the following syntax:
+		 *	ipfw set N delete set M
+		 */
+		if (use_set)
+			errx(EX_DATAERR, "invalid syntax");
 		do_set = 1;	/* delete set */
 		ac--; av++;
 	}
@@ -3054,7 +3235,13 @@
 	/* 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
@@ -3066,6 +3253,10 @@
 				    do_pipe == 1 ? p.pipe_nr : p.fs.fs_nr);
 			}
 		} else {
+			if (use_set)
+				rulenum = (i & 0xffff) | (5 << 24) |
+				    ((use_set - 1) << 16);
+			else
 			rulenum =  (i & 0xffff) | (do_set << 24);
 			i = do_cmd(IP_FW_DEL, &rulenum, sizeof rulenum);
 			if (i) {
@@ -3103,6 +3294,769 @@
 		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;	
+	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");
+	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 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, *sep; /* Token separator. */
+	/* Temporary buffer used to hold server pool ip's. */
+	char tmp_spool_buf[NAT_BUF_LEN]; 
+	int ac, i, 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, 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 {
+		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.
+	 */	
+	/* 
+	 * 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;
+			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, int len,
+		 int *_ac, char ***_av) 
+{
+	char **av;
+	int ac, i, 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
+show_nat(int ac, char **av);
+
+static void
+print_nat_config(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");
+}
+
+static void
+config_nat(int ac, char **av)
+{
+	struct cfg_nat *n;              /* Nat instance configuration. */
+	struct in_addr ip;
+	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:
+			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");
+
+	/* After every modification, we show the resultant rule. */
+	int _ac = 3;
+	char *_av[] = {"show", "config", id};
+	show_nat(_ac, _av);
+}
+
 static void
 config_pipe(int ac, char **av)
 {
@@ -3334,7 +4288,8 @@
 				end++;
 				p.bandwidth *= 1000000;
 			    }
-			    if (*end == 'B' ||
+			    if ((*end == 'B' &&
+				  _substrcmp2(end, "Bi", "Bit/s") != 0) ||
 			        _substrcmp2(end, "by", "bytes") == 0)
 				p.bandwidth *= 8;
 			    if (p.bandwidth < 0)
@@ -3462,36 +4417,50 @@
 }
 
 static void
-get_mac_addr_mask(char *p, uint8_t *addr, uint8_t *mask)
+get_mac_addr_mask(const char *p, uint8_t *addr, uint8_t *mask)
 {
 	int i, l;
-
-	for (i=0; i<6; i++)
-		addr[i] = mask[i] = 0;
-	if (strcmp(p, "any") == 0)
+	char *ap, *ptr, *optr;
+	struct ether_addr *mac;
+	const char *macset = "0123456789abcdefABCDEF:";
+
+	if (strcmp(p, "any") == 0) {
+		for (i = 0; i < ETHER_ADDR_LEN; i++)
+			addr[i] = mask[i] = 0;
 		return;
+	}
 
-	for (i=0; *p && i<6;i++, p++) {
-		addr[i] = strtol(p, &p, 16);
-		if (*p != ':') /* we start with the mask */
-			break;
-	}
-	if (*p == '/') { /* mask len */
-		l = strtol(p+1, &p, 0);
-		for (i=0; l>0; l -=8, i++)
-			mask[i] = (l >=8) ? 0xff : (~0) << (8-l);
-	} else if (*p == '&') { /* mask */
-		for (i=0, p++; *p && i<6;i++, p++) {
-			mask[i] = strtol(p, &p, 16);
-			if (*p != ':')
-				break;
+	optr = ptr = strdup(p);
+	if ((ap = strsep(&ptr, "&/")) != NULL && *ap != 0) {
+		l = strlen(ap);
+		if (strspn(ap, macset) != l || (mac = ether_aton(ap)) == NULL)
+			errx(EX_DATAERR, "Incorrect MAC address");
+		bcopy(mac, addr, ETHER_ADDR_LEN);
+	} else
+		errx(EX_DATAERR, "Incorrect MAC address");
+
+	if (ptr != NULL) { /* we have mask? */
+		if (p[ptr - optr - 1] == '/') { /* mask len */
+			l = strtol(ptr, &ap, 10);
+			if (*ap != 0 || l > ETHER_ADDR_LEN * 8 || l < 0)
+				errx(EX_DATAERR, "Incorrect mask length");
+			for (i = 0; l > 0 && i < ETHER_ADDR_LEN; l -= 8, i++)
+				mask[i] = (l >= 8) ? 0xff: (~0) << (8 - l);
+		} else { /* mask */
+			l = strlen(ptr);
+			if (strspn(ptr, macset) != l ||
+			    (mac = ether_aton(ptr)) == NULL)
+				errx(EX_DATAERR, "Incorrect mask");
+			bcopy(mac, mask, ETHER_ADDR_LEN);
 		}
-	} else if (*p == '\0') {
-		for (i=0; i<6; i++)
+	} else { /* default mask: ff:ff:ff:ff:ff:ff */
+		for (i = 0; i < ETHER_ADDR_LEN; i++)
 			mask[i] = 0xff;
 	}
-	for (i=0; i<6; i++)
+	for (i = 0; i < ETHER_ADDR_LEN; i++)
 		addr[i] &= mask[i];
+
+	free(optr);
 }
 
 /*
@@ -3566,7 +4535,8 @@
 
 	mac = (ipfw_insn_mac *)cmd;
 	get_mac_addr_mask(av[0], mac->addr, mac->mask);	/* dst */
-	get_mac_addr_mask(av[1], &(mac->addr[6]), &(mac->mask[6])); /* src */
+	get_mac_addr_mask(av[1], &(mac->addr[ETHER_ADDR_LEN]),
+	    &(mac->mask[ETHER_ADDR_LEN])); /* src */
 	return cmd;
 }
 
@@ -3695,36 +4665,52 @@
 add_src(ipfw_insn *cmd, char *av, u_char proto)
 {
 	struct in6_addr a;
+	char *host, *ch;
+	ipfw_insn *ret = NULL;
+
+	if ((host = strdup(av)) == NULL)
+		return NULL;
+	if ((ch = strrchr(host, '/')) != NULL)
+		*ch = '\0';
 
 	if (proto == IPPROTO_IPV6  || strcmp(av, "me6") == 0 ||
-	    inet_pton(AF_INET6, av, &a))
-		return add_srcip6(cmd, av);
+	    inet_pton(AF_INET6, host, &a))
+		ret = add_srcip6(cmd, av);
 	/* XXX: should check for IPv4, not !IPv6 */
-	if (proto == IPPROTO_IP || strcmp(av, "me") == 0 ||
-	    !inet_pton(AF_INET6, av, &a))
-		return add_srcip(cmd, av);
-	if (strcmp(av, "any") != 0)
-		return cmd;
+	if (ret == NULL && (proto == IPPROTO_IP || strcmp(av, "me") == 0 ||
+	    !inet_pton(AF_INET6, host, &a)))
+		ret = add_srcip(cmd, av);
+	if (ret == NULL && strcmp(av, "any") != 0)
+		ret = cmd;
 
-	return NULL;
+	free(host);
+	return ret;
 }
 
 static ipfw_insn *
 add_dst(ipfw_insn *cmd, char *av, u_char proto)
 {
 	struct in6_addr a;
+	char *host, *ch;
+	ipfw_insn *ret = NULL;
+
+	if ((host = strdup(av)) == NULL)
+		return NULL;
+	if ((ch = strrchr(host, '/')) != NULL)
+		*ch = '\0';
 
 	if (proto == IPPROTO_IPV6  || strcmp(av, "me6") == 0 ||
-	    inet_pton(AF_INET6, av, &a))
-		return add_dstip6(cmd, av);
+	    inet_pton(AF_INET6, host, &a))
+		ret = add_dstip6(cmd, av);
 	/* XXX: should check for IPv4, not !IPv6 */
-	if (proto == IPPROTO_IP || strcmp(av, "me") == 0 ||
-	    !inet_pton(AF_INET6, av, &a))
-		return add_dstip(cmd, av);
-	if (strcmp(av, "any") != 0)
-		return cmd;
+	if (ret == NULL && (proto == IPPROTO_IP || strcmp(av, "me") == 0 ||
+	    !inet_pton(AF_INET6, host, &a)))
+		ret = add_dstip(cmd, av);
+	if (ret == NULL && strcmp(av, "any") != 0)
+		ret = cmd;
 
-	return NULL;
+	free(host);
+	return ret;
 }
 
 /*
@@ -3759,7 +4745,7 @@
 	 * various flags used to record that we entered some fields.
 	 */
 	ipfw_insn *have_state = NULL;	/* check-state or keep-state */
-	ipfw_insn *have_log = NULL, *have_altq = NULL;
+	ipfw_insn *have_log = NULL, *have_altq = NULL, *have_tag = NULL;
 	size_t len;
 
 	int i;
@@ -3860,11 +4846,9 @@
 		break;
 
 	case TOK_QUEUE:
-		action->len = F_INSN_SIZE(ipfw_insn_pipe);
 		action->opcode = O_QUEUE;
 		goto chkarg;
 	case TOK_PIPE:
-		action->len = F_INSN_SIZE(ipfw_insn_pipe);
 		action->opcode = O_PIPE;
 		goto chkarg;
 	case TOK_SKIPTO:
@@ -3930,17 +4914,27 @@
 				    "illegal forwarding port ``%s''", s);
 			p->sa.sin_port = (u_short)i;
 		}
-		lookup_host(*av, &(p->sa.sin_addr));
-		}
+		if (_substrcmp(*av, "tablearg") == 0) 
+			p->sa.sin_addr.s_addr = INADDR_ANY;
+		else
+			lookup_host(*av, &(p->sa.sin_addr));
 		ac--; av++;
 		break;
-
+	    }
 	case TOK_COMMENT:
 		/* pretend it is a 'count' rule followed by the comment */
 		action->opcode = O_COUNT;
 		ac++; av--;	/* go back... */
 		break;
 
+	case TOK_NAT:
+ 		action->opcode = O_NAT;
+ 		action->len = F_INSN_SIZE(ipfw_insn_nat);
+ 		NEED1("missing nat number");
+ 	        action->arg1 = strtoul(*av, NULL, 10);
+ 		ac--; av++;
+ 		break;
+		
 	default:
 		errx(EX_DATAERR, "invalid action %s\n", av[-1]);
 	}
@@ -4002,6 +4996,20 @@
 		    }
 			break;
 
+		case TOK_TAG:
+		case TOK_UNTAG: {
+			uint16_t tag;
+
+			if (have_tag)
+				errx(EX_USAGE, "tag and untag cannot be "
+				    "specified more than once");
+			GET_UINT_ARG(tag, 1, 65534, i, rule_action_params);
+			have_tag = cmd;
+			fill_cmd(cmd, O_TAG, (i == TOK_TAG) ? 0: F_NOT, tag);
+			ac--; av++;
+			break;
+		}
+
 		default:
 			abort();
 		}
@@ -4445,39 +5453,38 @@
 			fill_cmd(cmd, O_KEEP_STATE, 0, 0);
 			break;
 
-		case TOK_LIMIT:
+		case TOK_LIMIT: {
+			ipfw_insn_limit *c = (ipfw_insn_limit *)cmd;
+			int val;
+
 			if (open_par)
-				errx(EX_USAGE, "limit cannot be part "
-				    "of an or block");
+				errx(EX_USAGE,
+				    "limit cannot be part of an or block");
 			if (have_state)
-				errx(EX_USAGE, "only one of keep-state "
-					"and limit is allowed");
-			NEED1("limit needs mask and # of connections");
+				errx(EX_USAGE, "only one of keep-state and "
+				    "limit is allowed");
 			have_state = cmd;
-		    {
-			ipfw_insn_limit *c = (ipfw_insn_limit *)cmd;
 
 			cmd->len = F_INSN_SIZE(ipfw_insn_limit);
 			cmd->opcode = O_LIMIT;
-			c->limit_mask = 0;
-			c->conn_limit = 0;
-			for (; ac >1 ;) {
-				int val;
+			c->limit_mask = c->conn_limit = 0;
 
-				val = match_token(limit_masks, *av);
-				if (val <= 0)
+			while (ac > 0) {
+				if ((val = match_token(limit_masks, *av)) <= 0)
 					break;
 				c->limit_mask |= val;
 				ac--; av++;
 			}
-			c->conn_limit = atoi(*av);
-			if (c->conn_limit == 0)
-				errx(EX_USAGE, "limit: limit must be >0");
+
 			if (c->limit_mask == 0)
-				errx(EX_USAGE, "missing limit mask");
+				errx(EX_USAGE, "limit: missing limit mask");
+
+			GET_UINT_ARG(c->conn_limit, 1, 65534, TOK_LIMIT,
+			    rule_options);
+
 			ac--; av++;
-		    }
 			break;
+		}
 
 		case TOK_PROTO:
 			NEED1("missing protocol");
@@ -4591,6 +5598,22 @@
 			ac = 0;
 			break;
 
+		case TOK_TAGGED:
+			if (ac > 0 && strpbrk(*av, "-,")) {
+				if (!add_ports(cmd, *av, 0, O_TAGGED))
+					errx(EX_DATAERR, "tagged: invalid tag"
+					    " list: %s", *av);
+			}
+			else {
+				uint16_t tag;
+
+				GET_UINT_ARG(tag, 1, 65534, TOK_TAGGED,
+				    rule_options);
+				fill_cmd(cmd, O_TAGGED, 0, tag);
+			}
+			ac--; av++;
+			break;
+
 		default:
 			errx(EX_USAGE, "unrecognised option [%d] %s\n", i, s);
 		}
@@ -4627,9 +5650,8 @@
 		fill_cmd(dst, O_PROBE_STATE, 0, 0);
 		dst = next_cmd(dst);
 	}
-	/*
-	 * copy all commands but O_LOG, O_KEEP_STATE, O_LIMIT, O_ALTQ
-	 */
+
+	/* copy all commands but O_LOG, O_KEEP_STATE, O_LIMIT, O_ALTQ, O_TAG */
 	for (src = (ipfw_insn *)cmdbuf; src != cmd; src += i) {
 		i = F_LEN(src);
 
@@ -4638,6 +5660,7 @@
 		case O_KEEP_STATE:
 		case O_LIMIT:
 		case O_ALTQ:
+		case O_TAG:
 			break;
 		default:
 			bcopy(src, dst, i * sizeof(uint32_t));
@@ -4658,9 +5681,7 @@
 	 */
 	rule->act_ofs = dst - rule->cmd;
 
-	/*
-	 * put back O_LOG, O_ALTQ if necessary
-	 */
+	/* put back O_LOG, O_ALTQ, O_TAG if necessary */
 	if (have_log) {
 		i = F_LEN(have_log);
 		bcopy(have_log, dst, i * sizeof(uint32_t));
@@ -4671,6 +5692,11 @@
 		bcopy(have_altq, dst, i * sizeof(uint32_t));
 		dst += i;
 	}
+	if (have_tag) {
+		i = F_LEN(have_tag);
+		bcopy(have_tag, dst, i * sizeof(uint32_t));
+		dst += i;
+	}
 	/*
 	 * copy all other actions
 	 */
@@ -4691,9 +5717,10 @@
 static void
 zero(int ac, char *av[], int optname /* IP_FW_ZERO or IP_FW_RESETLOG */)
 {
-	int rulenum;
+	uint32_t arg, saved_arg;
 	int failed = EX_OK;
 	char const *name = optname == IP_FW_ZERO ?  "ZERO" : "RESETLOG";
+	char const *errstr;
 
 	av++; ac--;
 
@@ -4711,15 +5738,21 @@
 	while (ac) {
 		/* Rule number */
 		if (isdigit(**av)) {
-			rulenum = atoi(*av);
+			arg = strtonum(*av, 0, 0xffff, &errstr);
+			if (errstr)
+				errx(EX_DATAERR,
+				    "invalid rule number %s\n", *av);
+			saved_arg = arg;
+			if (use_set)
+				arg |= (1 << 24) | ((use_set - 1) << 16);
 			av++;
 			ac--;
-			if (do_cmd(optname, &rulenum, sizeof rulenum)) {
+			if (do_cmd(optname, &arg, sizeof(arg))) {
 				warn("rule %u: setsockopt(IP_FW_%s)",
-				    rulenum, name);
+				    saved_arg, name);
 				failed = EX_UNAVAILABLE;
 			} else if (!do_quiet)
-				printf("Entry %d %s.\n", rulenum,
+				printf("Entry %d %s.\n", saved_arg,
 				    optname == IP_FW_ZERO ?
 					"cleared" : "logging count reset");
 		} else {
@@ -4750,7 +5783,12 @@
 		if (c == 'N')	/* user said no */
 			return;
 	}
-	if (do_cmd(cmd, NULL, 0) < 0)
+	/* `ipfw set N flush` - is the same that `ipfw delete set N` */
+	if (use_set) {
+		uint32_t arg = ((use_set - 1) & 0xffff) | (1 << 24);
+		if (do_cmd(IP_FW_DEL, &arg, sizeof(arg)) < 0)
+			err(EX_UNAVAILABLE, "setsockopt(IP_FW_DEL)");
+	} else if (do_cmd(cmd, NULL, 0) < 0)
 		err(EX_UNAVAILABLE, "setsockopt(IP_%s_FLUSH)",
 		    do_pipe ? "DUMMYNET" : "FW");
 	if (!do_quiet)
@@ -4811,14 +5849,37 @@
 		if (lookup_host(*av, (struct in_addr *)&ent.addr) != 0)
 			errx(EX_NOHOST, "hostname ``%s'' unknown", *av);
 		ac--; av++;
-		if (do_add && ac)
-			ent.value = strtoul(*av, NULL, 0);
-		else
+		if (do_add && ac) {
+			unsigned int tval;
+			/* isdigit is a bit of a hack here.. */
+			if (strchr(*av, (int)'.') == NULL && isdigit(**av))  {
+				ent.value = strtoul(*av, NULL, 0);
+			} else {
+		        	if (lookup_host(*av, (struct in_addr *)&tval) == 0) {
+					/* The value must be stored in host order	 *
+					 * so that the values < 65k can be distinguished */
+		       			ent.value = ntohl(tval); 
+				} else {
+					errx(EX_NOHOST, "hostname ``%s'' unknown", *av);
+				}
+			}
+		} else
 			ent.value = 0;
 		if (do_cmd(do_add ? IP_FW_TABLE_ADD : IP_FW_TABLE_DEL,
-		    &ent, sizeof(ent)) < 0)
-			err(EX_OSERR, "setsockopt(IP_FW_TABLE_%s)",
-			    do_add ? "ADD" : "DEL");
+		    &ent, sizeof(ent)) < 0) {
+			/* If running silent, don't bomb out on these errors. */
+			if (!(do_quiet && (errno == (do_add ? EEXIST : ESRCH))))
+				err(EX_OSERR, "setsockopt(IP_FW_TABLE_%s)",
+				    do_add ? "ADD" : "DEL");
+			/* In silent mode, react to a failed add by deleting */
+			if (do_add) {
+				do_cmd(IP_FW_TABLE_DEL, &ent, sizeof(ent));
+				if (do_cmd(IP_FW_TABLE_ADD,
+				    &ent, sizeof(ent)) < 0)
+					err(EX_OSERR,
+				            "setsockopt(IP_FW_TABLE_ADD)");
+			}
+		}
 	} else if (_substrcmp(*av, "flush") == 0) {
 		if (do_cmd(IP_FW_TABLE_FLUSH, &ent.tbl, sizeof(ent.tbl)) < 0)
 			err(EX_OSERR, "setsockopt(IP_FW_TABLE_FLUSH)");
@@ -4835,14 +5896,101 @@
 		if (do_cmd(IP_FW_TABLE_LIST, tbl, (uintptr_t)&l) < 0)
 			err(EX_OSERR, "getsockopt(IP_FW_TABLE_LIST)");
 		for (a = 0; a < tbl->cnt; a++) {
-			printf("%s/%u %u\n",
-			    inet_ntoa(*(struct in_addr *)&tbl->ent[a].addr),
-			    tbl->ent[a].masklen, tbl->ent[a].value);
+			/* Heuristic to print it the right way */
+			/* values < 64k are printed as numbers */
+			unsigned int tval;
+			tval = tbl->ent[a].value;
+			if (tval > 0xffff) {
+			    char tbuf[128];
+			    strncpy(tbuf, inet_ntoa(*(struct in_addr *)
+				&tbl->ent[a].addr), 127);
+			    /* inet_ntoa expects host order */
+			    tval = htonl(tval);
+			    printf("%s/%u %s\n", tbuf, tbl->ent[a].masklen,
+			        inet_ntoa(*(struct in_addr *)&tval));
+			} else {
+			    printf("%s/%u %u\n",
+			        inet_ntoa(*(struct in_addr *)&tbl->ent[a].addr),
+			        tbl->ent[a].masklen, tbl->ent[a].value);
+			}
 		}
 	} else
 		errx(EX_USAGE, "invalid table command %s", *av);
 }
 
+static void
+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, r;
+	uint8_t *data, *p;
+	char **lav, *endptr;
+
+	do_rule = 0;
+	nalloc = 1024;
+	size = 0;
+	data = NULL;
+	ac--; av++;
+
+	/* 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;
+		if ((data = realloc(data, nbytes)) == NULL)
+			err(EX_OSERR, "realloc");
+		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 (do_rule) {
+				if (!(frule <= n->id && lrule >= n->id))
+					continue;
+			}
+			print_nat_config(&data[i]);
+			i += sizeof(struct cfg_nat);
+			e = (struct cfg_redir *)&data[i];
+			if (e->mode == REDIR_ADDR || e->mode == REDIR_PORT ||
+			    e->mode == REDIR_PROTO)
+				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));
+		}
+	}
+}
+
 /*
  * Called with the arguments (excluding program name).
  * Returns 0 if successful, 1 if empty command, errx() in case of errors.
@@ -4851,6 +5999,7 @@
 ipfw_main(int oldac, char **oldav)
 {
 	int ch, ac, save_ac;
+	const char *errstr;
 	char **av, **save_av;
 	int do_acct = 0;		/* Show packet/byte count */
 
@@ -5031,58 +6180,83 @@
 	}
 
 	/*
-	 * optional: pipe or queue
+	 * Optional: pipe, queue or nat.
 	 */
+	do_nat = 0;
 	do_pipe = 0;
-	if (_substrcmp(*av, "pipe") == 0)
+	if (!strncmp(*av, "nat", strlen(*av)))
+ 	        do_nat = 1;
+ 	else if (!strncmp(*av, "pipe", strlen(*av)))
 		do_pipe = 1;
 	else if (_substrcmp(*av, "queue") == 0)
 		do_pipe = 2;
-	if (do_pipe) {
+	else if (!strncmp(*av, "set", strlen(*av))) {
+		if (ac > 1 && isdigit(av[1][0])) {
+			use_set = strtonum(av[1], 0, RESVD_SET, &errstr);
+			if (errstr)
+				errx(EX_DATAERR,
+				    "invalid set number %s\n", av[1]);
+			ac -= 2; av += 2; use_set++;
+		}
+	}
+
+	if (do_pipe || do_nat) {
 		ac--;
 		av++;
 	}
 	NEED1("missing command");
 
 	/*
-	 * For pipes and queues we normally say 'pipe NN config'
-	 * but the code is easier to parse as 'pipe config NN'
+	 * For pipes, queues and nats we normally say 'nat|pipe NN config'
+	 * but the code is easier to parse as 'nat|pipe config NN'
 	 * so we swap the two arguments.
 	 */
-	if (do_pipe > 0 && ac > 1 && isdigit(*av[0])) {
+	if ((do_pipe || do_nat) && ac > 1 && isdigit(*av[0])) {
 		char *p = av[0];
 
 		av[0] = av[1];
 		av[1] = p;
 	}
 
-	if (_substrcmp(*av, "add") == 0)
-		add(ac, av);
-	else if (do_pipe && _substrcmp(*av, "config") == 0)
-		config_pipe(ac, av);
-	else if (_substrcmp(*av, "delete") == 0)
-		delete(ac, av);
-	else if (_substrcmp(*av, "flush") == 0)
-		flush(do_force);
-	else if (_substrcmp(*av, "zero") == 0)
-		zero(ac, av, IP_FW_ZERO);
-	else if (_substrcmp(*av, "resetlog") == 0)
-		zero(ac, av, IP_FW_RESETLOG);
-	else if (_substrcmp(*av, "print") == 0 ||
-	         _substrcmp(*av, "list") == 0)
-		list(ac, av, do_acct);
-	else if (_substrcmp(*av, "set") == 0)
-		sets_handler(ac, av);
-	else if (_substrcmp(*av, "table") == 0)
-		table_handler(ac, av);
-	else if (_substrcmp(*av, "enable") == 0)
-		sysctl_handler(ac, av, 1);
-	else if (_substrcmp(*av, "disable") == 0)
-		sysctl_handler(ac, av, 0);
-	else if (_substrcmp(*av, "show") == 0)
-		list(ac, av, 1 /* show counters */);
-	else
-		errx(EX_USAGE, "bad command `%s'", *av);
+	int try_next = 0;
+	if (use_set == 0) {
+		if (_substrcmp(*av, "add") == 0)
+			add(ac, av);
+		else if (do_nat && _substrcmp(*av, "show") == 0)
+ 			show_nat(ac, av);
+		else if (do_pipe && _substrcmp(*av, "config") == 0)
+			config_pipe(ac, av);
+		else if (do_nat && _substrcmp(*av, "config") == 0)
+ 			config_nat(ac, av);
+			else if (_substrcmp(*av, "set") == 0)
+				sets_handler(ac, av);
+			else if (_substrcmp(*av, "table") == 0)
+				table_handler(ac, av);
+			else if (_substrcmp(*av, "enable") == 0)
+				sysctl_handler(ac, av, 1);
+			else if (_substrcmp(*av, "disable") == 0)
+				sysctl_handler(ac, av, 0);
+			else
+				try_next = 1;
+	}
+
+	if (use_set || try_next) {
+		if (_substrcmp(*av, "delete") == 0)
+			delete(ac, av);
+		else if (_substrcmp(*av, "flush") == 0)
+			flush(do_force);
+		else if (_substrcmp(*av, "zero") == 0)
+			zero(ac, av, IP_FW_ZERO);
+		else if (_substrcmp(*av, "resetlog") == 0)
+			zero(ac, av, IP_FW_RESETLOG);
+		else if (_substrcmp(*av, "print") == 0 ||
+		         _substrcmp(*av, "list") == 0)
+			list(ac, av, do_acct);
+		else if (_substrcmp(*av, "show") == 0)
+			list(ac, av, 1 /* show counters */);
+		else
+			errx(EX_USAGE, "bad command `%s'", *av);
+	}
 
 	/* Free memory allocated in the argument parsing. */
 	free_args(save_ac, save_av);
@@ -5129,6 +6303,8 @@
 			 */
 			av += optind - 1;
 			ac -= optind - 1;
+			if (ac < 2)
+				errx(EX_USAGE, "no filename argument");
 			av[ac-1] = NULL;
 			fprintf(stderr, "command is %s\n", av[0]);
 			break;
Index: ipfw.8
===================================================================
RCS file: /home/cvs/src/sbin/ipfw/ipfw.8,v
retrieving revision 1.2
retrieving revision 1.3
diff -L sbin/ipfw/ipfw.8 -L sbin/ipfw/ipfw.8 -u -r1.2 -r1.3
--- sbin/ipfw/ipfw.8
+++ sbin/ipfw/ipfw.8
@@ -1,7 +1,7 @@
 .\"
-.\" $FreeBSD: src/sbin/ipfw/ipfw.8,v 1.175.2.6 2006/03/09 09:18:36 ume Exp $
+.\" $FreeBSD: src/sbin/ipfw/ipfw.8,v 1.203.2.1 2007/11/29 18:42:15 maxim Exp $
 .\"
-.Dd January 16, 2006
+.Dd August 5, 2007
 .Dt IPFW 8
 .Os
 .Sh NAME
@@ -14,15 +14,17 @@
 .Ar rule
 .Nm
 .Op Fl acdefnNStT
+.Op Cm set Ar N
 .Brq Cm list | show
 .Op Ar rule | first-last ...
 .Nm
 .Op Fl f | q
+.Op Cm set Ar N
 .Cm flush
 .Nm
 .Op Fl q
+.Op Cm set Ar N
 .Brq Cm delete | zero | resetlog
-.Op Cm set
 .Op Ar number ...
 .Nm
 .Cm enable
@@ -63,6 +65,12 @@
 .Op Ar number ...
 .Pp
 .Nm
+.Cm nat
+.Ar number
+.Cm config
+.Ar config-options
+.Pp
+.Nm
 .Op Fl cfnNqS
 .Oo
 .Fl p Ar preproc
@@ -233,6 +241,8 @@
 or by processing a file of many
 .Nm
 rules across a remote login session.
+It also stops a table add or delete
+from failing if the entry already exists or is not present.
 If a
 .Cm flush
 is performed in normal (verbose) mode (with the default kernel
@@ -326,12 +336,12 @@
        |                       |
        +----------->-----------+
        ^                       V
- [ip(6)_input]           [ip(6)_output]     net.inet.ip.fw.enable=1
+ [ip(6)_input]           [ip(6)_output]     net.inet(6).ip(6).fw.enable=1
        |                       |
        ^                       V
  [ether_demux]        [ether_output_frame]  net.link.ether.ipfw=1
        |                       |
-       +-->--[bdg_forward]-->--+            net.link.ether.bridge_ipfw=1
+       +-->--[bdg_forward]-->--+            net.link.bridge.ipfw=1
        ^                       V
        |      to devices       |
 .Ed
@@ -412,14 +422,19 @@
 .Nm
 rules is the following:
 .Bd -ragged -offset indent
+.Bk -words
 .Op Ar rule_number
 .Op Cm set Ar set_number
 .Op Cm prob Ar match_probability
-.br
-.Ar "   " action
+.Ar action
 .Op Cm log Op Cm logamount Ar number
 .Op Cm altq Ar queue
+.Oo
+.Bro Cm tag | untag
+.Brc Ar number
+.Oc
 .Ar body
+.Ek
 .Ed
 .Pp
 where the body of the rule specifies which information is used
@@ -443,7 +458,7 @@
 .It IP options
 .It IPv6 Extension headers
 Fragmentation, Hop-by-Hop options,
-source routing, IPSec options.
+Routing Headers, Source routing rthdr0, Mobile IPv6 rthdr2, IPSec options.
 .It IPv6 Flow-ID
 .It Misc. TCP header fields
 TCP flags (SYN, FIN, ACK, RST, etc.),
@@ -550,6 +565,54 @@
 Note: logging is done after all other packet matching conditions
 have been successfully verified, and before performing the final
 action (accept, deny, etc.) on the packet.
+.It Cm tag Ar number
+When a packet matches a rule with the
+.Cm tag
+keyword, the numeric tag for the given
+.Ar number
+in the range 1..65534 will be attached to the packet.
+The tag acts as an internal marker (it is not sent out over
+the wire) that can be used to identify these packets later on.
+This can be used, for example, to provide trust between interfaces
+and to start doing policy-based filtering.
+A packet can have mutiple tags at the same time.
+Tags are "sticky", meaning once a tag is applied to a packet by a
+matching rule it exists until explicit removal.
+Tags are kept with the packet everywhere within the kernel, but are
+lost when packet leaves the kernel, for example, on transmitting
+packet out to the network or sending packet to a
+.Xr divert 4
+socket.
+.Pp
+To check for previously applied tags, use the
+.Cm tagged
+rule option.
+To delete previously applied tag, use the
+.Cm untag
+keyword.
+.Pp
+Note: since tags are kept with the packet everywhere in kernelspace,
+they can be set and unset anywhere in kernel network subsystem
+(using
+.Xr mbuf_tags 9
+facility), not only by means of
+.Xr ipfw 4
+.Cm tag
+and
+.Cm untag
+keywords.
+For example, there can be a specialized
+.Xr netgraph 4
+node doing traffic analyzing and tagging for later inspecting
+in firewall.
+.It Cm untag Ar number
+When a packet matches a rule with the
+.Cm untag
+keyword, the tag with the number
+.Ar number
+is searched among the tags attached to this packet and,
+if found, removed from it.
+Other tags bound to packet, if present, are left untouched.
 .It Cm altq Ar queue
 When a packet matches a rule with the
 .Cm altq
@@ -631,10 +694,14 @@
 socket bound to port
 .Ar port .
 The search terminates.
-.It Cm fwd | forward Ar ipaddr Ns Op , Ns Ar port
+.It Cm fwd | forward Ar ipaddr | tablearg Ns Op , Ns Ar port
 Change the next-hop on matching packets to
 .Ar ipaddr ,
 which can be an IP address or a host name.
+The next hop can also be supplied by the last table
+looked up for the packet by using the
+.Cm tablearg
+keyword instead of an explicit address.
 The search terminates if this rule matches.
 .Pp
 If
@@ -674,14 +741,13 @@
 .Cm fwd
 a custom kernel needs to be compiled with the option
 .Cd "options IPFIREWALL_FORWARD" .
-With the additional option
-.Cd "options IPFIREWALL_FORWARD_EXTENDED"
-all safeguards are removed and it also makes it possible to redirect
-packets destined to locally configured IP addresses.
-Please note that such rules apply to locally generated packets as
-well and great care is required to ensure proper behaviour for
-automatically generated packets like ICMP message size exceeded
-and others.
+.It Cm nat Ar nat_nr
+Pass packet to a
+nat instance
+(for network address translation, address redirect, etc.):
+see the
+.Sx NETWORK ADDRESS TRANSLATION (NAT)
+Section for further information.
 .It Cm pipe Ar pipe_nr
 Pass packet to a
 .Xr dummynet 4
@@ -869,7 +935,7 @@
 with multiple addresses) is provided for convenience only and
 its use is discouraged.
 .It Ar addr : Oo Cm not Oc Bro
-.Cm any | me | me6
+.Cm any | me | me6 |
 .Cm table Ns Pq Ar number Ns Op , Ns Ar value
 .Ar | addr-list | addr-set
 .Brc
@@ -900,20 +966,20 @@
 .It Ar addr Ns / Ns Ar masklen
 Matches all addresses with base
 .Ar addr
-(specified as an IP address or a hostname)
+(specified as an IP address, a network number, or a hostname)
 and mask width of
 .Cm masklen
 bits.
-As an example, 1.2.3.4/25 will match
+As an example, 1.2.3.4/25 or 1.2.3.0/25 will match
 all IP numbers from 1.2.3.0 to 1.2.3.127 .
 .It Ar addr Ns : Ns Ar mask
 Matches all addresses with base
 .Ar addr
-(specified as an IP address or a hostname)
+(specified as an IP address, a network number, or a hostname)
 and the mask of
 .Ar mask ,
 specified as a dotted quad.
-As an example, 1.2.3.4:255.0.255.0 will match
+As an example, 1.2.3.4:255.0.255.0 or 1.0.3.0:255.0.255.0 will match
 1.*.3.*.
 This form is advised only for non-contiguous
 masks.
@@ -926,7 +992,7 @@
 .It Ar list : Bro Ar num | num-num Brc Ns Op Ns , Ns Ar list
 Matches all addresses with base address
 .Ar addr
-(specified as an IP address or a hostname)
+(specified as an IP address, a network number, or a hostname)
 and whose last byte is in the list between braces { } .
 Note that there must be no spaces between braces and
 numbers (spaces after commas are allowed).
@@ -946,6 +1012,7 @@
 the complexity of rulesets.
 .br
 As an example, an address specified as 1.2.3.4/24{128,35-55,89}
+or 1.2.3.0/24{128,35-55,89}
 will match the following IP addresses:
 .br
 1.2.3.128, 1.2.3.35 to 1.2.3.55, 1.2.3.89 .
@@ -1060,8 +1127,12 @@
 .Pq Cm frag ,
 Hop-to-hop options
 .Pq Cm hopopt ,
-Source routing
+any type of Routing Header
 .Pq Cm route ,
+Source routing Routing Header Type 0
+.Pq Cm rthdr0 ,
+Mobile IPv6 Routing Header Type 2
+.Pq Cm rthdr2 ,
 Destination options
 .Pq Cm dstopt ,
 IPSec authentication headers
@@ -1085,14 +1156,10 @@
 A
 .Ar group
 may be specified by name or number.
-This option should be used only if debug.mpsafenet=0 to avoid possible
-deadlocks due to layering violations in its implementation.
 .It Cm jail Ar prisonID
 Matches all TCP or UDP packets sent by or received for the
 jail whos prison ID is
 .Ar prisonID .
-This option should be used only if debug.mpsafenet=0 to avoid possible
-deadlocks due to layering violations in its implementation.
 .It Cm icmptypes Ar types
 Matches ICMP packets whose ICMP type is in the list
 .Ar types .
@@ -1184,7 +1251,7 @@
 .It Cm ipsec
 Matches packets that have IPSEC history associated with them
 (i.e., the packet comes encapsulated in IPSEC, the kernel
-has IPSEC support and IPSEC_FILTERGIF option, and can correctly
+has IPSEC support and IPSEC_FILTERTUNNEL option, and can correctly
 decapsulate it).
 .Pp
 Note that specifying
@@ -1360,6 +1427,15 @@
 .It Cm src-port Ar ports
 Matches IP packets whose source port is one of the port(s)
 specified as argument.
+.It Cm tagged Ar tag-list
+Matches packets whose tags are included in
+.Ar tag-list ,
+which is either a single value or a list of values or ranges
+specified in the same way as
+.Ar ports .
+Tags can be applied to the packet using
+.Cm tag
+rule action parameter (see it's description for details on tags).
 .It Cm tcpack Ar ack
 TCP packets only.
 Match if the TCP header acknowledgment number field is set to
@@ -1428,8 +1504,6 @@
 A
 .Ar user
 may be matched by name or identification number.
-This option should be used only if debug.mpsafenet=0 to avoid possible
-deadlocks due to layering violations in its implementation.
 .It Cm verrevpath
 For incoming packets,
 a routing table lookup is done on the packet's source address.
@@ -1525,12 +1599,21 @@
 The
 .Cm tablearg
 feature provides the ability to use a value, looked up in the table, as
-the argument for a rule action.
+the argument for a rule action, action parameter or rule option.
 This can significantly reduce number of rules in some configurations.
 The
 .Cm tablearg
 argument can be used with the following actions:
-.Cm pipe , queue, divert, tee, netgraph, ngtee .
+.Cm pipe , queue, divert, tee, netgraph, ngtee, fwd
+action parameters:
+.Cm tag, untag,
+rule options:
+.Cm limit, tagged.
+.Pp
+When used with
+.Cm fwd
+it is possible to supply table entries with values
+that are in the form of IP addresses or hostnames.
 See the
 .Sx EXAMPLES
 Section for example usage of tables and the tablearg keyword.
@@ -1597,7 +1680,7 @@
 and
 .Cm limit
 options of
-.Nm rules.
+.Nm rules .
 .Pp
 Dynamic rules are created when a packet matches a
 .Cm keep-state
@@ -1884,8 +1967,7 @@
 .El
 .Pp
 When used with IPv6 data, dummynet currently has several limitations.
-First, debug.mpsafenet=0 must be set.
-Second, the information necessicary to route link-local packets to an
+Information necessary to route link-local packets to an
 interface is not avalable after processing by dummynet so those packets
 are dropped in the output path.
 Care should be taken to insure that link-local packets are not passed to
@@ -1965,6 +2047,59 @@
 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 NETWORK ADDRESS TRANSLATION (NAT)
+The nat configuration command is the following:
+.Bd -ragged -offset indent
+.Bk -words
+.Cm nat 
+.Ar nat_number 
+.Cm config 
+.Ar nat-configuration
+.Ek
+.Ed
+.Pp
+.
+The following parameters can be configured:
+.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
+To let the packet continue after being (de)aliased, set the sysctl variable
+.Em net.inet.ip.fw.one_pass 
+to 0.
+For more information about aliasing modes, refer to
+.Xr libalias 3
+.
+See Section
+.Sx EXAMPLES
+for some examples about nat usage.
+.Sh REDIRECT AND LSNAT SUPPORT IN IPFW
+Redirect and LSNAT support follow closely the syntax used in
+.Xr natd 8
+. 
+See Section
+.Sx EXAMPLES
+for some examples on how to do redirect and lsnat.
 .Sh SYSCTL VARIABLES
 A set of
 .Xr sysctl 8
@@ -2049,6 +2184,8 @@
 Enables the firewall.
 Setting this variable to 0 lets you run your machine without
 firewall even if compiled in.
+.It Em net.inet6.ip6.fw.enable : No 1
+provides the same functionality as above for the IPv6 case.
 .It Em net.inet.ip.fw.one_pass : No 1
 When set, the packet exiting from the
 .Xr dummynet 4
@@ -2067,7 +2204,7 @@
 Controls whether layer-2 packets are passed to
 .Nm .
 Default is no.
-.It Em net.link.ether.bridge_ipfw : No 0
+.It Em net.link.bridge.ipfw : No 0
 Controls whether bridged packets are passed to
 .Nm .
 Default is no.
@@ -2319,7 +2456,16 @@
 .Dl "ipfw table 1 add 192.168.0.0/27 4"
 .Dl "ipfw table 1 add 192.168.0.2 1"
 .Dl "..."
-.Dl "ipfw pipe tablearg ip from table(1) to any"
+.Dl "ipfw add pipe tablearg ip from table(1) to any"
+.Pp
+Using the
+.Cm fwd
+action, the table entries may include hostnames and IP addresses.
+.Pp
+.Dl "ipfw table 1 add 192.168.2.0/24 10.23.2.1"
+.Dl "ipfw table 1 add 192.168.0.0/27 router1.dmz"
+.Dl "..."
+.Dl "ipfw add 100 fwd tablearg ip from any to table(1)"
 .Ss SETS OF RULES
 To add a set of rules atomically, e.g.\& set 18:
 .Pp
@@ -2342,13 +2488,78 @@
 Otherwise, e.g.\& if
 you cannot access your box, the ruleset will be disabled after
 the sleep terminates thus restoring the previous situation.
+.Pp
+To show rules of the specific set:
+.Pp
+.Dl "ipfw set 18 show"
+.Pp
+To show rules of the disabled set:
+.Pp
+.Dl "ipfw -S set 18 show"
+.Pp
+To clear a specific rule counters of the specific set:
+.Pp
+.Dl "ipfw set 18 zero NN"
+.Pp
+To delete a specific rule of the specific set:
+.Pp
+.Dl "ipfw set 18 delete NN"
+.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 redirect_addr 10.0.0.1 10.0.0.66"
+.Dl "			 redirect_port tcp 192.168.0.1:80 500"
+.Dl "			 redirect_proto udp 192.168.1.43 192.168.1.1"
+.Dl "			 redirect_addr 192.168.0.10,192.168.0.11"
+.Dl "			 	    10.0.0.100	# LSNAT"
+.Dl "			 redirect_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 redirect_addr 10.0.0.1 10.0.0.66"
+.Dl "ipfw nat 2 config redirect_port tcp 192.168.0.1:80 500"
+.Dl "ipfw nat 3 config redirect_proto udp 192.168.1.43 192.168.1.1"
+.Dl "ipfw nat 4 config redirect_addr 192.168.0.10,192.168.0.11,192.168.0.12" 
+.Dl "				         10.0.0.100"
+.Dl "ipfw nat 5 config redirect_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 ,
 .Xr altq 4 ,
-.Xr bridge 4 ,
 .Xr divert 4 ,
 .Xr dummynet 4 ,
+.Xr if_bridge 4 ,
 .Xr ip 4 ,
 .Xr ipfirewall 4 ,
 .Xr ng_ipfw 4 ,
@@ -2383,12 +2594,15 @@
 .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.
 .Sh BUGS
-Use of dummynet with IPv6 requires that debug.mpsafenet be set to 0.
-.Pp
 The syntax has grown over the years and sometimes it might be confusing.
 Unfortunately, backward compatibility prevents cleaning up mistakes
 made in the definition of the syntax.
@@ -2432,6 +2646,13 @@
 .Xr setuid 2
 or similar system calls.
 .Pp
-Rules which use uid, gid or jail based matching should be used only
-if debug.mpsafenet=0 to avoid possible deadlocks due to layering
-violations in its implementation.
+Rule syntax is subject to the command line environment and some patterns
+may need to be escaped with the backslash character
+or quoted appropriately.
+.Pp
+Due to the architecture of 
+.Xr libalias 3 , 
+ipfw nat is not compatible with the tcp segmentation offloading
+(TSO). Thus, to reliably nat your network traffic, please disable TSO
+on your NICs using
+.Xr ifconfig 8 .


More information about the Midnightbsd-cvs mailing list