ipfw2 for IPV6

Brooks Davis brooks at one-eyed-alien.net
Fri Sep 3 14:50:51 PDT 2004


I'm working on updating the IPFW2 for IPv6 patch Luigi posted back in
April.  I've got it partially working with pfil, but I've run into some
issues with linklocal addresses and dummynet6.  Inbound rules work fine,
but output rules do not because the route struct is not carried in to
the pfil hook and thus the output interface is lost.

I'm looking for comments on the best way to solve this problem.  I don't
know IPv6 all that well, so I'm not sure what to propose.

The work is being done in perforce at:

//depot/user/brooks/dummynet6

I've included a patch against current below.  Be aware that you must
run with debug.mpsafenet=0 if you want to try IPv6 output rules.  The
current code doesn't handle the case where the firewall changes the
destination, but modulo bugs, we are probably at feature parity with
ip6fw.

-- Brooks

Changed files:
	sbin/ipfw/ipfw2.c
	sys/netinet/ip_dummynet.c
	sys/netinet/ip_dummynet.h
	sys/netinet/ip_fw.h
	sys/netinet/ip_fw2.c
	sys/netinet/ip_fw_pfil.c

--- ../cleanup/sbin/ipfw/ipfw2.c	Wed Sep  1 08:01:19 2004
+++ sbin/ipfw/ipfw2.c	Thu Sep  2 16:40:48 2004
@@ -45,10 +45,12 @@
 #include <sysexits.h>
 
 #include <net/if.h>
+#include <net/route.h> /* def. of struct route */
 #include <netinet/in.h>
 #include <netinet/in_systm.h>
 #include <netinet/ip.h>
 #include <netinet/ip_icmp.h>
+#include <netinet/icmp6.h>
 #include <netinet/ip_fw.h>
 #include <netinet/ip_dummynet.h>
 #include <netinet/tcp.h>
@@ -253,6 +255,13 @@
 	TOK_DROPTAIL,
 	TOK_PROTO,
 	TOK_WEIGHT,
+
+	TOK_IPV6,
+	TOK_FLOWID,
+	TOK_ICMP6TYPES,
+	TOK_EXT6HDR,
+	TOK_DSTIP6,
+	TOK_SRCIP6,
 };
 
 struct _s_x dummynet_params[] = {
@@ -275,6 +284,13 @@
 	{ "delay",		TOK_DELAY },
 	{ "pipe",		TOK_PIPE },
 	{ "queue",		TOK_QUEUE },
+
+	{ "flow-id",		TOK_FLOWID},
+	{ "dst-ipv6",		TOK_DSTIP6},
+	{ "dst-ip6",		TOK_DSTIP6},
+	{ "src-ipv6",		TOK_SRCIP6},
+	{ "src-ip6",		TOK_SRCIP6},
+
 	{ "dummynet-params",	TOK_NULL },
 	{ NULL, 0 }	/* terminator */
 };
@@ -299,6 +315,7 @@
 	{ "unreach",		TOK_UNREACH },
 	{ "check-state",	TOK_CHECKSTATE },
 	{ "//",			TOK_COMMENT },
+
 	{ NULL, 0 }	/* terminator */
 };
 
@@ -352,6 +369,16 @@
 	{ "ipsec",		TOK_IPSEC },
 	{ "//",			TOK_COMMENT },
 
+	{ "icmp6type",		TOK_ICMP6TYPES },
+	{ "icmp6types",		TOK_ICMP6TYPES },
+	{ "ext6hdr",		TOK_EXT6HDR},
+	{ "flow-id",		TOK_FLOWID},
+	{ "ipv6",		TOK_IPV6},
+	{ "dst-ipv6",		TOK_DSTIP6},
+	{ "dst-ip6",		TOK_DSTIP6},
+	{ "src-ipv6",		TOK_SRCIP6},
+	{ "src-ip6",		TOK_SRCIP6},
+
 	{ "not",		TOK_NOT },		/* pseudo option */
 	{ "!", /* escape ? */	TOK_NOT },		/* pseudo option */
 	{ "or",			TOK_OR },		/* pseudo option */
@@ -848,6 +875,196 @@
 	}
 }
 
+/* XXX ipv6 stuff */
+/* 
+ * Print the ip address contained in a command.
+ */
+static void
+print_ip6(ipfw_insn_ip6 *cmd, char const *s)
+{
+       struct hostent *he = NULL;
+       int len = F_LEN((ipfw_insn *) cmd) - 1;
+       struct in6_addr *a = &(cmd->addr6);
+       char trad[255];
+
+       printf("%s%s ", cmd->o.len & F_NOT ? " not": "", s);
+
+       if (cmd->o.opcode == O_IP6_SRC_ME || cmd->o.opcode == O_IP6_DST_ME) {
+               printf("me6");
+               return;
+       }
+       if (cmd->o.opcode == O_IP6) {
+               printf(" ipv6");
+               return;
+       }
+
+       /*
+        * len == 4 indicates a single IP, whereas lists of 1 or more
+        * addr/mask pairs have len = (2n+1). We convert len to n so we
+        * use that to count the number of entries.
+        */
+
+       for (len = len / 4; len > 0; len -= 2, a += 2) {
+           int mb =        /* mask length */
+               (cmd->o.opcode == O_IP6_SRC || cmd->o.opcode == O_IP6_DST) ?
+               128 : contigmask((uint8_t *)&(a[1]), 128);
+
+           if (mb == 128 && do_resolv)
+               he = gethostbyaddr((char *)a, sizeof(*a), AF_INET6);
+           if (he != NULL)             /* resolved to name */
+               printf("%s", he->h_name);
+           else if (mb == 0)           /* any */
+               printf("any");
+           else {          /* numeric IP followed by some kind of mask */
+               if (inet_ntop(AF_INET6,  a, trad, sizeof( trad ) ) == NULL)
+                   printf("Error ntop in print_ip6\n");
+               printf("%s",  trad );
+               if (mb < 0)     /* XXX not really legal... */
+                   printf(":%s",
+                       inet_ntop(AF_INET6, &a[1], trad, sizeof(trad)));
+               else if (mb < 128)
+                   printf("/%d", mb);
+           }
+           if (len > 2)
+               printf(",");
+       }
+}
+
+static void
+fill_icmp6types(ipfw_insn_icmp6 *cmd, char *av)
+{
+       uint8_t type;
+
+       cmd->d[0] = 0;
+       while (*av) {
+           if (*av == ',')
+               av++;
+           type = strtoul(av, &av, 0);
+           if (*av != ',' && *av != '\0')
+               errx(EX_DATAERR, "invalid ICMP6 type");
+           if (type > ICMP6_MAXTYPE)
+               errx(EX_DATAERR, "ICMP6 type out of range");
+           cmd->d[type / 32] |= ( 1 << (type % 32));
+       }
+       cmd->o.opcode = O_ICMP6TYPE;
+       cmd->o.len |= F_INSN_SIZE(ipfw_insn_icmp6);
+}
+
+
+static void
+print_icmp6types(ipfw_insn_u32 *cmd)
+{
+       int i, j;
+       char sep= ' ';
+
+       printf(" ipv6 icmp6types");
+       for (i = 0; i < 7; i++)
+               for (j=0; j < 32; ++j) {
+                       if ( (cmd->d[i] & (1 << (j))) == 0)
+                               continue;
+                       printf("%c%d", sep, (i*32 + j));
+                       sep = ',';
+               }
+}
+
+static void
+print_flow6id( ipfw_insn_u32 *cmd)
+{
+       uint16_t i, limit = cmd->o.arg1;
+       char sep = ',';
+
+       printf(" flow-id ");
+       for( i=0; i < limit; ++i) {
+               if (i == limit - 1)
+                       sep = ' ';
+               printf("%d%c", cmd->d[i], sep);
+       }
+}
+
+/* structure and define for the extension header in ipv6 */
+static struct _s_x ext6hdrcodes[] = {
+       { "frag",       EXT_FRAGMENT },
+       { "hopopt",     EXT_HOPOPTS },
+       { "route",      EXT_ROUTING },
+       { "ah",         EXT_AH },
+       { "esp",        EXT_ESP },
+       { NULL,         0 }
+};
+
+/* fills command for the extension header filtering */
+int
+fill_ext6hdr( ipfw_insn *cmd, char *av)
+{
+       int tok;
+       char *s = av;
+
+       cmd->arg1 = 0;
+
+       while(s) {
+           av = strsep( &s, ",") ;
+           tok = match_token(ext6hdrcodes, av);
+           switch (tok) {
+           case EXT_FRAGMENT:
+               cmd->arg1 |= EXT_FRAGMENT;
+               break;
+
+           case EXT_HOPOPTS:
+               cmd->arg1 |= EXT_HOPOPTS;
+               break;
+
+           case EXT_ROUTING:
+               cmd->arg1 |= EXT_ROUTING;
+               break;
+
+           case EXT_AH:
+               cmd->arg1 |= EXT_AH;
+               break;
+
+           case EXT_ESP:
+               cmd->arg1 |= EXT_ESP;
+               break;
+
+           default:
+               errx( EX_DATAERR, "invalid option for ipv6 exten headear" );
+               break;
+           }
+       }
+       if (cmd->arg1 == 0 )
+           return 0;
+       cmd->opcode = O_EXT_HDR;
+       cmd->len |= F_INSN_SIZE( ipfw_insn );
+       return 1;
+}
+
+void
+print_ext6hdr( ipfw_insn *cmd )
+{
+       char sep = ' ';
+
+       printf(" extension header:");
+       if (cmd->arg1 & EXT_FRAGMENT ) {
+           printf("%cfragmentation", sep);
+           sep = ',';
+       }
+       if (cmd->arg1 & EXT_HOPOPTS ) {
+           printf("%chop options", sep);
+           sep = ',';
+       }
+       if (cmd->arg1 & EXT_ROUTING ) {
+           printf("%crouting options", sep);
+           sep = ',';
+       }
+       if (cmd->arg1 & EXT_AH ) {
+           printf("%cauthentication header", sep);
+           sep = ',';
+       }
+       if (cmd->arg1 & EXT_ESP ) {
+           printf("%cencapsulated security payload", sep);
+       }
+}
+
+/* XXX end of ipv6 stuff */
+
 /*
  * show_ipfw() prints the body of an ipfw rule.
  * Because the standard rule has at least proto src_ip dst_ip, we use
@@ -866,6 +1083,7 @@
 #define	HAVE_DSTIP	0x0004
 #define	HAVE_MAC	0x0008
 #define	HAVE_MACTYPE	0x0010
+#define	HAVE_PROTO6	0x0080
 #define	HAVE_OPTIONS	0x8000
 
 #define	HAVE_IP		(HAVE_PROTO | HAVE_SRCIP | HAVE_DSTIP)
@@ -888,6 +1106,9 @@
 		return;
 	}
 	if ( !(*flags & HAVE_OPTIONS)) {
+		/* XXX: This is what the patch has, but shouldn't that be PROTO6? */
+		if ( !(*flags & HAVE_PROTO) && (want & HAVE_PROTO6))
+			printf(" ipv6");
 		if ( !(*flags & HAVE_PROTO) && (want & HAVE_PROTO))
 			printf(" ip");
 		if ( !(*flags & HAVE_SRCIP) && (want & HAVE_SRCIP))
@@ -1130,6 +1351,37 @@
 			flags |= HAVE_DSTIP;
 			break;
 
