Skip site navigation (1)Skip section navigation (2)
Date:      Sun, 25 Dec 2011 18:59:22 +0400
From:      "Alexander V. Chernikov" <melifaro@FreeBSD.org>
To:        Jason Hellenthal <jhell@DataIX.net>
Cc:        Pawel Tyll <ptyll@nitronet.pl>, freebsd-net@freebsd.org, "Andrey V. Elsukov" <ae@FreeBSD.org>, freebsd-ipfw@freebsd.org
Subject:   IPFW eXtended tables [Was: Re: IPFW tables, dummynet and IPv6]
Message-ID:  <4EF73A4A.3050902@FreeBSD.org>
In-Reply-To: <20111220163355.GA87584@DataIX.net>
References:  <1674097252.20111218125051@nitronet.pl>	<4EEDD566.8020609@FreeBSD.org> <20111220163355.GA87584@DataIX.net>

next in thread | previous in thread | raw e-mail | index | archive | help
This is an OpenPGP/MIME signed message (RFC 2440 and 3156)
--------------enigABFE081192EEA4308BF10E2C
Content-Type: multipart/mixed; boundary="------------030403040904080105030905"

This is a multi-part message in MIME format.
--------------030403040904080105030905
Content-Type: text/plain; charset=ISO-8859-1
Content-Transfer-Encoding: quoted-printable

Hello everyone.

Final patch version now uses single IP_FW3 socket option.
Together with other changes this makes me think such changes should be
reviewed by a wider number of people. If there are no
objections/comments I plan to commit this on tuesday.

Changes:
* Tables (actually, radix trees) are now created/freed on demand.
* Tables can be of different types (CIDR and interfaces are supported at
the moment)
* Each tables has 2 pointers (basic and eXtended tree) which are
initialized independently permitting both IPv4/IPv6 address to be
specified in the same table without performance loss
* Every new opcode uses IP_FW3 socket option

This change does not break ABI, old ipfw(8) binary can configure IPv4
addresses on CIDR-type tables and flush every table.

--------------030403040904080105030905
Content-Type: text/plain;
 name="ipfw_tables8.diff"
Content-Transfer-Encoding: quoted-printable
Content-Disposition: inline;
 filename="ipfw_tables8.diff"

Index: sbin/ipfw/ipfw2.c
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
--- sbin/ipfw/ipfw2.c	(revision 228874)
+++ sbin/ipfw/ipfw2.c	(working copy)
@@ -42,6 +42,8 @@
 #include <timeconv.h>	/* _long_to_time */
 #include <unistd.h>
 #include <fcntl.h>
+#include <sys/param.h>	/* MIN */
+#include <stddef.h>	/* offsetof */
=20
 #include <net/ethernet.h>
 #include <net/if.h>		/* only IFNAMSIZ */
@@ -57,6 +59,12 @@ struct cmdline_opts co;	/* global options */
=20
 int resvd_set_number =3D RESVD_SET;
=20
+int ipfw_socket =3D -1;
+
+#ifndef s6_addr32
+#define s6_addr32 __u6_addr.__u6_addr32
+#endif
+
 #define GET_UINT_ARG(arg, min, max, tok, s_x) do {			\
 	if (!av[0])							\
 		errx(EX_USAGE, "%s: missing argument", match_value(s_x, tok)); \
