Add IPv6 support for NPF. trunk
authorzoltan <zoltan@NetBSD.org>
Fri, 04 Nov 2011 01:00:27 +0000
branchtrunk
changeset 206570 9b38640bf4fe
parent 206569 a6883a0d1839
child 206571 6f62489fa0fb
Add IPv6 support for NPF.
lib/libnpf/npf.c
lib/libnpf/npf.h
sys/net/npf/npf.h
sys/net/npf/npf_alg_icmp.c
sys/net/npf/npf_ctl.c
sys/net/npf/npf_handler.c
sys/net/npf/npf_impl.h
sys/net/npf/npf_inet.c
sys/net/npf/npf_instr.c
sys/net/npf/npf_nat.c
sys/net/npf/npf_ncode.h
sys/net/npf/npf_processor.c
sys/net/npf/npf_sendpkt.c
sys/net/npf/npf_session.c
sys/net/npf/npf_state.c
sys/net/npf/npf_tableset.c
usr.sbin/npf/npfctl/npf_data.c
usr.sbin/npf/npfctl/npf_ncgen.c
usr.sbin/npf/npfctl/npf_parser.c
usr.sbin/npf/npfctl/npfctl.c
usr.sbin/npf/npfctl/npfctl.h
--- a/lib/libnpf/npf.c	Fri Nov 04 00:22:33 2011 +0000
+++ b/lib/libnpf/npf.c	Fri Nov 04 01:00:27 2011 +0000
@@ -1,4 +1,4 @@
-/*	$NetBSD: npf.c,v 1.2 2011/02/02 15:17:37 rmind Exp $	*/
+/*	$NetBSD: npf.c,v 1.3 2011/11/04 01:00:28 zoltan Exp $	*/
 
 /*-
  * Copyright (c) 2010-2011 The NetBSD Foundation, Inc.
@@ -30,7 +30,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: npf.c,v 1.2 2011/02/02 15:17:37 rmind Exp $");
+__KERNEL_RCSID(0, "$NetBSD: npf.c,v 1.3 2011/11/04 01:00:28 zoltan Exp $");
 
 #include <sys/types.h>
 #include <netinet/in_systm.h>
@@ -429,18 +429,21 @@
 }
 
 int
-npf_table_add_entry(nl_table_t *tl, in_addr_t addr, in_addr_t mask)
+npf_table_add_entry(nl_table_t *tl, npf_addr_t *addr, npf_netmask_t mask)
 {
 	prop_dictionary_t tldict = tl->ntl_dict, entdict;
 	prop_array_t tblents;
+	prop_data_t addrdata;
 
 	/* Create the table entry. */
 	entdict = prop_dictionary_create();
 	if (entdict) {
 		return ENOMEM;
 	}
-	prop_dictionary_set_uint32(entdict, "addr", addr);
-	prop_dictionary_set_uint32(entdict, "mask", mask);
+	addrdata = prop_data_create_data(addr, sizeof(npf_addr_t));
+	prop_dictionary_set(entdict, "addr", addrdata);
+	prop_dictionary_set_uint8(entdict, "mask", mask);
+	prop_object_release(addrdata);
 
 	/* Insert the entry. */
 	tblents = prop_dictionary_get(tldict, "entries");
--- a/lib/libnpf/npf.h	Fri Nov 04 00:22:33 2011 +0000
+++ b/lib/libnpf/npf.h	Fri Nov 04 01:00:27 2011 +0000
@@ -1,4 +1,4 @@
-/*	$NetBSD: npf.h,v 1.1 2011/02/02 02:20:25 rmind Exp $	*/
+/*	$NetBSD: npf.h,v 1.2 2011/11/04 01:00:28 zoltan Exp $	*/
 
 /*-
  * Copyright (c) 2011 The NetBSD Foundation, Inc.
@@ -84,7 +84,7 @@
 int		npf_nat_insert(nl_config_t *, nl_nat_t *, pri_t);
 
 nl_table_t *	npf_table_create(int, int);
-int		npf_table_add_entry(nl_table_t *, in_addr_t, in_addr_t);
+int		npf_table_add_entry(nl_table_t *, npf_addr_t *, npf_netmask_t);
 bool		npf_table_exists_p(nl_config_t *, u_int);
 int		npf_table_insert(nl_config_t *, nl_table_t *);
 void		npf_table_destroy(nl_table_t *);
--- a/sys/net/npf/npf.h	Fri Nov 04 00:22:33 2011 +0000
+++ b/sys/net/npf/npf.h	Fri Nov 04 01:00:27 2011 +0000
@@ -1,4 +1,4 @@
-/*	$NetBSD: npf.h,v 1.8 2011/02/02 23:01:34 rmind Exp $	*/
+/*	$NetBSD: npf.h,v 1.9 2011/11/04 01:00:27 zoltan Exp $	*/
 
 /*-
  * Copyright (c) 2009-2011 The NetBSD Foundation, Inc.
@@ -55,8 +55,11 @@
  * Public declarations and definitions.
  */
 
-/* Storage of address (both for IPv4 and IPv6). */
+/* Storage of address (both for IPv4 and IPv6) and netmask */
 typedef struct in6_addr		npf_addr_t;
+typedef uint_fast8_t		npf_netmask_t;
+
+#define	NPF_NO_NETMASK		(npf_netmask_t)~0
 
 #if defined(_KERNEL) || defined(_NPF_TESTING)
 
@@ -80,7 +83,7 @@
 
 #define	NPC_IP4		0x01	/* Indicates fetched IPv4 header. */
 #define	NPC_IP6		0x02	/* Indicates IPv6 header. */
-#define	NPC_IPFRAG	0x04	/* IPv4 fragment. */
+#define	NPC_IPFRAG	0x04	/* IPv4/IPv6 fragment. */
 #define	NPC_LAYER4	0x08	/* Layer 4 has been fetched. */
 
 #define	NPC_TCP		0x10	/* TCP header. */
@@ -98,6 +101,8 @@
 	npf_addr_t *		npc_dstip;
 	/* Size (v4 or v6) of IP addresses. */
 	int			npc_ipsz;
+	size_t			npc_hlen;
+	int			npc_next_proto;
 	/* IPv4, IPv6. */
 	union {
 		struct ip	v4;
@@ -111,6 +116,71 @@
 	} npc_l4;
 } npf_cache_t;
 