+		case O_IP6_SRC:
+		case O_IP6_SRC_MASK:
+		case O_IP6_SRC_ME:
+			show_prerequisites(&flags, HAVE_PROTO6, 0);
+			if (!(flags & HAVE_SRCIP))
+				printf(" from");
+			if ((cmd->len & F_OR) && !or_block)
+				printf(" {");
+			print_ip6((ipfw_insn_ip6 *)cmd,
+			(flags & HAVE_OPTIONS) ? " src-ip6" : "");
+			flags |= HAVE_SRCIP | HAVE_PROTO;
+			break;
+
+		case O_IP6_DST:
+		case O_IP6_DST_MASK:
+		case O_IP6_DST_ME:
+			show_prerequisites(&flags, HAVE_PROTO|HAVE_SRCIP, 0);
+			if (!(flags & HAVE_DSTIP))
+				printf(" to");
+			if ((cmd->len & F_OR) && !or_block)
+				printf(" {");
+			print_ip6((ipfw_insn_ip6 *)cmd,
+			(flags & HAVE_OPTIONS) ? " dst-ip6" : "");
+			flags |= HAVE_DSTIP;
+			break;
+
+		case O_FLOW6ID:
+			print_flow6id( (ipfw_insn_u32 *) cmd );
+			flags |= HAVE_OPTIONS;
+			break;
+
 		case O_IP_DSTPORT:
 			show_prerequisites(&flags, HAVE_IP, 0);
 		case O_IP_SRCPORT:
@@ -1141,14 +1393,15 @@
 			break;
 
 		case O_PROTO: {
-			struct protoent *pe;
+			struct protoent *pe = NULL;
 
 			if ((cmd->len & F_OR) && !or_block)
 				printf(" {");
 			if (cmd->len & F_NOT)
 				printf(" not");
 			proto = cmd->arg1;
-			pe = getprotobynumber(cmd->arg1);
+			if (proto != 41)	/* XXX: ipv6 is special */
+				pe = getprotobynumber(cmd->arg1);
 			if (flags & HAVE_OPTIONS)
 				printf(" proto");
 			if (pe)
@@ -1332,6 +1585,18 @@
 			    }
 				break;
 
+			case O_IP6:
+				printf(" ipv6");
+				break;
+
+			case O_ICMP6TYPE:
+				print_icmp6types((ipfw_insn_u32 *)cmd);
+				break;
+
+			case O_EXT_HDR:
+				print_ext6hdr( (ipfw_insn *) cmd );
+				break;
+
 			default:
 				printf(" [opcode %d len %d]",
 				    cmd->opcode, cmd->len);
@@ -1428,42 +1693,104 @@
 static void
 list_queues(struct dn_flow_set *fs, struct dn_flow_queue *q)
 {
-	int l;
+	int l, index_print = 0;
+	char buff[255];
 
-	printf("    mask: 0x%02x 0x%08x/0x%04x -> 0x%08x/0x%04x\n",
-	    fs->flow_mask.proto,
-	    fs->flow_mask.src_ip, fs->flow_mask.src_port,
-	    fs->flow_mask.dst_ip, fs->flow_mask.dst_port);
 	if (fs->rq_elements == 0)
 		return;
 
-	printf("BKT Prot ___Source IP/port____ "
-	    "____Dest. IP/port____ Tot_pkt/bytes Pkt/Byte Drp\n");
 	if (do_sort != 0)
 		heapsort(q, fs->rq_elements, sizeof *q, sort_q);
+
+	/*
+	 * Do IPv4 stuff
+	 */
 	for (l = 0; l < fs->rq_elements; l++) {
-		struct in_addr ina;
-		struct protoent *pe;
+		if (!IS_IP6_FLOW_ID(&(q[l].id))) {
+			struct in_addr ina;
+			struct protoent *pe;
 
-		ina.s_addr = htonl(q[l].id.src_ip);
-		printf("%3d ", q[l].hash_slot);
-		pe = getprotobynumber(q[l].id.proto);
-		if (pe)
-			printf("%-4s ", pe->p_name);
-		else
-			printf("%4u ", q[l].id.proto);
-		printf("%15s/%-5d ",
-		    inet_ntoa(ina), q[l].id.src_port);
-		ina.s_addr = htonl(q[l].id.dst_ip);
-		printf("%15s/%-5d ",
-		    inet_ntoa(ina), q[l].id.dst_port);
-		printf("%4qu %8qu %2u %4u %3u\n",
-		    q[l].tot_pkts, q[l].tot_bytes,
-		    q[l].len, q[l].len_bytes, q[l].drops);
-		if (verbose)
-			printf("   S %20qd  F %20qd\n",
-			    q[l].S, q[l].F);
+			if (!index_print) {
+				index_print = 1;
+				printf("\n        mask: 0x%02x 0x%08x/0x%04x -> 0x%08x/0x%04x\n",
+				    fs->flow_mask.proto,
+				    fs->flow_mask.src_ip,
+				    fs->flow_mask.src_port,
+				    fs->flow_mask.dst_ip,
+				    fs->flow_mask.dst_port);
+				printf("    BKT Prot ___Source IP/port____ "
+				    "____Dest. IP/port____ Tot_pkt/bytes Pkt/Byte Drp\n");
+			}
+			printf("    %3d ", q[l].hash_slot);
+
+			pe = getprotobynumber(q[l].id.proto);
+			if (pe)
+				printf("%-4s ", pe->p_name);
+			else
+				printf("%4u ", q[l].id.proto);
+			ina.s_addr = htonl(q[l].id.src_ip);
+			printf("%15s/%-5d ",
+			    inet_ntoa(ina), q[l].id.src_port);
+			ina.s_addr = htonl(q[l].id.dst_ip);
+			printf("%15s/%-5d ",
+			    inet_ntoa(ina), q[l].id.dst_port);
+			printf("%4qu %8qu %2u %4u %3u\n",
+			    q[l].tot_pkts, q[l].tot_bytes,
+			    q[l].len, q[l].len_bytes, q[l].drops);
+			if (verbose)
+				printf("   S %20qd  F %20qd\n",
+				    q[l].S, q[l].F);
+		}
+	}
+		
+		/*
+		 * Do IPv6 stuff
+		 */
+
+	index_print = 0;
+	for (l = 0; l < fs->rq_elements; l++) {
+		if (IS_IP6_FLOW_ID(&(q[l].id))) {
+			struct protoent *pe;
+
+			if (!index_print) {
+				index_print = 1;
+				printf("\n        mask: proto: 0x%02x, flow_id: 0x%08x,  ",
+				fs->flow_mask.proto, fs->flow_mask.flow_id6 );
+				inet_ntop(AF_INET6, &(fs->flow_mask.src_ip6),
+				buff, sizeof(buff) );
+				printf("%s/0x%04x -> ", buff, fs->flow_mask.src_port);
+				inet_ntop( AF_INET6, &(fs->flow_mask.dst_ip6),
+				buff, sizeof(buff) );
+				printf("%s/0x%04x\n", buff, fs->flow_mask.dst_port);
+
+				printf("    BKT ___Prot___ _flow-id_ "
+				"______________Source IPv6/port_______________ "
+				"_______________Dest. IPv6/port_______________ "
+				"Tot_pkt/bytes Pkt/Byte Drp\n");
+			}
+			printf("    %3d ", q[l].hash_slot);
+			pe = getprotobynumber(q[l].id.proto);
+			if (pe)
+				printf("%9s ", pe->p_name);
+			else
+				printf("%9u ", q[l].id.proto);
+			printf("%7d  %39s/%-5d ", q[l].id.flow_id6,
+			    inet_ntop(AF_INET6, &(q[l].id.src_ip6),
+			    buff, sizeof(buff)),
+			    q[l].id.src_port);
+			printf(" %39s/%-5d ",
+			    inet_ntop(AF_INET6, &(q[l].id.dst_ip6),
+			    buff, sizeof(buff)),
+			    q[l].id.dst_port);
+			printf(" %4qu %8qu %2u %4u %3u\n",
+			    q[l].tot_pkts, q[l].tot_bytes,
+			    q[l].len, q[l].len_bytes, q[l].drops);
+			if (verbose)
+				printf("   S %20qd  F %20qd\n",
+				    q[l].S, q[l].F);
+		}
 	}
+	printf("\n");
 }
 
 static void
@@ -1852,7 +2179,7 @@
 	if (do_dynamic && ndyn) {
 		printf("## Dynamic rules:\n");
 		for (lac = ac, lav = av; lac != 0; lac--) {
-			rnum = strtoul(*lav++, &endptr, 10);
+			last = rnum = strtoul(*lav++, &endptr, 10);
 			if (*endptr == '-')
 				last = strtoul(endptr+1, &endptr, 10);
 			if (*endptr)
@@ -1905,17 +2232,22 @@
 "ACTION:	check-state | allow | count | deny | reject | skipto N |\n"
 "		{divert|tee} PORT | forward ADDR | pipe N | queue N\n"
 "ADDR:		[ MAC dst src ether_type ] \n"
-"		[ from IPADDR [ PORT ] to IPADDR [ PORTLIST ] ]\n"
+"		[ ip from IPADDR [ PORT ] to IPADDR [ PORTLIST ] ]\n"
+"		[ ipv6 from IP6ADDR [ PORT ] to IP6ADDR [ PORTLIST ] ]\n"
 "IPADDR:	[not] { any | me | ip/bits{x,y,z} | table(t[,v]) | IPLIST }\n"
 "IPLIST:	{ ip | ip/bits | ip:mask }[,IPLIST]\n"
+"IP6ADDR:	[not] { any | me | me6 | ip6/bits | IP6LIST }\n"
+"IP6LIST:	{ ip6 | ip6/bits }[,IP6LIST]\n"
 "OPTION_LIST:	OPTION [OPTION_LIST]\n"
-"OPTION:	bridged | {dst-ip|src-ip} ADDR | {dst-port|src-port} LIST |\n"
+"OPTION:	bridged | {dst-ip|src-ip} IPADDR | {dst-port|src-port} LIST |\n"
 "	estab | frag | {gid|uid} N | icmptypes LIST | in | out | ipid LIST |\n"
 "	iplen LIST | ipoptions SPEC | ipprecedence | ipsec | iptos SPEC |\n"
 "	ipttl LIST | ipversion VER | keep-state | layer2 | limit ... |\n"
 "	mac ... | mac-type LIST | proto LIST | {recv|xmit|via} {IF|IPADDR} |\n"
 "	setup | {tcpack|tcpseq|tcpwin} NN | tcpflags SPEC | tcpoptions SPEC |\n"
-"	verrevpath | versrcreach | antispoof\n"
+"	verrevpath | icmp6types LIST | ext6hdr LIST |\n"
+"	  {dst-ip6|src-ip6|dst-ipv6|src-ipv6} IP6ADDR |\n"
+"	  flow-id N[,N]\n"
 );
 exit(0);
 }
@@ -2124,6 +2456,227 @@
     cmd->o.len |= len+1;
 }
 