@@ -370,33 +378,65 @@ safe_realloc(void *ptr, size_t size)
 int
 do_cmd(int optname, void *optval, uintptr_t optlen)
 {
-	static int s =3D -1;	/* the socket */
 	int i;
=20
 	if (co.test_only)
 		return 0;
=20
-	if (s =3D=3D -1)
-		s =3D socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
-	if (s < 0)
+	if (ipfw_socket =3D=3D -1)
+		ipfw_socket =3D socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
+	if (ipfw_socket < 0)
 		err(EX_UNAVAILABLE, "socket");
=20
 	if (optname =3D=3D IP_FW_GET || optname =3D=3D IP_DUMMYNET_GET ||
-	    optname =3D=3D IP_FW_ADD || optname =3D=3D IP_FW_TABLE_LIST ||
-	    optname =3D=3D IP_FW_TABLE_GETSIZE ||
+	    optname =3D=3D IP_FW_ADD || optname =3D=3D IP_FW3 ||
 	    optname =3D=3D IP_FW_NAT_GET_CONFIG ||
 	    optname < 0 ||
 	    optname =3D=3D IP_FW_NAT_GET_LOG) {
 		if (optname < 0)
 			optname =3D -optname;
-		i =3D getsockopt(s, IPPROTO_IP, optname, optval,
+		i =3D getsockopt(ipfw_socket, IPPROTO_IP, optname, optval,
 			(socklen_t *)optlen);
 	} else {
-		i =3D setsockopt(s, IPPROTO_IP, optname, optval, optlen);
+		i =3D setsockopt(ipfw_socket, IPPROTO_IP, optname, optval, optlen);
 	}
 	return i;
 }
=20
+/*
+ * do_setcmd3 - pass ipfw control cmd to kernel
+ * @optname: option name
+ * @optval: pointer to option data
+ * @optlen: option length
+ *
+ * Function encapsulates option value in IP_FW3 socket option
+ * and calls setsockopt().
+ * Function returns 0 on success or -1 overwise.
+ */
+int
+do_setcmd3(int optname, void *optval, socklen_t optlen)
+{
+	socklen_t len;
+	ip_fw3_opheader *op3;
+
+	if (co.test_only)
+		return (0);
+
+	if (ipfw_socket =3D=3D -1)
+		ipfw_socket =3D socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
+	if (ipfw_socket < 0)
+		err(EX_UNAVAILABLE, "socket");
+
+	len =3D sizeof(ip_fw3_opheader) + optlen;
+	op3 =3D alloca(len);
+	/* Zero reserved fields */
+	memset(op3, 0, sizeof(ip_fw3_opheader));
+	memcpy(op3 + 1, optval, optlen);
+	op3->opcode =3D optname;
+
+	return setsockopt(ipfw_socket, IPPROTO_IP, IP_FW3, op3, len);
+}
+
 /**
  * match_token takes a table and a string, returns the value associated
  * with the string (-1 in case of failure).
@@ -3854,7 +3894,7 @@ ipfw_flush(int force)
 }
=20
=20
-static void table_list(ipfw_table_entry ent, int need_header);
+static void table_list(uint16_t num, int need_header);
=20
 /*
  * This one handles all table-related commands
@@ -3866,12 +3906,12 @@ ipfw_flush(int force)
 void
 ipfw_table_handler(int ac, char *av[])
 {
-	ipfw_table_entry ent;
+	ipfw_table_xentry xent;
 	int do_add;
 	int is_all;
 	size_t len;
 	char *p;
-	uint32_t a;
+	uint32_t a, type, mask, addrlen;
 	uint32_t tables_max;
=20
 	len =3D sizeof(tables_max);
@@ -3886,18 +3926,20 @@ ipfw_table_handler(int ac, char *av[])
 #endif
 	}
=20
+	memset(&xent, 0, sizeof(xent));
+
 	ac--; av++;
 	if (ac && isdigit(**av)) {
-		ent.tbl =3D atoi(*av);
+		xent.tbl =3D atoi(*av);
 		is_all =3D 0;
 		ac--; av++;
 	} else if (ac && _substrcmp(*av, "all") =3D=3D 0) {
-		ent.tbl =3D 0;
+		xent.tbl =3D 0;
 		is_all =3D 1;
 		ac--; av++;
 	} else
 		errx(EX_USAGE, "table number or 'all' keyword required");
-	if (ent.tbl >=3D tables_max)
+	if (xent.tbl >=3D tables_max)
 		errx(EX_USAGE, "The table number exceeds the maximum allowed "
 			"value (%d)", tables_max - 1);
 	NEED1("table needs command");
@@ -3910,104 +3952,181 @@ ipfw_table_handler(int ac, char *av[])
 		do_add =3D **av =3D=3D 'a';
 		ac--; av++;
 		if (!ac)
-			errx(EX_USAGE, "IP address required");
-		p =3D strchr(*av, '/');
-		if (p) {
-			*p++ =3D '\0';
-			ent.masklen =3D atoi(p);
-			if (ent.masklen > 32)
-				errx(EX_DATAERR, "bad width ``%s''", p);
-		} else
-			ent.masklen =3D 32;
-		if (lookup_host(*av, (struct in_addr *)&ent.addr) !=3D 0)
-			errx(EX_NOHOST, "hostname ``%s'' unknown", *av);
+			errx(EX_USAGE, "address required");
+		/*=20
+		 * Let's try to guess type by agrument.
+		 * Possible types:=20
+		 * 1) IPv4[/mask]
+		 * 2) IPv6[/mask]
+		 * 3) interface name
+		 * 4) port ?
+		 */
+		type =3D 0;
+		if (ishexnumber(*av[0])) {
+			/* Remove / if exists */
+			if ((p =3D strchr(*av, '/')) !=3D NULL) {
+				*p =3D '\0';
+				mask =3D atoi(p + 1);
+			}
+
+			if (inet_pton(AF_INET, *av, &xent.k.addr6) =3D=3D 1) {
+				type =3D IPFW_TABLE_CIDR;
+				if ((p !=3D NULL) && (mask > 32))
+					errx(EX_DATAERR, "bad IPv4 mask width: %s", p + 1);
+				xent.masklen =3D p ? mask : 32;
+				addrlen =3D sizeof(struct in_addr);
+			} else if (inet_pton(AF_INET6, *av, &xent.k.addr6) =3D=3D 1) {
+				type =3D IPFW_TABLE_CIDR;
+				if ((p !=3D NULL) && (mask > 128))
+					errx(EX_DATAERR, "bad IPv6 mask width: %s", p + 1);
+				xent.masklen =3D p ? mask : 128;
+				addrlen =3D sizeof(struct in6_addr);
+			}
+		}
+
+		if ((type =3D=3D 0) && (strchr(*av, '.') =3D=3D NULL)) {
+			/* Assume interface name. Copy significant data only */
+			mask =3D MIN(strlen(*av), IF_NAMESIZE - 1);
+			memcpy(xent.k.iface, *av, mask);
+			/* Set mask to exact match */
+			xent.masklen =3D 8 * IF_NAMESIZE;
+			type =3D IPFW_TABLE_INTERFACE;
+			addrlen =3D IF_NAMESIZE;
+		}
+
+		if (type =3D=3D 0) {
+			if (lookup_host(*av, (struct in_addr *)&xent.k.addr6) !=3D 0)
+				errx(EX_NOHOST, "hostname ``%s'' unknown", *av);
+			xent.masklen =3D 32;
+			type =3D IPFW_TABLE_CIDR;
+			addrlen =3D sizeof(struct in_addr);
+		}
+
+		xent.type =3D type;
+		xent.len =3D offsetof(ipfw_table_xentry, k) + addrlen;
+
 		ac--; av++;
 		if (do_add && ac) {
 			unsigned int tval;
 			/* isdigit is a bit of a hack here.. */
 			if (strchr(*av, (int)'.') =3D=3D NULL && isdigit(**av))  {
-				ent.value =3D strtoul(*av, NULL, 0);
+				xent.value =3D strtoul(*av, NULL, 0);
 			} else {
 				if (lookup_host(*av, (struct in_addr *)&tval) =3D=3D 0) {
 					/* The value must be stored in host order	 *
 					 * so that the values < 65k can be distinguished */
-		       			ent.value =3D ntohl(tval);
+		       			xent.value =3D ntohl(tval);
 				} else {
 					errx(EX_NOHOST, "hostname ``%s'' unknown", *av);
 				}
 			}
 		} else
-			ent.value =3D 0;
-		if (do_cmd(do_add ? IP_FW_TABLE_ADD : IP_FW_TABLE_DEL,
-		    &ent, sizeof(ent)) < 0) {
+			xent.value =3D 0;
+		if (do_setcmd3(do_add ? IP_FW_TABLE_XADD : IP_FW_TABLE_XDEL,
+		    &xent, xent.len) < 0) {
 			/* If running silent, don't bomb out on these errors. */
 			if (!(co.do_quiet && (errno =3D=3D (do_add ? EEXIST : ESRCH))))
 				err(EX_OSERR, "setsockopt(IP_FW_TABLE_%s)",
-				    do_add ? "ADD" : "DEL");
+				    do_add ? "XADD" : "XDEL");
 			/* 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)
+				do_setcmd3(IP_FW_TABLE_XDEL, &xent, xent.len);
+				if (do_setcmd3(IP_FW_TABLE_XADD, &xent, xent.len) < 0)
 					err(EX_OSERR,
-					    "setsockopt(IP_FW_TABLE_ADD)");
+					    "setsockopt(IP_FW_TABLE_XADD)");
 			}
 		}
 	} else if (_substrcmp(*av, "flush") =3D=3D 0) {
-		a =3D is_all ? tables_max : (uint32_t)(ent.tbl + 1);
+		a =3D is_all ? tables_max : (uint32_t)(xent.tbl + 1);
 		do {
-			if (do_cmd(IP_FW_TABLE_FLUSH, &ent.tbl,
-			    sizeof(ent.tbl)) < 0)
+			if (do_cmd(IP_FW_TABLE_FLUSH, &xent.tbl,
+			    sizeof(xent.tbl)) < 0)
 				err(EX_OSERR, "setsockopt(IP_FW_TABLE_FLUSH)");
-		} while (++ent.tbl < a);
+		} while (++xent.tbl < a);
 	} else if (_substrcmp(*av, "list") =3D=3D 0) {
-		a =3D is_all ? tables_max : (uint32_t)(ent.tbl + 1);
+		a =3D is_all ? tables_max : (uint32_t)(xent.tbl + 1);
 		do {
-			table_list(ent, is_all);
-		} while (++ent.tbl < a);
+			table_list(xent.tbl, is_all);
+		} while (++xent.tbl < a);
 	} else
 		errx(EX_USAGE, "invalid table command %s", *av);
 }
=20
 static void
-table_list(ipfw_table_entry ent, int need_header)
+table_list(uint16_t num, int need_header)
 {
-	ipfw_table *tbl;
+	ipfw_xtable *tbl;
+	ipfw_table_xentry *xent;
 	socklen_t l;
-	uint32_t a;
+	uint32_t *a, sz, tval;
+	char tbuf[128];
+	struct in6_addr *addr6;
+	ip_fw3_opheader *op3;
=20
-	a =3D ent.tbl;
-	l =3D sizeof(a);
-	if (do_cmd(IP_FW_TABLE_GETSIZE, &a, (uintptr_t)&l) < 0)
-		err(EX_OSERR, "getsockopt(IP_FW_TABLE_GETSIZE)");
+	/* Prepend value with IP_FW3 header */
+	l =3D sizeof(ip_fw3_opheader) + sizeof(uint32_t);
+	op3 =3D alloca(l);
+	/* Zero reserved fields */
+	memset(op3, 0, sizeof(ip_fw3_opheader));
+	a =3D (uint32_t *)(op3 + 1);
+	*a =3D num;
+	op3->opcode =3D IP_FW_TABLE_XGETSIZE;
+	if (do_cmd(IP_FW3, op3, (uintptr_t)&l) < 0)
+		err(EX_OSERR, "getsockopt(IP_FW_TABLE_XGETSIZE)");
=20
 	/* If a is zero we have nothing to do, the table is empty. */
-	if (a =3D=3D 0)
+	if (*a =3D=3D 0)
 		return;
=20
-	l =3D sizeof(*tbl) + a * sizeof(ipfw_table_entry);
+	l =3D *a;
 	tbl =3D safe_calloc(1, l);
-	tbl->tbl =3D ent.tbl;
-	if (do_cmd(IP_FW_TABLE_LIST, tbl, (uintptr_t)&l) < 0)
-		err(EX_OSERR, "getsockopt(IP_FW_TABLE_LIST)");
+	tbl->opheader.opcode =3D IP_FW_TABLE_XLIST;
+	tbl->tbl =3D num;
+	if (do_cmd(IP_FW3, tbl, (uintptr_t)&l) < 0)
+		err(EX_OSERR, "getsockopt(IP_FW_TABLE_XLIST)");
 	if (tbl->cnt && need_header)
 		printf("---table(%d)---\n", tbl->tbl);
-	for (a =3D 0; a < tbl->cnt; a++) {
-		unsigned int tval;
-		tval =3D tbl->ent[a].value;
-		if (co.do_value_as_ip) {
-			char tbuf[128];
-			strncpy(tbuf, inet_ntoa(*(struct in_addr *)
-				&tbl->ent[a].addr), 127);
-			/* inet_ntoa expects network order */
-			tval =3D 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, tval);
+	sz =3D tbl->size - sizeof(ipfw_xtable);
+	xent =3D &tbl->xent[0];
+	while (sz > 0) {
+		switch (tbl->type) {
+		case IPFW_TABLE_CIDR:
+			/* IPv4 or IPv6 prefixes */
+			tval =3D xent->value;
+			addr6 =3D &xent->k.addr6;
+
+			if ((addr6->s6_addr32[0] =3D=3D 0) && (addr6->s6_addr32[1] =3D=3D 0) =
&&=20
+					(addr6->s6_addr32[2] =3D=3D 0)) {
+				/* IPv4 address */
+				inet_ntop(AF_INET, &addr6->s6_addr32[3], tbuf, sizeof(tbuf));
+			} else {
+				/* IPv6 address */
+				inet_ntop(AF_INET6, addr6, tbuf, sizeof(tbuf));
+			}
+
+			if (co.do_value_as_ip) {
+				tval =3D htonl(tval);
+				printf("%s/%u %s\n", tbuf, xent->masklen,
+					inet_ntoa(*(struct in_addr *)&tval));
+			} else
+				printf("%s/%u %u\n", tbuf, xent->masklen, tval);
+			break;
+		case IPFW_TABLE_INTERFACE:
+			/* Interface names, direct match at the moment */
+			tval =3D xent->value;
+			if (co.do_value_as_ip) {
+				tval =3D htonl(tval);
+				printf("%s/%s %s\n", xent->k.iface, xent->k.iface,
+					inet_ntoa(*(struct in_addr *)&tval));
+			} else
+				printf("%s/%s %u\n", xent->k.iface, xent->k.iface, tval);
 		}
+
+		if (sz < xent->len)
+			break;
+		sz -=3D xent->len;
+		xent =3D (void *)xent + xent->len;
 	}
+
 	free(tbl);
 }
Index: sys/netinet/ip_fw.h
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
--- sys/netinet/ip_fw.h	(revision 228874)
+++ sys/netinet/ip_fw.h	(working copy)
@@ -62,6 +62,19 @@
  */
 #define	IPFW_CALLSTACK_SIZE	16
=20
+/* IP_FW3 header/opcodes */
+typedef struct _ip_fw3_opheader {
+	uint16_t opcode;	/* Operation opcode */
+	uint16_t reserved[3];	/* Align to 64-bit boundary */
+} ip_fw3_opheader;
+
+
+/* IPFW extented tables support */
+#define	IP_FW_TABLE_XADD	86	/* add entry */
+#define	IP_FW_TABLE_XDEL	87	/* delete entry */
+#define	IP_FW_TABLE_XGETSIZE	88	/* get table size */
+#define	IP_FW_TABLE_XLIST	89	/* list table contents */
+
 /*
  * The kernel representation of ipfw rules is made of a list of
  * 'instructions' (for all practical purposes equivalent to BPF
@@ -581,6 +594,11 @@ struct _ipfw_dyn_rule {
 /*
  * These are used for lookup tables.
  */
+
+#define	IPFW_TABLE_CIDR		1	/* Table for holding IPv4/IPv6 prefixes */
+#define	IPFW_TABLE_INTERFACE	2	/* Table for holding interface names */
+#define	IPFW_TABLE_MAXTYPE	2	/* Maximum valid number */
+
 typedef struct	_ipfw_table_entry {
 	in_addr_t	addr;		/* network address		*/
 	u_int32_t	value;		/* value			*/
@@ -588,6 +606,19 @@ typedef struct	_ipfw_table_entry {
 	u_int8_t	masklen;	/* mask length			*/
 } ipfw_table_entry;
=20
+typedef struct	_ipfw_table_xentry {
+	uint16_t	len;		/* Total entry length		*/
+	uint8_t		type;		/* entry type			*/
+	uint8_t		masklen;	/* mask length			*/
+	uint16_t	tbl;		/* table number			*/
+	uint32_t	value;		/* value			*/
+	union {
+		/* Longest field needs to be aligned by 4-byte boundary	*/
+		struct in6_addr	addr6;	/* IPv6 address 		*/
+		char	iface[IF_NAMESIZE];	/* interface name	*/
+	} k;
+} ipfw_table_xentry;
+
 typedef struct	_ipfw_table {
 	u_int32_t	size;		/* size of entries in bytes	*/
 	u_int32_t	cnt;		/* # of entries			*/
@@ -595,4 +626,13 @@ typedef struct	_ipfw_table {
 	ipfw_table_entry ent[0];	/* entries			*/
 } ipfw_table;
=20
+typedef struct	_ipfw_xtable {
+	ip_fw3_opheader	opheader;	/* eXtended tables are controlled via IP_FW3 =
*/
+	uint32_t	size;		/* size of entries in bytes	*/
+	uint32_t	cnt;		/* # of entries			*/
+	uint16_t	tbl;		/* table number			*/
+	uint8_t		type;		/* table type			*/
+	ipfw_table_xentry xent[0];	/* entries			*/
+} ipfw_xtable;
+
 #endif /* _IPFW2_H */
Index: sys/netinet/ipfw/ip_fw_private.h
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
--- sys/netinet/ipfw/ip_fw_private.h	(revision 228874)
+++ sys/netinet/ipfw/ip_fw_private.h	(working copy)
@@ -216,8 +216,6 @@ struct ip_fw_chain {
 	int		n_rules;	/* number of static rules */
 	int		static_len;	/* total len of static rules */
 	struct ip_fw	**map;		/* array of rule ptrs to ease lookup */
-	LIST_HEAD(nat_list, cfg_nat) nat;       /* list of nat entries */
-	struct radix_node_head *tables[IPFW_TABLES_MAX];
 #if defined( __linux__ ) || defined( _WIN32 )
 	spinlock_t rwmtx;
 	spinlock_t uh_lock;
@@ -227,6 +225,10 @@ struct ip_fw_chain {
 #endif
 	uint32_t	id;		/* ruleset id */
 	uint32_t	gencnt;		/* generation count */
+	LIST_HEAD(nat_list, cfg_nat) nat;       /* list of nat entries */
+	struct radix_node_head *tables[IPFW_TABLES_MAX];	/* IPv4 tables */
+	struct radix_node_head *xtables[IPFW_TABLES_MAX];	/* extended tables */=

+	uint8_t		tabletype[IPFW_TABLES_MAX];	/* Table type */
 };
=20
 struct sockopt;	/* used by tcp_var.h */
@@ -273,16 +275,20 @@ int ipfw_check_hook(void *arg, struct mbuf **m0, s
 struct radix_node;
 int ipfw_lookup_table(struct ip_fw_chain *ch, uint16_t tbl, in_addr_t ad=
dr,
     uint32_t *val);
+int ipfw_lookup_table_extended(struct ip_fw_chain *ch, uint16_t tbl, voi=
d *paddr,
+    uint32_t *val, int type);
 int ipfw_init_tables(struct ip_fw_chain *ch);
 void ipfw_destroy_tables(struct ip_fw_chain *ch);
 int ipfw_flush_table(struct ip_fw_chain *ch, uint16_t tbl);
-int ipfw_add_table_entry(struct ip_fw_chain *ch, uint16_t tbl, in_addr_t=
 addr,
-    uint8_t mlen, uint32_t value);
+int ipfw_add_table_entry(struct ip_fw_chain *ch, uint16_t tbl, void *pad=
dr,
+    uint8_t plen, uint8_t mlen, uint8_t type, uint32_t value);
+int ipfw_del_table_entry(struct ip_fw_chain *ch, uint16_t tbl, void *pad=
dr,
+    uint8_t plen, uint8_t mlen, uint8_t type);
+int ipfw_count_table(struct ip_fw_chain *ch, uint32_t tbl, uint32_t *cnt=
);
 int ipfw_dump_table_entry(struct radix_node *rn, void *arg);
-int ipfw_del_table_entry(struct ip_fw_chain *ch, uint16_t tbl, in_addr_t=
 addr,
-    uint8_t mlen);
-int ipfw_count_table(struct ip_fw_chain *ch, uint32_t tbl, uint32_t *cnt=
);
 int ipfw_dump_table(struct ip_fw_chain *ch, ipfw_table *tbl);
+int ipfw_count_xtable(struct ip_fw_chain *ch, uint32_t tbl, uint32_t *cn=
t);
+int ipfw_dump_xtable(struct ip_fw_chain *ch, ipfw_xtable *tbl);
=20
 /* In ip_fw_nat.c -- XXX to be moved to ip_var.h */
=20
Index: sys/netinet/ipfw/ip_fw_sockopt.c
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
--- sys/netinet/ipfw/ip_fw_sockopt.c	(revision 228874)
+++ sys/netinet/ipfw/ip_fw_sockopt.c	(working copy)
@@ -668,7 +668,6 @@ check_ipfw_struct(struct ip_fw *rule, int size)
 			    cmdlen !=3D F_INSN_SIZE(ipfw_insn_u32))
 				goto bad_size;
 			break;
-
 		case O_MACADDR2:
 			if (cmdlen !=3D F_INSN_SIZE(ipfw_insn_mac))
 				goto bad_size;
@@ -941,6 +940,7 @@ ipfw_getrules(struct ip_fw_chain *chain, void *buf
 }
=20
=20
+#define IP_FW3_OPLENGTH(x)	((x)->sopt_valsize - sizeof(ip_fw3_opheader))=

 /**
  * {set|get}sockopt parser.
  */
@@ -949,10 +949,13 @@ ipfw_ctl(struct sockopt *sopt)
 {
 #define	RULE_MAXSIZE	(256*sizeof(u_int32_t))
 	int error;
-	size_t size;
+	size_t size, len, valsize;
 	struct ip_fw *buf, *rule;
 	struct ip_fw_chain *chain;
 	u_int32_t rulenum[2];
+	uint32_t opt;
+	char xbuf[128];
+	ip_fw3_opheader *op3 =3D NULL;
=20
 	error =3D priv_check(sopt->sopt_td, PRIV_NETINET_IPFW);
 	if (error)
@@ -972,7 +975,17 @@ ipfw_ctl(struct sockopt *sopt)
 	chain =3D &V_layer3_chain;
 	error =3D 0;
=20
-	switch (sopt->sopt_name) {
+	/* Save original valsize before it is altered via sooptcopyin() */
+	valsize =3D sopt->sopt_valsize;
+	if ((opt =3D sopt->sopt_name) =3D=3D IP_FW3) {
+		if ((error =3D sooptcopyin(sopt, xbuf, sizeof(xbuf),
+			sizeof(ip_fw3_opheader))) !=3D 0)
+			return (error);
+		op3 =3D (ip_fw3_opheader *)xbuf;
+		opt =3D op3->opcode;
+	}
+
+	switch (opt) {
 	case IP_FW_GET:
 		/*
 		 * pass up a copy of the current rules. Static rules
@@ -1111,7 +1124,8 @@ ipfw_ctl(struct sockopt *sopt)
 			if (error)
 				break;
 			error =3D ipfw_add_table_entry(chain, ent.tbl,
-			    ent.addr, ent.masklen, ent.value);
+			    &ent.addr, sizeof(ent.addr), ent.masklen,=20
+			    IPFW_TABLE_CIDR, ent.value);
 		}
 		break;
=20
@@ -1124,10 +1138,36 @@ ipfw_ctl(struct sockopt *sopt)
 			if (error)
 				break;
 			error =3D ipfw_del_table_entry(chain, ent.tbl,
-			    ent.addr, ent.masklen);
+			    &ent.addr, sizeof(ent.addr), ent.masklen, IPFW_TABLE_CIDR);
 		}
 		break;
=20
+	case IP_FW_TABLE_XADD: /* IP_FW3 */
+	case IP_FW_TABLE_XDEL: /* IP_FW3 */
+		{
+			ipfw_table_xentry *xent =3D (ipfw_table_xentry *)(op3 + 1);
+
+			/* Check minimum header size */
+			if (IP_FW3_OPLENGTH(sopt) < offsetof(ipfw_table_xentry, k)) {
+				error =3D EINVAL;
+				break;
+			}
+
+			/* Check if len field is valid */
+			if ((len =3D xent->len - offsetof(ipfw_table_xentry, k)) >=20
+					sizeof(ipfw_table_xentry)) {
+				error =3D EINVAL;
+				break;
+			}
+
+			error =3D (opt =3D=3D IP_FW_TABLE_XADD) ?
+				ipfw_add_table_entry(chain, xent->tbl, &xent->k,=20
+					len, xent->masklen, xent->type, xent->value) :
+				ipfw_del_table_entry(chain, xent->tbl, &xent->k,
+					len, xent->masklen, xent->type);
+		}
+		break;
+
 	case IP_FW_TABLE_FLUSH:
 		{
 			u_int16_t tbl;
@@ -1136,9 +1176,7 @@ ipfw_ctl(struct sockopt *sopt)
 			    sizeof(tbl), sizeof(tbl));
 			if (error)
 				break;
-			IPFW_WLOCK(chain);
 			error =3D ipfw_flush_table(chain, tbl);
-			IPFW_WUNLOCK(chain);
 		}
 		break;
=20
@@ -1187,6 +1225,62 @@ ipfw_ctl(struct sockopt *sopt)
 		}
 		break;
=20
+	case IP_FW_TABLE_XGETSIZE: /* IP_FW3 */
+		{
+			uint32_t *tbl;
+
+			if (IP_FW3_OPLENGTH(sopt) < sizeof(uint32_t)) {
+				error =3D EINVAL;
+				break;
+			}
+
+			tbl =3D (uint32_t *)(op3 + 1);
+
+			IPFW_RLOCK(chain);
+			error =3D ipfw_count_xtable(chain, *tbl, tbl);
+			IPFW_RUNLOCK(chain);
+			if (error)
+				break;
+			error =3D sooptcopyout(sopt, op3, sopt->sopt_valsize);
+		}
+		break;
+
+	case IP_FW_TABLE_XLIST: /* IP_FW3 */
+		{
+			ipfw_xtable *tbl;
+
+			if ((size =3D valsize) < sizeof(ipfw_xtable)) {
+				error =3D EINVAL;
+				break;
+			}
+
+			tbl =3D malloc(size, M_TEMP, M_ZERO | M_WAITOK);
+			memcpy(tbl, op3, sizeof(ipfw_xtable));
+
+			/* Get maximum number of entries we can store */
+			tbl->size =3D (size - sizeof(ipfw_xtable)) /
+			    sizeof(ipfw_table_xentry);
+			IPFW_RLOCK(chain);
+			error =3D ipfw_dump_xtable(chain, tbl);
+			IPFW_RUNLOCK(chain);
+			if (error) {
+				free(tbl, M_TEMP);
+				break;
+			}
+
+			/* Revert size field back to bytes */
+			tbl->size =3D tbl->size * sizeof(ipfw_table_xentry) +
+				sizeof(ipfw_table);
+			/*=20
+			 * Since we call sooptcopyin() with small buffer, sopt_valsize is
+			 * decreased to reflect supplied buffer size. Set it back to original=
 value
+			 */
+			sopt->sopt_valsize =3D valsize;
+			error =3D sooptcopyout(sopt, tbl, size);
+			free(tbl, M_TEMP);
+		}
+		break;
+
 	/*--- NAT operations are protected by the IPFW_LOCK ---*/
 	case IP_FW_NAT_CFG:
 		if (IPFW_NAT_LOADED)
Index: sys/netinet/ipfw/ip_fw_table.c
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
--- sys/netinet/ipfw/ip_fw_table.c	(revision 228874)
+++ sys/netinet/ipfw/ip_fw_table.c	(working copy)
@@ -76,6 +76,29 @@ struct table_entry {
 	u_int32_t		value;
 };
=20
+struct xaddr_iface {
+	uint8_t		if_len;		/* length of this struct */
+	uint8_t		pad[7];		/* Align name */
+	char 		ifname[IF_NAMESIZE];	/* Interface name */
+};
+
+struct table_xentry {
+	struct radix_node	rn[2];
+	union {
+#ifdef INET6
+		struct sockaddr_in6	addr6;
+#endif
+		struct xaddr_iface	iface;
+	} a;
+	union {
+#ifdef INET6
+		struct sockaddr_in6	mask6;
+#endif
+		struct xaddr_iface	ifmask;
+	} m;
+	u_int32_t		value;
+};
+
 /*
  * The radix code expects addr and mask to be array of bytes,
  * with the first byte being the length of the array. rn_inithead
@@ -87,57 +110,264 @@ struct table_entry {
  */
 #define KEY_LEN(v)	*((uint8_t *)&(v))
 #define KEY_OFS		(8*offsetof(struct sockaddr_in, sin_addr))
+/*
+ * Do not require radix to compare more than actual IPv4/IPv6 address
+ */
+#define KEY_LEN_INET	(offsetof(struct sockaddr_in, sin_addr) + sizeof(in=
_addr_t))
+#define KEY_LEN_INET6	(offsetof(struct sockaddr_in6, sin6_addr) + sizeof=
(struct in6_addr))
=20
+static inline void
+ipv6_writemask(struct in6_addr *addr6, uint8_t mask)
+{
+	uint32_t *cp;
+
+	for (cp =3D (uint32_t *)addr6; mask >=3D 32; mask -=3D 32)
+		*cp++ =3D 0xFFFFFFFF;
+	*cp =3D htonl(mask ? ~((1 << (32 - mask)) - 1) : 0);
+}
+
 int
-ipfw_add_table_entry(struct ip_fw_chain *ch, uint16_t tbl, in_addr_t add=
r,
-    uint8_t mlen, uint32_t value)
+ipfw_add_table_entry(struct ip_fw_chain *ch, uint16_t tbl, void *paddr,
+    uint8_t plen, uint8_t mlen, uint8_t type, uint32_t value)
 {
-	struct radix_node_head *rnh;
+	struct radix_node_head *rnh, **rnh_ptr;
 	struct table_entry *ent;
+	struct table_xentry *xent;
 	struct radix_node *rn;
+	in_addr_t addr;
+	int offset;
+	void *ent_ptr;
+	struct sockaddr *addr_ptr, *mask_ptr;
+	char c;
=20
 	if (tbl >=3D IPFW_TABLES_MAX)
 		return (EINVAL);
-	rnh =3D ch->tables[tbl];
-	ent =3D malloc(sizeof(*ent), M_IPFW_TBL, M_NOWAIT | M_ZERO);
-	if (ent =3D=3D NULL)
-		return (ENOMEM);
-	ent->value =3D value;
-	KEY_LEN(ent->addr) =3D KEY_LEN(ent->mask) =3D 8;
-	ent->mask.sin_addr.s_addr =3D htonl(mlen ? ~((1 << (32 - mlen)) - 1) : =
0);
-	ent->addr.sin_addr.s_addr =3D addr & ent->mask.sin_addr.s_addr;
+
+	switch (type) {
+	case IPFW_TABLE_CIDR:
+		if (plen =3D=3D sizeof(in_addr_t)) {
+			ent =3D malloc(sizeof(*ent), M_IPFW_TBL, M_WAITOK | M_ZERO);
+			ent->value =3D value;
+			/* Set 'total' structure length */
+			KEY_LEN(ent->addr) =3D KEY_LEN_INET;
+			KEY_LEN(ent->mask) =3D KEY_LEN_INET;
+			/* Set offset of IPv4 address in bits */
+			offset =3D (8 * offsetof(struct sockaddr_in, sin_addr));
+			ent->mask.sin_addr.s_addr =3D htonl(mlen ? ~((1 << (32 - mlen)) - 1) =
: 0);
+			addr =3D *((in_addr_t *)paddr);
+			ent->addr.sin_addr.s_addr =3D addr & ent->mask.sin_addr.s_addr;
+			/* Set pointers */
+			rnh_ptr =3D &ch->tables[tbl];
+			ent_ptr =3D ent;
+			addr_ptr =3D (struct sockaddr *)&ent->addr;
+			mask_ptr =3D (struct sockaddr *)&ent->mask;
+#ifdef INET6
+		} else if (plen =3D=3D sizeof(struct in6_addr)) {
+			/* IPv6 case */
+			if (mlen > 128)
+				return (EINVAL);
+			xent =3D malloc(sizeof(*xent), M_IPFW_TBL, M_WAITOK | M_ZERO);
+			xent->value =3D value;
+			/* Set 'total' structure length */
+			KEY_LEN(xent->a.addr6) =3D KEY_LEN_INET6;
+			KEY_LEN(xent->m.mask6) =3D KEY_LEN_INET6;
+			/* Set offset of IPv6 address in bits */
+			offset =3D (8 * offsetof(struct sockaddr_in6, sin6_addr));
+			ipv6_writemask(&xent->m.mask6.sin6_addr, mlen);
+			memcpy(&xent->a.addr6.sin6_addr, paddr, sizeof(struct in6_addr));
+			APPLY_MASK(&xent->a.addr6.sin6_addr, &xent->m.mask6.sin6_addr);
+			/* Set pointers */
+			rnh_ptr =3D &ch->xtables[tbl];
+			ent_ptr =3D xent;
+			addr_ptr =3D (struct sockaddr *)&xent->a.addr6;
+			mask_ptr =3D (struct sockaddr *)&xent->m.mask6;
+#endif
+		} else {
+			/* Unknown CIDR type */
+			return (EINVAL);
+		}
+		break;
+=09
+	case IPFW_TABLE_INTERFACE:
+		/* Check if string is terminated */
+		c =3D ((char *)paddr)[IF_NAMESIZE - 1];
+		((char *)paddr)[IF_NAMESIZE - 1] =3D '\0';
+		if (((mlen =3D strlen((char *)paddr)) =3D=3D IF_NAMESIZE - 1) && (c !=3D=
 '\0'))
+			return (EINVAL);
+
+		xent =3D malloc(sizeof(*xent), M_IPFW_TBL, M_WAITOK | M_ZERO);
+		xent->value =3D value;
+		/* Set 'total' structure length */
+		KEY_LEN(xent->a.iface) =3D mlen;
+		KEY_LEN(xent->m.ifmask) =3D mlen;
+		/* Set offset of interface name in bits */
+		offset =3D (8 * offsetof(struct xaddr_iface, ifname));
+		/* Assume direct match */
+		/* FIXME: Add interface pattern matching */
+#if 0
+		memset(xent->m.ifmask.ifname, 0xFF, IF_NAMESIZE);
+		mask_ptr =3D (struct sockaddr *)&xent->m.ifmask;
+#endif
+		mask_ptr =3D NULL;
+		memcpy(xent->a.iface.ifname, paddr, mlen);
+		/* Set pointers */
+		rnh_ptr =3D &ch->xtables[tbl];
+		ent_ptr =3D xent;
+		addr_ptr =3D (struct sockaddr *)&xent->a.iface;
+		break;
+
+	default:
+		return (EINVAL);
+	}
+
 	IPFW_WLOCK(ch);
-	rn =3D rnh->rnh_addaddr(&ent->addr, &ent->mask, rnh, (void *)ent);
+
+	/* Check if tabletype is valid */
+	if ((ch->tabletype[tbl] !=3D 0) && (ch->tabletype[tbl] !=3D type)) {
+		IPFW_WUNLOCK(ch);
+		free(ent_ptr, M_IPFW_TBL);
+		return (EINVAL);
+	}
+
+	/* Check if radix tree exists */
+	if ((rnh =3D *rnh_ptr) =3D=3D NULL) {
+		IPFW_WUNLOCK(ch);
+		/* Create radix for a new table */
+		if (!rn_inithead((void **)&rnh, offset)) {
+			free(ent_ptr, M_IPFW_TBL);
+			return (ENOMEM);
+		}
+
+		IPFW_WLOCK(ch);
+		if (*rnh_ptr !=3D NULL) {
+			/* Tree is already attached by other thread */
+			rn_detachhead((void **)&rnh);
+			rnh =3D *rnh_ptr;
+			/* Check table type another time */
+			if (ch->tabletype[tbl] !=3D type) {
+				IPFW_WUNLOCK(ch);
+				free(ent_ptr, M_IPFW_TBL);
+				return (EINVAL);
+			}
+		} else {
+			*rnh_ptr =3D rnh;
+			/*=20
+			 * Set table type. It can be set already
+			 * (if we have IPv6-only table) but setting
+			 * it another time does not hurt
+			 */
+			ch->tabletype[tbl] =3D type;
+		}
+	}
+
+	rn =3D rnh->rnh_addaddr(addr_ptr, mask_ptr, rnh, ent_ptr);
+	IPFW_WUNLOCK(ch);
+
 	if (rn =3D=3D NULL) {
-		IPFW_WUNLOCK(ch);
-		free(ent, M_IPFW_TBL);
+		free(ent_ptr, M_IPFW_TBL);
 		return (EEXIST);
 	}
-	IPFW_WUNLOCK(ch);
 	return (0);
 }
=20
 int
-ipfw_del_table_entry(struct ip_fw_chain *ch, uint16_t tbl, in_addr_t add=
r,
-    uint8_t mlen)
+ipfw_del_table_entry(struct ip_fw_chain *ch, uint16_t tbl, void *paddr,
+    uint8_t plen, uint8_t mlen, uint8_t type)
 {
-	struct radix_node_head *rnh;
+	struct radix_node_head *rnh, **rnh_ptr;
 	struct table_entry *ent;
+	in_addr_t addr;
 	struct sockaddr_in sa, mask;
+	struct sockaddr *sa_ptr, *mask_ptr;
+	char c;
=20
 	if (tbl >=3D IPFW_TABLES_MAX)
 		return (EINVAL);
-	rnh =3D ch->tables[tbl];
-	KEY_LEN(sa) =3D KEY_LEN(mask) =3D 8;
-	mask.sin_addr.s_addr =3D htonl(mlen ? ~((1 << (32 - mlen)) - 1) : 0);
-	sa.sin_addr.s_addr =3D addr & mask.sin_addr.s_addr;
+
+	switch (type) {
+	case IPFW_TABLE_CIDR:
+		if (plen =3D=3D sizeof(in_addr_t)) {
+			/* Set 'total' structure length */
+			KEY_LEN(sa) =3D KEY_LEN_INET;
+			KEY_LEN(mask) =3D KEY_LEN_INET;
+			mask.sin_addr.s_addr =3D htonl(mlen ? ~((1 << (32 - mlen)) - 1) : 0);=

+			addr =3D *((in_addr_t *)paddr);
+			sa.sin_addr.s_addr =3D addr & mask.sin_addr.s_addr;
+			rnh_ptr =3D &ch->tables[tbl];
+			sa_ptr =3D (struct sockaddr *)&sa;
+			mask_ptr =3D (struct sockaddr *)&mask;
+#ifdef INET6
+		} else if (plen =3D=3D sizeof(struct in6_addr)) {
+			/* IPv6 case */
+			if (mlen > 128)
+				return (EINVAL);
+			struct sockaddr_in6 sa6, mask6;
+			memset(&sa6, 0, sizeof(struct sockaddr_in6));
+			memset(&mask6, 0, sizeof(struct sockaddr_in6));
+			/* Set 'total' structure length */
+			KEY_LEN(sa6) =3D KEY_LEN_INET6;
+			KEY_LEN(mask6) =3D KEY_LEN_INET6;
+			ipv6_writemask(&mask6.sin6_addr, mlen);
+			memcpy(&sa6.sin6_addr, paddr, sizeof(struct in6_addr));
+			APPLY_MASK(&sa6.sin6_addr, &mask6.sin6_addr);
+			rnh_ptr =3D &ch->xtables[tbl];
+			sa_ptr =3D (struct sockaddr *)&sa6;
+			mask_ptr =3D (struct sockaddr *)&mask6;
+#endif
+		} else {
+			/* Unknown CIDR type */
+			return (EINVAL);
+		}
+		break;
+
+	case IPFW_TABLE_INTERFACE:
+		/* Check if string is terminated */
+		c =3D ((char *)paddr)[IF_NAMESIZE - 1];
+		((char *)paddr)[IF_NAMESIZE - 1] =3D '\0';
+		if (((mlen =3D strlen((char *)paddr)) =3D=3D IF_NAMESIZE - 1) && (c !=3D=
 '\0'))
+			return (EINVAL);
+
+		struct xaddr_iface ifname, ifmask;
+		memset(&ifname, 0, sizeof(ifname));
+
+		/* Set 'total' structure length */
+		KEY_LEN(ifname) =3D mlen;
+		KEY_LEN(ifmask) =3D mlen;
+		/* Assume direct match */
+		/* FIXME: Add interface pattern matching */
+#if 0
+		memset(ifmask.ifname, 0xFF, IF_NAMESIZE);
+		mask_ptr =3D (struct sockaddr *)&ifmask;
+#endif
+		mask_ptr =3D NULL;
+		memcpy(ifname.ifname, paddr, mlen);
+		/* Set pointers */
+		rnh_ptr =3D &ch->xtables[tbl];
+		sa_ptr =3D (struct sockaddr *)&ifname;
+
+		break;
+
+	default:
+		return (EINVAL);
+	}
+
 	IPFW_WLOCK(ch);
-	ent =3D (struct table_entry *)rnh->rnh_deladdr(&sa, &mask, rnh);
-	if (ent =3D=3D NULL) {
+	if ((rnh =3D *rnh_ptr) =3D=3D NULL) {
 		IPFW_WUNLOCK(ch);
 		return (ESRCH);
 	}
+
+	if (ch->tabletype[tbl] !=3D type) {
+		IPFW_WUNLOCK(ch);
+		return (EINVAL);
+	}
+
+	ent =3D (struct table_entry *)rnh->rnh_deladdr(sa_ptr, mask_ptr, rnh);
 	IPFW_WUNLOCK(ch);
+
+	if (ent =3D=3D NULL)
+		return (ESRCH);
+
 	free(ent, M_IPFW_TBL);
 	return (0);
 }
@@ -158,15 +388,38 @@ flush_table_entry(struct radix_node *rn, void *arg
 int
 ipfw_flush_table(struct ip_fw_chain *ch, uint16_t tbl)
 {
-	struct radix_node_head *rnh;
+	struct radix_node_head *rnh, *xrnh;
=20
-	IPFW_WLOCK_ASSERT(ch);
-
 	if (tbl >=3D IPFW_TABLES_MAX)
 		return (EINVAL);
-	rnh =3D ch->tables[tbl];
-	KASSERT(rnh !=3D NULL, ("NULL IPFW table"));
-	rnh->rnh_walktree(rnh, flush_table_entry, rnh);
+
+	/*
+	 * We free both (IPv4 and extended) radix trees and
+	 * clear table type here to permit table to be reused
+	 * for different type without module reload
+	 */
+
+	IPFW_WLOCK(ch);
+	/* Set IPv4 table pointer to zero */
+	if ((rnh =3D ch->tables[tbl]) !=3D NULL)
+		ch->tables[tbl] =3D NULL;
+	/* Set extended table pointer to zero */
+	if ((xrnh =3D ch->xtables[tbl]) !=3D NULL)
+		ch->xtables[tbl] =3D NULL;
+	/* Zero table type */
+	ch->tabletype[tbl] =3D 0;
+	IPFW_WUNLOCK(ch);
+
+	if (rnh !=3D NULL) {
+		rnh->rnh_walktree(rnh, flush_table_entry, rnh);
+		rn_detachhead((void **)&rnh);
+	}
+
+	if (xrnh !=3D NULL) {
+		xrnh->rnh_walktree(xrnh, flush_table_entry, xrnh);
+		rn_detachhead((void **)&xrnh);
+	}
+
 	return (0);
 }
=20
@@ -174,31 +427,15 @@ void
 ipfw_destroy_tables(struct ip_fw_chain *ch)
 {
 	uint16_t tbl;
-	struct radix_node_head *rnh;
=20
-	IPFW_WLOCK_ASSERT(ch);
-
-	for (tbl =3D 0; tbl < IPFW_TABLES_MAX; tbl++) {
+	for (tbl =3D 0; tbl < IPFW_TABLES_MAX; tbl++)
 		ipfw_flush_table(ch, tbl);
-		rnh =3D ch->tables[tbl];
-		rn_detachhead((void **)&rnh);
-	}
 }
=20
 int
 ipfw_init_tables(struct ip_fw_chain *ch)
-{=20
-	int i;
-	uint16_t j;
-
-	for (i =3D 0; i < IPFW_TABLES_MAX; i++) {
-		if (!rn_inithead((void **)&ch->tables[i], KEY_OFS)) {
-			for (j =3D 0; j < i; j++) {
-				(void) ipfw_flush_table(ch, j);
-			}
-			return (ENOMEM);
-		}
-	}
+{
+	/* Init tables on demand */
 	return (0);
 }
=20
@@ -212,8 +449,9 @@ ipfw_lookup_table(struct ip_fw_chain *ch, uint16_t
=20
 	if (tbl >=3D IPFW_TABLES_MAX)
 		return (0);
-	rnh =3D ch->tables[tbl];
-	KEY_LEN(sa) =3D 8;
+	if ((rnh =3D ch->tables[tbl]) =3D=3D NULL)
+		return (0);
+	KEY_LEN(sa) =3D KEY_LEN_INET;
 	sa.sin_addr.s_addr =3D addr;
 	ent =3D (struct table_entry *)(rnh->rnh_lookup(&sa, NULL, rnh));
 	if (ent !=3D NULL) {
@@ -223,6 +461,45 @@ ipfw_lookup_table(struct ip_fw_chain *ch, uint16_t
 	return (0);
 }
=20
+int
+ipfw_lookup_table_extended(struct ip_fw_chain *ch, uint16_t tbl, void *p=
addr,
+    uint32_t *val, int type)
+{
+	struct radix_node_head *rnh;
+	struct table_xentry *xent;
+	struct sockaddr_in6 sa6;
+	struct xaddr_iface iface;
+
+	if (tbl >=3D IPFW_TABLES_MAX)
+		return (0);
+	if ((rnh =3D ch->xtables[tbl]) =3D=3D NULL)
+		return (0);
+
+	switch (type) {
+	case IPFW_TABLE_CIDR:
+		KEY_LEN(sa6) =3D KEY_LEN_INET6;
+		memcpy(&sa6.sin6_addr, paddr, sizeof(struct in6_addr));
+		xent =3D (struct table_xentry *)(rnh->rnh_lookup(&sa6, NULL, rnh));
+		break;
+
+	case IPFW_TABLE_INTERFACE:
+		KEY_LEN(iface) =3D strlcpy(iface.ifname, (char *)paddr, IF_NAMESIZE);
+		/* Assume direct match */
+		/* FIXME: Add interface pattern matching */
+		xent =3D (struct table_xentry *)(rnh->rnh_lookup(&iface, NULL, rnh));
+		break;
+
+	default:
+		return (0);
+	}
+
+	if (xent !=3D NULL) {
+		*val =3D xent->value;
+		return (1);
+	}
+	return (0);
+}
+
 static int
 count_table_entry(struct radix_node *rn, void *arg)
 {
@@ -239,8 +516,9 @@ ipfw_count_table(struct ip_fw_chain *ch, uint32_t
=20
 	if (tbl >=3D IPFW_TABLES_MAX)
 		return (EINVAL);
-	rnh =3D ch->tables[tbl];
 	*cnt =3D 0;
+	if ((rnh =3D ch->tables[tbl]) =3D=3D NULL)
+		return (0);
 	rnh->rnh_walktree(rnh, count_table_entry, cnt);
 	return (0);
 }
@@ -273,9 +551,122 @@ ipfw_dump_table(struct ip_fw_chain *ch, ipfw_table
=20
 	if (tbl->tbl >=3D IPFW_TABLES_MAX)
 		return (EINVAL);
-	rnh =3D ch->tables[tbl->tbl];
 	tbl->cnt =3D 0;
+	if ((rnh =3D ch->tables[tbl->tbl]) =3D=3D NULL)
+		return (0);
 	rnh->rnh_walktree(rnh, dump_table_entry, tbl);
 	return (0);
 }
+
+static int
+count_table_xentry(struct radix_node *rn, void *arg)
+{
+	uint32_t * const cnt =3D arg;
+
+	(*cnt) +=3D sizeof(ipfw_table_xentry);
+	return (0);
+}
+
+int
+ipfw_count_xtable(struct ip_fw_chain *ch, uint32_t tbl, uint32_t *cnt)
+{
+	struct radix_node_head *rnh;
+
+	if (tbl >=3D IPFW_TABLES_MAX)
+		return (EINVAL);
+	*cnt =3D 0;
+	if ((rnh =3D ch->tables[tbl]) !=3D NULL)
+		rnh->rnh_walktree(rnh, count_table_xentry, cnt);
+	if ((rnh =3D ch->xtables[tbl]) !=3D NULL)
+		rnh->rnh_walktree(rnh, count_table_xentry, cnt);
+	/* Return zero if table is empty */
+	if (*cnt > 0)
+		(*cnt) +=3D sizeof(ipfw_xtable);
+	return (0);
+}
+
+
+static int
+dump_table_xentry_base(struct radix_node *rn, void *arg)
+{
+	struct table_entry * const n =3D (struct table_entry *)rn;
+	ipfw_xtable * const tbl =3D arg;
+	ipfw_table_xentry *xent;
+
+	/* Out of memory, returning */
+	if (tbl->cnt =3D=3D tbl->size)
+		return (1);
+	xent =3D &tbl->xent[tbl->cnt];
+	xent->len =3D sizeof(ipfw_table_xentry);
+	xent->tbl =3D tbl->tbl;
+	if (in_nullhost(n->mask.sin_addr))
+		xent->masklen =3D 0;
+	else
+		xent->masklen =3D 33 - ffs(ntohl(n->mask.sin_addr.s_addr));
+	/* Save IPv4 address as deprecated IPv6 compatible */
+	xent->k.addr6.s6_addr32[3] =3D n->addr.sin_addr.s_addr;
+	xent->value =3D n->value;
+	tbl->cnt++;
+	return (0);
+}
+
+static int
+dump_table_xentry_extended(struct radix_node *rn, void *arg)
+{
+	struct table_xentry * const n =3D (struct table_xentry *)rn;
+	ipfw_xtable * const tbl =3D arg;
+	ipfw_table_xentry *xent;
+#ifdef INET6
+	int i;
+	uint32_t *v;
+#endif
+	/* Out of memory, returning */
+	if (tbl->cnt =3D=3D tbl->size)
+		return (1);
+	xent =3D &tbl->xent[tbl->cnt];
+	xent->len =3D sizeof(ipfw_table_xentry);
+	xent->tbl =3D tbl->tbl;
+
+	switch (tbl->type) {
+#ifdef INET6
+	case IPFW_TABLE_CIDR:
+		/* Count IPv6 mask */
+		v =3D (uint32_t *)&n->m.mask6.sin6_addr;
+		for (i =3D 0; i < sizeof(struct in6_addr) / 4; i++, v++)
+			xent->masklen +=3D bitcount32(*v);
+		memcpy(&xent->k, &n->a.addr6.sin6_addr, sizeof(struct in6_addr));
+		break;
+#endif
+	case IPFW_TABLE_INTERFACE:
+		/* Assume exact mask */
+		xent->masklen =3D 8 * IF_NAMESIZE;
+		memcpy(&xent->k, &n->a.iface.ifname, IF_NAMESIZE);
+		break;
+=09
+	default:
+		/* unknown, skip entry */
+		return (0);
+	}
+
+	xent->value =3D n->value;
+	tbl->cnt++;
+	return (0);
+}
+
+int
+ipfw_dump_xtable(struct ip_fw_chain *ch, ipfw_xtable *tbl)
+{
+	struct radix_node_head *rnh;
+
+	if (tbl->tbl >=3D IPFW_TABLES_MAX)
+		return (EINVAL);
+	tbl->cnt =3D 0;
+	tbl->type =3D ch->tabletype[tbl->tbl];
+	if ((rnh =3D ch->tables[tbl->tbl]) !=3D NULL)
+		rnh->rnh_walktree(rnh, dump_table_xentry_base, tbl);
+	if ((rnh =3D ch->xtables[tbl->tbl]) !=3D NULL)
+		rnh->rnh_walktree(rnh, dump_table_xentry_extended, tbl);
+	return (0);
+}
+
 /* end of file */
Index: sys/netinet/ipfw/ip_fw2.c
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
--- sys/netinet/ipfw/ip_fw2.c	(revision 228874)
+++ sys/netinet/ipfw/ip_fw2.c	(working copy)
@@ -1449,6 +1449,17 @@ do {								\
 					    ((ipfw_insn_u32 *)cmd)->d[0] =3D=3D v;
 				    else
 					tablearg =3D v;
+				} else if (is_ipv6) {
+					uint32_t v =3D 0;
+					void *pkey =3D (cmd->opcode =3D=3D O_IP_DST_LOOKUP) ?
+						&args->f_id.dst_ip6: &args->f_id.src_ip6;
+					match =3D ipfw_lookup_table_extended(chain,
+							cmd->arg1, pkey, &v,
+							IPFW_TABLE_CIDR);
+					if (cmdlen =3D=3D F_INSN_SIZE(ipfw_insn_u32))
+						match =3D ((ipfw_insn_u32 *)cmd)->d[0] =3D=3D v;
+					if (match)
+						tablearg =3D v;
 				}
 				break;
=20
@@ -2630,12 +2641,12 @@ vnet_ipfw_uninit(const void *unused)
 	IPFW_UH_WLOCK(chain);
=20
 	IPFW_WLOCK(chain);
+	ipfw_dyn_uninit(0);	/* run the callout_drain */
 	IPFW_WUNLOCK(chain);
-	IPFW_WLOCK(chain);
=20
-	ipfw_dyn_uninit(0);	/* run the callout_drain */
 	ipfw_destroy_tables(chain);
 	reap =3D NULL;
+	IPFW_WLOCK(chain);
 	for (i =3D 0; i < chain->n_rules; i++) {
 		rule =3D chain->map[i];
 		rule->x_next =3D reap;

--------------030403040904080105030905--

--------------enigABFE081192EEA4308BF10E2C
Content-Type: application/pgp-signature; name="signature.asc"
Content-Description: OpenPGP digital signature
Content-Disposition: attachment; filename="signature.asc"

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v2.0.18 (FreeBSD)
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org/

iEYEARECAAYFAk73OlgACgkQwcJ4iSZ1q2nzvQCfTDW9r7rVhSOHKD3QAEL93Myb
x1EAn1z3xVlHujXf7Me5dvyiqOh4HQLB
=hhVM
-----END PGP SIGNATURE-----

--------------enigABFE081192EEA4308BF10E2C--



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?4EF73A4A.3050902>