+/* Max length is 32 for IPv4 and 128 for IPv6 */
+static inline void
+npf_generate_mask(npf_addr_t *dst, const npf_netmask_t omask)
+{
+	uint_fast8_t length = omask;
+
+	KASSERT(length <= 128);
+	memset(dst, 0, sizeof(npf_addr_t));
+	for (int i = 0; i < 4; i++) {
+		if (length >= 32) {
+			dst->s6_addr32[i] = htonl(0xffffffff);
+			length -= 32;
+		} else {
+			dst->s6_addr32[i] = htonl(0xffffffff << (32 - length));
+			length = 0;
+		}  
+	}
+}
+
+static inline void
+npf_calculate_masked_addr(npf_addr_t *dst, const npf_addr_t *src, const npf_netmask_t omask)
+{
+	npf_addr_t mask;
+
+	npf_generate_mask(&mask, omask);
+	for (int i = 0; i < 4; i++) {
+		dst->s6_addr32[i] =
+			src->s6_addr32[i] & mask.s6_addr32[i];
+	}
+}
+
+/*
+ * compare two addresses, either v4 or v6
+ * if the mask is NULL, ignore it
+ */
+static inline int
+npf_compare_cidr(const npf_addr_t *addr1, const npf_netmask_t mask1,
+		 const npf_addr_t *addr2, const npf_netmask_t mask2)
+{
+	npf_addr_t realmask1, realmask2;
+
+	if (mask1 != NPF_NO_NETMASK) {
+		npf_generate_mask(&realmask1, mask1);
+	}
+	if (mask2 != NPF_NO_NETMASK) {
+		npf_generate_mask(&realmask2, mask2);
+	}
+	for (int i = 0; i < 4; i++) {
+		const uint32_t x = mask1 != NPF_NO_NETMASK ?
+				addr1->s6_addr32[i] & realmask1.s6_addr32[i] : 
+				addr1->s6_addr32[i];
+		const uint32_t y = mask2 != NPF_NO_NETMASK ?
+				addr2->s6_addr32[i] & realmask2.s6_addr32[i] :
+				addr2->s6_addr32[i];
+		if (x < y) {
+			return -1;
+		}
+		if (x > y) {
+			return 1;
+		}
+	}
+
+	return 0;
+}
+
 static inline bool
 npf_iscached(const npf_cache_t *npc, const int inf)
 {
@@ -121,10 +191,15 @@
 static inline int
 npf_cache_ipproto(const npf_cache_t *npc)
 {
-	const struct ip *ip = &npc->npc_ip.v4;
+	KASSERT(npf_iscached(npc, NPC_IP46));
+	return npc->npc_next_proto;
+}
 
+static inline int
+npf_cache_hlen(const npf_cache_t *npc, nbuf_t *nbuf)
+{
 	KASSERT(npf_iscached(npc, NPC_IP46));
-	return ip->ip_p;
+	return npc->npc_hlen;
 }
 
 /* Network buffer interface. */
@@ -190,8 +265,8 @@
 typedef struct npf_ioctl_table {
 	int			nct_action;
 	u_int			nct_tid;
-	in_addr_t		nct_addr;
-	in_addr_t		nct_mask;
+	npf_addr_t		nct_addr;
+	npf_netmask_t		nct_mask;
 	int			_reserved;
 } npf_ioctl_table_t;
 
--- a/sys/net/npf/npf_alg_icmp.c	Fri Nov 04 00:22:33 2011 +0000
+++ b/sys/net/npf/npf_alg_icmp.c	Fri Nov 04 01:00:27 2011 +0000
@@ -1,4 +1,4 @@
-/*	$NetBSD: npf_alg_icmp.c,v 1.6 2011/01/18 20:33:45 rmind Exp $	*/
+/*	$NetBSD: npf_alg_icmp.c,v 1.7 2011/11/04 01:00:27 zoltan Exp $	*/
 
 /*-
  * Copyright (c) 2010 The NetBSD Foundation, Inc.
@@ -34,7 +34,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: npf_alg_icmp.c,v 1.6 2011/01/18 20:33:45 rmind Exp $");
+__KERNEL_RCSID(0, "$NetBSD: npf_alg_icmp.c,v 1.7 2011/11/04 01:00:27 zoltan Exp $");
 
 #include <sys/param.h>
 #include <sys/kernel.h>
@@ -119,7 +119,8 @@
 	struct ip *ip = &npc->npc_ip.v4;
 	in_port_t dport;
 
-	KASSERT(npf_iscached(npc, NPC_IP46 | NPC_LAYER4));
+	KASSERT(npf_iscached(npc, NPC_IP46));
+	KASSERT(npf_iscached(npc, NPC_LAYER4));
 
 	/* Check for low TTL. */
 	if (ip->ip_ttl > TR_MAX_TTL) {
@@ -247,10 +248,10 @@
 	KASSERT(npf_iscached(npc, NPC_ICMP));
 
 	/* Advance to ICMP header. */
-	struct ip *ip = &npc->npc_ip.v4;
 	void *n_ptr = nbuf_dataptr(nbuf);
+	const size_t hlen = npf_cache_hlen(npc, nbuf);
 
-	if ((n_ptr = nbuf_advance(&nbuf, n_ptr, ip->ip_hl << 2)) == NULL) {
+	if ((n_ptr = nbuf_advance(&nbuf, n_ptr, hlen)) == NULL) {
 		return false;
 	}
 
@@ -297,7 +298,8 @@
 		return false;
 	}
 	/* XXX: Restore inversion (inefficient). */
-	KASSERT(npf_iscached(&enpc, NPC_IP46 | NPC_LAYER4));
+	KASSERT(npf_iscached(&enpc, NPC_IP46));
+	KASSERT(npf_iscached(&enpc, NPC_LAYER4));
 	npfa_srcdst_invert(&enpc);
 
 	/*
@@ -306,7 +308,7 @@
 	 * embedded packet changes, while data is not rewritten in the cache.
 	 */
 	const int proto = npf_cache_ipproto(&enpc);
-	const struct ip * const ip = &npc->npc_ip.v4, *eip = &enpc.npc_ip.v4;
+	const struct ip *eip = &enpc.npc_ip.v4;
 	const struct icmp * const ic = &npc->npc_l4.icmp;
 	uint16_t cksum = ic->icmp_cksum, ecksum = eip->ip_sum, l4cksum;
 	npf_nat_t *nt = ntptr;
@@ -331,7 +333,7 @@
 	 * to the embedded IP header after ICMP header.
 	 */
 	void *n_ptr = nbuf_dataptr(nbuf), *cnbuf = nbuf, *cnptr = n_ptr;
-	u_int offby = (ip->ip_hl << 2) + offsetof(struct icmp, icmp_ip);
+	u_int offby = npf_cache_hlen(npc, nbuf) + offsetof(struct icmp, icmp_ip);
 
 	if ((n_ptr = nbuf_advance(&nbuf, n_ptr, offby)) == NULL) {
 		return false;
@@ -365,7 +367,7 @@
 	}
 	cksum = npf_fixup16_cksum(cksum, ecksum, eip->ip_sum);
 
-	offby = (ip->ip_hl << 2) + offsetof(struct icmp, icmp_cksum);
+	offby = npf_cache_hlen(npc, nbuf) + offsetof(struct icmp, icmp_cksum);
 	if (nbuf_advstore(&cnbuf, &cnptr, offby, sizeof(uint16_t), &cksum)) {
 		return false;
 	}
--- a/sys/net/npf/npf_ctl.c	Fri Nov 04 00:22:33 2011 +0000
+++ b/sys/net/npf/npf_ctl.c	Fri Nov 04 01:00:27 2011 +0000
@@ -1,4 +1,4 @@
-/*	$NetBSD: npf_ctl.c,v 1.6 2011/02/02 02:20:25 rmind Exp $	*/
+/*	$NetBSD: npf_ctl.c,v 1.7 2011/11/04 01:00:27 zoltan Exp $	*/
 
 /*-
  * Copyright (c) 2009-2011 The NetBSD Foundation, Inc.
@@ -37,7 +37,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: npf_ctl.c,v 1.6 2011/02/02 02:20:25 rmind Exp $");
+__KERNEL_RCSID(0, "$NetBSD: npf_ctl.c,v 1.7 2011/11/04 01:00:27 zoltan Exp $");
 
 #include <sys/param.h>
 #include <sys/conf.h>
@@ -120,12 +120,13 @@
 		}
 		eit = prop_array_iterator(entries);
 		while ((ent = prop_object_iterator_next(eit)) != NULL) {
-			in_addr_t addr, mask;	/* XXX: IPv6 */
+			const npf_addr_t *addr;
+			npf_netmask_t mask;
 
 			/* Get address and mask.  Add a table entry. */
-			prop_dictionary_get_uint32(ent, "addr", &addr);
-			prop_dictionary_get_uint32(ent, "mask", &mask);
-			error = npf_table_add_v4cidr(tblset, tid, addr, mask);
+			addr = (const npf_addr_t *)prop_data_data_nocopy(prop_dictionary_get(ent, "addr"));
+			prop_dictionary_get_uint8(ent, "mask", &mask);
+			error = npf_table_add_cidr(tblset, tid, addr, mask);
 			if (error)
 				break;
 		}
@@ -600,16 +601,16 @@
 	npf_core_enter(); /* XXXSMP */
 	switch (nct->nct_action) {
 	case NPF_IOCTL_TBLENT_ADD:
-		error = npf_table_add_v4cidr(NULL, nct->nct_tid,
-		    nct->nct_addr, nct->nct_mask);
+		error = npf_table_add_cidr(NULL, nct->nct_tid,
+		    &nct->nct_addr, nct->nct_mask);
 		break;
 	case NPF_IOCTL_TBLENT_REM:
-		error = npf_table_rem_v4cidr(NULL, nct->nct_tid,
-		    nct->nct_addr, nct->nct_mask);
+		error = npf_table_rem_cidr(NULL, nct->nct_tid,
+		    &nct->nct_addr, nct->nct_mask);
 		break;
 	default:
 		/* XXX */
-		error = npf_table_match_v4addr(nct->nct_tid, nct->nct_addr);
+		error = npf_table_match_addr(nct->nct_tid, &nct->nct_addr);
 		if (error) {
 			error = EINVAL;
 		}
--- a/sys/net/npf/npf_handler.c	Fri Nov 04 00:22:33 2011 +0000
+++ b/sys/net/npf/npf_handler.c	Fri Nov 04 01:00:27 2011 +0000
@@ -1,4 +1,4 @@
-/*	$NetBSD: npf_handler.c,v 1.7 2011/02/02 02:20:25 rmind Exp $	*/
+/*	$NetBSD: npf_handler.c,v 1.8 2011/11/04 01:00:27 zoltan Exp $	*/
 
 /*-
  * Copyright (c) 2009-2010 The NetBSD Foundation, Inc.
@@ -34,7 +34,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: npf_handler.c,v 1.7 2011/02/02 02:20:25 rmind Exp $");
+__KERNEL_RCSID(0, "$NetBSD: npf_handler.c,v 1.8 2011/11/04 01:00:27 zoltan Exp $");
 
 #include <sys/param.h>
 #include <sys/systm.h>
@@ -48,6 +48,8 @@
 #include <netinet/in_systm.h>
 #include <netinet/in.h>
 #include <netinet/ip_var.h>
+#include <netinet/ip6.h>
+#include <netinet6/ip6_var.h>
 
 #include "npf_impl.h"
 
@@ -57,6 +59,7 @@
  */
 static struct pfil_head *	npf_ph_if = NULL;
 static struct pfil_head *	npf_ph_inet = NULL;
+static struct pfil_head *	npf_ph_inet6 = NULL;
 
 static bool			default_pass = true;
 
@@ -86,7 +89,7 @@
 	npf_ruleset_t *rlset;
 	npf_rule_t *rl;
 	npf_rproc_t *rp;
-	int retfl, error;
+	int retfl, error, ret;
 
 	/*
 	 * Initialise packet information cache.
@@ -96,15 +99,28 @@
 	error = 0;
 	retfl = 0;
 	rp = NULL;
+	ret = 0;
 
 	/* Cache everything.  Determine whether it is an IPv4 fragment. */
-	if (npf_cache_all(&npc, nbuf) && npf_iscached(&npc, NPC_IPFRAG)) {
-		struct ip *ip = nbuf_dataptr(*mp);
-		/*
-		 * Pass to IPv4 reassembly mechanism.
-		 */
-		if (ip_reass_packet(mp, ip) != 0) {
-			/* Failed; invalid fragment(s) or packet. */
+	/* Cache IP information */
+	npf_cache_all(&npc, nbuf);
+
+	if (npf_iscached(&npc, NPC_IPFRAG)) {
+		if (npf_iscached(&npc, NPC_IP4)) {
+			struct ip *ip = nbuf_dataptr(*mp);
+		 	/* Pass to IPv4 reassembly mechanism. */
+			ret = ip_reass_packet(mp, ip);
+		} else if (npf_iscached(&npc, NPC_IP6)) {
+			/* frag6_input's offset is the start of the fragment header */
+			size_t hlen = npf_cache_hlen(&npc, nbuf);
+
+			/* Pass to IPv6 reassembly mechanism. */
+			ret = ip6_reass_packet(mp, hlen);
+		} else {
+			KASSERT(false);
+		}
+
+		if (ret != 0) {
 			error = EINVAL;
 			se = NULL;
 			goto out;
@@ -115,6 +131,13 @@
 		}
 		/* Reassembly is complete, we have the final packet. */
 		nbuf = (nbuf_t *)*mp;
+
+		/*
+		 * Before reassembly, we can't cache anything above layer3,
+		 * but at this point, it's reassembled - let's cache it again
+		 */
+		npc.npc_info = 0;
+		npf_cache_all(&npc, nbuf);
 	}
 
 	/* Inspect the list of sessions. */
@@ -230,6 +253,7 @@
 	/* Capture point of any activity in interfaces and IP layer. */
 	npf_ph_if = pfil_head_get(PFIL_TYPE_IFNET, 0);
 	npf_ph_inet = pfil_head_get(PFIL_TYPE_AF, AF_INET);
+	npf_ph_inet6 = pfil_head_get(PFIL_TYPE_AF, AF_INET6);
 	if (npf_ph_if == NULL || npf_ph_inet == NULL) {
 		npf_ph_if = NULL;
 		error = ENOENT;
@@ -246,6 +270,9 @@
 	    PFIL_WAITOK | PFIL_ALL, npf_ph_inet);
 	KASSERT(error == 0);
 
+	error = pfil_add_hook(npf_packet_handler, NULL,
+	    PFIL_WAITOK | PFIL_ALL, npf_ph_inet6);
+	KASSERT(error == 0);
 fail:
 	KERNEL_UNLOCK_ONE(NULL);
 	mutex_exit(softnet_lock);
@@ -265,6 +292,8 @@
 
 	if (npf_ph_if) {
 		(void)pfil_remove_hook(npf_packet_handler, NULL,
+		    PFIL_ALL, npf_ph_inet6);
+		(void)pfil_remove_hook(npf_packet_handler, NULL,
 		    PFIL_ALL, npf_ph_inet);
 		(void)pfil_remove_hook(npf_ifhook, NULL,
 		    PFIL_IFADDR | PFIL_IFNET, npf_ph_if);
--- a/sys/net/npf/npf_impl.h	Fri Nov 04 00:22:33 2011 +0000
+++ b/sys/net/npf/npf_impl.h	Fri Nov 04 01:00:27 2011 +0000
@@ -1,4 +1,4 @@
-/*	$NetBSD: npf_impl.h,v 1.7 2011/02/02 02:20:25 rmind Exp $	*/
+/*	$NetBSD: npf_impl.h,v 1.8 2011/11/04 01:00:27 zoltan Exp $	*/
 
 /*-
  * Copyright (c) 2009-2011 The NetBSD Foundation, Inc.
@@ -161,7 +161,7 @@
 uint16_t	npf_fixup32_cksum(uint16_t, uint32_t, uint32_t);
 uint16_t	npf_addr_cksum(uint16_t, int, npf_addr_t *, npf_addr_t *);
 uint32_t	npf_addr_sum(const int, const npf_addr_t *, const npf_addr_t *);
-int		npf_tcpsaw(npf_cache_t *, tcp_seq *, tcp_seq *, uint32_t *);
+int		npf_tcpsaw(npf_cache_t *, nbuf_t *, tcp_seq *, tcp_seq *, uint32_t *);
 bool		npf_fetch_tcpopts(const npf_cache_t *, nbuf_t *,
 		    uint16_t *, int *);
 bool		npf_normalize(npf_cache_t *, nbuf_t *, bool, bool, u_int, u_int);
@@ -169,10 +169,10 @@
 
 /* Complex instructions. */
 int		npf_match_ether(nbuf_t *, int, int, uint16_t, uint32_t *);
-int		npf_match_ip4table(npf_cache_t *, nbuf_t *, void *,
+int		npf_match_table(npf_cache_t *, nbuf_t *, void *,
 		    const int, const u_int);
-int		npf_match_ip4mask(npf_cache_t *, nbuf_t *, void *,
-		    const int, in_addr_t, in_addr_t);
+int		npf_match_ipmask(npf_cache_t *, nbuf_t *, void *,
+		    const int, const npf_addr_t *, const npf_netmask_t);
 int		npf_match_tcp_ports(npf_cache_t *, nbuf_t *, void *,
 		    const int, const uint32_t);
 int		npf_match_udp_ports(npf_cache_t *, nbuf_t *, void *,
@@ -197,11 +197,11 @@
 npf_table_t *	npf_table_get(npf_tableset_t *, u_int);
 void		npf_table_put(npf_table_t *);
 int		npf_table_check(npf_tableset_t *, u_int, int);
-int		npf_table_add_v4cidr(npf_tableset_t *, u_int,
-		    in_addr_t, in_addr_t);
-int		npf_table_rem_v4cidr(npf_tableset_t *, u_int,
-		    in_addr_t, in_addr_t);
-int		npf_table_match_v4addr(u_int, in_addr_t);
+int		npf_table_add_cidr(npf_tableset_t *, u_int,
+		    const npf_addr_t *, const npf_netmask_t);
+int		npf_table_rem_cidr(npf_tableset_t *, u_int,
+		    const npf_addr_t *, const npf_netmask_t);
+int		npf_table_match_addr(u_int, const npf_addr_t *);
 
 /* Ruleset interface. */
 npf_ruleset_t *	npf_ruleset_create(void);
--- a/sys/net/npf/npf_inet.c	Fri Nov 04 00:22:33 2011 +0000
+++ b/sys/net/npf/npf_inet.c	Fri Nov 04 01:00:27 2011 +0000
@@ -1,4 +1,4 @@
-/*	$NetBSD: npf_inet.c,v 1.6 2011/01/18 20:33:45 rmind Exp $	*/
+/*	$NetBSD: npf_inet.c,v 1.7 2011/11/04 01:00:27 zoltan Exp $	*/
 
 /*-
  * Copyright (c) 2009-2011 The NetBSD Foundation, Inc.
@@ -34,7 +34,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: npf_inet.c,v 1.6 2011/01/18 20:33:45 rmind Exp $");
+__KERNEL_RCSID(0, "$NetBSD: npf_inet.c,v 1.7 2011/11/04 01:00:27 zoltan Exp $");
 
 #include <sys/param.h>
 #include <sys/kernel.h>
@@ -126,18 +126,29 @@
  * Returns all values in host byte-order.
  */
 int
-npf_tcpsaw(npf_cache_t *npc, tcp_seq *seq, tcp_seq *ack, uint32_t *win)
+npf_tcpsaw(npf_cache_t *npc, nbuf_t *nbuf, tcp_seq *seq, tcp_seq *ack, uint32_t *win)
 {
-	struct ip *ip = &npc->npc_ip.v4;
 	struct tcphdr *th = &npc->npc_l4.tcp;
 
-	KASSERT(npf_iscached(npc, NPC_IP46 | NPC_TCP));
+	KASSERT(npf_iscached(npc, NPC_TCP));
 
 	*seq = ntohl(th->th_seq);
 	*ack = ntohl(th->th_ack);
 	*win = (uint32_t)ntohs(th->th_win);
 
-	return ntohs(ip->ip_len) - (ip->ip_hl << 2) - (th->th_off << 2);
+	/*
+	 * total length of packet - header length - tcp header length
+	 */
+	if (npf_iscached(npc, NPC_IP4)) {
+		struct ip *ip = &npc->npc_ip.v4;
+		return ntohs(ip->ip_len) - npf_cache_hlen(npc, nbuf) - (th->th_off << 2);
+	} else {
+		KASSERT(npf_iscached(npc, NPC_IP6));
+		struct ip6_hdr *ip6 = &npc->npc_ip.v6;
+		return ntohs(ip6->ip6_plen) - (th->th_off << 2);
+	}
+
+	return 0;
 }
 
 /*
@@ -148,14 +159,13 @@
     uint16_t *mss, int *wscale)
 {
 	void *n_ptr = nbuf_dataptr(nbuf);
-	const struct ip *ip = &npc->npc_ip.v4;
 	const struct tcphdr *th = &npc->npc_l4.tcp;
 	int topts_len, step;
 	uint16_t val16;
 	uint8_t val;
 
-	KASSERT(npf_iscached(npc, NPC_IP46 | NPC_TCP));
-
+	KASSERT(npf_iscached(npc, NPC_IP46));
+	KASSERT(npf_iscached(npc, NPC_TCP));
 	/* Determine if there are any TCP options, get their length. */
 	topts_len = (th->th_off << 2) - sizeof(struct tcphdr);
 	if (topts_len <= 0) {
@@ -165,7 +175,7 @@
 	KASSERT(topts_len <= MAX_TCPOPTLEN);
 
 	/* First step: IP and TCP header up to options. */
-	step = (ip->ip_hl << 2) + sizeof(struct tcphdr);
+	step = npf_cache_hlen(npc, nbuf) + sizeof(struct tcphdr);
 next:
 	if (nbuf_advfetch(&nbuf, &n_ptr, step, sizeof(val), &val)) {
 		return false;
@@ -229,6 +239,7 @@
 npf_fetch_ip(npf_cache_t *npc, nbuf_t *nbuf, void *n_ptr)
 {
 	struct ip *ip;
+	struct ip6_hdr *ip6;
 	uint8_t ver;
 
 	if (nbuf_fetch_datum(nbuf, n_ptr, sizeof(uint8_t), &ver)) {
@@ -255,10 +266,59 @@
 		npc->npc_srcip = (npf_addr_t *)&ip->ip_src;
 		npc->npc_dstip = (npf_addr_t *)&ip->ip_dst;
 		npc->npc_info |= NPC_IP4;
+		npc->npc_hlen = ip->ip_hl << 2;
+		npc->npc_next_proto = npc->npc_ip.v4.ip_p;
 		break;
 
 	case (IPV6_VERSION >> 4):
-		/* TODO */
+		ip6 = &npc->npc_ip.v6;
+		if (nbuf_fetch_datum(nbuf, n_ptr, sizeof(struct ip6_hdr), ip6)) {
+			return false;
+		}
+
+		struct ip6_ext ip6e;
+		size_t toskip = sizeof(struct ip6_hdr);
+		bool processing_ends = false;
+		npc->npc_next_proto = ip6->ip6_nxt;
+		npc->npc_hlen = 0; 
+
+		do {
+			/* advance the length of the previous known header,
+			   and fetch the next extension header's length */
+			if (nbuf_advfetch(&nbuf, &n_ptr, toskip, sizeof(struct ip6_ext), &ip6e)) {
+				return false;
+			}
+
+			switch (npc->npc_next_proto) {
+			case IPPROTO_DSTOPTS:
+			case IPPROTO_ROUTING:
+				toskip = (ip6e.ip6e_len + 1) << 3;
+				break;
+			case IPPROTO_FRAGMENT:
+				npc->npc_info |= NPC_IPFRAG;
+				toskip = sizeof(struct ip6_frag);
+				break;
+			case IPPROTO_AH:
+				toskip = (ip6e.ip6e_len + 2) << 2;
+				break;
+			default:
+				processing_ends = true;
+				break;
+			}
+
+			npc->npc_hlen += toskip;
+			
+			if (!processing_ends) {
+				npc->npc_next_proto = ip6e.ip6e_nxt;
+			}
+		} while (!processing_ends);
+
+		npc->npc_ipsz = sizeof(struct in6_addr);
+		npc->npc_srcip = (npf_addr_t *)&ip6->ip6_src;
+		npc->npc_dstip = (npf_addr_t *)&ip6->ip6_dst;
+		npc->npc_info |= NPC_IP6;
+		break;
+
 	default:
 		return false;
 	}
@@ -268,22 +328,19 @@
 bool
 npf_fetch_tcp(npf_cache_t *npc, nbuf_t *nbuf, void *n_ptr)
 {
-	struct ip *ip = &npc->npc_ip.v4;
 	struct tcphdr *th;
-	u_int hlen;
 
 	/* Must have IP header processed for its length and protocol. */
 	if (!npf_iscached(npc, NPC_IP46) && !npf_fetch_ip(npc, nbuf, n_ptr)) {
 		return false;
 	}
-	if (ip->ip_p != IPPROTO_TCP) {
+	if (npf_cache_ipproto(npc) != IPPROTO_TCP) {
 		return false;
 	}
-	hlen = ip->ip_hl << 2;
 	th = &npc->npc_l4.tcp;
 
 	/* Fetch TCP header. */
-	if (nbuf_advfetch(&nbuf, &n_ptr, hlen, sizeof(struct tcphdr), th)) {
+	if (nbuf_advfetch(&nbuf, &n_ptr, npf_cache_hlen(npc, nbuf), sizeof(struct tcphdr), th)) {
 		return false;
 	}
 
@@ -297,7 +354,7 @@
 {
 	struct ip *ip = &npc->npc_ip.v4;
 	struct udphdr *uh;
-	u_int hlen;
+	size_t hlen;
 
 	/* Must have IP header processed for its length and protocol. */
 	if (!npf_iscached(npc, NPC_IP46) && !npf_fetch_ip(npc, nbuf, n_ptr)) {
@@ -306,8 +363,8 @@
 	if (ip->ip_p != IPPROTO_UDP) {
 		return false;
 	}
-	hlen = ip->ip_hl << 2;
 	uh = &npc->npc_l4.udp;
+	hlen = npf_cache_hlen(npc, nbuf);
 
 	/* Fetch ICMP header. */
 	if (nbuf_advfetch(&nbuf, &n_ptr, hlen, sizeof(struct udphdr), uh)) {
@@ -329,7 +386,8 @@
 {
 	struct ip *ip = &npc->npc_ip.v4;
 	struct icmp *ic;
-	u_int hlen, iclen;
+	u_int iclen;
+	size_t hlen;
 
 	/* Must have IP header processed for its length and protocol. */
 	if (!npf_iscached(npc, NPC_IP46) && !npf_fetch_ip(npc, nbuf, n_ptr)) {
@@ -338,8 +396,8 @@
 	if (ip->ip_p != IPPROTO_ICMP) {
 		return false;
 	}
-	hlen = ip->ip_hl << 2;
 	ic = &npc->npc_l4.icmp;
+	hlen = npf_cache_hlen(npc, nbuf);
 
 	/* Fetch basic ICMP header, up to the "data" point. */
 	iclen = offsetof(struct icmp, icmp_data);
@@ -417,8 +475,7 @@
     in_port_t port)
 {
 	const int proto = npf_cache_ipproto(npc);
-	struct ip *ip = &npc->npc_ip.v4;
-	u_int offby = ip->ip_hl << 2;
+	u_int offby = npf_cache_hlen(npc, nbuf);
 	in_port_t *oport;
 
 	KASSERT(npf_iscached(npc, NPC_TCP) || npf_iscached(npc, NPC_UDP));
@@ -481,7 +538,7 @@
 			return false;
 
 		ip->ip_sum = ipsum;
-		offby = (ip->ip_hl << 2) - offby;
+		offby = npf_cache_hlen(npc, nbuf) - offby;
 	} else {
 		/* No checksum for IPv6. */
 		KASSERT(npf_iscached(npc, NPC_IP6));
@@ -494,7 +551,7 @@
 	if (proto == IPPROTO_ICMP || port == 0) {
 		return true;
 	}
-	KASSERT(npf_iscached(npc, NPC_TCP | NPC_UDP));
+	KASSERT(npf_iscached(npc, NPC_TCP) || npf_iscached(npc, NPC_UDP));
 
 	/* Calculate TCP/UDP checksum. */
 	if (proto == IPPROTO_TCP) {
@@ -591,7 +648,6 @@
     bool no_df, bool rnd, u_int minttl, u_int maxmss)
 {
 	void *n_ptr = nbuf_dataptr(nbuf);
-	struct ip *ip = &npc->npc_ip.v4;
 	struct tcphdr *th = &npc->npc_l4.tcp;
 	uint16_t cksum, mss;
 	int offby, wscale;
@@ -631,7 +687,7 @@
 	if (!npf_fetch_tcpopts(npc, nbuf, &mss, &wscale)) {
 		return false;
 	}
-	offby = (ip->ip_hl << 2) + offsetof(struct tcphdr, th_sum);
+	offby = npf_cache_hlen(npc, nbuf) + offsetof(struct tcphdr, th_sum);
 	if (nbuf_advstore(&nbuf, &n_ptr, offby, sizeof(cksum), &cksum)) {
 		return false;
 	}
--- a/sys/net/npf/npf_instr.c	Fri Nov 04 00:22:33 2011 +0000
+++ b/sys/net/npf/npf_instr.c	Fri Nov 04 01:00:27 2011 +0000
@@ -1,4 +1,4 @@
-/*	$NetBSD: npf_instr.c,v 1.5 2011/01/18 20:33:45 rmind Exp $	*/
+/*	$NetBSD: npf_instr.c,v 1.6 2011/11/04 01:00:27 zoltan Exp $	*/
 
 /*-
  * Copyright (c) 2009-2010 The NetBSD Foundation, Inc.
@@ -34,7 +34,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: npf_instr.c,v 1.5 2011/01/18 20:33:45 rmind Exp $");
+__KERNEL_RCSID(0, "$NetBSD: npf_instr.c,v 1.6 2011/11/04 01:00:27 zoltan Exp $");
 
 #include <sys/param.h>
 #include <sys/kernel.h>
@@ -93,11 +93,10 @@
  * npf_match_ip4table: match IPv4 address against NPF table.
  */
 int
-npf_match_ip4table(npf_cache_t *npc, nbuf_t *nbuf, void *n_ptr,
+npf_match_table(npf_cache_t *npc, nbuf_t *nbuf, void *n_ptr,
     const int sd, const u_int tid)
 {
-	struct ip *ip = &npc->npc_ip.v4;
-	in_addr_t ip4addr;
+	npf_addr_t *addr;
 
 	if (!npf_iscached(npc, NPC_IP46)) {
 		if (!npf_fetch_ip(npc, nbuf, n_ptr)) {
@@ -105,21 +104,20 @@
 		}
 		KASSERT(npf_iscached(npc, NPC_IP46));
 	}
-	ip4addr = sd ? ip->ip_src.s_addr : ip->ip_dst.s_addr;
+	addr = sd ? npc->npc_srcip : npc->npc_dstip;
 
 	/* Match address against NPF table. */
-	return npf_table_match_v4addr(tid, ip4addr);
+	return npf_table_match_addr(tid, addr);
 }
 
 /*
- * npf_match_ip4mask: match IPv4 address against netaddr/subnet.
+ * npf_match_ipmask: match an address against netaddr/mask.
  */
 int
-npf_match_ip4mask(npf_cache_t *npc, nbuf_t *nbuf, void *n_ptr,
-    const int sd, in_addr_t netaddr, in_addr_t subnet)
+npf_match_ipmask(npf_cache_t *npc, nbuf_t *nbuf, void *n_ptr,
+    const int sd, const npf_addr_t *netaddr, npf_netmask_t omask)
 {
-	struct ip *ip = &npc->npc_ip.v4;
-	in_addr_t ip4addr;
+	npf_addr_t *addr1, addr2;
 
 	if (!npf_iscached(npc, NPC_IP46)) {
 		if (!npf_fetch_ip(npc, nbuf, n_ptr)) {
@@ -127,9 +125,15 @@
 		}
 		KASSERT(npf_iscached(npc, NPC_IP46));
 	}
-	ip4addr = sd ? ip->ip_src.s_addr : ip->ip_dst.s_addr;
+	if (omask == 0)
+		return 0;
 
-	return (ip4addr & subnet) == netaddr ? 0 : -1;
+	addr1 = sd ? npc->npc_srcip : npc->npc_dstip;
+	npf_calculate_masked_addr(&addr2, netaddr, omask);
+	if (memcmp(addr1, &addr2, npc->npc_ipsz)) {
+		return -1;
+	}
+	return 0;
 }
 
 /*
--- a/sys/net/npf/npf_nat.c	Fri Nov 04 00:22:33 2011 +0000
+++ b/sys/net/npf/npf_nat.c	Fri Nov 04 01:00:27 2011 +0000
@@ -1,4 +1,4 @@
-/*	$NetBSD: npf_nat.c,v 1.6 2011/02/02 02:20:25 rmind Exp $	*/
+/*	$NetBSD: npf_nat.c,v 1.7 2011/11/04 01:00:27 zoltan Exp $	*/
 
 /*-
  * Copyright (c) 2010-2011 The NetBSD Foundation, Inc.
@@ -76,7 +76,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: npf_nat.c,v 1.6 2011/02/02 02:20:25 rmind Exp $");
+__KERNEL_RCSID(0, "$NetBSD: npf_nat.c,v 1.7 2011/11/04 01:00:27 zoltan Exp $");
 
 #include <sys/param.h>
 #include <sys/kernel.h>
@@ -419,7 +419,8 @@
 	const int proto = npf_cache_ipproto(npc);
 	npf_nat_t *nt;
 
-	KASSERT(npf_iscached(npc, NPC_IP46 | NPC_LAYER4));
+	KASSERT(npf_iscached(npc, NPC_IP46));
+	KASSERT(npf_iscached(npc, NPC_LAYER4));
 
 	/* New NAT association. */
 	nt = pool_cache_get(nat_cache, PR_NOWAIT);
@@ -531,7 +532,7 @@
 	switch (npf_cache_ipproto(npc)) {
 	case IPPROTO_TCP:
 	case IPPROTO_UDP:
-		KASSERT(npf_iscached(npc, NPC_TCP | NPC_UDP));
+		KASSERT(npf_iscached(npc, NPC_TCP) || npf_iscached(npc, NPC_UDP));
 		/* Rewrite source/destination port. */
 		if (!npf_rwrport(npc, nbuf, n_ptr, di, port)) {
 			return EINVAL;
--- a/sys/net/npf/npf_ncode.h	Fri Nov 04 00:22:33 2011 +0000
+++ b/sys/net/npf/npf_ncode.h	Fri Nov 04 01:00:27 2011 +0000
@@ -1,4 +1,4 @@
-/*	$NetBSD: npf_ncode.h,v 1.4 2010/12/18 01:07:25 rmind Exp $	*/
+/*	$NetBSD: npf_ncode.h,v 1.5 2011/11/04 01:00:27 zoltan Exp $	*/
 
 /*-
  * Copyright (c) 2009-2010 The NetBSD Foundation, Inc.
@@ -114,8 +114,9 @@
 #define	NPF_OPCODE_ETHER		0x80
 
 #define	NPF_OPCODE_IP4MASK		0x90
-#define	NPF_OPCODE_IP4TABLE		0x91
+#define	NPF_OPCODE_TABLE		0x91
 #define	NPF_OPCODE_ICMP4		0x92
+#define	NPF_OPCODE_IP6MASK		0x93
 
 #define	NPF_OPCODE_TCP_PORTS		0xa0
 #define	NPF_OPCODE_UDP_PORTS		0xa1
--- a/sys/net/npf/npf_processor.c	Fri Nov 04 00:22:33 2011 +0000
+++ b/sys/net/npf/npf_processor.c	Fri Nov 04 01:00:27 2011 +0000
@@ -1,4 +1,4 @@
-/*	$NetBSD: npf_processor.c,v 1.4 2010/12/18 01:07:25 rmind Exp $	*/
+/*	$NetBSD: npf_processor.c,v 1.5 2011/11/04 01:00:27 zoltan Exp $	*/
 
 /*-
  * Copyright (c) 2009-2010 The NetBSD Foundation, Inc.
@@ -54,7 +54,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: npf_processor.c,v 1.4 2010/12/18 01:07:25 rmind Exp $");
+__KERNEL_RCSID(0, "$NetBSD: npf_processor.c,v 1.5 2011/11/04 01:00:27 zoltan Exp $");
 
 #include <sys/param.h>
 #include <sys/kernel.h>
@@ -146,6 +146,8 @@
 	uint32_t	regs[NPF_NREGS];
 	/* Local, state variables. */
 	uint32_t d, i, n;
+	npf_addr_t addr;
+	uint32_t mask;
 	u_int lcount;
 	int cmpval;
 
@@ -284,13 +286,20 @@
 	case NPF_OPCODE_IP4MASK:
 		/* Source/destination, network address, subnet mask. */
 		i_ptr = nc_fetch_word(i_ptr, &d);
-		i_ptr = nc_fetch_double(i_ptr, &n, &i);
-		cmpval = npf_match_ip4mask(npc, nbuf, n_ptr, d, n, i);
+		i_ptr = nc_fetch_double(i_ptr, &addr.s6_addr32[0], &mask);
+		cmpval = npf_match_ipmask(npc, nbuf, n_ptr, d, &addr, (npf_netmask_t)mask);
 		break;
-	case NPF_OPCODE_IP4TABLE:
+	case NPF_OPCODE_IP6MASK:
+		i_ptr = nc_fetch_word(i_ptr, &d);
+		i_ptr = nc_fetch_double(i_ptr, &addr.s6_addr32[0], &addr.s6_addr32[1]);
+		i_ptr = nc_fetch_double(i_ptr, &addr.s6_addr32[2], &addr.s6_addr32[3]);
+		i_ptr = nc_fetch_word(i_ptr, &mask);
+		cmpval = npf_match_ipmask(npc, nbuf, n_ptr, d, &addr, (npf_netmask_t)mask);
+		break;
+	case NPF_OPCODE_TABLE:
 		/* Source/destination, NPF table ID. */
 		i_ptr = nc_fetch_double(i_ptr, &n, &i);
-		cmpval = npf_match_ip4table(npc, nbuf, n_ptr, n, i);
+		cmpval = npf_match_table(npc, nbuf, n_ptr, n, i);
 		break;
 	case NPF_OPCODE_TCP_PORTS:
 		/* Source/destination, port range. */
@@ -441,7 +450,10 @@
 	case NPF_OPCODE_IP4MASK:
 		error = nc_ptr_check(&iptr, nc, sz, 3, NULL, 0);
 		break;
-	case NPF_OPCODE_IP4TABLE:
+	case NPF_OPCODE_IP6MASK:
+		error = nc_ptr_check(&iptr, nc, sz, 6, NULL, 0);
+		break;
+	case NPF_OPCODE_TABLE:
 		error = nc_ptr_check(&iptr, nc, sz, 2, NULL, 0);
 		break;
 	case NPF_OPCODE_TCP_PORTS:
--- a/sys/net/npf/npf_sendpkt.c	Fri Nov 04 00:22:33 2011 +0000
+++ b/sys/net/npf/npf_sendpkt.c	Fri Nov 04 01:00:27 2011 +0000
@@ -1,4 +1,4 @@
-/*	$NetBSD: npf_sendpkt.c,v 1.4 2011/01/18 20:33:46 rmind Exp $	*/
+/*	$NetBSD: npf_sendpkt.c,v 1.5 2011/11/04 01:00:27 zoltan Exp $	*/
 
 /*-
  * Copyright (c) 2010 The NetBSD Foundation, Inc.
@@ -34,7 +34,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: npf_sendpkt.c,v 1.4 2011/01/18 20:33:46 rmind Exp $");
+__KERNEL_RCSID(0, "$NetBSD: npf_sendpkt.c,v 1.5 2011/11/04 01:00:27 zoltan Exp $");
 
 #include <sys/param.h>
 #include <sys/kernel.h>
@@ -45,6 +45,9 @@
 #include <netinet/ip_icmp.h>
 #include <netinet/ip_var.h>
 #include <netinet/tcp.h>
+#include <netinet/ip6.h>
+#include <netinet/icmp6.h>
+#include <netinet6/ip6_var.h>
 #include <sys/mbuf.h>
 
 #include "npf_impl.h"
@@ -58,16 +61,17 @@
 npf_return_tcp(npf_cache_t *npc, nbuf_t *nbuf)
 {
 	struct mbuf *m;
-	struct ip *oip, *ip;
+	struct ip *ip = NULL;
+	struct ip6_hdr *ip6 = NULL;
 	struct tcphdr *oth, *th;
 	tcp_seq seq, ack;
 	int tcpdlen, len;
 	uint32_t win;
 
 	/* Fetch relevant data. */
-	KASSERT(npf_iscached(npc, NPC_IP46 | NPC_LAYER4));
-	tcpdlen = npf_tcpsaw(npc, &seq, &ack, &win);
-	oip = &npc->npc_ip.v4;
+	KASSERT(npf_iscached(npc, NPC_IP46));
+	KASSERT(npf_iscached(npc, NPC_LAYER4));
+	tcpdlen = npf_tcpsaw(npc, nbuf, &seq, &ack, &win);
 	oth = &npc->npc_l4.tcp;
 
 	if (oth->th_flags & TH_RST) {
@@ -75,7 +79,11 @@
 	}
 
 	/* Create and setup a network buffer. */
-	len = sizeof(struct ip) + sizeof(struct tcphdr);
+	if (npc->npc_info & NPC_IP4)
+		len = sizeof(struct ip) + sizeof(struct tcphdr);
+	else
+		len = sizeof(struct ip6_hdr) + sizeof(struct tcphdr);
+
 	m = m_gethdr(M_DONTWAIT, MT_HEADER);
 	if (m == NULL) {
 		return ENOMEM;
@@ -84,20 +92,37 @@
 	m->m_len = len;
 	m->m_pkthdr.len = len;
 
-	ip = mtod(m, struct ip *);
-	memset(ip, 0, len);
+	if (npc->npc_info & NPC_IP4) {
+		struct ip *oip = &npc->npc_ip.v4;
+		ip = mtod(m, struct ip *);
+		memset(ip, 0, len);
+
+		/*
+	 	* First fill of IPv4 header, for TCP checksum.
+	 	* Note: IP length contains TCP header length.
+	 	*/
+		ip->ip_p = IPPROTO_TCP;
+		ip->ip_src.s_addr = oip->ip_dst.s_addr;
+		ip->ip_dst.s_addr = oip->ip_src.s_addr;
+		ip->ip_len = htons(sizeof(struct tcphdr));
 
-	/*
-	 * First fill of IPv4 header, for TCP checksum.
-	 * Note: IP length contains TCP header length.
-	 */
-	ip->ip_p = IPPROTO_TCP;
-	ip->ip_src.s_addr = oip->ip_dst.s_addr;
-	ip->ip_dst.s_addr = oip->ip_src.s_addr;
-	ip->ip_len = htons(sizeof(struct tcphdr));
+		th = (struct tcphdr *)(ip + 1);
+	} else {
+		struct ip6_hdr *oip = &npc->npc_ip.v6;
+		ip6 = mtod(m, struct ip6_hdr *);
+		memset(ip6, 0, len);
+
+		ip6->ip6_nxt = IPPROTO_TCP;
+		ip6->ip6_hlim = IPV6_DEFHLIM;
+		memcpy(&ip6->ip6_src, &oip->ip6_dst, sizeof(struct in6_addr));
+		memcpy(&ip6->ip6_dst, &oip->ip6_src, sizeof(struct in6_addr));		
+		ip6->ip6_plen = htons(len);
+		ip6->ip6_vfc = IPV6_VERSION;
+
+		th = (struct tcphdr *)(ip6 + 1);
+	}
 
 	/* Construct TCP header and compute the checksum. */
-	th = (struct tcphdr *)(ip + 1);
 	th->th_sport = oth->th_dport;
 	th->th_dport = oth->th_sport;
 	th->th_seq = htonl(ack);
@@ -107,33 +132,48 @@
 	th->th_ack = htonl(seq + tcpdlen);
 	th->th_off = sizeof(struct tcphdr) >> 2;
 	th->th_flags = TH_ACK | TH_RST;
-	th->th_sum = in_cksum(m, len);
+
+	if (npc->npc_info & NPC_IP4) {
+		th->th_sum = in_cksum(m, len);
 
-	/* Second fill of IPv4 header, fill correct IP length. */
-	ip->ip_v = IPVERSION;
-	ip->ip_hl = sizeof(struct ip) >> 2;
-	ip->ip_tos = IPTOS_LOWDELAY;
-	ip->ip_len = htons(len);
-	ip->ip_ttl = DEFAULT_IP_TTL;
+		 /* Second fill of IPv4 header, fill correct IP length. */
+		ip->ip_v = IPVERSION;
+		ip->ip_hl = sizeof(struct ip) >> 2;
+		ip->ip_tos = IPTOS_LOWDELAY;
+		ip->ip_len = htons(len);
+		ip->ip_ttl = DEFAULT_IP_TTL;
+	} else {
+		th->th_sum = in6_cksum(m, IPPROTO_TCP, sizeof(struct ip6_hdr), sizeof(struct tcphdr));
+	}
 
 	/* Pass to IP layer. */
-	return ip_output(m, NULL, NULL, IP_FORWARDING, NULL, NULL);
+	if (npc->npc_info & NPC_IP4) {
+		return ip_output(m, NULL, NULL, IP_FORWARDING, NULL, NULL);
+	} else {
+		return ip6_output(m, NULL, NULL, IPV6_FORWARDING, NULL, NULL, NULL);
+	}
 }
 
 /*
  * npf_return_icmp: return an ICMP error.
  */
 static int
-npf_return_icmp(nbuf_t *nbuf)
+npf_return_icmp(npf_cache_t *npc, nbuf_t *nbuf)
 {
 	struct mbuf *m = nbuf;
 
-	icmp_error(m, ICMP_UNREACH, ICMP_UNREACH_ADMIN_PROHIBIT, 0, 0);
+	if (npf_iscached(npc, NPC_IP4)) {
+		icmp_error(m, ICMP_UNREACH, ICMP_UNREACH_ADMIN_PROHIBIT, 0, 0);
+	} else {
+		KASSERT(npf_iscached(npc, NPC_IP6));
+		icmp6_error(m, ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_ADMIN, 0);
+	}
 	return 0;
 }
 
 /*
  * npf_return_block: return TCP reset or ICMP host unreachable packet.
+ * TODO: user should be able to specify exact ICMP error codes in config
  */
 void
 npf_return_block(npf_cache_t *npc, nbuf_t *nbuf, const int retfl)
@@ -154,7 +194,7 @@
 		break;
 	case IPPROTO_UDP:
 		if (retfl & NPF_RULE_RETICMP) {
-			(void)npf_return_icmp(nbuf);
+			(void)npf_return_icmp(npc, nbuf);
 		}
 		break;
 	}
--- a/sys/net/npf/npf_session.c	Fri Nov 04 00:22:33 2011 +0000
+++ b/sys/net/npf/npf_session.c	Fri Nov 04 01:00:27 2011 +0000
@@ -1,4 +1,4 @@
-/*	$NetBSD: npf_session.c,v 1.8 2011/02/02 02:20:25 rmind Exp $	*/
+/*	$NetBSD: npf_session.c,v 1.9 2011/11/04 01:00:27 zoltan Exp $	*/
 
 /*-
  * Copyright (c) 2010-2011 The NetBSD Foundation, Inc.
@@ -74,7 +74,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: npf_session.c,v 1.8 2011/02/02 02:20:25 rmind Exp $");
+__KERNEL_RCSID(0, "$NetBSD: npf_session.c,v 1.9 2011/11/04 01:00:27 zoltan Exp $");
 
 #include <sys/param.h>
 #include <sys/kernel.h>
@@ -506,7 +506,8 @@
 	if (!sess_tracking) {
 		return NULL;
 	}
-	KASSERT(npf_iscached(npc, NPC_IP46 | NPC_LAYER4));
+	KASSERT(npf_iscached(npc, NPC_IP46));
+	KASSERT(npf_iscached(npc, NPC_LAYER4));
 
 	/* Allocate and initialise new state. */
 	se = pool_cache_get(sess_cache, PR_NOWAIT);
--- a/sys/net/npf/npf_state.c	Fri Nov 04 00:22:33 2011 +0000
+++ b/sys/net/npf/npf_state.c	Fri Nov 04 01:00:27 2011 +0000
@@ -1,4 +1,4 @@
-/*	$NetBSD: npf_state.c,v 1.4 2011/04/25 22:16:21 yamt Exp $	*/
+/*	$NetBSD: npf_state.c,v 1.5 2011/11/04 01:00:27 zoltan Exp $	*/
 
 /*-
  * Copyright (c) 2010 The NetBSD Foundation, Inc.
@@ -34,7 +34,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: npf_state.c,v 1.4 2011/04/25 22:16:21 yamt Exp $");
+__KERNEL_RCSID(0, "$NetBSD: npf_state.c,v 1.5 2011/11/04 01:00:27 zoltan Exp $");
 
 #include <sys/param.h>
 #include <sys/systm.h>
@@ -85,7 +85,7 @@
 	uint32_t win;
 
 	KASSERT(npf_iscached(npc, NPC_TCP));
-	tcpdlen = npf_tcpsaw(__UNCONST(npc), &seq, &ack, &win);
+	tcpdlen = npf_tcpsaw(__UNCONST(npc), nbuf, &seq, &ack, &win);
 	end = seq + tcpdlen;
 	if (tcpfl & TH_SYN) {
 		end++;
@@ -326,7 +326,8 @@
 {
 	const int proto = npf_cache_ipproto(npc);
 
-	KASSERT(npf_iscached(npc, NPC_IP46 | NPC_LAYER4));
+	KASSERT(npf_iscached(npc, NPC_IP46));
+	KASSERT(npf_iscached(npc, NPC_LAYER4));
 
 	mutex_init(&nst->nst_lock, MUTEX_DEFAULT, IPL_SOFTNET);
 
--- a/sys/net/npf/npf_tableset.c	Fri Nov 04 00:22:33 2011 +0000
+++ b/sys/net/npf/npf_tableset.c	Fri Nov 04 01:00:27 2011 +0000
@@ -1,4 +1,4 @@
-/*	$NetBSD: npf_tableset.c,v 1.5 2011/02/02 02:20:25 rmind Exp $	*/
+/*	$NetBSD: npf_tableset.c,v 1.6 2011/11/04 01:00:27 zoltan Exp $	*/
 
 /*-
  * Copyright (c) 2009-2010 The NetBSD Foundation, Inc.
@@ -39,7 +39,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: npf_tableset.c,v 1.5 2011/02/02 02:20:25 rmind Exp $");
+__KERNEL_RCSID(0, "$NetBSD: npf_tableset.c,v 1.6 2011/11/04 01:00:27 zoltan Exp $");
 
 #include <sys/param.h>
 #include <sys/kernel.h>
@@ -63,8 +63,8 @@
 		rb_node_t		rbnode;
 	} te_entry;
 	/* IPv4 CIDR block. */
-	in_addr_t			te_addr;
-	in_addr_t			te_mask;
+	npf_addr_t			te_addr;
+	npf_netmask_t			te_mask;
 };
 
 LIST_HEAD(npf_hashl, npf_tblent);
@@ -163,28 +163,18 @@
 {
 	const npf_tblent_t * const te1 = n1;
 	const npf_tblent_t * const te2 = n2;
-	const in_addr_t x = te1->te_addr & te1->te_mask;
-	const in_addr_t y = te2->te_addr & te2->te_mask;
 
-	if (x < y)
-		return -1;
-	if (x > y)
-		return 1;
-	return 0;
+	return npf_compare_cidr(&te1->te_addr, te1->te_mask,
+				&te2->te_addr, te2->te_mask);
 }
 
 static signed int
 table_rbtree_cmp_key(void *ctx, const void *n1, const void *key)
 {
 	const npf_tblent_t * const te = n1;
-	const in_addr_t x = te->te_addr & te->te_mask;
-	const in_addr_t y = *(const in_addr_t *)key;
+	const npf_addr_t *t2 = key;
 
-	if (x < y)
-		return -1;
-	if (x > y)
-		return 1;
-	return 0;
+	return npf_compare_cidr(&te->te_addr, te->te_mask, t2, NPF_NO_NETMASK);
 }
 
 static const rb_tree_ops_t table_rbtree_ops = {
@@ -199,7 +189,7 @@
  */
 
 static inline struct npf_hashl *
-table_hash_bucket(npf_table_t *t, void *buf, size_t sz)
+table_hash_bucket(npf_table_t *t, const void *buf, size_t sz)
 {
 	const uint32_t hidx = hash32_buf(buf, sz, HASH32_BUF_INIT);
 
@@ -348,21 +338,21 @@
 }
 
 /*
- * npf_table_add_v4cidr: add an IPv4 CIDR into the table.
+ * npf_table_add_cidr: add an IPv4 or IPv6 CIDR into the table.
  */
 int
-npf_table_add_v4cidr(npf_tableset_t *tset, u_int tid,
-    in_addr_t addr, in_addr_t mask)
+npf_table_add_cidr(npf_tableset_t *tset, u_int tid,
+    const npf_addr_t *addr, const npf_netmask_t mask)
 {
 	struct npf_hashl *htbl;
 	npf_tblent_t *e, *it;
 	npf_table_t *t;
-	in_addr_t val;
+	npf_addr_t val;
 	int error = 0;
 
 	/* Allocate and setup entry. */
 	e = pool_cache_get(tblent_cache, PR_WAITOK);
-	e->te_addr = addr;
+	memcpy(&e->te_addr, addr, sizeof(npf_addr_t));
 	e->te_mask = mask;
 
 	/* Locks the table. */
@@ -374,12 +364,19 @@
 	switch (t->t_type) {
 	case NPF_TABLE_HASH:
 		/* Generate hash value from: address & mask. */
-		val = addr & mask;
-		htbl = table_hash_bucket(t, &val, sizeof(in_addr_t));
+		npf_calculate_masked_addr(&val, addr, mask);
+		htbl = table_hash_bucket(t, &val, sizeof(npf_addr_t));
 		/* Lookup to check for duplicates. */
 		LIST_FOREACH(it, htbl, te_entry.hashq) {
-			if (it->te_addr == addr && it->te_mask == mask)
-				break;
+			if (it->te_mask == mask) {
+				const uint32_t *addr1 = it->te_addr.s6_addr32;
+				const uint32_t *addr2 = addr->s6_addr32;
+				const size_t len = sizeof(npf_addr_t);
+
+				if (memcmp(addr1, addr2, len) == 0) {
+					break;
+				}
+			}
 		}
 		/* If no duplicate - insert entry. */
 		if (__predict_true(it == NULL)) {
@@ -409,13 +406,13 @@
  * npf_table_rem_v4cidr: remove an IPv4 CIDR from the table.
  */
 int
-npf_table_rem_v4cidr(npf_tableset_t *tset, u_int tid,
-    in_addr_t addr, in_addr_t mask)
+npf_table_rem_cidr(npf_tableset_t *tset, u_int tid,
+    const npf_addr_t *addr, const npf_netmask_t mask)
 {
 	struct npf_hashl *htbl;
 	npf_tblent_t *e;
 	npf_table_t *t;
-	in_addr_t val;
+	npf_addr_t val;
 	int error;
 
 	e = NULL;
@@ -429,11 +426,18 @@
 	switch (t->t_type) {
 	case NPF_TABLE_HASH:
 		/* Generate hash value from: (address & mask). */
-		val = addr & mask;
-		htbl = table_hash_bucket(t, &val, sizeof(in_addr_t));
+		npf_calculate_masked_addr(&val, addr, mask);
+		htbl = table_hash_bucket(t, &val, sizeof(npf_addr_t));
 		LIST_FOREACH(e, htbl, te_entry.hashq) {
-			if (e->te_addr == addr && e->te_mask == mask)
-				break;
+			if (e->te_mask == mask) {
+				const uint32_t *addr1 = e->te_addr.s6_addr32;
+				const uint32_t *addr2 = addr->s6_addr32;
+				const size_t len = sizeof(npf_addr_t);
+
+				if (memcmp(addr1, addr2, len) == 0) {
+					break;
+				}
+			}
 		}
 		if (__predict_true(e != NULL)) {
 			LIST_REMOVE(e, te_entry.hashq);
@@ -443,7 +447,7 @@
 		break;
 	case NPF_TABLE_RBTREE:
 		/* Key: (address & mask). */
-		val = addr & mask;
+		npf_calculate_masked_addr(&val, addr, mask);
 		e = rb_tree_find_node(&t->t_rbtree, &val);
 		if (__predict_true(e != NULL)) {
 			rb_tree_remove_node(&t->t_rbtree, e);
@@ -464,11 +468,11 @@
 }
 
 /*
- * npf_table_match_v4addr: find the table according to ID, lookup and
+ * npf_table_match_addr: find the table according to ID, lookup and
  * match the contents with specified IPv4 address.
  */
 int
-npf_table_match_v4addr(u_int tid, in_addr_t ip4addr)
+npf_table_match_addr(u_int tid, const npf_addr_t *addr)
 {
 	struct npf_hashl *htbl;
 	npf_tblent_t *e = NULL;
@@ -481,16 +485,17 @@
 	}
 	switch (t->t_type) {
 	case NPF_TABLE_HASH:
-		htbl = table_hash_bucket(t, &ip4addr, sizeof(in_addr_t));
+		htbl = table_hash_bucket(t, addr, sizeof(npf_addr_t));
 		LIST_FOREACH(e, htbl, te_entry.hashq) {
-			if ((ip4addr & e->te_mask) == e->te_addr) {
-				break;
-			}
+			if (npf_compare_cidr(addr, e->te_mask, &e->te_addr, NPF_NO_NETMASK) == 0)
+				break;			
 		}
 		break;
 	case NPF_TABLE_RBTREE:
-		e = rb_tree_find_node(&t->t_rbtree, &ip4addr);
-		KASSERT((ip4addr & e->te_mask) == e->te_addr);
+		e = rb_tree_find_node(&t->t_rbtree, addr);
+		if (e != NULL) {
+			KASSERT(npf_compare_cidr(addr, e->te_mask, &e->te_addr, NPF_NO_NETMASK) == 0);
+		}
 		break;
 	default:
 		KASSERT(false);
--- a/usr.sbin/npf/npfctl/npf_data.c	Fri Nov 04 00:22:33 2011 +0000
+++ b/usr.sbin/npf/npfctl/npf_data.c	Fri Nov 04 01:00:27 2011 +0000
@@ -1,4 +1,4 @@
-/*	$NetBSD: npf_data.c,v 1.7 2011/02/02 02:20:25 rmind Exp $	*/
+/*	$NetBSD: npf_data.c,v 1.8 2011/11/04 01:00:28 zoltan Exp $	*/
 
 /*-
  * Copyright (c) 2009-2011 The NetBSD Foundation, Inc.
@@ -31,7 +31,7 @@
  */
 
 #include <sys/cdefs.h>
-__RCSID("$NetBSD: npf_data.c,v 1.7 2011/02/02 02:20:25 rmind Exp $");
+__RCSID("$NetBSD: npf_data.c,v 1.8 2011/11/04 01:00:28 zoltan Exp $");
 
 #include <sys/types.h>
 #include <sys/socket.h>
@@ -86,14 +86,14 @@
  */
 
 struct ifaddrs *
-npfctl_getif(char *ifname, unsigned int *if_idx, bool reqaddr)
+npfctl_getif(char *ifname, unsigned int *if_idx, bool reqaddr, sa_family_t addrtype)
 {
 	struct ifaddrs *ifent;
 	struct sockaddr_in *sin;
 
 	for (ifent = ifs_list; ifent != NULL; ifent = ifent->ifa_next) {
 		sin = (struct sockaddr_in *)ifent->ifa_addr;
-		if (sin->sin_family != AF_INET && reqaddr)
+		if (sin->sin_family != addrtype && reqaddr)
 			continue;
 		if (strcmp(ifent->ifa_name, ifname) == 0)
 			break;
@@ -105,27 +105,6 @@
 }
 
 bool
-npfctl_parse_v4mask(char *ostr, in_addr_t *addr, in_addr_t *mask)
-{
-	char *str = xstrdup(ostr);
-	char *p = strchr(str, '/');
-	u_int bits;
-	bool ret;
-
-	/* In network byte order. */
-	if (p) {
-		*p++ = '\0';
-		bits = (u_int)atoi(p);
-		*mask = bits ? htonl(0xffffffff << (32 - bits)) : 0;
-	} else {
-		*mask = 0xffffffff;
-	}
-	ret = inet_aton(str, (struct in_addr *)addr) != 0;
-	free(str);
-	return ret;
-}
-
-bool
 npfctl_parse_port(char *ostr, bool *range, in_port_t *fport, in_port_t *tport)
 {
 	char *str = xstrdup(ostr), *sep;
@@ -154,29 +133,90 @@
 }
 
 void
-npfctl_parse_cidr(char *str, in_addr_t *addr, in_addr_t *mask)
+npfctl_create_mask(sa_family_t family, u_int length, npf_addr_t *omask)
+{
+	uint32_t part;
+	uint32_t *mask = (uint32_t*)omask;
+
+	memset(omask, 0, sizeof(npf_addr_t));
+	if (family == AF_INET) {
+		part = htonl(0xffffffff << (32 - length));
+		memcpy(mask, &part, 4);
+	} else if (family == AF_INET6) {
+		while (length > 32) {
+			part = htonl(0xffffffff);
+			memcpy(mask, &part, 4);
+			mask += 1;
+			length -= 32;
+		}
+		part = htonl(0xffffffff << (32 - length));
+		memcpy(mask, &part, 4);
+	}
+}
+
+sa_family_t
+npfctl_get_addrfamily(const char *ostr)
+{
+	struct addrinfo hint, *res = NULL;
+	int ret; 
+	char *str = xstrdup(ostr);
+	char *p = strchr(str, '/');
+	sa_family_t family;
+
+	if (p)
+	    *p = '\0';
+	memset(&hint, '\0', sizeof(hint));
+	hint.ai_family = PF_UNSPEC;
+	hint.ai_flags = AI_NUMERICHOST;
+	ret = getaddrinfo(str, NULL, &hint, &res);
+	if (ret) {
+		family = AF_UNSPEC;
+	} else {
+		family = res->ai_family;
+	}
+	freeaddrinfo(res);
+	free(str);
+	return family;
+}
+
+sa_family_t
+npfctl_parse_cidr(char *str, sa_family_t addrfamily, npf_addr_t *addr, npf_netmask_t *mask)
 {
 
 	if (strcmp(str, "any") == 0) {
-		*addr = 0x0;
-		*mask = 0x0;
-
+		memset(addr, 0, sizeof(npf_addr_t));
+		memset(mask, 0, sizeof(npf_netmask_t));
 	} else if (isalpha((unsigned char)*str)) {
+		/* TODO: handle multiple addresses per interface */
 		struct ifaddrs *ifa;
 		struct sockaddr_in *sin;
 		u_int idx;
-
-		if ((ifa = npfctl_getif(str, &idx, true)) == NULL) {
+		if ((ifa = npfctl_getif(str, &idx, true, AF_INET)) == NULL) {
 			errx(EXIT_FAILURE, "invalid interface '%s'", str);
 		}
 		/* Interface address. */
 		sin = (struct sockaddr_in *)ifa->ifa_addr;
-		*addr = sin->sin_addr.s_addr;
-		*mask = 0xffffffff;
+		memcpy(addr, &(sin->sin_addr.s_addr), sizeof(struct in_addr));
+		//v4mask = 0xffffffff; - TODO!
+	} else {
+		char *p = strchr(str, '/');
+		if (p != NULL) {
+			*p++ = '\0';
+			*mask = atoi(p);
+		} else {
+			if (addrfamily == AF_INET)
+				*mask = 32;
+			else
+				*mask = 128;
+		}
+		memset(addr, 0, sizeof(npf_addr_t));
+		int ret = inet_pton(addrfamily, str, addr);
+		if (ret != 1) {
+			printf("TODO: error");
+		}
+	}
 
-	} else if (!npfctl_parse_v4mask(str, addr, mask)) {
-		errx(EXIT_FAILURE, "invalid CIDR '%s'\n", str);
-	}
+	return addrfamily;
 }
 
 static bool
@@ -228,18 +268,18 @@
 	l = 1;
 	buf = NULL;
 	while (getline(&buf, &n, fp) != -1) {
-		in_addr_t addr, mask;
+		npf_addr_t addr;
+		npf_netmask_t mask;
 
 		if (*buf == '\n' || *buf == '#')
 			continue;
 
-		/* IPv4 CIDR: a.b.c.d/mask */
-		if (!npfctl_parse_v4mask(buf, &addr, &mask)) {
+		if (!npfctl_parse_cidr(buf, npfctl_get_addrfamily(buf), &addr, &mask)) {
 			errx(EXIT_FAILURE, "invalid table entry at line %d", l);
 		}
 
 		/* Create and add table entry. */
-		npf_table_add_entry(tl, addr, mask);
+		npf_table_add_entry(tl, &addr, mask);
 		l++;
 	}
 	if (buf != NULL) {
@@ -252,7 +292,7 @@
  */
 
 static void
-npfctl_rulenc_v4cidr(void **nc, int nblocks[], var_t *dat, bool sd)
+npfctl_rulenc_cidr(void **nc, int nblocks[], var_t *dat, bool sd, sa_family_t addrfamily)
 {
 	element_t *el = dat->v_elements;
 	int foff;
@@ -267,15 +307,21 @@
 		return;
 	}
 
-	/* Generate v4 CIDR matching blocks. */
+	/* Generate v4/v6 CIDR matching blocks. */
 	for (el = dat->v_elements; el != NULL; el = el->e_next) {
-		in_addr_t addr, mask;
+		npf_addr_t addr;
+		npf_netmask_t mask;
 
-		npfctl_parse_cidr(el->e_data, &addr, &mask);
-
-		nblocks[1]--;
+		npfctl_parse_cidr(el->e_data, addrfamily, &addr, &mask);
+		if (addrfamily == AF_INET)
+		    nblocks[1]--;
+		else if (addrfamily == AF_INET6)
+		    nblocks[3]--;
 		foff = npfctl_failure_offset(nblocks);
-		npfctl_gennc_v4cidr(nc, foff, addr, mask, sd);
+		if (addrfamily == AF_INET) 
+			npfctl_gennc_v4cidr(nc, foff, &addr, mask, sd);
+		else if (addrfamily == AF_INET6)
+			npfctl_gennc_v6cidr(nc, foff, &addr, mask, sd);
 	}
 }
 
@@ -304,10 +350,10 @@
 
 static void
 npfctl_rulenc_block(void **nc, int nblocks[], var_t *cidr, var_t *ports,
-    bool both, bool tcpudp, bool sd)
+    bool both, bool tcpudp, bool sd, sa_family_t addrfamily)
 {
 
-	npfctl_rulenc_v4cidr(nc, nblocks, cidr, sd);
+	npfctl_rulenc_cidr(nc, nblocks, cidr, sd, addrfamily);
 	if (ports == NULL) {
 		return;
 	}
@@ -320,9 +366,9 @@
 
 void
 npfctl_rule_ncode(nl_rule_t *rl, char *proto, char *tcpfl, int icmp_type,
-    int icmp_code, var_t *from, var_t *fports, var_t *to, var_t *tports)
+    int icmp_code, var_t *from, sa_family_t addrfamily, var_t *fports, var_t *to, var_t *tports)
 {
-	int nblocks[3] = { 0, 0, 0 };
+	int nblocks[4] = { 0, 0, 0, 0 };
 	bool icmp, tcpudp, both;
 	void *ncptr, *nc;
 	size_t sz, foff;
@@ -370,22 +416,30 @@
 	if (from && from->v_count) {
 		if (from->v_type == VAR_TABLE)
 			nblocks[0] += 1;
-		else
-			nblocks[1] += from->v_count;
+		else {
+			if (addrfamily == AF_INET)
+				nblocks[1] += from->v_count;
+			else
+				nblocks[3] += from->v_count;
+		}
 		if (fports && fports->v_count)
 			nblocks[0] += fports->v_count * (both ? 2 : 1);
 	}
 	if (to && to->v_count) {
 		if (to->v_type == VAR_TABLE)
 			nblocks[0] += 1;
-		else
-			nblocks[1] += to->v_count;
+		else {
+			if (addrfamily == AF_INET)
+				nblocks[1] += to->v_count;
+			else
+				nblocks[3] += to->v_count;
+		}
 		if (tports && tports->v_count)
 			nblocks[0] += tports->v_count * (both ? 2 : 1);
 	}
 
 	/* Any n-code to generate? */
-	if (!icmp && (nblocks[0] + nblocks[1] + nblocks[2]) == 0) {
+	if (!icmp && (nblocks[0] + nblocks[1] + nblocks[2] + nblocks[3]) == 0) {
 		/* Done, if none. */
 		return;
 	}
@@ -399,15 +453,15 @@
 	nc = ncptr;
 
 	/*
-	 * Generate v4 CIDR matching blocks and TCP/UDP port matching.
+	 * Generate v4/v6 CIDR matching blocks and TCP/UDP port matching.
 	 */
 	if (from) {
 		npfctl_rulenc_block(&nc, nblocks, from, fports,
-		    both, tcpudp, true);
+		    both, tcpudp, true, addrfamily);
 	}
 	if (to) {
 		npfctl_rulenc_block(&nc, nblocks, to, tports,
-		    both, tcpudp, false);
+		    both, tcpudp, false, addrfamily);
 	}
 
 	if (icmp) {
--- a/usr.sbin/npf/npfctl/npf_ncgen.c	Fri Nov 04 00:22:33 2011 +0000
+++ b/usr.sbin/npf/npfctl/npf_ncgen.c	Fri Nov 04 01:00:27 2011 +0000
@@ -1,4 +1,4 @@
-/*	$NetBSD: npf_ncgen.c,v 1.4 2010/12/18 01:07:26 rmind Exp $	*/
+/*	$NetBSD: npf_ncgen.c,v 1.5 2011/11/04 01:00:28 zoltan Exp $	*/
 
 /*-
  * Copyright (c) 2009-2010 The NetBSD Foundation, Inc.
@@ -37,9 +37,10 @@
  */
 
 #include <sys/cdefs.h>
-__RCSID("$NetBSD: npf_ncgen.c,v 1.4 2010/12/18 01:07:26 rmind Exp $");
+__RCSID("$NetBSD: npf_ncgen.c,v 1.5 2011/11/04 01:00:28 zoltan Exp $");
 
 #include <sys/types.h>
+#include <string.h>
 
 #include "npfctl.h"
 
@@ -54,11 +55,13 @@
 	 * - 5 words each by npfctl_gennc_ports/tbl(), stored in nblocks[0].
 	 * - 6 words each by npfctl_gennc_v4cidr(), stored in nblocks[1].
 	 * - 4 words by npfctl_gennc_{icmp,tcpfl}(), stored in nblocks[2].
+	 * - 9 words each by npfctl_gennc_v6cidr(), stored in nblocks[3].
 	 * - 4 words by npfctl_gennc_complete(), single last fragment.
 	 */
 	return nblocks[0] * 5 * sizeof(uint32_t) +
 	    nblocks[1] * 6 * sizeof(uint32_t) +
 	    nblocks[2] * 4 * sizeof(uint32_t) +
+	    nblocks[3] * 9 * sizeof(uint32_t) +
 	    4 * sizeof(uint32_t);
 }
 
@@ -68,7 +71,7 @@
 size_t
 npfctl_failure_offset(int nblocks[])
 {
-	size_t tblport_blocks, v4cidr_blocks, icmp_tcpfl;
+	size_t tblport_blocks, v4cidr_blocks, v6cidr_blocks, icmp_tcpfl;
 	/*
 	 * Take into account all blocks (plus 2 words for comparison each),
 	 * and additional 4 words to skip the last comparison and success path.
@@ -76,7 +79,8 @@
 	tblport_blocks = (3 + 2) * nblocks[0];
 	v4cidr_blocks = (4 + 2) * nblocks[1];
 	icmp_tcpfl = (2 + 2) * nblocks[2];
-	return tblport_blocks + v4cidr_blocks + icmp_tcpfl + 4;
+	v6cidr_blocks = (7 + 2) * nblocks[3];
+	return tblport_blocks + v4cidr_blocks + v6cidr_blocks + icmp_tcpfl + 4;
 }
 
 #if 0
@@ -115,20 +119,46 @@
 }
 #endif
 
+void
+npfctl_gennc_v6cidr(void **ncptr, int foff,
+    const npf_addr_t *netaddr, const npf_netmask_t mask, bool sd)
+{
+	uint32_t *nc = *ncptr;
+	const uint32_t *addr = (const uint32_t *)netaddr;
+
+	/* OP, direction, netaddr/subnet (10 words) */
+	*nc++ = NPF_OPCODE_IP6MASK;
+	*nc++ = (sd ? 0x01 : 0x00);
+	*nc++ = addr[0];
+	*nc++ = addr[1];
+	*nc++ = addr[2];
+	*nc++ = addr[3];
+	*nc++ = mask;
+
+	/* If not equal, jump to failure block, continue otherwise (2 words). */
+	*nc++ = NPF_OPCODE_BNE;
+	*nc++ = foff;
+
+	/* + 9 words. */
+	*ncptr = (void *)nc;
+}
+
+
 /*
  * npfctl_gennc_v4cidr: fragment to match IPv4 CIDR.
  */
 void
 npfctl_gennc_v4cidr(void **ncptr, int foff,
-    in_addr_t netaddr, in_addr_t subnet, bool sd)
+    const npf_addr_t *netaddr, const npf_netmask_t mask, bool sd)
 {
 	uint32_t *nc = *ncptr;
+	const uint32_t *addr = (const uint32_t *)netaddr;
 
 	/* OP, direction, netaddr/subnet (4 words) */
 	*nc++ = NPF_OPCODE_IP4MASK;
 	*nc++ = (sd ? 0x01 : 0x00);
-	*nc++ = netaddr;
-	*nc++ = subnet;
+	*nc++ = addr[0];
+	*nc++ = mask;
 
 	/* If not equal, jump to failure block, continue otherwise (2 words). */
 	*nc++ = NPF_OPCODE_BNE;
@@ -200,7 +230,7 @@
 	uint32_t *nc = *ncptr;
 
 	/* OP, direction, table ID (3 words). */
-	*nc++ = NPF_OPCODE_IP4TABLE;
+	*nc++ = NPF_OPCODE_TABLE;
 	*nc++ = (sd ? 0x01 : 0x00);
 	*nc++ = tid;
 
--- a/usr.sbin/npf/npfctl/npf_parser.c	Fri Nov 04 00:22:33 2011 +0000
+++ b/usr.sbin/npf/npfctl/npf_parser.c	Fri Nov 04 01:00:27 2011 +0000
@@ -1,4 +1,4 @@
-/*	$NetBSD: npf_parser.c,v 1.6 2011/02/02 02:20:25 rmind Exp $	*/
+/*	$NetBSD: npf_parser.c,v 1.7 2011/11/04 01:00:28 zoltan Exp $	*/
 
 /*-
  * Copyright (c) 2009-2011 The NetBSD Foundation, Inc.
@@ -31,7 +31,7 @@
  */
 
 #include <sys/cdefs.h>
-__RCSID("$NetBSD: npf_parser.c,v 1.6 2011/02/02 02:20:25 rmind Exp $");
+__RCSID("$NetBSD: npf_parser.c,v 1.7 2011/11/04 01:00:28 zoltan Exp $");
 
 #include <stdio.h>
 #include <stdlib.h>
@@ -161,7 +161,9 @@
 	char *iface = npfctl_val_single(v, p);
 	u_int if_idx;
 
-	if (iface == NULL || npfctl_getif(iface, &if_idx, reqaddr) == NULL) {
+	if (iface == NULL ||
+		((npfctl_getif(iface, &if_idx, reqaddr, AF_INET) == NULL) &&
+		 (npfctl_getif(iface, &if_idx, reqaddr, AF_INET6) == NULL))) {
 		errx(EXIT_FAILURE, "invalid interface '%s'", iface);
 	}
 	return if_idx;
@@ -269,6 +271,7 @@
 	u_int if_idx = 0;
 	int ret, attr = 0;
 	nl_rule_t *rl;
+	sa_family_t addrfamily = AF_UNSPEC;
 
 	DPRINTF(("rule\t|%s|\n", buf));
 
@@ -324,10 +327,11 @@
 		PARSE_NEXT_TOKEN();
 	}
 
-	/* inet, inet6 (TODO) */
 	if (strcmp(p, "inet") == 0) {
+		addrfamily = AF_INET;
 		PARSE_NEXT_TOKEN();
 	} else if (strcmp(p, "inet6") == 0) {
+		addrfamily = AF_INET6;
 		PARSE_NEXT_TOKEN();
 	}
 
@@ -361,6 +365,8 @@
 	/* from <addr> port <port | range> */
 	if (strcmp(p, "from") == 0) {
 		PARSE_NEXT_TOKEN();
+		if (addrfamily == AF_UNSPEC)
+			addrfamily = npfctl_get_addrfamily(p);
 		from_v = npfctl_parsevalue(p);
 
 		PARSE_NEXT_TOKEN_NOCHECK();
@@ -375,6 +381,8 @@
 	/* to <addr> port <port | range> */
 	if (p && strcmp(p, "to") == 0) {
 		PARSE_NEXT_TOKEN();
+		if (addrfamily == AF_UNSPEC)
+			addrfamily = npfctl_get_addrfamily(p);
 		to_v = npfctl_parsevalue(p);
 
 		PARSE_NEXT_TOKEN_NOCHECK();
@@ -454,7 +462,7 @@
 	 */
 	rl = npf_rule_create(NULL, attr, if_idx);
 	npfctl_rule_ncode(rl, proto, tcp_flags, icmp_type, icmp_code,
-	    from_v, fports, to_v, tports);
+	    from_v, addrfamily, fports, to_v, tports);
 	if (rproc && npf_rule_setproc(npf_conf, rl, rproc) != 0) {
 		errx(EXIT_FAILURE, "procedure '%s' is not defined", rproc);
 	}
@@ -574,19 +582,16 @@
 		return PARSE_ERR();
 	}
 	PARSE_NEXT_TOKEN_NOCHECK();
-	if (p == NULL || *p != '"') {
+	if (p == NULL) {
 		return PARSE_ERR();
 	}
-	if (strcmp(p, "hash")) {
+	if (strcmp(p, "hash") == 0) {
 		type = NPF_TABLE_HASH;
-	} else if (strcmp(p, "tree")) {
+	} else if (strcmp(p, "tree") == 0) {
 		type = NPF_TABLE_RBTREE;
 	} else {
 		errx(EXIT_FAILURE, "invalid table type '%s'", p);
 	}
-	if ((p = strchr(++p, '"')) == NULL) {
-		return PARSE_ERR();
-	}
 	*p = '\0';
 
 	/*
@@ -631,8 +636,8 @@
 	var_t *ifvar, *from_v, *to_v, *raddr_v;
 	var_t *tports = NULL, *rport_v = NULL;
 	char *p, *sptr, *raddr_s, *rport_s;
-	in_addr_t raddr4, _dummy;
 	npf_addr_t raddr;
+	npf_netmask_t dummy;
 	bool binat, rdr;
 	nl_nat_t *nat;
 	u_int if_idx;
@@ -702,8 +707,7 @@
 	PARSE_NEXT_TOKEN();
 	raddr_v = npfctl_parsevalue(p);
 	raddr_s = npfctl_val_single(raddr_v, p);
-	npfctl_parse_cidr(raddr_s, &raddr4, &_dummy);
-	memcpy(&raddr, &raddr4, sizeof(struct in_addr)); /* XXX IPv6 */
+	npfctl_parse_cidr(raddr_s, npfctl_get_addrfamily(raddr_s), &raddr, &dummy);
 
 	if (rdr) {
 		PARSE_NEXT_TOKEN();
@@ -739,7 +743,7 @@
 		nat = npf_nat_create(NPF_NATIN, NPF_NAT_PORTS,
 		    if_idx, &raddr, AF_INET, rport);
 	}
-	npfctl_rule_ncode(nat, NULL, NULL, -1, -1, from_v, NULL, to_v, tports);
+	npfctl_rule_ncode(nat, NULL, NULL, -1, -1, from_v, AF_INET, NULL, to_v, tports);
 	(void)npf_nat_insert(npf_conf, nat, NPF_PRI_NEXT);
 
 	/*
@@ -751,15 +755,13 @@
 	 */
 	if (binat) {
 		char *taddr_s = npfctl_val_single(from_v, NULL);
-		in_addr_t taddr4;
 		npf_addr_t taddr;
 		nl_nat_t *bn;
 
-		npfctl_parse_cidr(taddr_s, &taddr4, &_dummy);
-		memcpy(&taddr, &taddr4, sizeof(struct in_addr)); /* XXX IPv6 */
+		npfctl_parse_cidr(taddr_s, npfctl_get_addrfamily(taddr_s), &taddr, &dummy);
 		bn = npf_nat_create(NPF_NATIN, 0, if_idx, &taddr, AF_INET, 0);
 		npfctl_rule_ncode(bn, NULL, NULL, -1, -1,
-		    to_v, NULL, raddr_v, NULL);
+		    to_v, AF_INET, NULL, raddr_v, NULL);
 		(void)npf_nat_insert(npf_conf, bn, NPF_PRI_NEXT);
 	}
 	return 0;
--- a/usr.sbin/npf/npfctl/npfctl.c	Fri Nov 04 00:22:33 2011 +0000
+++ b/usr.sbin/npf/npfctl/npfctl.c	Fri Nov 04 01:00:27 2011 +0000
@@ -1,4 +1,4 @@
-/*	$NetBSD: npfctl.c,v 1.6 2011/08/31 13:32:38 joerg Exp $	*/
+/*	$NetBSD: npfctl.c,v 1.7 2011/11/04 01:00:28 zoltan Exp $	*/
 
 /*-
  * Copyright (c) 2009-2011 The NetBSD Foundation, Inc.
@@ -30,7 +30,7 @@
  */
 
 #include <sys/cdefs.h>
-__RCSID("$NetBSD: npfctl.c,v 1.6 2011/08/31 13:32:38 joerg Exp $");
+__RCSID("$NetBSD: npfctl.c,v 1.7 2011/11/04 01:00:28 zoltan Exp $");
 
 #include <sys/ioctl.h>
 #include <sys/stat.h>
@@ -252,7 +252,8 @@
 			tbl.nct_action = 0;
 			arg = argv[3];
 		}
-		if (!npfctl_parse_v4mask(arg,
+		if (!npfctl_parse_cidr(arg,
+		    npfctl_get_addrfamily(arg),
 		    &tbl.nct_addr, &tbl.nct_mask)) {
 			errx(EXIT_FAILURE, "invalid CIDR '%s'", arg);
 		}
@@ -276,7 +277,7 @@
 		}
 		break;
 	}
-	if (ret == -1) {
+	if (ret) {
 		err(EXIT_FAILURE, "ioctl");
 	}
 	close(fd);
--- a/usr.sbin/npf/npfctl/npfctl.h	Fri Nov 04 00:22:33 2011 +0000
+++ b/usr.sbin/npf/npfctl/npfctl.h	Fri Nov 04 01:00:27 2011 +0000
@@ -1,4 +1,4 @@
-/*	$NetBSD: npfctl.h,v 1.6 2011/02/02 02:20:25 rmind Exp $	*/
+/*	$NetBSD: npfctl.h,v 1.7 2011/11/04 01:00:28 zoltan Exp $	*/
 
 /*-
  * Copyright (c) 2009-2011 The NetBSD Foundation, Inc.
@@ -74,22 +74,25 @@
 void		npfctl_init_data(void);
 int		npfctl_ioctl_send(int);
 
-struct ifaddrs *npfctl_getif(char *, unsigned int *, bool);
-bool		npfctl_parse_v4mask(char *, in_addr_t *, in_addr_t *);
-void		npfctl_parse_cidr(char *, in_addr_t *, in_addr_t *);
+struct ifaddrs *npfctl_getif(char *, unsigned int *, bool, sa_family_t);
+void		npfctl_create_mask(sa_family_t, u_int, npf_addr_t *);
+sa_family_t	npfctl_get_addrfamily(const char *);
+sa_family_t	npfctl_parse_cidr(char *, sa_family_t, npf_addr_t *, npf_netmask_t *);
 bool		npfctl_parse_port(char *, bool *, in_port_t *, in_port_t *);
 
 void		npfctl_fill_table(nl_table_t *, char *);
 
 void		npfctl_rule_ncode(nl_rule_t *, char *, char *,
-		    int, int, var_t *, var_t *, var_t *, var_t *);
+		    int, int, var_t *, sa_family_t, var_t *, var_t *, var_t *);
 
 size_t		npfctl_calc_ncsize(int []);
 size_t		npfctl_failure_offset(int []);
 
 void		npfctl_gennc_ether(void **, int, uint16_t);
 void		npfctl_gennc_v4cidr(void **, int,
-		    in_addr_t, in_addr_t, bool);
+		    const npf_addr_t *, const npf_netmask_t, bool);
+void		npfctl_gennc_v6cidr(void **, int,
+		    const npf_addr_t *, const npf_netmask_t, bool);
 void		npfctl_gennc_icmp(void **, int, int, int);
 void		npfctl_gennc_tcpfl(void **, int , uint8_t, uint8_t);
 void		npfctl_gennc_ports(void **, int,