+/* XXX more ipv6 stuff */
+/* Try to find ipv6 address by hostname */
+static int
+lookup_host6 (char *host, struct in6_addr *ip6addr)
+{
+       struct hostent *he;
+
+       if (!inet_pton(AF_INET6, host, ip6addr)) {
+               if ((he = gethostbyname2(host, AF_INET6)) == NULL)
+                       return(-1);
+               memcpy( ip6addr, he->h_addr_list[0], sizeof( struct in6_addr));
+       }
+       return(0);
+}
+
+/* n2mask sets n bits of the mask */
+
+static void
+n2mask(struct in6_addr *mask, int n)
+{
+       static int minimask[9] = {
+           0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff
+       };
+       u_char  *p;
+       int     i;
+
+       memset(mask, 0, sizeof(struct in6_addr));
+       p = (u_char *) mask;
+       for (i = 0; i < 16; i++, p++, n -= 8) {
+               if (n >= 8) {
+                       *p = 0xff;
+                       continue;
+               }
+               *p = minimask[n];
+               break;
+       }
+       return;
+}
+    
+/*
+ * fills the addr and mask fields in the instruction as appropriate from av.
+ * Update length as appropriate.
+ * The following formats are allowed:
+ *     any     matches any IP6. Actually returns an empty instruction.
+ *     me      returns O_IP6_*_ME
+ *
+ *     03f1::234:123:0342                single IP6 addres
+ *     03f1::234:123:0342/24            address/mask
+ *     03f1::234:123:0342/24,03f1::234:123:0343/               List of address
+ *
+ * Set of address (as in ipv6) not supported because ipv6 address
+ * are typically random past the initial prefix.
+ * Return 1 on success, 0 on failure.
+ */
+
+static int
+fill_ip6(ipfw_insn_ip6 *cmd, char *av)
+{
+       int len = 0;
+       struct in6_addr *d = &(cmd->addr6);
+       /* Needed for multiple address.
+        * Note d[1] points to struct in6_add r mask6 of cmd
+        */
+
+       cmd->o.len &= ~F_LEN_MASK;      /* zero len */
+
+       if (!strncmp(av, "any", strlen(av)))
+               return 1;
+
+
+       if (!strncmp(av, "me", strlen(av))) {   /* Set the data for "me" opt*/
+               cmd->o.len |= F_INSN_SIZE(ipfw_insn);
+               return 1;
+       }
+       if (!strncmp(av, "me6", strlen(av))) {  /* Set the data for "me" opt*/
+               cmd->o.len |= F_INSN_SIZE(ipfw_insn);
+               return 1;
+       }
+
+       av = strdup(av);
+       while (av) {
+               /*
+                * After the address we can have '/' indicating a mask,
+                * or ',' indicating another address follows.
+                */
+
+               char *p;
+               int masklen;
+               char md = '\0';
+
+               if ((p = strpbrk( av, "/,")) ) {
+                       md = *p;        /* save the separator */
+                       *p = '\0';      /* terminate address string */
+                       p++;            /* and skip past it */
+               }
+               /* now p points to NULL, mask or next entry */
+
+               /* lookup stores address in *d as a side effect */
+               if (lookup_host6(av, d) != 0) {
+                       /* failed. Free memory and go */
+                       errx(EX_DATAERR, "bad address \"%s\"", av);
+               }
+               /* next, look at the mask, if any */
+               masklen = (md == '/') ? atoi(p) : 128;
+               if (masklen > 128 || masklen < 0)
+                       errx(EX_DATAERR, "bad width \"%s\''", p);
+               else
+                       n2mask( &d[1], masklen);
+
+               APPLY_MASK( d, &d[1])   /* mask base address with mask */
+
+               /* find next separator */
+
+               if (md == '/') {        /* find separator past the mask */
+                       p = strpbrk(p, ",");
+                       if (p)
+                               p++;
+               }
+               av = p;
+
+               /* Check this entry */
+               if (masklen == 0) {
+                   /*
+                    * 'any' turns the entire list into a NOP.
+                    * 'not any' never matches, so it is removed from the
+                    * list unless it is the only item, in which case we
+                    * report an error.
+                    */
+                   if (cmd->o.len & F_NOT) { /* "not any" never matches */
+                       if (av == NULL && len == 0) /* only this entry */
+                               errx(EX_DATAERR, "not any never matches");
+                   }
+                   /* else do nothing and skip this entry */
+                   continue;
+               }
+
+               /*
+                * A single IP can be stored alone
+                */
+               if (masklen == 128 && av == NULL && len == 0) {
+                   len = F_INSN_SIZE(struct in6_addr);
+                   break;
+               }
+
+               /* Update length and pointer to arguments */
+               len += F_INSN_SIZE(struct in6_addr)*2;
+               d += 2;
+       } /* end while */
+
+       /* Total lenght of the command, remember that 1 is the size of the base command */
+       cmd->o.len |= len+1;
+       free(av);
+       return 1;
+}
+
+/*
+ * fills command for ipv6 flow-id filtering
+ * note that the 20 bit flow number is stored in a array of u_int32_t
+ * it's supported lists of flow-id, so in the o.arg1 we store how many
+ * additional flow-id we want to filter, the basic is 1
+ */
+void
+fill_flow6( ipfw_insn_u32 *cmd, char *av )
+{
+       u_int32_t type;         /* Current flow number */
+       u_int16_t nflow = 0;    /* Current flow index */
+       char *s = av;
+       cmd->d[0] = 0;          /* Initializing the base number*/
+
+       while (s) {
+               av = strsep( &s, ",") ;
+               type = strtoul(av, &av, 0);
+               if (*av != ',' && *av != '\0')
+                       errx(EX_DATAERR, "invalid ipv6 flow number %s", av);
+               if (type > 0xfffff)
+                       errx(EX_DATAERR, "flow number out of range %s", av);
+               cmd->d[nflow] |= type;
+               nflow++;
+       }
+       if( nflow > 0 ) {
+               cmd->o.opcode = O_FLOW6ID;
+               cmd->o.len |= F_INSN_SIZE(ipfw_insn_u32) + nflow;
+               cmd->o.arg1 = nflow;
+       }
+       else {
+               errx(EX_DATAERR, "invalid ipv6 flow number %s", av);
+       }
+}
+
+static ipfw_insn *
+add_srcip6(ipfw_insn *cmd, char *av)
+{
+       fill_ip6( (ipfw_insn_ip6 *) cmd, av);
+       if (F_LEN(cmd) == 0)    /* any */
+               ;
+       if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn))       /* "me" */
+               cmd->opcode = O_IP6_SRC_ME;
+       else if (F_LEN(cmd) == (F_INSN_SIZE(struct in6_addr) + F_INSN_SIZE(ipfw_insn)))
+               /* single IP, no mask*/
+               cmd->opcode = O_IP6_SRC;
+       else                                            /* addr/mask opt */
+               cmd->opcode = O_IP6_SRC_MASK;
+       return cmd;
+}
+
+static ipfw_insn *
+add_dstip6(ipfw_insn *cmd, char *av)
+{
+       fill_ip6((ipfw_insn_ip6 *)cmd, av);
+       if (F_LEN(cmd) == 0)    /* any */
+               ;
+       if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn))       /* "me" */
+               cmd->opcode = O_IP6_DST_ME;
+       else if (F_LEN(cmd) == (F_INSN_SIZE(struct in6_addr) + F_INSN_SIZE(ipfw_insn)))
+               /* single IP, no mask*/
+               cmd->opcode = O_IP6_DST;
+       else                                            /* addr/mask opt */
+               cmd->opcode = O_IP6_DST_MASK;
+       return cmd;
+}
+/* end ipv6 stuff */
 
 /*
  * helper function to process a set of flags and set bits in the
@@ -2236,7 +2789,6 @@
 	struct dn_pipe p;
 	int i;
 	char *end;
-	uint32_t a;
 	void *par = NULL;
 
 	memset(&p, 0, sizeof p);
@@ -2298,16 +2850,15 @@
 			 */
 			par = NULL;
 
-			p.fs.flow_mask.dst_ip = 0;
-			p.fs.flow_mask.src_ip = 0;
-			p.fs.flow_mask.dst_port = 0;
-			p.fs.flow_mask.src_port = 0;
-			p.fs.flow_mask.proto = 0;
+			bzero(&p.fs.flow_mask, sizeof(p.fs.flow_mask));
 			end = NULL;
 
 			while (ac >= 1) {
 			    uint32_t *p32 = NULL;
 			    uint16_t *p16 = NULL;
+			    uint32_t *p20 = NULL;
+			    struct in6_addr *pa6 = NULL;
+			    uint32_t a;		/* the mask */
 
 			    tok = match_token(dummynet_params, *av);
 			    ac--; av++;
@@ -2321,6 +2872,9 @@
 				    p.fs.flow_mask.dst_port = ~0;
 				    p.fs.flow_mask.src_port = ~0;
 				    p.fs.flow_mask.proto = ~0;
+				    n2mask( &(p.fs.flow_mask.dst_ip6), 128);
+				    n2mask( &(p.fs.flow_mask.src_ip6), 128);
+				    p.fs.flow_mask.flow_id6 = ~0;
 				    p.fs.flags_fs |= DN_HAVE_FLOW_MASK;
 				    goto end_mask;
 
@@ -2332,6 +2886,18 @@
 				    p32 = &p.fs.flow_mask.src_ip;
 				    break;
 
+			    case TOK_DSTIP6:
+				    pa6 =  &(p.fs.flow_mask.dst_ip6);
+				    break;
+
+			    case TOK_SRCIP6:
+				    pa6 = &(p.fs.flow_mask.src_ip6);
+				    break;
+
+			    case TOK_FLOWID:
+				    p20 = &p.fs.flow_mask.flow_id6;
+				    break;
+
 			    case TOK_DSTPORT:
 				    p16 = &p.fs.flow_mask.dst_port;
 				    break;
@@ -2349,22 +2915,35 @@
 			    }
 			    if (ac < 1)
 				    errx(EX_USAGE, "mask: value missing");
-			    if (*av[0] == '/') {
+			    if (*av[0] == '/') { /* mask len */
 				    a = strtoul(av[0]+1, &end, 0);
-				    a = (a == 32) ? ~0 : (1 << a) - 1;
-			    } else
+				    /* convert to a mask for non IPv6 */
+				    if (pa6 == NULL)
+					    a = (a == 32) ? ~0 : (1 << a) - 1;
+			    } else	/* explicit mask (non IPv6) */
 				    a = strtoul(av[0], &end, 0);
 			    if (p32 != NULL)
 				    *p32 = a;
 			    else if (p16 != NULL) {
-				    if (a > 65535)
+				    if (a > 0xffff)
 					    errx(EX_DATAERR,
-						"mask: must be 16 bit");
+						"port mask must be 16 bit");
 				    *p16 = (uint16_t)a;
+			    } else if (p20 != NULL) {
+				    if (a > 0xfffff)
+					    errx(EX_DATAERR,
+						"flow_id mask must be 20 bit");
+				    *p20 = (uint32_t)a;
+			    } else if (pa6 != NULL) {
+				    if (a < 0 || a > 128)
+					    errx(EX_DATAERR,
+						"in6addr invalid mask len");
+				    else
+					    n2mask(pa6, a);
 			    } else {
-				    if (a > 255)
+				    if (a > 0xff)
 					    errx(EX_DATAERR,
-						"mask: must be 8 bit");
+						"porto mask must be 8 bit");
 				    p.fs.flow_mask.proto = (uint8_t)a;
 			    }
 			    if (a != 0)
@@ -2468,7 +3047,7 @@
 			break;
 
 		default:
-			errx(EX_DATAERR, "unrecognised option ``%s''", av[-1]);
+			errx(EX_DATAERR, "unrecognised option ``%s''", *av);
 		}
 	}
 	if (do_pipe == 1) {
@@ -2684,21 +3263,25 @@
 }
 
 static ipfw_insn *
-add_proto(ipfw_insn *cmd, char *av)
+add_proto(ipfw_insn *cmd, char *av, u_char *proto)
 {
 	struct protoent *pe;
-	u_char proto = 0;
+
+	*proto = IPPROTO_IP;
 
 	if (!strncmp(av, "all", strlen(av)))
 		; /* same as "ip" */
-	else if ((proto = atoi(av)) > 0)
+	else if ((*proto = atoi(av)) > 0)
 		; /* all done! */
 	else if ((pe = getprotobyname(av)) != NULL)
-		proto = pe->p_proto;
+		*proto = pe->p_proto;
+	else if(!strncmp(av, "ipv6", strlen(av)) ||
+	    !strncmp(av, "ip6", strlen(av)) )
+		*proto = IPPROTO_IPV6;
 	else
 		return NULL;
-	if (proto != IPPROTO_IP)
-		fill_cmd(cmd, O_PROTO, 0, proto);
+	if (proto != IPPROTO_IP && *proto != IPPROTO_IPV6)
+		fill_cmd(cmd, O_PROTO, 0, *proto);
 	return cmd;
 }
 
@@ -2749,6 +3332,38 @@
 	return NULL;
 }
 
+static ipfw_insn *
+add_src(ipfw_insn *cmd, char *av, u_char proto)
+{
+       struct in6_addr a;
+        if( proto == IPPROTO_IPV6  || strcmp( av, "me6") == 0 || inet_pton(AF_INET6, av, &a ))
+         return add_srcip6(cmd, av);
+
+       if (proto == IPPROTO_IP || strcmp( av, "me") == 0 || !inet_pton(AF_INET6, av, &a ) ) 
+         return add_srcip(cmd, av);
+
+       if( !strcmp( av, "any") )
+         return cmd; 
+
+       return NULL;  /* bad address */
+}
+
+static ipfw_insn *
+add_dst(ipfw_insn *cmd, char *av, u_char proto)
+{
+       struct in6_addr a;
+        if( proto == IPPROTO_IPV6  || strcmp( av, "me6") == 0 || inet_pton(AF_INET6, av, &a ))
+         return add_dstip6(cmd, av);
+
+       if (proto == IPPROTO_IP || strcmp( av, "me") == 0 || !inet_pton(AF_INET6, av, &a ) ) 
+         return add_dstip(cmd, av);
+
+       if( !strcmp( av, "any") )
+         return cmd; 
+
+       return NULL;  /* bad address */
+}
+
 /*
  * Parse arguments and assemble the microinstructions which make up a rule.
  * Rules are added into the 'rulebuf' and then copied in the correct order
@@ -2772,7 +3387,7 @@
 	 */
 	static uint32_t rulebuf[255], actbuf[255], cmdbuf[255];
 
-	ipfw_insn *src, *dst, *cmd, *action, *prev=NULL;
+	ipfw_insn *src, *dst, *cmd, *action, *prev=NULL, *retval=NULL;
 	ipfw_insn *first_cmd;	/* first match pattern */
 
 	struct ip_fw *rule;
@@ -3051,11 +3666,9 @@
     OR_START(get_proto);
 	NOT_BLOCK;
 	NEED1("missing protocol");
-	if (add_proto(cmd, *av)) {
+	if (add_proto(cmd, *av, &proto)) {
 		av++; ac--;
-		if (F_LEN(cmd) == 0)	/* plain IP */
-			proto = 0;
-		else {
+		if (F_LEN(cmd) != 0) {	/* plain IP */
 			proto = cmd->arg1;
 			prev = cmd;
 			cmd = next_cmd(cmd);
@@ -3079,13 +3692,16 @@
     OR_START(source_ip);
 	NOT_BLOCK;	/* optional "not" */
 	NEED1("missing source address");
-	if (add_srcip(cmd, *av)) {
+	retval = add_src(cmd, *av, proto);
+
+	if (retval) {
 		ac--; av++;
 		if (F_LEN(cmd) != 0) {	/* ! any */
 			prev = cmd;
 			cmd = next_cmd(cmd);
 		}
-	}
+	} else
+		errx(EX_USAGE, "bad source address %s", *av);
     OR_BLOCK(source_ip);
 
 	/*
@@ -3114,13 +3730,16 @@
     OR_START(dest_ip);
 	NOT_BLOCK;	/* optional "not" */
 	NEED1("missing dst address");
-	if (add_dstip(cmd, *av)) {
+	retval = add_dst(cmd, *av, proto);
+
+	if (retval) {
 		ac--; av++;
 		if (F_LEN(cmd) != 0) {	/* ! any */
 			prev = cmd;
 			cmd = next_cmd(cmd);
 		}
-	}
+	} else
+		errx( EX_USAGE, "bad destination address %s", *av);
     OR_BLOCK(dest_ip);
 
 	/*
@@ -3226,6 +3845,12 @@
 			av++; ac--;
 			break;
 
+		case TOK_ICMP6TYPES:
+			NEED1("icmptypes requires list of types");
+			fill_icmp6types((ipfw_insn_icmp6 *)cmd, *av);
+			av++; ac--;
+			break;
+
 		case TOK_IPTTL:
 			NEED1("ipttl requires TTL");
 			if (strpbrk(*av, "-,")) {
@@ -3418,8 +4043,9 @@
 
 		case TOK_PROTO:
 			NEED1("missing protocol");
-			if (add_proto(cmd, *av)) {
-				proto = cmd->arg1;
+			if (add_proto(cmd, *av, &proto)) {
+				if ( proto == IPPROTO_IPV6 )
+					fill_cmd(cmd, O_IP6, 0, 0);
 				ac--; av++;
 			} else
 				errx(EX_DATAERR, "invalid protocol ``%s''",
@@ -3440,6 +4066,20 @@
 			}
 			break;
 
+		case TOK_SRCIP6:
+			NEED1("missing source IP6");
+			if (add_srcip6(cmd, *av)) {
+				ac--; av++;
+			}
+			break;
+
+		case TOK_DSTIP6:
+			NEED1("missing destination IP6");
+			if (add_dstip6(cmd, *av)) {
+				ac--; av++;
+			}
+			break;
+
 		case TOK_SRCPORT:
 			NEED1("missing source port");
 			if (!strncmp(*av, "any", strlen(*av)) ||
@@ -3493,6 +4133,24 @@
 			av += ac;
 			ac = 0;
 			break;
+
+		case TOK_IPV6:
+			fill_cmd(cmd, O_IP6, 0, 0);
+			ac--; av++;
+			break;
+
+		case TOK_EXT6HDR:
+			fill_ext6hdr( cmd, *av );
+			ac--; av++;
+			break;
+
+		case TOK_FLOWID:
+			if (proto != IPPROTO_IPV6 )
+				errx( EX_USAGE, "flow-id filter is active only for ipv6 protocol\n");
+			fill_flow6( (ipfw_insn_u32 *) cmd, *av );
+			ac--;av++;
+			break;
+
 
 		default:
 			errx(EX_USAGE, "unrecognised option [%d] %s\n", i, s);
--- ../cleanup/sys/netinet/ip_dummynet.c	Thu Aug 26 21:19:18 2004
+++ sys/netinet/ip_dummynet.c	Fri Sep  3 13:37:35 2004
@@ -77,6 +77,9 @@
 #include <netinet/if_ether.h> /* for struct arpcom */
 #include <net/bridge.h>
 
+#include <netinet/ip6.h>       /* for ip6_input, ip6_output prototypes */
+#include <netinet6/ip6_var.h>
+
 /*
  * We keep a private variable for the simulation time, but we could
  * probably use an existing one ("softticks" in sys/kern/kern_timeout.c)
@@ -461,6 +464,14 @@
 	    ip_input(m) ;
 	    break ;
 
+	case DN_TO_IP6_IN:
+	    ip6_input(m) ;
+	    break ;
+
+	case DN_TO_IP6_OUT:
+	    (void)ip6_output(m, NULL, NULL, pkt->flags, NULL, NULL, NULL);
+	    break ;
+
 	case DN_TO_BDG_FWD :
 	    /*
 	     * The bridge requires/assumes the Ethernet header is
@@ -898,36 +909,79 @@
 {
     int i = 0 ; /* we need i and q for new allocations */
     struct dn_flow_queue *q, *prev;
+    int is_v6 = IS_IP6_FLOW_ID(id);
 
     if ( !(fs->flags_fs & DN_HAVE_FLOW_MASK) )
 	q = fs->rq[0] ;
     else {
-	/* first, do the masking */
-	id->dst_ip &= fs->flow_mask.dst_ip ;
-	id->src_ip &= fs->flow_mask.src_ip ;
+	/* first, do the masking, then hash */
 	id->dst_port &= fs->flow_mask.dst_port ;
 	id->src_port &= fs->flow_mask.src_port ;
 	id->proto &= fs->flow_mask.proto ;
 	id->flags = 0 ; /* we don't care about this one */
-	/* then, hash function */
-	i = ( (id->dst_ip) & 0xffff ) ^
-	    ( (id->dst_ip >> 15) & 0xffff ) ^
-	    ( (id->src_ip << 1) & 0xffff ) ^
-	    ( (id->src_ip >> 16 ) & 0xffff ) ^
-	    (id->dst_port << 1) ^ (id->src_port) ^
-	    (id->proto );
+	if (is_v6) {
+	    APPLY_MASK(&id->dst_ip6, &fs->flow_mask.dst_ip6);
+	    APPLY_MASK(&id->src_ip6, &fs->flow_mask.src_ip6);
+	    id->flow_id6 &= fs->flow_mask.flow_id6;
+
+	    i = ((id->dst_ip6.__u6_addr.__u6_addr32[0]) & 0xffff)^
+		((id->dst_ip6.__u6_addr.__u6_addr32[1]) & 0xffff)^
+		((id->dst_ip6.__u6_addr.__u6_addr32[2]) & 0xffff)^
+		((id->dst_ip6.__u6_addr.__u6_addr32[3]) & 0xffff)^
+
+		((id->dst_ip6.__u6_addr.__u6_addr32[0] >> 15) & 0xffff)^
+		((id->dst_ip6.__u6_addr.__u6_addr32[1] >> 15) & 0xffff)^
+		((id->dst_ip6.__u6_addr.__u6_addr32[2] >> 15) & 0xffff)^
+		((id->dst_ip6.__u6_addr.__u6_addr32[3] >> 15) & 0xffff)^
+
+		((id->src_ip6.__u6_addr.__u6_addr32[0] << 1) & 0xfffff)^
+		((id->src_ip6.__u6_addr.__u6_addr32[1] << 1) & 0xfffff)^
+		((id->src_ip6.__u6_addr.__u6_addr32[2] << 1) & 0xfffff)^
+		((id->src_ip6.__u6_addr.__u6_addr32[3] << 1) & 0xfffff)^
+
+		((id->src_ip6.__u6_addr.__u6_addr32[0] << 16) & 0xffff)^
+		((id->src_ip6.__u6_addr.__u6_addr32[1] << 16) & 0xffff)^
+		((id->src_ip6.__u6_addr.__u6_addr32[2] << 16) & 0xffff)^
+		((id->src_ip6.__u6_addr.__u6_addr32[3] << 16) & 0xffff)^
+
+		(id->dst_port << 1) ^ (id->src_port) ^
+		(id->proto ) ^
+		(id->flow_id6);
+	} else {
+	    id->dst_ip &= fs->flow_mask.dst_ip ;
+	    id->src_ip &= fs->flow_mask.src_ip ;
+
+	    i = ( (id->dst_ip) & 0xffff ) ^
+		( (id->dst_ip >> 15) & 0xffff ) ^
+		( (id->src_ip << 1) & 0xffff ) ^
+		( (id->src_ip >> 16 ) & 0xffff ) ^
+		(id->dst_port << 1) ^ (id->src_port) ^
+		(id->proto );
+	}
 	i = i % fs->rq_size ;
 	/* finally, scan the current list for a match */
 	searches++ ;
 	for (prev=NULL, q = fs->rq[i] ; q ; ) {
 	    search_steps++;
-	    if (id->dst_ip == q->id.dst_ip &&
+	    if (is_v6 &&
+		    IN6_ARE_ADDR_EQUAL(&id->dst_ip6,&q->id.dst_ip6) &&  
+		    IN6_ARE_ADDR_EQUAL(&id->src_ip6,&q->id.src_ip6) &&  
+		    id->dst_port == q->id.dst_port &&
+		    id->src_port == q->id.src_port &&
+		    id->proto == q->id.proto &&
+		    id->flags == q->id.flags &&
+		    id->flow_id6 == q->id.flow_id6)
+		break ; /* found */
+
+	    if (!is_v6 && id->dst_ip == q->id.dst_ip &&
 		    id->src_ip == q->id.src_ip &&
 		    id->dst_port == q->id.dst_port &&
 		    id->src_port == q->id.src_port &&
 		    id->proto == q->id.proto &&
 		    id->flags == q->id.flags)
 		break ; /* found */
+
+	    /* No match. Check if we can expire the entry */
 	    else if (pipe_expire && q->head == NULL && q->S == q->F+1 ) {
 		/* entry is idle and not in any heap, expire it */
 		struct dn_flow_queue *old_q = q ;
@@ -1065,7 +1119,7 @@
 {
 #if IPFW2
     struct dn_flow_set *fs;
-    ipfw_insn *cmd = rule->cmd + rule->act_ofs;
+    ipfw_insn *cmd = ACTION_PTR(rule);
 
     if (cmd->opcode == O_LOG)
 	cmd += F_LEN(cmd);
@@ -1132,7 +1186,7 @@
     struct dn_flow_queue *q = NULL ;
     int is_pipe;
 #if IPFW2
-    ipfw_insn *cmd = fwa->rule->cmd + fwa->rule->act_ofs;
+    ipfw_insn *cmd = ACTION_PTR(fwa->rule);
 #endif
 
     KASSERT(m->m_nextpkt == NULL,
@@ -1202,8 +1256,9 @@
     pkt->dn_dir = dir ;
 
     pkt->ifp = fwa->oif;
-    if (dir == DN_TO_IP_OUT)
+    if (dir == DN_TO_IP_OUT || dir == DN_TO_IP6_OUT)
 	pkt->flags = fwa->flags;
+
     if (q->head == NULL)
 	q->head = m;
     else
@@ -1372,7 +1427,7 @@
  * remove references from all ipfw rules to all pipes.
  */
 static void
-dummynet_flush()
+dummynet_flush(void)
 {
     struct dn_pipe *curr_p, *p ;
     struct dn_flow_set *fs, *curr_fs;
@@ -2017,7 +2072,7 @@
 ip_dn_init(void)
 {
     if (bootverbose)
-	    printf("DUMMYNET initialized (011031)\n");
+	    printf("DUMMYNET with IPv6 initialized (040826)\n");
 
     DUMMYNET_LOCK_INIT();
 
--- ../cleanup/sys/netinet/ip_dummynet.h	Thu Aug 26 21:19:18 2004
+++ sys/netinet/ip_dummynet.h	Fri Aug 27 13:12:06 2004
@@ -124,10 +124,13 @@
 #define DN_TO_BDG_FWD	3
 #define DN_TO_ETH_DEMUX	4
 #define DN_TO_ETH_OUT	5
+#define DN_TO_IP6_IN	6
+#define DN_TO_IP6_OUT	7
 
     dn_key output_time;		/* when the pkt is due for delivery	*/
     struct ifnet *ifp;		/* interface, for ip_output		*/
     int flags ;			/* flags, for ip_output (IPv6 ?)	*/
+    struct _ip6dn_args ip6opt;	/* XXX ipv6 options			*/
 };
 #endif /* _KERNEL */
 
--- ../cleanup/sys/netinet/ip_fw.h	Thu Aug 26 21:19:19 2004
+++ sys/netinet/ip_fw.h	Fri Aug 27 13:12:06 2004
@@ -134,11 +134,31 @@
 	O_IP_DST_LOOKUP,	/* arg1=table number, u32=value	*/
 	O_ANTISPOOF,		/* none				*/
 	O_JAIL,			/* u32 = id			*/
+	O_IP6_SRC,		/* address without mask		*/
+	O_IP6_SRC_ME,		/* my addresses			*/
+	O_IP6_SRC_MASK,		/* address with the mask	*/
+	O_IP6_DST,
+	O_IP6_DST_ME,
+	O_IP6_DST_MASK,
+	O_FLOW6ID,		/* for flow id tag in the ipv6 pkt */
+	O_ICMP6TYPE,		/* icmp6 packet type filtering	*/
+	O_EXT_HDR,		/* filtering for ipv6 extension header */
+	O_IP6,
 
 	O_LAST_OPCODE		/* not an opcode!		*/
 };
 
 /*
+ * The extension header are filtered only for presence using a bit
+ * vector with a flag for each header.
+ */
+#define EXT_FRAGMENT	0x1
+#define EXT_HOPOPTS	0x2
+#define EXT_ROUTING	0x4
+#define EXT_AH		0x8
+#define EXT_ESP		0x10
+
+/*
  * Template for instructions.
  *
  * ipfw_insn is used for all instructions which require no operands,
@@ -274,6 +294,30 @@
 	u_int32_t log_left;	/* how many left to log 	*/
 } ipfw_insn_log;
 
+/* Apply ipv6 mask on ipv6 addr */
+#define APPLY_MASK(addr,mask)                          \
+    (addr)->__u6_addr.__u6_addr32[0] &= (mask)->__u6_addr.__u6_addr32[0]; \
+    (addr)->__u6_addr.__u6_addr32[1] &= (mask)->__u6_addr.__u6_addr32[1]; \
+    (addr)->__u6_addr.__u6_addr32[2] &= (mask)->__u6_addr.__u6_addr32[2]; \
+    (addr)->__u6_addr.__u6_addr32[3] &= (mask)->__u6_addr.__u6_addr32[3];
+
+/* Structure for ipv6 */
+typedef struct _ipfw_insn_ip6 {
+       ipfw_insn o;
+       struct in6_addr addr6;
+       struct in6_addr mask6;
+} ipfw_insn_ip6;
+
+/* Used to support icmp6 types */
+typedef struct _ipfw_insn_icmp6 {
+       ipfw_insn o;
+       uint32_t d[7]; /* XXX This number si related to the netinet/icmp6.h
+                       *     define ICMP6_MAXTYPE
+                       *     as follows: n = ICMP6_MAXTYPE/32 + 1
+                        *     Actually is 203 
+                       */
+} ipfw_insn_icmp6;
+
 /*
  * Here we have the structure representing an ipfw rule.
  *
@@ -336,8 +380,14 @@
 	u_int16_t	src_port;
 	u_int8_t	proto;
 	u_int8_t	flags;	/* protocol-specific flags */
+	uint8_t		addr_type; /* 4 = ipv4, 6 = ipv6, 1=ether ? */
+	struct in6_addr dst_ip6;	/* could also store MAC addr! */
+	struct in6_addr src_ip6;
+	u_int32_t	flow_id6;
 };
 
+#define IS_IP6_FLOW_ID(id)	((id)->addr_type == 6)
+
 /*
  * Dynamic ipfw rule.
  */
@@ -410,6 +460,21 @@
 #define	IP_FW_PORT_DENY_FLAG	0x40000
 
 /*
+ * Structure for collecting parameters to dummynet for ip6_output forwarding
+ */
+struct _ip6dn_args {
+       struct ip6_pktopts *opt_or;
+       struct route_in6 ro_or;
+       int flags_or;
+       struct ip6_moptions *im6o_or;
+       struct ifnet *origifp_or;
+       struct ifnet *ifp_or;
+       struct sockaddr_in6 dst_or;
+       u_long mtu_or;
+       struct route_in6 ro_pmtu_or;
+};
+
+/*
  * Arguments for calling ipfw_chk() and dummynet_io(). We put them
  * all into a structure because this way it is easier and more
  * efficient to pass variables around and extend the interface.
@@ -425,6 +490,8 @@
 
 	struct ipfw_flow_id f_id;	/* grabbed from IP header	*/
 	u_int32_t	retval;
+
+	struct _ip6dn_args	dummypar; /* dummynet->ip6_output */
 };
 
 /*
--- ../cleanup/sys/netinet/ip_fw2.c	Thu Aug 26 21:19:21 2004
+++ sys/netinet/ip_fw2.c	Thu Sep  2 20:22:12 2004
@@ -82,6 +82,9 @@
 #include <netinet6/ipsec.h>
 #endif
 
+#include <netinet/ip6.h>
+#include <netinet/icmp6.h>
+
 #include <netinet/if_ether.h> /* XXX for ETHERTYPE_IP */
 
 #include <machine/in_cksum.h>	/* XXX for in_cksum */
@@ -277,14 +280,19 @@
 
 
 /*
- * This macro maps an ip pointer into a layer3 header pointer of type T
+ * L3HDR maps an ipv4 pointer into a layer3 header pointer of type T
+ * Other macros just cast void * into the appropriate type
  */
-#define	L3HDR(T, ip) ((T *)((u_int32_t *)(ip) + (ip)->ip_hl))
+#define	L3HDR(T, ip)	((T *)((u_int32_t *)(ip) + (ip)->ip_hl))
+#define TCP(p)		((struct tcphdr *)(p))
+#define UDP(p)		((struct udphdr *)(p))
+#define ICMP(p)		((struct icmp *)(p))
+#define ICMP6(p)	((struct icmp6_hdr *)(p))
 
 static __inline int
-icmptype_match(struct ip *ip, ipfw_insn_u32 *cmd)
+icmptype_match(struct icmp *icmp, ipfw_insn_u32 *cmd)
 {
-	int type = L3HDR(struct icmp,ip)->icmp_type;
+	int type = icmp->icmp_type;
 
 	return (type <= ICMP_MAXTYPE && (cmd->d[0] & (1<<type)) );
 }
@@ -293,9 +301,10 @@
     (1 << ICMP_TSTAMP) | (1 << ICMP_IREQ) | (1 << ICMP_MASKREQ) )
 
 static int
-is_icmp_query(struct ip *ip)
+is_icmp_query(struct icmp *icmp)
 {
-	int type = L3HDR(struct icmp, ip)->icmp_type;
+	int type = icmp->icmp_type;
+
 	return (type <= ICMP_MAXTYPE && (TT & (1<<type)) );
 }
 #undef TT
@@ -371,10 +380,9 @@
 }
 
 static int
-tcpopts_match(struct ip *ip, ipfw_insn *cmd)
+tcpopts_match(struct tcphdr *tcp, ipfw_insn *cmd)
 {
 	int optlen, bits = 0;
-	struct tcphdr *tcp = L3HDR(struct tcphdr,ip);
 	u_char *cp = (u_char *)(tcp + 1);
 	int x = (tcp->th_off << 2) - sizeof(struct tcphdr);
 
@@ -515,6 +523,83 @@
 	return 1;
 }
 
+/*
+ * ipv6 specific rules here...
+ */
+static __inline int
+icmp6type_match (int type, ipfw_insn_u32 *cmd)
+{
+       return (type <= ICMP6_MAXTYPE && (cmd->d[type/32] & (1<<(type%32)) ) );
+}
+
+static int
+flow6id_match( int curr_flow, ipfw_insn_u32 *cmd )
+{
+       int i;
+       for (i=0; i <= cmd->o.arg1; ++i )
+               if (curr_flow == cmd->d[i] )
+                       return 1;
+       return 0;
+}
+
+/* support for IP6_*_ME opcodes */
+static int
+search_ip6_addr_net (struct in6_addr * ip6_addr)
+{
+       struct ifnet *mdc;
+       struct ifaddr *mdc2;
+       struct in6_ifaddr *fdm;
+       struct in6_addr copia;
+
+       TAILQ_FOREACH(mdc, &ifnet, if_link)
+               for (mdc2 = mdc->if_addrlist.tqh_first; mdc2;
+                   mdc2 = mdc2->ifa_list.tqe_next) {
+                       if (!mdc2->ifa_addr)
+                               continue;
+                       if (mdc2->ifa_addr->sa_family == AF_INET6) {
+                               fdm = (struct in6_ifaddr *)mdc2;
+                               copia = fdm->ia_addr.sin6_addr;
+                               /* need for leaving scope_id in the sock_addr */
+                               in6_clearscope(&copia);
+                               if (IN6_ARE_ADDR_EQUAL(ip6_addr, &copia))
+                                       return 1;
+                       }
+               }
+       return 0;
+}
+
+static int
+verify_rev_path6(struct in6_addr *src, struct ifnet *ifp)
+{
+       static struct route_in6 ro;
+       struct sockaddr_in6 *dst;
+
+       dst = (struct sockaddr_in6 * )&(ro.ro_dst);
+
+       if ( !(IN6_ARE_ADDR_EQUAL (src, &dst->sin6_addr) )) {
+               bzero(dst, sizeof(*dst));
+               dst->sin6_family = AF_INET6;
+               dst->sin6_len = sizeof(*dst);
+               dst->sin6_addr = *src;
+               rtalloc_ign((struct route *)&ro, RTF_CLONING);
+       }
+       if ((ro.ro_rt == NULL) || (ifp == NULL) ||
+           (ro.ro_rt->rt_ifp->if_index != ifp->if_index))
+               return 0;
+       return 1;
+}
+static __inline int
+hash_packet6(struct ipfw_flow_id *id)
+{
+       u_int32_t i;
+       i= (id->dst_ip6.__u6_addr.__u6_addr32[0]) ^
+       (id->dst_ip6.__u6_addr.__u6_addr32[1]) ^
+       (id->dst_ip6.__u6_addr.__u6_addr32[2]) ^
+       (id->dst_ip6.__u6_addr.__u6_addr32[3]) ^
+       (id->dst_port) ^ (id->src_port) ^ (id->flow_id6);
+       return i;
+}
+/* end of ipv6 opcodes */
 
 static u_int64_t norule_counter;	/* counter for ipfw_log(NULL...) */
 
@@ -718,7 +803,8 @@
 {
 	u_int32_t i;
 
-	i = (id->dst_ip) ^ (id->src_ip) ^ (id->dst_port) ^ (id->src_port);
+	i = IS_IP6_FLOW_ID(id) ? hash_packet6(id):
+	    (id->dst_ip) ^ (id->src_ip) ^ (id->dst_port) ^ (id->src_port);
 	i &= (curr_dyn_buckets - 1);
 	return i;
 }
@@ -857,19 +943,40 @@
 		}
 		if (pkt->proto == q->id.proto &&
 		    q->dyn_type != O_LIMIT_PARENT) {
-			if (pkt->src_ip == q->id.src_ip &&
-			    pkt->dst_ip == q->id.dst_ip &&
+			if (IS_IP6_FLOW_ID(pkt)) {
+			    if (IN6_ARE_ADDR_EQUAL(&(pkt->src_ip6),
+				&(q->id.src_ip6)) &&
+			    IN6_ARE_ADDR_EQUAL(&(pkt->dst_ip6),
+				&(q->id.dst_ip6)) &&
 			    pkt->src_port == q->id.src_port &&
 			    pkt->dst_port == q->id.dst_port ) {
 				dir = MATCH_FORWARD;
 				break;
-			}
-			if (pkt->src_ip == q->id.dst_ip &&
-			    pkt->dst_ip == q->id.src_ip &&
-			    pkt->src_port == q->id.dst_port &&
-			    pkt->dst_port == q->id.src_port ) {
-				dir = MATCH_REVERSE;
-				break;
+			    }
+			    if (IN6_ARE_ADDR_EQUAL(&(pkt->src_ip6),
+				    &(q->id.dst_ip6)) &&
+				IN6_ARE_ADDR_EQUAL(&(pkt->dst_ip6),
+				    &(q->id.src_ip6)) &&
+				pkt->src_port == q->id.dst_port &&
+				pkt->dst_port == q->id.src_port ) {
+				    dir = MATCH_REVERSE;
+				    break;
+			    }
+			} else {
+			    if (pkt->src_ip == q->id.src_ip &&
+				pkt->dst_ip == q->id.dst_ip &&
+				pkt->src_port == q->id.src_port &&
+				pkt->dst_port == q->id.dst_port ) {
+				    dir = MATCH_FORWARD;
+				    break;
+			    }
+			    if (pkt->src_ip == q->id.dst_ip &&
+				pkt->dst_ip == q->id.src_ip &&
+				pkt->src_port == q->id.dst_port &&
+				pkt->dst_port == q->id.src_port ) {
+				    dir = MATCH_REVERSE;
+				    break;
+			    }
 			}
 		}
 next:
@@ -1067,15 +1174,25 @@
 	IPFW_DYN_LOCK_ASSERT();
 
 	if (ipfw_dyn_v) {
+		int is_v6 = IS_IP6_FLOW_ID(pkt);
 		i = hash_packet( pkt );
 		for (q = ipfw_dyn_v[i] ; q != NULL ; q=q->next)
 			if (q->dyn_type == O_LIMIT_PARENT &&
 			    rule== q->rule &&
 			    pkt->proto == q->id.proto &&
-			    pkt->src_ip == q->id.src_ip &&
-			    pkt->dst_ip == q->id.dst_ip &&
 			    pkt->src_port == q->id.src_port &&
-			    pkt->dst_port == q->id.dst_port) {
+			    pkt->dst_port == q->id.dst_port &&
+			    (
+				(is_v6 &&
+				 IN6_ARE_ADDR_EQUAL(&(pkt->src_ip6),
+					&(q->id.src_ip6)) &&
+				 IN6_ARE_ADDR_EQUAL(&(pkt->dst_ip6),
+					&(q->id.dst_ip6))) ||
+				(!is_v6 &&
+				 pkt->src_ip == q->id.src_ip &&
+				 pkt->dst_ip == q->id.dst_ip)
+			    )
+			) {
 				q->expire = time_second + dyn_short_lifetime;
 				DEB(printf("ipfw: lookup_dyn_parent found 0x%p\n",q);)
 				return q;
@@ -1149,10 +1266,17 @@
 		id.dst_port = id.src_port = 0;
 		id.proto = args->f_id.proto;
 
-		if (limit_mask & DYN_SRC_ADDR)
-			id.src_ip = args->f_id.src_ip;
-		if (limit_mask & DYN_DST_ADDR)
-			id.dst_ip = args->f_id.dst_ip;
+		if (IS_IP6_FLOW_ID (&(args->f_id))) {
+			if (limit_mask & DYN_SRC_ADDR)
+				id.src_ip6 = args->f_id.src_ip6;
+			if (limit_mask & DYN_DST_ADDR)
+				id.dst_ip6 = args->f_id.dst_ip6;
+		} else {
+			if (limit_mask & DYN_SRC_ADDR)
+				id.src_ip = args->f_id.src_ip;
+			if (limit_mask & DYN_DST_ADDR)
+				id.dst_ip = args->f_id.dst_ip;
+		}
 		if (limit_mask & DYN_SRC_PORT)
 			id.src_port = args->f_id.src_port;
 		if (limit_mask & DYN_DST_PORT)
@@ -1730,97 +1854,192 @@
 	struct in_addr src_ip, dst_ip;		/* NOTE: network format	*/
 	u_int16_t ip_len=0;
 	int pktlen;
+	/*
+	 * dyn_dir = MATCH_UNKNOWN when rules unchecked,
+	 * 	MATCH_NONE when checked and not matched (q = NULL),
+	 *	MATCH_FORWARD or MATCH_REVERSE otherwise (q != NULL)
+	 */
 	int dyn_dir = MATCH_UNKNOWN;
 	ipfw_dyn_rule *q = NULL;
 	struct ip_fw_chain *chain = &layer3_chain;
 	struct m_tag *mtag;
+	/*
+	 * We store in ulp a pointer to the upper layer protocol header.
+	 * In the ipv4 case this is easy to determine from the header,
+	 * but for ipv6 we might have some additional headers in the
+	 * middle. ulp is NULL if not found.
+	 */
+	void *ulp = NULL;		/* upper layer protocol pointer.  */
+	/* XXX ipv6 variables */
+	int is_ipv6 = 0;
+	u_int16_t ext_hd = 0;	/* bits vector for extension header filtering */
+	/* end of ipv6 variables */
 
 	if (m->m_flags & M_SKIP_FIREWALL)
 		return 0;	/* accept */
-	/*
-	 * dyn_dir = MATCH_UNKNOWN when rules unchecked,
-	 * 	MATCH_NONE when checked and not matched (q = NULL),
-	 *	MATCH_FORWARD or MATCH_REVERSE otherwise (q != NULL)
-	 */
-
 	pktlen = m->m_pkthdr.len;
-	if (args->eh == NULL ||		/* layer 3 packet */
-		( m->m_pkthdr.len >= sizeof(struct ip) &&
-		    ntohs(args->eh->ether_type) == ETHERTYPE_IP))
-			hlen = ip->ip_hl << 2;
+	proto = args->f_id.proto = 0;	/* mark f_id invalid */
 
-	/*
-	 * Collect parameters into local variables for faster matching.
-	 */
-	if (hlen == 0) {	/* do not grab addresses for non-ip pkts */
-		proto = args->f_id.proto = 0;	/* mark f_id invalid */
-		goto after_ip_checks;
-	}
+	/* Identify ipv6 packets and fill up variables. */
+	if (pktlen >= sizeof(struct ip6_hdr) &&
+	    (!args->eh || ntohs(args->eh->ether_type)==ETHERTYPE_IPV6) &&
+	    mtod(m, struct ip *)->ip_v == 6) {
+		is_ipv6 = 1;
+		args->f_id.addr_type = 6;
+		hlen = sizeof(struct ip6_hdr);
+		proto = mtod(m, struct ip6_hdr *)->ip6_nxt;
 
-	proto = args->f_id.proto = ip->ip_p;
-	src_ip = ip->ip_src;
-	dst_ip = ip->ip_dst;
-	if (args->eh != NULL) { /* layer 2 packets are as on the wire */
-		offset = ntohs(ip->ip_off) & IP_OFFMASK;
-		ip_len = ntohs(ip->ip_len);
-	} else {
-		offset = ip->ip_off & IP_OFFMASK;
-		ip_len = ip->ip_len;
-	}
-	pktlen = ip_len < pktlen ? ip_len : pktlen;
+		/*
+		 * PULLUP6(len, p, T) makes sure that len + sizeof(T) is
+		 * contiguous, then it sets p to point at the offset "len" in
+		 * the mbuf. WARNING: the pointer might become stale after
+		 * other pullups (but we never use it this way).
+		 */
+#define PULLUP6(len, p, T)						\
+			do {						\
+				int x = (len) + sizeof(T);		\
+				if ((m)->m_len < x) {			\
+					args->m = m = m_pullup(m, x);	\
+					if (m == 0)			\
+						goto pullup_failed;	\
+				}					\
+				p = (mtod(m, char *) + (len));		\
+			} while (0)
+
+		/* Search extension headers to find upper layer protocols */
+		while (ulp == NULL) {
+			switch (proto) {
+			case IPPROTO_ICMPV6:
+				PULLUP6(hlen, ulp, struct icmp6_hdr);
+				args->f_id.flags = ICMP6(ulp)->icmp6_type;
+				break;
+
+			case IPPROTO_TCP:
+				PULLUP6(hlen, ulp, struct tcphdr);
+				dst_port = TCP(ulp)->th_dport;
+				src_port = TCP(ulp)->th_sport;
+				args->f_id.flags = TCP(ulp)->th_flags;
+				break;
+
+			case IPPROTO_UDP:
+				PULLUP6(hlen, ulp, struct udphdr);
+				dst_port = UDP(ulp)->uh_dport;
+				src_port = UDP(ulp)->uh_sport;
+				break;
+
+			case IPPROTO_HOPOPTS:
+				PULLUP6(hlen, ulp, struct ip6_hbh);
+				ext_hd |= EXT_HOPOPTS;
+				hlen += sizeof(struct ip6_hbh);
+				proto = ((struct ip6_hbh *)ulp)->ip6h_nxt;
+				ulp = NULL;
+				break;
+
+			case IPPROTO_ROUTING:
+				PULLUP6(hlen, ulp, struct ip6_rthdr);
+				ext_hd |= EXT_ROUTING;
+				hlen += sizeof(struct ip6_rthdr);
+				proto = ((struct ip6_rthdr *)ulp)->ip6r_nxt;
+				ulp = NULL;
+				break;
+
+			case IPPROTO_FRAGMENT:
+				PULLUP6(hlen, ulp, struct ip6_frag);
+				ext_hd |= EXT_FRAGMENT;
+				hlen += sizeof (struct ip6_frag);
+				proto = ((struct ip6_frag *)ulp)->ip6f_nxt;
+				offset = 1;
+				ulp = NULL; /* XXX is it correct ? */
+				break;
+
+			case IPPROTO_AH:
+			case IPPROTO_NONE:
+			case IPPROTO_ESP:
+				PULLUP6(hlen, ulp, struct ip6_ext);
+				if (proto == IPPROTO_AH)
+				ext_hd |= EXT_AH;
+				else if (proto == IPPROTO_ESP)
+				ext_hd |= EXT_ESP;
+				hlen += ((struct ip6_ext *)ulp)->ip6e_len +
+					    sizeof (struct ip6_ext);
+				proto = ((struct ip6_ext *)ulp)->ip6e_nxt;
+				ulp = NULL;
+				break;
 
-#define PULLUP_TO(len)						\
-		do {						\
-			if ((m)->m_len < (len)) {		\
-			    args->m = m = m_pullup(m, (len));	\
-			    if (m == 0)				\
-				goto pullup_failed;		\
-			    ip = mtod(m, struct ip *);		\
-			}					\
-		} while (0)
+			default:
+				printf( "IPFW2: IPV6 - Unknown Extension Header (%d)\n",
+				    proto);
+				return 0; /* deny */
+				break;
+			} /*switch */
+		}
+		args->f_id.src_ip6 = mtod(m,struct ip6_hdr *)->ip6_src;
+		args->f_id.dst_ip6 = mtod(m,struct ip6_hdr *)->ip6_dst;
+		args->f_id.src_ip = 0;
+		args->f_id.dst_ip = 0;
+		args->f_id.flow_id6 = ntohs(mtod(m, struct ip6_hdr *)->ip6_flow);
+		/* hlen != 0 is used to detect ipv4 packets, so clear it now */
+		hlen = 0;
+	} else if (pktlen >= sizeof(struct ip) &&
+	    (!args->eh || ntohs(args->eh->ether_type) == ETHERTYPE_IP) &&
+	    mtod(m, struct ip *)->ip_v == 4) {
+		ip = mtod(m, struct ip *);
+		hlen = ip->ip_hl << 2;
+		args->f_id.addr_type = 4;
 
-	if (offset == 0) {
-		switch (proto) {
-		case IPPROTO_TCP:
-		    {
-			struct tcphdr *tcp;
+		/*
+		 * Collect parameters into local variables for faster matching.
+		 */
 
-			PULLUP_TO(hlen + sizeof(struct tcphdr));
-			tcp = L3HDR(struct tcphdr, ip);
-			dst_port = tcp->th_dport;
-			src_port = tcp->th_sport;
-			args->f_id.flags = tcp->th_flags;
-			}
-			break;
+		proto = ip->ip_p;
+		src_ip = ip->ip_src;
+		dst_ip = ip->ip_dst;
+		if (args->eh != NULL) { /* layer 2 packets are as on the wire */
+			offset = ntohs(ip->ip_off) & IP_OFFMASK;
+			ip_len = ntohs(ip->ip_len);
+		} else {
+			offset = ip->ip_off & IP_OFFMASK;
+			ip_len = ip->ip_len;
+		}
+		pktlen = ip_len < pktlen ? ip_len : pktlen;
 
-		case IPPROTO_UDP:
-		    {
-			struct udphdr *udp;
+		if (offset == 0) {
+			switch (proto) {
+			case IPPROTO_TCP:
+				PULLUP6(hlen, ulp, struct tcphdr);
+				dst_port = TCP(ulp)->th_dport;
+				src_port = TCP(ulp)->th_sport;
+				args->f_id.flags = TCP(ulp)->th_flags;
+				break;
 
-			PULLUP_TO(hlen + sizeof(struct udphdr));
-			udp = L3HDR(struct udphdr, ip);
-			dst_port = udp->uh_dport;
-			src_port = udp->uh_sport;
-			}
-			break;
+			case IPPROTO_UDP:
+				PULLUP6(hlen, ulp, struct udphdr);
+				dst_port = UDP(ulp)->uh_dport;
+				src_port = UDP(ulp)->uh_sport;
+				break;
 
-		case IPPROTO_ICMP:
-			PULLUP_TO(hlen + 4);	/* type, code and checksum. */
-			args->f_id.flags = L3HDR(struct icmp, ip)->icmp_type;
-			break;
+			case IPPROTO_ICMP:
+				/*
+				 * we only care for 4 bytes: type, code,
+				 * checksum
+				 */
+				PULLUP6(hlen, ulp, struct icmp);
+				args->f_id.flags = ICMP(ulp)->icmp_type;
+				break;
 
-		default:
-			break;
+			default:
+				break;
+			}
 		}
-#undef PULLUP_TO
-	}
-
-	args->f_id.src_ip = ntohl(src_ip.s_addr);
-	args->f_id.dst_ip = ntohl(dst_ip.s_addr);
-	args->f_id.src_port = src_port = ntohs(src_port);
-	args->f_id.dst_port = dst_port = ntohs(dst_port);
 
-after_ip_checks:
+		args->f_id.src_ip = ntohl(src_ip.s_addr);
+		args->f_id.dst_ip = ntohl(dst_ip.s_addr);
+	}
+	if (proto) { /* we may have port numbers, store them */
+		args->f_id.proto = proto;
+		args->f_id.src_port = src_port = ntohs(src_port);
+		args->f_id.dst_port = dst_port = ntohs(dst_port);
+	}
 	IPFW_LOCK(chain);		/* XXX expensive? can we run lock free? */
 	mtag = m_tag_find(m, PACKET_TAG_DIVERT, NULL);
 	if (args->rule) {
@@ -1926,11 +2145,13 @@
 			case O_JAIL:
 				/*
 				 * We only check offset == 0 && proto != 0,
-				 * as this ensures that we have an IPv4
+				 * as this ensures that we have a
 				 * packet with the ports info.
 				 */
 				if (offset!=0)
 					break;
+				if (is_ipv6) /* XXX to be fixed later */
+					break;
 				if (proto == IPPROTO_TCP ||
 				    proto == IPPROTO_UDP)
 					match = check_uidgid(
@@ -1985,7 +2206,7 @@
 				break;
 
 			case O_FRAG:
-				match = (hlen > 0 && offset != 0);
+				match = (offset != 0);
 				break;
 
 			case O_IN:	/* "out" is "not in" */
@@ -2087,7 +2308,7 @@
 			case O_IP_DSTPORT:
 				/*
 				 * offset == 0 && proto != 0 is enough
-				 * to guarantee that we have an IPv4
+				 * to guarantee that we have a
 				 * packet with port info.
 				 */
 				if ((proto==IPPROTO_UDP || proto==IPPROTO_TCP)
@@ -2107,15 +2328,25 @@
 
 			case O_ICMPTYPE:
 				match = (offset == 0 && proto==IPPROTO_ICMP &&
-				    icmptype_match(ip, (ipfw_insn_u32 *)cmd) );
+				    icmptype_match(ICMP(ulp), (ipfw_insn_u32 *)cmd) );
+				break;
+
+			case O_ICMP6TYPE:
+				match = is_ipv6 && offset == 0 &&
+				    proto==IPPROTO_ICMPV6 &&
+				    icmp6type_match(
+					ICMP6(ulp)->icmp6_type,
+					(ipfw_insn_u32 *)cmd);
 				break;
 
 			case O_IPOPT:
-				match = (hlen > 0 && ipopts_match(ip, cmd) );
+				match = (hlen > 0 &&
+				    ipopts_match(mtod(m, struct ip *), cmd) );
 				break;
 
 			case O_IPVER:
-				match = (hlen > 0 && cmd->arg1 == ip->ip_v);
+				match = (hlen > 0 &&
+				    cmd->arg1 == mtod(m, struct ip *)->ip_v);
 				break;
 
 			case O_IPID:
@@ -2129,9 +2360,9 @@
 				    if (cmd->opcode == O_IPLEN)
 					x = ip_len;
 				    else if (cmd->opcode == O_IPTTL)
-					x = ip->ip_ttl;
+					x = mtod(m, struct ip *)->ip_ttl;
 				    else /* must be IPID */
-					x = ntohs(ip->ip_id);
+					x = ntohs(mtod(m, struct ip *)->ip_id);
 				    if (cmdlen == 1) {
 					match = (cmd->arg1 == x);
 					break;
@@ -2146,48 +2377,46 @@
 
 			case O_IPPRECEDENCE:
 				match = (hlen > 0 &&
-				    (cmd->arg1 == (ip->ip_tos & 0xe0)) );
+				    (cmd->arg1 == (mtod(m, struct ip *)->ip_tos & 0xe0)) );
 				break;
 
 			case O_IPTOS:
 				match = (hlen > 0 &&
-				    flags_match(cmd, ip->ip_tos));
+				    flags_match(cmd, mtod(m, struct ip *)->ip_tos));
 				break;
 
 			case O_TCPFLAGS:
 				match = (proto == IPPROTO_TCP && offset == 0 &&
-				    flags_match(cmd,
-					L3HDR(struct tcphdr,ip)->th_flags));
+				    flags_match(cmd, TCP(ulp)->th_flags));
 				break;
 
 			case O_TCPOPTS:
 				match = (proto == IPPROTO_TCP && offset == 0 &&
-				    tcpopts_match(ip, cmd));
+				    tcpopts_match(TCP(ulp), cmd));
 				break;
 
 			case O_TCPSEQ:
 				match = (proto == IPPROTO_TCP && offset == 0 &&
 				    ((ipfw_insn_u32 *)cmd)->d[0] ==
-					L3HDR(struct tcphdr,ip)->th_seq);
+					TCP(ulp)->th_seq);
 				break;
 
 			case O_TCPACK:
 				match = (proto == IPPROTO_TCP && offset == 0 &&
 				    ((ipfw_insn_u32 *)cmd)->d[0] ==
-					L3HDR(struct tcphdr,ip)->th_ack);
+					TCP(ulp)->th_ack);
 				break;
 
 			case O_TCPWIN:
 				match = (proto == IPPROTO_TCP && offset == 0 &&
-				    cmd->arg1 ==
-					L3HDR(struct tcphdr,ip)->th_win);
+				    cmd->arg1 == TCP(ulp)->th_win);
 				break;
 
 			case O_ESTAB:
 				/* reject packets which have SYN only */
 				/* XXX should i also check for TH_ACK ? */
 				match = (proto == IPPROTO_TCP && offset == 0 &&
-				    (L3HDR(struct tcphdr,ip)->th_flags &
+				    ( TCP(ulp)->th_flags &
 				     (TH_RST | TH_ACK | TH_SYN)) != TH_SYN);
 				break;
 
@@ -2203,8 +2432,12 @@
 
 			case O_VERREVPATH:
 				/* Outgoing packets automatically pass/match */
-				match = (hlen > 0 && ((oif != NULL) ||
+				/* XXX BED: verify_path was verify_rev_path in the diff... */
+				match = ((oif != NULL) ||
 				    (m->m_pkthdr.rcvif == NULL) ||
+				    (is_ipv6 ?
+					verify_rev_path6(&(args->f_id.src_ip6),
+					    m->m_pkthdr.rcvif) :
 				    verify_path(src_ip, m->m_pkthdr.rcvif)));
 				break;
 
@@ -2235,6 +2468,60 @@
 				/* otherwise no match */
 				break;
 
+			case O_IP6_SRC:
+				match = is_ipv6 &&
+				    IN6_ARE_ADDR_EQUAL(&args->f_id.src_ip6,
+				    &((ipfw_insn_ip6 *)cmd)->addr6);
+				break;
+
+			case O_IP6_DST:
+				match = is_ipv6 &&
+				IN6_ARE_ADDR_EQUAL(&args->f_id.dst_ip6,
+				    &((ipfw_insn_ip6 *)cmd)->addr6);
+				break;
+			case O_IP6_SRC_MASK:
+				if (is_ipv6) {
+					ipfw_insn_ip6 *te = (ipfw_insn_ip6 *)cmd;
+					struct in6_addr p = args->f_id.src_ip6;
+
+					APPLY_MASK(&p, &te->mask6);
+					match = IN6_ARE_ADDR_EQUAL(&te->addr6, &p);
+				}
+				break;
+
+			case O_IP6_DST_MASK:
+				if (is_ipv6) {
+					ipfw_insn_ip6 *te = (ipfw_insn_ip6 *)cmd;
+					struct in6_addr p = args->f_id.dst_ip6;
+
+					APPLY_MASK(&p, &te->mask6);
+					match = IN6_ARE_ADDR_EQUAL(&te->addr6, &p);
+				}
+				break;
+
+			case O_IP6_SRC_ME:
+				match= is_ipv6 && search_ip6_addr_net(&args->f_id.src_ip6);
+			break;
+
+			case O_IP6_DST_ME:
+				match= is_ipv6 && search_ip6_addr_net(&args->f_id.dst_ip6);
+			break;
+
+			case O_FLOW6ID:
+				match = is_ipv6 &&
+				    flow6id_match(args->f_id.flow_id6,
+				    (ipfw_insn_u32 *) cmd);
+				break;
+
+			case O_EXT_HDR:
+				match = is_ipv6 &&
+				    (ext_hd & ((ipfw_insn *) cmd)->arg1);
+				break;
+
+			case O_IP6:
+				match = is_ipv6;
+				break;
+
 			/*
 			 * The second set of opcodes represents 'actions',
 			 * i.e. the terminal part of a rule once the packet
@@ -2297,7 +2584,7 @@
 				if (dyn_dir == MATCH_UNKNOWN &&
 				    (q = lookup_dyn_rule(&args->f_id,
 				     &dyn_dir, proto == IPPROTO_TCP ?
-					L3HDR(struct tcphdr, ip) : NULL))
+					TCP(ulp) : NULL))
 					!= NULL) {
 					/*
 					 * Found dynamic entry, update stats
@@ -2378,7 +2665,7 @@
 				 */
 				if (hlen > 0 &&
 				    (proto != IPPROTO_ICMP ||
-				     is_icmp_query(ip)) &&
+				     is_icmp_query(ICMP(ulp))) &&
 				    !(m->m_flags & (M_BCAST|M_MCAST)) &&
 				    !IN_MULTICAST(ntohl(dst_ip.s_addr))) {
 					send_reject(args, cmd->arg1,
@@ -2859,6 +3146,10 @@
 		case O_VERSRCREACH:
 		case O_ANTISPOOF:
 		case O_IPSEC:
+		case O_IP6_SRC_ME:
+		case O_IP6_DST_ME:
+		case O_EXT_HDR:
+		case O_IP6:
 			if (cmdlen != F_INSN_SIZE(ipfw_insn))
 				goto bad_size;
 			break;
@@ -2985,9 +3276,32 @@
 				return EINVAL;
 			}
 			break;
+		case O_IP6_SRC:
+		case O_IP6_DST:
+			if (cmdlen != F_INSN_SIZE(struct in6_addr) +
+			    F_INSN_SIZE(ipfw_insn))
+				goto bad_size;
+			break;
+
+		case O_FLOW6ID:
+			if (cmdlen != F_INSN_SIZE(ipfw_insn_u32) +
+			    ((ipfw_insn_u32 *)cmd)->o.arg1)
+				goto bad_size;
+			break;
+
+		case O_IP6_SRC_MASK:
+		case O_IP6_DST_MASK:
+			if ( !(cmdlen & 1) || cmdlen > 127)
+				goto bad_size;
+			break;
+		case O_ICMP6TYPE:
+			if( cmdlen != F_INSN_SIZE( ipfw_insn_icmp6 ) )
+				goto bad_size;
+			break;
+
 		default:
 			printf("ipfw: opcode %d, unknown opcode\n",
-				cmd->opcode);
+			    cmd->opcode);
 			return EINVAL;
 		}
 	}
@@ -3379,7 +3693,7 @@
 	}
 
 	ip_fw_default_rule = layer3_chain.rules;
-	printf("ipfw2 initialized, divert %s, "
+	printf("ipfw2 (+ipv6) initialized, divert %s, "
 		"rule-based forwarding "
 #ifdef IPFIREWALL_FORWARD
 		"enabled, "
--- ../cleanup/sys/netinet/ip_fw_pfil.c	Fri Aug 27 15:18:18 2004
+++ sys/netinet/ip_fw_pfil.c	Thu Sep  2 22:37:05 2004
@@ -31,6 +31,7 @@
 #include "opt_ipdn.h"
 #include "opt_ipdivert.h"
 #include "opt_inet.h"
+#include "opt_inet6.h"
 #ifndef INET
 #error IPFIREWALL requires INET.
 #endif /* INET */
@@ -111,7 +112,10 @@
 		goto pass;
 
 	if (DUMMYNET_LOADED && (ipfw & IP_FW_PORT_DYNT_FLAG) != 0) {
-		ip_dn_io_ptr(*m0, ipfw & 0xffff, DN_TO_IP_IN, &args);
+		if (mtod(*m0, struct ip *)->ip_v == 4)
+			ip_dn_io_ptr(*m0, ipfw & 0xffff, DN_TO_IP_IN, &args);
+		else if (mtod(*m0, struct ip *)->ip_v == 6)
+			ip_dn_io_ptr(*m0, ipfw & 0xffff, DN_TO_IP6_IN, &args);
 		*m0 = NULL;
 		return 0;		/* packet consumed */
 	}
@@ -194,7 +198,10 @@
 		goto pass;
 
 	if (DUMMYNET_LOADED && (ipfw & IP_FW_PORT_DYNT_FLAG) != 0) {
-		ip_dn_io_ptr(*m0, ipfw & 0xffff, DN_TO_IP_OUT, &args);
+		if (mtod(*m0, struct ip *)->ip_v == 4)
+			ip_dn_io_ptr(*m0, ipfw & 0xffff, DN_TO_IP_OUT, &args);
+		else if (mtod(*m0, struct ip *)->ip_v == 6)
+			ip_dn_io_ptr(*m0, ipfw & 0xffff, DN_TO_IP6_OUT, &args);
 		*m0 = NULL;
 		return 0;		/* packet consumed */
 	}
@@ -326,6 +333,9 @@
 ipfw_hook(void)
 {
 	struct pfil_head *pfh_inet;
+#ifdef INET6
+	struct pfil_head *pfh_inet6;
+#endif
 
 	if (ipfw_pfil_hooked)
 		return EEXIST;
@@ -333,9 +343,18 @@
 	pfh_inet = pfil_head_get(PFIL_TYPE_AF, AF_INET);
 	if (pfh_inet == NULL)
 		return ENOENT;
+#ifdef INET6
+	pfh_inet6 = pfil_head_get(PFIL_TYPE_AF, AF_INET6);
+	if (pfh_inet6 == NULL)
+		return ENOENT;
+#endif
 
 	pfil_add_hook(ipfw_check_in, NULL, PFIL_IN | PFIL_WAITOK, pfh_inet);
 	pfil_add_hook(ipfw_check_out, NULL, PFIL_OUT | PFIL_WAITOK, pfh_inet);
+#ifdef INET6
+	pfil_add_hook(ipfw_check_in, NULL, PFIL_IN | PFIL_WAITOK, pfh_inet6);
+	pfil_add_hook(ipfw_check_out, NULL, PFIL_OUT | PFIL_WAITOK, pfh_inet6);
+#endif
 
 	return 0;
 }
@@ -344,6 +363,9 @@
 ipfw_unhook(void)
 {
 	struct pfil_head *pfh_inet;
+#ifdef INET6
+	struct pfil_head *pfh_inet6;
+#endif
 
 	if (!ipfw_pfil_hooked)
 		return ENOENT;
@@ -351,9 +373,18 @@
 	pfh_inet = pfil_head_get(PFIL_TYPE_AF, AF_INET);
 	if (pfh_inet == NULL)
 		return ENOENT;
+#ifdef INET6
+	pfh_inet6 = pfil_head_get(PFIL_TYPE_AF, AF_INET6);
+	if (pfh_inet6 == NULL)
+		return ENOENT;
+#endif
 
 	pfil_remove_hook(ipfw_check_in, NULL, PFIL_IN | PFIL_WAITOK, pfh_inet);
 	pfil_remove_hook(ipfw_check_out, NULL, PFIL_OUT | PFIL_WAITOK, pfh_inet);
+#ifdef INET6
+	pfil_remove_hook(ipfw_check_in, NULL, PFIL_IN | PFIL_WAITOK, pfh_inet6);
+	pfil_remove_hook(ipfw_check_out, NULL, PFIL_OUT | PFIL_WAITOK, pfh_inet6);
+#endif
 
 	return 0;
 }


-- 
Any statement of the form "X is the one, true Y" is FALSE.
PGP fingerprint 655D 519C 26A7 82E7 2529  9BF0 5D8E 8BE9 F238 1AD4
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 189 bytes
Desc: not available
Url : http://lists.freebsd.org/pipermail/freebsd-ipfw/attachments/20040903/ce6fc1a1/attachment.bin


More information about the freebsd-ipfw mailing list