Pull up following revision(s) (requested by rmind in ticket #354): netbsd-6
authorriz <riz@NetBSD.org>
Tue, 26 Jun 2012 00:07:16 +0000
branchnetbsd-6
changeset 255901 477bbfa0efcd
parent 255900 a08c1d7bb839
child 255902 371d680e746b
Pull up following revision(s) (requested by rmind in ticket #354): sys/net/npf/npf_state_tcp.c: revision 1.4 sys/net/npf/npf_state_tcp.c: revision 1.5 sys/net/npf/npf_state_tcp.c: revision 1.6 usr.sbin/npf/npftest/npftest.c: revision 1.1 usr.sbin/npf/npftest/libnpftest/npf_mbuf_subr.c: revision 1.1 usr.sbin/npf/npftest/npftest.c: revision 1.2 usr.sbin/npf/npftest/libnpftest/npf_mbuf_subr.c: revision 1.2 usr.sbin/npf/npfctl/npf_data.c: revision 1.11 usr.sbin/npf/npftest/npftest.c: revision 1.3 usr.sbin/npf/npfctl/npf_data.c: revision 1.12 usr.sbin/npf/npftest/npftest.h: revision 1.1 usr.sbin/npf/npfctl/npf_parse.y: revision 1.5 usr.sbin/npf/npfctl/npf_data.c: revision 1.13 sys/net/npf/npf.h: revision 1.16 usr.sbin/npf/npftest/npftest.h: revision 1.2 usr.sbin/npf/npfctl/npf_parse.y: revision 1.6 usr.sbin/npf/npftest/npftest.h: revision 1.3 usr.sbin/npf/npfctl/npf_parse.y: revision 1.7 usr.sbin/npf/npfctl/npf_ncgen.c: revision 1.10 usr.sbin/npf/npfctl/npf_build.c: revision 1.6 usr.sbin/npf/npfctl/npf_parse.y: revision 1.8 usr.sbin/npf/npfctl/npf_build.c: revision 1.7 usr.sbin/npf/npftest/libnpftest/npf_state_test.c: revision 1.1 usr.sbin/npf/npftest/libnpftest/npf_nbuf_test.c: revision 1.1 usr.sbin/npf/npfctl/npf_build.c: revision 1.8 usr.sbin/npf/npftest/libnpftest/npf_table_test.c: revision 1.1 usr.sbin/npf/npfctl/npf_build.c: revision 1.9 usr.sbin/npf/npfctl/npf.conf.5: revision 1.10 usr.sbin/npf/npfctl/npf.conf.5: revision 1.11 usr.sbin/npf/npfctl/npf.conf.5: revision 1.12 sys/net/npf/npf_state.c: revision 1.7 usr.sbin/npf/npfctl/npfctl.c: revision 1.11 usr.sbin/npf/npfctl/npfctl.c: revision 1.12 usr.sbin/npf/npfctl/Makefile: revision 1.7 sys/rump/net/lib/libnet/Makefile: revision 1.14 sys/net/npf/npf_mbuf.c: revision 1.7 usr.sbin/npf/npftest/Makefile: revision 1.1 usr.sbin/npf/npftest/Makefile: revision 1.2 usr.sbin/npf/npftest/libnpftest/Makefile: revision 1.1 usr.sbin/npf/npfctl/npf_scan.l: revision 1.2 usr.sbin/npf/npftest/npfstream.c: revision 1.1 usr.sbin/npf/npftest/libnpftest/Makefile: revision 1.2 usr.sbin/npf/npfctl/npf_scan.l: revision 1.3 usr.sbin/npf/npftest/libnpftest/Makefile: revision 1.3 usr.sbin/npf/npfctl/npfctl.h: revision 1.12 sys/rump/dev/lib/libnpf/Makefile: revision 1.2 usr.sbin/npf/npfctl/npfctl.h: revision 1.14 sys/rump/dev/lib/libnpf/Makefile: revision 1.3 usr.sbin/npf/npfctl/npfctl.h: revision 1.15 usr.sbin/npf/npfctl/npf_ncgen.c: revision 1.9 sys/net/npf/npf_ctl.c: revision 1.15 usr.sbin/npf/npfctl/npf_var.c: revision 1.4 usr.sbin/npf/npfctl/npf_var.h: revision 1.2 usr.sbin/npf/npfctl/npf_var.c: revision 1.5 sys/net/npf/npf_impl.h: revision 1.13 sys/net/npf/npf_sendpkt.c: revision 1.10 sys/net/npf/npf_impl.h: revision 1.14 usr.sbin/npf/npfctl/npf_disassemble.c: revision 1.4 sys/net/npf/npf_impl.h: revision 1.15 sys/net/npf/npf_handler.c: revision 1.16 usr.sbin/npf/npftest/libnpftest/npf_test.h: revision 1.1 usr.sbin/npf/npftest/libnpftest/npf_processor_test.c: revision 1.1 usr.sbin/npf/npfctl/npf_disassemble.c: revision 1.5 sys/net/npf/npf_handler.c: revision 1.17 usr.sbin/npf/npftest/libnpftest/npf_test.h: revision 1.2 sys/net/npf/npf_ncode.h: revision 1.7 usr.sbin/npf/npftest/libnpftest/npf_test_subr.c: revision 1.1 usr.sbin/npf/npftest/libnpftest/npf_test.h: revision 1.3 sys/net/npf/npf_ncode.h: revision 1.8 npf_tcp_inwindow: in a case of negative skew, bump the maximum seen value of SEQ+LEN in the receiver's side correctly (using ACK from the sender's side). PR/46265 from Changli Gao. rumpnet_net: add pfil.c Update rumpdev_npf; use WARNS=4. Add initial NPF regression tests integrated with RUMP framework (running the kernel part of NPF in userland). Other tests will be added once converted to RUMP framework. All tests are in the public domain. Some Makefile fixes from christos@. - Fix double-free case on ICMP return case. - npf_pfil_register: handle kernels without INET6 option correctly. - Reduce some #ifdefs. npfctl(8): add show-config command. Also, update syntax. npftest: add a stream processor, which prints out the TCP state information. A tool for debugging connection tracking from tcpdump -w captured data. npftest: add a module for TCP state tracking and add few test cases. npf_state_tcp: add an assert; fix some comments while here. - Rework NPF NAT syntax to be more structured and support future additions of different types and configurations of NAT. - npfctl: improve disassemble and show-config command functionality. - Fix custom ICMP code and type filtering. make this compile again. remove error(1) output Remove superfluous Pp - make each element of a variable hold a type - change get_type to take an index, so we can get the individual types of each element (since primitive elements can be in lists) - make port_range primitive - add a routine to convert a variable of primitives to a variable containing - only port ranges. remove extra rule that got merged...
sys/net/npf/npf.h
sys/net/npf/npf_ctl.c
sys/net/npf/npf_handler.c
sys/net/npf/npf_impl.h
sys/net/npf/npf_mbuf.c
sys/net/npf/npf_ncode.h
sys/net/npf/npf_sendpkt.c
sys/net/npf/npf_state.c
sys/net/npf/npf_state_tcp.c
sys/rump/dev/lib/libnpf/Makefile
sys/rump/net/lib/libnet/Makefile
usr.sbin/npf/npfctl/Makefile
usr.sbin/npf/npfctl/npf.conf.5
usr.sbin/npf/npfctl/npf_build.c
usr.sbin/npf/npfctl/npf_data.c
usr.sbin/npf/npfctl/npf_disassemble.c
usr.sbin/npf/npfctl/npf_ncgen.c
usr.sbin/npf/npfctl/npf_parse.y
usr.sbin/npf/npfctl/npf_scan.l
usr.sbin/npf/npfctl/npf_var.c
usr.sbin/npf/npfctl/npf_var.h
usr.sbin/npf/npfctl/npfctl.c
usr.sbin/npf/npfctl/npfctl.h
usr.sbin/npf/npftest/Makefile
usr.sbin/npf/npftest/libnpftest/Makefile
usr.sbin/npf/npftest/libnpftest/npf_mbuf_subr.c
usr.sbin/npf/npftest/libnpftest/npf_nbuf_test.c
usr.sbin/npf/npftest/libnpftest/npf_processor_test.c
usr.sbin/npf/npftest/libnpftest/npf_state_test.c
usr.sbin/npf/npftest/libnpftest/npf_table_test.c
usr.sbin/npf/npftest/libnpftest/npf_test.h
usr.sbin/npf/npftest/libnpftest/npf_test_subr.c
usr.sbin/npf/npftest/npfstream.c
usr.sbin/npf/npftest/npftest.c
usr.sbin/npf/npftest/npftest.h
--- a/sys/net/npf/npf.h	Mon Jun 25 00:43:40 2012 +0000
+++ b/sys/net/npf/npf.h	Tue Jun 26 00:07:16 2012 +0000
@@ -1,4 +1,4 @@
-/*	$NetBSD: npf.h,v 1.14.2.1 2012/04/03 17:22:52 riz Exp $	*/
+/*	$NetBSD: npf.h,v 1.14.2.2 2012/06/26 00:07:16 riz Exp $	*/
 
 /*-
  * Copyright (c) 2009-2011 The NetBSD Foundation, Inc.
@@ -45,10 +45,6 @@
 #include <netinet/in_systm.h>
 #include <netinet/in.h>
 
-#ifdef _NPF_TESTING
-#include "testing.h"
-#endif
-
 #define	NPF_VERSION		4
 
 /*
@@ -62,7 +58,7 @@
 #define	NPF_MAX_NETMASK		(128)
 #define	NPF_NO_NETMASK		((npf_netmask_t)~0)
 
-#if defined(_KERNEL) || defined(_NPF_TESTING)
+#if defined(_KERNEL)
 
 /* Network buffer. */
 typedef void			nbuf_t;
--- a/sys/net/npf/npf_ctl.c	Mon Jun 25 00:43:40 2012 +0000
+++ b/sys/net/npf/npf_ctl.c	Tue Jun 26 00:07:16 2012 +0000
@@ -1,4 +1,4 @@
-/*	$NetBSD: npf_ctl.c,v 1.12.2.1 2012/04/03 17:22:53 riz Exp $	*/
+/*	$NetBSD: npf_ctl.c,v 1.12.2.2 2012/06/26 00:07:16 riz Exp $	*/
 
 /*-
  * Copyright (c) 2009-2012 The NetBSD Foundation, Inc.
@@ -37,7 +37,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: npf_ctl.c,v 1.12.2.1 2012/04/03 17:22:53 riz Exp $");
+__KERNEL_RCSID(0, "$NetBSD: npf_ctl.c,v 1.12.2.2 2012/06/26 00:07:16 riz Exp $");
 
 #include <sys/param.h>
 #include <sys/conf.h>
@@ -434,15 +434,14 @@
 	int error;
 
 	/* Retrieve the dictionary. */
-#ifdef _KERNEL
+#ifndef _NPF_TESTING
 	error = prop_dictionary_copyin_ioctl(pref, cmd, &npf_dict);
 	if (error)
 		return error;
 #else
-	npf_dict = prop_dictionary_internalize_from_file(data);
-	if (npf_dict == NULL)
-		return EINVAL;
+	npf_dict = (prop_dictionary_t)pref;
 #endif
+
 	/* Dictionary for error reporting. */
 	errdict = prop_dictionary_create();
 
@@ -507,7 +506,7 @@
 
 	/* Error report. */
 	prop_dictionary_set_int32(errdict, "errno", error);
-#ifdef _KERNEL
+#ifndef _NPF_TESTING
 	prop_dictionary_copyout_ioctl(pref, cmd, errdict);
 #endif
 	prop_object_release(errdict);
@@ -544,17 +543,11 @@
 	const char *name;
 	int error;
 
-#ifdef _KERNEL
 	/* Retrieve and construct the rule. */
 	error = prop_dictionary_copyin_ioctl(pref, cmd, &dict);
 	if (error) {
 		return error;
 	}
-#else
-	dict = prop_dictionary_internalize_from_file(data);
-	if (dict == NULL)
-		return EINVAL;
-#endif
 
 	/* Dictionary for error reporting. */
 	errdict = prop_dictionary_create();
@@ -580,9 +573,7 @@
 
 	/* Error report. */
 	prop_dictionary_set_int32(errdict, "errno", error);
-#ifdef _KERNEL
 	prop_dictionary_copyout_ioctl(pref, cmd, errdict);
-#endif
 	prop_object_release(errdict);
 	return error;
 }
@@ -612,11 +603,7 @@
 	/* Set the session list, NAT policy list and export the dictionary. */
 	prop_dictionary_set(sesdict, "session-list", selist);
 	prop_dictionary_set(sesdict, "nat-policy-list", nplist);
-#ifdef _KERNEL
 	error = prop_dictionary_copyout_ioctl(pref, cmd, sesdict);
-#else
-	error = prop_dictionary_externalize_to_file(sesdict, data) ? 0 : errno;
-#endif
 fail:
 	prop_object_release(sesdict);
 	return error;
@@ -636,15 +623,10 @@
 	int error;
 
 	/* Retrieve the dictionary containing session and NAT policy lists. */
-#ifdef _KERNEL
 	error = prop_dictionary_copyin_ioctl(pref, cmd, &sesdict);
 	if (error)
 		return error;
-#else
-	sesdict = prop_dictionary_internalize_from_file(data);
-	if (sesdict == NULL)
-		return EINVAL;
-#endif
+
 	/*
 	 * Note: session objects contain the references to the NAT policy
 	 * entries.  Therefore, no need to directly access it.
--- a/sys/net/npf/npf_handler.c	Mon Jun 25 00:43:40 2012 +0000
+++ b/sys/net/npf/npf_handler.c	Tue Jun 26 00:07:16 2012 +0000
@@ -1,4 +1,4 @@
-/*	$NetBSD: npf_handler.c,v 1.13.2.1 2012/04/03 17:22:53 riz Exp $	*/
+/*	$NetBSD: npf_handler.c,v 1.13.2.2 2012/06/26 00:07:16 riz Exp $	*/
 
 /*-
  * Copyright (c) 2009-2012 The NetBSD Foundation, Inc.
@@ -34,7 +34,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: npf_handler.c,v 1.13.2.1 2012/04/03 17:22:53 riz Exp $");
+__KERNEL_RCSID(0, "$NetBSD: npf_handler.c,v 1.13.2.2 2012/06/26 00:07:16 riz Exp $");
 
 #include <sys/types.h>
 #include <sys/param.h>
@@ -61,8 +61,6 @@
 static struct pfil_head *	npf_ph_inet = NULL;
 static struct pfil_head *	npf_ph_inet6 = NULL;
 
-int	npf_packet_handler(void *, struct mbuf **, ifnet_t *, int);
-
 /*
  * npf_ifhook: hook handling interface changes.
  */
@@ -236,17 +234,20 @@
 	 * Depending on the flags and protocol, return TCP reset (RST) or
 	 * ICMP destination unreachable.
 	 */
-	if (retfl) {
-		npf_return_block(&npc, nbuf, retfl);
+	if (retfl && npf_return_block(&npc, nbuf, retfl)) {
+		*mp = NULL;
 	}
+
 	if (error) {
 		npf_stats_inc(NPF_STAT_ERROR);
 	} else {
 		error = ENETUNREACH;
 	}
-	m_freem(*mp);
-	*mp = NULL;
 
+	if (*mp) {
+		m_freem(*mp);
+		*mp = NULL;
+	}
 	return error;
 }
 
@@ -271,7 +272,7 @@
 	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_inet6 == NULL) {
+	if (!npf_ph_if || (!npf_ph_inet && !npf_ph_inet6)) {
 		npf_ph_if = NULL;
 		error = ENOENT;
 		goto fail;
@@ -283,13 +284,16 @@
 	KASSERT(error == 0);
 
 	/* Packet IN/OUT handler on all interfaces and IP layer. */
-	error = pfil_add_hook(npf_packet_handler, NULL,
-	    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);
+	if (npf_ph_inet) {
+		error = pfil_add_hook(npf_packet_handler, NULL,
+		    PFIL_WAITOK | PFIL_ALL, npf_ph_inet);
+		KASSERT(error == 0);
+	}
+	if (npf_ph_inet6) {
+		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);
@@ -308,15 +312,19 @@
 	KERNEL_LOCK(1, NULL);
 
 	if (npf_ph_if) {
-		(void)pfil_remove_hook(npf_packet_handler, NULL,
-		    PFIL_ALL, npf_ph_inet6);
+		(void)pfil_remove_hook(npf_ifhook, NULL,
+		    PFIL_IFADDR | PFIL_IFNET, npf_ph_if);
+	}
+	if (npf_ph_inet) {
 		(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);
+	}
+	if (npf_ph_inet6) {
+		(void)pfil_remove_hook(npf_packet_handler, NULL,
+		    PFIL_ALL, npf_ph_inet6);
+	}
 
-		npf_ph_if = NULL;
-	}
+	npf_ph_if = NULL;
 
 	KERNEL_UNLOCK_ONE(NULL);
 	mutex_exit(softnet_lock);
--- a/sys/net/npf/npf_impl.h	Mon Jun 25 00:43:40 2012 +0000
+++ b/sys/net/npf/npf_impl.h	Tue Jun 26 00:07:16 2012 +0000
@@ -1,4 +1,4 @@
-/*	$NetBSD: npf_impl.h,v 1.10.2.1 2012/04/03 17:22:53 riz Exp $	*/
+/*	$NetBSD: npf_impl.h,v 1.10.2.2 2012/06/26 00:07:16 riz Exp $	*/
 
 /*-
  * Copyright (c) 2009-2012 The NetBSD Foundation, Inc.
@@ -37,7 +37,7 @@
 #ifndef _NPF_IMPL_H_
 #define _NPF_IMPL_H_
 
-#if !defined(_KERNEL) && !defined(_NPF_TESTING)
+#if !defined(_KERNEL)
 #error "Kernel-level header only"
 #endif
 
@@ -51,10 +51,6 @@
 #include "npf.h"
 #include "npf_ncode.h"
 
-#ifdef _NPF_TESTING
-#include "testing.h"
-#endif
-
 #ifdef _NPF_DEBUG
 #define	NPF_PRINTF(x)	printf x
 #else
@@ -119,6 +115,13 @@
 	npf_tcpstate_t	nst_tcpst[2];
 } npf_state_t;
 
+#if defined(_NPF_TESTING)
+void		npf_state_sample(npf_state_t *, bool);
+#define	NPF_TCP_STATE_SAMPLE(n, r)	npf_state_sample(n, r)
+#else
+#define	NPF_TCP_STATE_SAMPLE(n, r)
+#endif
+
 /*
  * INTERFACES.
  */
@@ -153,6 +156,7 @@
 int		npf_pfil_register(void);
 void		npf_pfil_unregister(void);
 bool		npf_pfil_registered_p(void);
+int		npf_packet_handler(void *, struct mbuf **, ifnet_t *, int);
 void		npf_log_packet(npf_cache_t *, nbuf_t *, int);
 
 /* Protocol helpers. */
@@ -177,7 +181,7 @@
 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);
-void		npf_return_block(npf_cache_t *, nbuf_t *, const int);
+bool		npf_return_block(npf_cache_t *, nbuf_t *, const int);
 
 /* Complex instructions. */
 int		npf_match_ether(nbuf_t *, int, int, uint16_t, uint32_t *);
--- a/sys/net/npf/npf_mbuf.c	Mon Jun 25 00:43:40 2012 +0000
+++ b/sys/net/npf/npf_mbuf.c	Tue Jun 26 00:07:16 2012 +0000
@@ -1,4 +1,4 @@
-/*	$NetBSD: npf_mbuf.c,v 1.6 2011/01/18 20:33:46 rmind Exp $	*/
+/*	$NetBSD: npf_mbuf.c,v 1.6.14.1 2012/06/26 00:07:16 riz Exp $	*/
 
 /*-
  * Copyright (c) 2009-2011 The NetBSD Foundation, Inc.
@@ -37,7 +37,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: npf_mbuf.c,v 1.6 2011/01/18 20:33:46 rmind Exp $");
+__KERNEL_RCSID(0, "$NetBSD: npf_mbuf.c,v 1.6.14.1 2012/06/26 00:07:16 riz Exp $");
 
 #include <sys/param.h>
 #include <sys/mbuf.h>
@@ -117,7 +117,7 @@
 
 	/* Current offset in mbuf. */
 	off = (uintptr_t)n_ptr - mtod(m, uintptr_t);
-	KASSERT(off < m->m_len);
+	KASSERT(off < (u_int)m->m_len);
 	wmark = m->m_len;
 
 	/* Is datum overlapping? */
@@ -153,7 +153,7 @@
 		off = 0;
 	}
 	KASSERT(n_ptr == d || mtod(m, uint8_t *) == d);
-	KASSERT(len <= m->m_len);
+	KASSERT(len <= (u_int)m->m_len);
 
 	/* Non-overlapping case: fetch the actual data. */
 	if (wr == NBUF_DATA_WRITE) {
--- a/sys/net/npf/npf_ncode.h	Mon Jun 25 00:43:40 2012 +0000
+++ b/sys/net/npf/npf_ncode.h	Tue Jun 26 00:07:16 2012 +0000
@@ -1,4 +1,4 @@
-/*	$NetBSD: npf_ncode.h,v 1.5.6.1 2012/04/03 17:22:53 riz Exp $	*/
+/*	$NetBSD: npf_ncode.h,v 1.5.6.2 2012/06/26 00:07:16 riz Exp $	*/
 
 /*-
  * Copyright (c) 2009-2010 The NetBSD Foundation, Inc.
@@ -41,7 +41,7 @@
 
 #include "npf.h"
 
-#if defined(_KERNEL) || defined(_NPF_TESTING)
+#if defined(_KERNEL)
 /*
  * N-code processing, validation & building.
  */
@@ -143,9 +143,9 @@
 # define	NPF_OPERAND_PORT_RANGE		14
 
 static const struct npf_instruction {
-	const char *name;
-	uint8_t op[4];
-} npf_instructions[256] = {
+	const char *	name;
+	uint8_t		op[4];
+} npf_instructions[] = {
 	[NPF_OPCODE_RET] = {
 		.name = "ret",
 		.op = {
@@ -247,7 +247,8 @@
 			[1] = NPF_OPERAND_REGISTER,
 		},
 	},
-	[NPF_OPCODE_DIV] = { .name = "div",
+	[NPF_OPCODE_DIV] = {
+		.name = "div",
 		.op = {
 			[0] = NPF_OPERAND_VALUE,
 			[1] = NPF_OPERAND_REGISTER,
--- a/sys/net/npf/npf_sendpkt.c	Mon Jun 25 00:43:40 2012 +0000
+++ b/sys/net/npf/npf_sendpkt.c	Tue Jun 26 00:07:16 2012 +0000
@@ -1,4 +1,4 @@
-/*	$NetBSD: npf_sendpkt.c,v 1.8.4.1 2012/04/03 17:22:53 riz Exp $	*/
+/*	$NetBSD: npf_sendpkt.c,v 1.8.4.2 2012/06/26 00:07:16 riz Exp $	*/
 
 /*-
  * Copyright (c) 2010-2011 The NetBSD Foundation, Inc.
@@ -34,7 +34,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: npf_sendpkt.c,v 1.8.4.1 2012/04/03 17:22:53 riz Exp $");
+__KERNEL_RCSID(0, "$NetBSD: npf_sendpkt.c,v 1.8.4.2 2012/06/26 00:07:16 riz Exp $");
 
 #include <sys/param.h>
 #include <sys/types.h>
@@ -54,6 +54,12 @@
 
 #define	DEFAULT_IP_TTL		(ip_defttl)
 
+#ifndef INET6
+#define	in6_cksum(...)		0
+#define	ip6_output(...)		0
+#define	icmp6_error(m, ...)	m_freem(m)
+#endif
+
 /*
  * npf_return_tcp: return a TCP reset (RST) packet.
  */
@@ -81,9 +87,10 @@
 	/* Create and setup a network buffer. */
 	if (npf_iscached(npc, NPC_IP4)) {
 		len = sizeof(struct ip) + sizeof(struct tcphdr);
+	} else if (npf_iscached(npc, NPC_IP6)) {
+		len = sizeof(struct ip6_hdr) + sizeof(struct tcphdr);
 	} else {
-		KASSERT(npf_iscached(npc, NPC_IP6));
-		len = sizeof(struct ip6_hdr) + sizeof(struct tcphdr);
+		return EINVAL;
 	}
 
 	m = m_gethdr(M_DONTWAIT, MT_HEADER);
@@ -149,24 +156,15 @@
 		ip->ip_ttl = DEFAULT_IP_TTL;
 	} else {
 		KASSERT(npf_iscached(npc, NPC_IP6));
-#ifdef INET6
 		th->th_sum = in6_cksum(m, IPPROTO_TCP, sizeof(struct ip6_hdr),
 		    sizeof(struct tcphdr));
-#else
-		KASSERT(false);
-#endif
 	}
 
 	/* Pass to IP layer. */
 	if (npf_iscached(npc, NPC_IP4)) {
 		return ip_output(m, NULL, NULL, IP_FORWARDING, NULL, NULL);
-	} else {
-#ifdef INET6
-		return ip6_output(m, NULL, NULL, IPV6_FORWARDING, NULL, NULL, NULL);
-#else
-		return 0;
-#endif
 	}
+	return ip6_output(m, NULL, NULL, IPV6_FORWARDING, NULL, NULL, NULL);
 }
 
 /*
@@ -179,40 +177,41 @@
 
 	if (npf_iscached(npc, NPC_IP4)) {
 		icmp_error(m, ICMP_UNREACH, ICMP_UNREACH_ADMIN_PROHIBIT, 0, 0);
-	} else {
-		KASSERT(npf_iscached(npc, NPC_IP6));
-#ifdef INET6
+		return 0;
+	} else if (npf_iscached(npc, NPC_IP6)) {
 		icmp6_error(m, ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_ADMIN, 0);
-#endif
+		return 0;
 	}
-	return 0;
+	return EINVAL;
 }
 
 /*
  * npf_return_block: return TCP reset or ICMP host unreachable packet.
- * TODO: user should be able to specify exact ICMP error codes in config
+ *
+ * => Returns true if the buffer was consumed (freed) and false otherwise.
  */
-void
+bool
 npf_return_block(npf_cache_t *npc, nbuf_t *nbuf, const int retfl)
 {
 	void *n_ptr = nbuf_dataptr(nbuf);
 
 	if (!npf_iscached(npc, NPC_IP46) && !npf_fetch_ip(npc, nbuf, n_ptr)) {
-		return;
+		return false;
 	}
 	switch (npf_cache_ipproto(npc)) {
 	case IPPROTO_TCP:
 		if (retfl & NPF_RULE_RETRST) {
 			if (!npf_fetch_tcp(npc, nbuf, n_ptr)) {
-				return;
+				return false;
 			}
 			(void)npf_return_tcp(npc);
 		}
 		break;
 	case IPPROTO_UDP:
-		if (retfl & NPF_RULE_RETICMP) {
-			(void)npf_return_icmp(npc, nbuf);
-		}
+		if (retfl & NPF_RULE_RETICMP)
+			if (npf_return_icmp(npc, nbuf) == 0)
+				return true;
 		break;
 	}
+	return false;
 }
--- a/sys/net/npf/npf_state.c	Mon Jun 25 00:43:40 2012 +0000
+++ b/sys/net/npf/npf_state.c	Tue Jun 26 00:07:16 2012 +0000
@@ -1,4 +1,4 @@
-/*	$NetBSD: npf_state.c,v 1.6 2011/11/29 20:05:30 rmind Exp $	*/
+/*	$NetBSD: npf_state.c,v 1.6.4.1 2012/06/26 00:07:16 riz Exp $	*/
 
 /*-
  * Copyright (c) 2010-2011 The NetBSD Foundation, Inc.
@@ -34,7 +34,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: npf_state.c,v 1.6 2011/11/29 20:05:30 rmind Exp $");
+__KERNEL_RCSID(0, "$NetBSD: npf_state.c,v 1.6.4.1 2012/06/26 00:07:16 riz Exp $");
 
 #include <sys/param.h>
 #include <sys/systm.h>
@@ -134,6 +134,7 @@
 	default:
 		ret = false;
 	}
+	NPF_TCP_STATE_SAMPLE(nst, ret);
 	mutex_exit(&nst->nst_lock);
 
 	if (__predict_false(!ret)) {
--- a/sys/net/npf/npf_state_tcp.c	Mon Jun 25 00:43:40 2012 +0000
+++ b/sys/net/npf/npf_state_tcp.c	Tue Jun 26 00:07:16 2012 +0000
@@ -1,4 +1,4 @@
-/*	$NetBSD: npf_state_tcp.c,v 1.3 2011/12/08 23:36:57 rmind Exp $	*/
+/*	$NetBSD: npf_state_tcp.c,v 1.3.2.1 2012/06/26 00:07:16 riz Exp $	*/
 
 /*-
  * Copyright (c) 2010-2011 The NetBSD Foundation, Inc.
@@ -34,7 +34,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: npf_state_tcp.c,v 1.3 2011/12/08 23:36:57 rmind Exp $");
+__KERNEL_RCSID(0, "$NetBSD: npf_state_tcp.c,v 1.3.2.1 2012/06/26 00:07:16 riz Exp $");
 
 #include <sys/param.h>
 #include <sys/types.h>
@@ -50,18 +50,11 @@
 
 #include "npf_impl.h"
 
-#if defined(_NPF_TESTING)
-void	npf_state_sample(npf_state_t *);
-#define	NPF_TCP_STATE_SAMPLE(nst)	npf_state_sample(nst)
-#else
-#define	NPF_TCP_STATE_SAMPLE(nst)
-#endif
-
 /*
  * NPF TCP states.  Note: these states are different from the TCP FSM
- * states of RFC 793.  Mind that packet filter is a man-in-the-middle.
+ * states of RFC 793.  The packet filter is a man-in-the-middle.
  */
-#define NPF_TCPS_OK		(-1)
+#define	NPF_TCPS_OK		(-1)
 #define	NPF_TCPS_CLOSED		0
 #define	NPF_TCPS_SYN_SENT	1
 #define	NPF_TCPS_SIMSYN_SENT	2
@@ -337,8 +330,9 @@
 	 */
 	if (__predict_false(fstate->nst_maxwin == 0)) {
 		/*
-		 * Should be first SYN or re-transmission of SYN.  State of
-		 * other side will get set with a SYN-ACK reply (see below).
+		 * Normally, it should be the first SYN or a re-transmission
+		 * of SYN.  The state of the other side will get set with a
+		 * SYN-ACK reply (see below).
 		 */
 		fstate->nst_end = end;
 		fstate->nst_maxend = end;
@@ -377,6 +371,7 @@
 			    wscale : 0;
 		}
 	}
+
 	if ((tcpfl & TH_ACK) == 0) {
 		/* Pretend that an ACK was sent. */
 		ack = tstate->nst_end;
@@ -389,11 +384,9 @@
 		end = fstate->nst_end;
 		seq = end;
 	}
-
-	NPF_TCP_STATE_SAMPLE(nst);
 #if 0
 	/* Strict in-order sequence for RST packets. */
-	if (((tcpfl & TH_RST) != 0) && (fstate->nst_end - seq) > 1) {
+	if ((tcpfl & TH_RST) != 0 && (fstate->nst_end - seq) > 1) {
 		return false;
 	}
 #endif
@@ -413,7 +406,7 @@
 	}
 
 	/*
-	 * Boundaries for valid acknowledgments (III, IV) - on predicted
+	 * Boundaries for valid acknowledgments (III, IV) - one predicted
 	 * window up or down, since packets may be fragmented.
 	 */
 	ackskew = tstate->nst_end - ack;
@@ -429,8 +422,9 @@
 	 * Negative ackskew might be due to fragmented packets.  Since the
 	 * total length of the packet is unknown - bump the boundary.
 	 */
+
 	if (ackskew < 0) {
-		tstate->nst_end = end;
+		tstate->nst_end = ack;
 	}
 	/* Keep track of the maximum window seen. */
 	if (fstate->nst_maxwin < win) {
@@ -453,6 +447,8 @@
 	const int tcpfl = th->th_flags, state = nst->nst_state;
 	int nstate;
 
+	KASSERT(mutex_owned(&nst->nst_lock));
+
 	/* Look for a transition to a new state. */
 	if (__predict_true((tcpfl & TH_RST) == 0)) {
 		const int flagcase = npf_tcpfl2case(tcpfl);
@@ -463,6 +459,7 @@
 	} else {
 		nstate = NPF_TCPS_CLOSED;
 	}
+
 	/* Determine whether TCP packet really belongs to this connection. */
 	if (!npf_tcp_inwindow(npc, nbuf, nst, di)) {
 		return false;
@@ -470,6 +467,7 @@
 	if (__predict_true(nstate == NPF_TCPS_OK)) {
 		return true;
 	}
+
 	nst->nst_state = nstate;
 	return true;
 }
--- a/sys/rump/dev/lib/libnpf/Makefile	Mon Jun 25 00:43:40 2012 +0000
+++ b/sys/rump/dev/lib/libnpf/Makefile	Tue Jun 26 00:07:16 2012 +0000
@@ -1,16 +1,22 @@
-#	$NetBSD: Makefile,v 1.1 2010/10/14 22:39:30 haad Exp $
+#	$NetBSD: Makefile,v 1.1.18.1 2012/06/26 00:07:21 riz Exp $
+#
+# Public Domain.
 #
 
 .PATH:	${.CURDIR}/../../../../net/npf
 
 LIB=	rumpdev_npf
 
-SRCS=	npf.c npf_ctl.c npf_handler.c npf_instr.c npf_mbuf.c
-SRCS+=	npf_processor.c npf_ruleset.c npf_tableset.c npf_inet.c
-SRCS+=	npf_session.c npf_nat.c npf_alg.c
+SRCS=	npf.c npf_alg.c npf_ctl.c npf_handler.c
+SRCS+=	npf_inet.c npf_instr.c npf_log.c npf_mbuf.c npf_nat.c
+SRCS+=	npf_processor.c npf_ruleset.c npf_rproc.c npf_sendpkt.c
+SRCS+=	npf_session.c npf_state.c npf_state_tcp.c npf_tableset.c
 
 SRCS+=	component.c
 
+WARNS=	4
+
+CPPFLAGS+=	-D_NPF_TESTING
 CPPFLAGS+=	-I${.CURDIR}/../../../librump/rumpvfs
 
 .include <bsd.lib.mk>
--- a/sys/rump/net/lib/libnet/Makefile	Mon Jun 25 00:43:40 2012 +0000
+++ b/sys/rump/net/lib/libnet/Makefile	Tue Jun 26 00:07:16 2012 +0000
@@ -1,4 +1,4 @@
-#	$NetBSD: Makefile,v 1.13 2011/09/24 21:11:23 christos Exp $
+#	$NetBSD: Makefile,v 1.13.8.1 2012/06/26 00:07:20 riz Exp $
 #
 
 .PATH:	${.CURDIR}/../../../../net ${.CURDIR}/../../../../compat/common
@@ -8,7 +8,7 @@
 # iffy stuff
 SRCS=	if.c if_loop.c route.c rtbl.c rtsock.c rtsock_50.c raw_usrreq.c	\
 	raw_cb.c if_media.c link_proto.c net_stats.c if_ethersubr.c rfc6056.c
-SRCS+=	if_43.c uipc_syscalls_50.c
+SRCS+=	if_43.c pfil.c uipc_syscalls_50.c
 SRCS+=	component.c
 
 CPPFLAGS+=	-I${.CURDIR}/opt -I${.CURDIR}/../libnetinet/opt
--- a/usr.sbin/npf/npfctl/Makefile	Mon Jun 25 00:43:40 2012 +0000
+++ b/usr.sbin/npf/npfctl/Makefile	Tue Jun 26 00:07:16 2012 +0000
@@ -1,4 +1,4 @@
-# $NetBSD: Makefile,v 1.5.2.1 2012/04/03 17:22:54 riz Exp $
+# $NetBSD: Makefile,v 1.5.2.2 2012/06/26 00:07:20 riz Exp $
 
 PROG=		npfctl
 MAN=		npfctl.8 npf.conf.5
@@ -9,7 +9,6 @@
 CPPFLAGS+=	-I${.CURDIR}
 SRCS+=		npf_scan.l npf_parse.y
 YHEADER=	1
-YFLAGS+=	-v
 
 LDADD+=		-lnpf -lprop -lutil -ly
 DPADD+=		${LIBNPF} ${LIBPROP} ${LIBUTIL}
--- a/usr.sbin/npf/npfctl/npf.conf.5	Mon Jun 25 00:43:40 2012 +0000
+++ b/usr.sbin/npf/npfctl/npf.conf.5	Tue Jun 26 00:07:16 2012 +0000
@@ -1,4 +1,4 @@
-.\"    $NetBSD: npf.conf.5,v 1.9 2012/02/06 00:41:36 rmind Exp $
+.\"    $NetBSD: npf.conf.5,v 1.9.2.1 2012/06/26 00:07:20 riz Exp $
 .\"
 .\" Copyright (c) 2009-2012 The NetBSD Foundation, Inc.
 .\" All rights reserved.
@@ -27,7 +27,7 @@
 .\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 .\" POSSIBILITY OF SUCH DAMAGE.
 .\"
-.Dd February 5, 2012
+.Dd June 14, 2012
 .Dt NPF.CONF 5
 .Os
 .Sh NAME
@@ -64,13 +64,13 @@
 Rules defined first are accordingly inspected first.
 All rules in the group are inspected sequentially, and the last matching
 dictates the action to be taken.
-Rules, however, may be explicitly marked as final (that is, "quick").
+Rules, however, may be explicitly marked as final.
 In such cases, processing stops after encountering the first matching rule
 marked as final.
 If there is no matching rule in the custom group, then rules in the default
 group will be inspected.
 .Pp
-Stateful filtering is supported using the "keep state" keyword.
+Stateful filtering is supported using the "stateful" keyword.
 In such cases, state (a session) is created and any further packets
 of the connection are tracked.
 Packets in backwards stream, after having been confirmed to belong to
@@ -99,7 +99,6 @@
 routine, for example:
 .Pp
 ifconfig npflog0 create
-.Pp
 .Ss Network address translation
 Rules for address translation can be added.
 Translation is performed on the specified interface, assigning the specified
@@ -133,7 +132,7 @@
 .\" -----
 .Sh GRAMMAR
 .Bd -literal
-line		= ( def | table | nat | group | rproc )
+line		= ( def | table | map | group | rproc )
 
 def		= ( \*[Lt]name\*[Gt] "=" "{ a, b, ... }" | "\*[Lt]text\*[Gt]" | "$\*[Lt]interface\*[Gt]" )
 iface		= ( \*[Lt]interface\*[Gt] | def )
@@ -141,9 +140,9 @@
 table		= "table" \*[Lt]tid\*[Gt] "type" ( "hash" | "tree" )
 		  ( "dynamic" | "file" \*[Lt]path\*[Gt] )
 
-nat		= "nat" iface filt-opts "->" \*[Lt]addr\*[Gt]
-binat		= "binat" iface filt-opts "->" \*[Lt]addr\*[Gt]
-rdr		= "rdr" iface filt-opts "->" \*[Lt]addr\*[Gt] port-opts
+map-di		= ( "->" | "<-" | "<->" )
+map-type	= ( "static" | "dynamic" )
+map		= "map" iface maptype \*[Lt]seg1\*[Gt] mapdi \*[Lt]seg2\*[Gt] [ "pass" filt-opts ]
 
 rproc		= "procedure" \*[Lt]name\*[Gt] procs
 procs		= "{" op1 \*[Lt]newline\*[Gt], op2 \*[Lt]newline\*[Gt], ... "}"
@@ -155,9 +154,9 @@
 
 ruleset		= "{" rule1 \*[Lt]newline\*[Gt], rule2 \*[Lt]newline\*[Gt], ... "}"
 
-rule		= ( "block" block-opts | "pass" ) [ "in" | out" ] [ "quick" ]
+rule		= ( "block" block-opts | "pass" ) [ "stateful" ] [ "in" | out" ] [ "final" ]
 		  [ "on" iface ] [ "family" fam-opt ] [ "proto" \*[Lt]protocol\*[Gt] ]
-		  ( "all" | filt-opts ) [ "keep state" ] [ "apply" rproc ] }
+		  ( "all" | filt-opts ) [ "apply" rproc ] }
 
 fam-opt		= [ "inet" | "inet6" ]
 block-opts	= [ "return-rst" | "return-icmp" | "return" ]
@@ -180,13 +179,15 @@
 $ext_if = "wm0"
 $int_if = "wm1"
 
-$services_tcp = { http, https, smtp, domain, 6000 }
-$services_udp = { domain, ntp, 6000 }
-
 table <1> type hash file "/etc/npf_blacklist"
 table <2> type tree dynamic
 
-nat $ext_if from 192.168.0.0/24 to any -> $ext_if
+$services_tcp = { http, https, smtp, domain, 6000, 9022 }
+$services_udp = { domain, ntp, 6000 }
+$localnet = { 10.1.1.0/24 }
+
+map $ext_if dynamic 10.1.1.0/24 -> $ext_if
+map $ext_if dynamic 10.1.1.2 port 22 <- $ext_if 9022
 
 procedure "log" {
 	log: npflog0
@@ -197,20 +198,20 @@
 }
 
 group (name "external", interface $ext_if) {
-	block in quick from \*[Lt]1\*[Gt]
-	pass out quick from $ext_if keep state apply "rid"
+	pass stateful out final from $ext_if apply "rid"
 
-	pass in quick family inet proto tcp to $ext_if port ssh apply "log"
-	pass in quick proto tcp to $ext_if port $services_tcp
-	pass in quick proto udp to $ext_if port $services_udp
-	pass in quick proto tcp to $ext_if port 49151-65535	# Passive FTP
-	pass in quick proto udp to $ext_if port 33434-33600	# Traceroute
+	block in final from \*[Lt]1\*[Gt]
+	pass in final family inet proto tcp to $ext_if port ssh apply "log"
+	pass in final proto tcp to $ext_if port $services_tcp
+	pass in final proto udp to $ext_if port $services_udp
+	pass in final proto tcp to $ext_if port 49151-65535	# Passive FTP
+	pass in final proto udp to $ext_if port 33434-33600	# Traceroute
 }
 
 group (name "internal", interface $int_if) {
 	block in all
-	pass in quick from \*[Lt]2\*[Gt]
-	pass out quick all
+	pass in final from \*[Lt]2\*[Gt]
+	pass out final all
 }
 
 group (default) {
--- a/usr.sbin/npf/npfctl/npf_build.c	Mon Jun 25 00:43:40 2012 +0000
+++ b/usr.sbin/npf/npfctl/npf_build.c	Tue Jun 26 00:07:16 2012 +0000
@@ -1,4 +1,4 @@
-/*	$NetBSD: npf_build.c,v 1.4.2.1 2012/04/03 17:22:53 riz Exp $	*/
+/*	$NetBSD: npf_build.c,v 1.4.2.2 2012/06/26 00:07:19 riz Exp $	*/
 
 /*-
  * Copyright (c) 2011-2012 The NetBSD Foundation, Inc.
@@ -34,7 +34,7 @@
  */
 
 #include <sys/cdefs.h>
-__RCSID("$NetBSD: npf_build.c,v 1.4.2.1 2012/04/03 17:22:53 riz Exp $");
+__RCSID("$NetBSD: npf_build.c,v 1.4.2.2 2012/06/26 00:07:19 riz Exp $");
 
 #include <sys/types.h>
 #include <sys/ioctl.h>
@@ -90,10 +90,11 @@
 	return npf_table_exists_p(npf_conf, atoi(id));
 }
 
-static in_port_t *
+static in_port_t
 npfctl_get_singleport(const npfvar_t *vp)
 {
 	port_range_t *pr;
+	in_port_t *port;
 
 	if (npfvar_get_count(vp) > 1) {
 		yyerror("multiple ports are not valid");
@@ -102,7 +103,8 @@
 	if (pr->pr_start != pr->pr_end) {
 		yyerror("port range is not valid");
 	}
-	return &pr->pr_start;
+	port = &pr->pr_start;
+	return *port;
 }
 
 static fam_addr_mask_t *
@@ -161,7 +163,7 @@
 static void
 npfctl_build_vars(nc_ctx_t *nc, sa_family_t family, npfvar_t *vars, int opts)
 {
-	const int type = npfvar_get_type(vars);
+	const int type = npfvar_get_type(vars, 0);
 	size_t i;
 
 	npfctl_ncgen_group(nc);
@@ -247,13 +249,15 @@
 npfctl_build_ncode(nl_rule_t *rl, sa_family_t family, const opt_proto_t *op,
     const filt_opts_t *fopts, bool invert)
 {
+	const addr_port_t *apfrom = &fopts->fo_from;
+	const addr_port_t *apto = &fopts->fo_to;
 	nc_ctx_t *nc;
 	void *code;
 	size_t len;
 
 	if (family == AF_UNSPEC && op->op_proto == -1 &&
-	    op->op_opts == NULL && !fopts->fo_from && !fopts->fo_to &&
-	    !fopts->fo_from_port_range && !fopts->fo_to_port_range)
+	    op->op_opts == NULL && !apfrom->ap_netaddr && !apto->ap_netaddr &&
+	    !apfrom->ap_portrange && !apto->ap_portrange)
 		return false;
 
 	int srcflag = NC_MATCH_SRC;
@@ -267,19 +271,19 @@
 	nc = npfctl_ncgen_create();
 
 	/* Build IP address blocks. */
-	npfctl_build_vars(nc, family, fopts->fo_from, srcflag);
-	npfctl_build_vars(nc, family, fopts->fo_to, dstflag);
+	npfctl_build_vars(nc, family, apfrom->ap_netaddr, srcflag);
+	npfctl_build_vars(nc, family, apto->ap_netaddr, dstflag);
 
 	/* Build layer 4 protocol blocks. */
 	int pflag = npfctl_build_proto(nc, op);
 
 	/* Build port-range blocks. */
-	if (fopts->fo_from_port_range) {
-		npfctl_build_vars(nc, family, fopts->fo_from_port_range,
+	if (apfrom->ap_portrange) {
+		npfctl_build_vars(nc, family, apfrom->ap_portrange,
 		    srcflag | pflag);
 	}
-	if (fopts->fo_to_port_range) {
-		npfctl_build_vars(nc, family, fopts->fo_to_port_range,
+	if (apto->ap_portrange) {
+		npfctl_build_vars(nc, family, apto->ap_portrange,
 		    dstflag | pflag);
 	}
 
@@ -289,7 +293,7 @@
 	code = npfctl_ncgen_complete(nc, &len);
 	if (npf_debug) {
 		extern int yylineno;
-		printf("RULE AT LINE %d\n", yylineno - 1);
+		printf("RULE AT LINE %d\n", yylineno);
 		npfctl_ncgen_print(code, len);
 	}
 	if (npf_rule_setcode(rl, NPF_CODE_NCODE, code, len) == -1) {
@@ -335,7 +339,7 @@
 			return;
 		}
 
-		const int type = npfvar_get_type(arg->ma_opts);
+		const int type = npfvar_get_type(arg->ma_opts, 0);
 		if (type != -1 && type != NPFVAR_NUM) {
 			yyerror("option '%s' is not numeric", aval);
 		}
@@ -430,64 +434,105 @@
  * given filter options.
  */
 void
-npfctl_build_nat(int type, u_int if_idx, const filt_opts_t *fopts,
-    npfvar_t *var1, npfvar_t *var2)
+npfctl_build_nat(int sd, int type, u_int if_idx, const addr_port_t *ap1,
+    const addr_port_t *ap2, const filt_opts_t *fopts)
 {
-	opt_proto_t op = { .op_proto = -1, .op_opts = NULL };
+	const opt_proto_t op = { .op_proto = -1, .op_opts = NULL };
+	fam_addr_mask_t *am1, *am2;
+	filt_opts_t imfopts;
+	sa_family_t family;
 	nl_nat_t *nat;
-	fam_addr_mask_t *ai;
+
+	if (sd == NPFCTL_NAT_STATIC) {
+		yyerror("static NAT is not yet supported");
+	}
+	assert(sd == NPFCTL_NAT_DYNAMIC);
+	assert(if_idx != 0);
+
+	family = AF_INET;
 
-	assert(type != 0 && if_idx != 0);
-	assert(fopts != NULL && var1 != NULL);
+	if (type & NPF_NATIN) {
+		if (!ap1->ap_netaddr) {
+			yyerror("inbound network segment is not specified");
+		}
+		am1 = npfctl_get_singlefam(ap1->ap_netaddr);
+		if (am1->fam_family != AF_INET) {
+			yyerror("IPv6 NAT is not supported");
+		}
+		assert(am1 != NULL);
+	} else
+		am1 = NULL;
 
-	ai = npfctl_get_singlefam(var1);
-	assert(ai != NULL);
-	if (ai->fam_family != AF_INET) {
-		yyerror("IPv6 NAT is not supported");
+	if (type & NPF_NATOUT) {
+		if (!ap2->ap_netaddr) {
+			yyerror("outbound network segment is not specified");
+		}
+		am2 = npfctl_get_singlefam(ap2->ap_netaddr);
+		if (am2->fam_family != family) {
+			yyerror("IPv6 NAT is not supported");
+		}
+		assert(am2 != NULL);
+	} else
+		am2 = NULL;
+
+	/*
+	 * If filter criteria is not specified explicitly, apply implicit
+	 * filtering according to the given network segements.
+	 */
+	if (!fopts) {
+		memset(&imfopts, 0, sizeof(filt_opts_t));
+		if (type & NPF_NATOUT) {
+			memcpy(&imfopts.fo_from, ap1, sizeof(addr_port_t));
+		}
+		if (type & NPF_NATIN) {
+			memcpy(&imfopts.fo_to, ap2, sizeof(addr_port_t));
+		}
+		fopts = &imfopts;
 	}
 
 	switch (type) {
-	case NPFCTL_RDR: {
+	case NPF_NATIN:
+		assert(am1 != NULL);
 		/*
 		 * Redirection: an inbound NAT with a specific port.
 		 */
-		in_port_t *port = npfctl_get_singleport(var2);
+		if (!ap1->ap_portrange) {
+			yyerror("inbound port is not specified");
+		}
+		in_port_t port = npfctl_get_singleport(ap1->ap_portrange);
 		nat = npf_nat_create(NPF_NATIN, NPF_NAT_PORTS,
-		    if_idx, &ai->fam_addr, ai->fam_family, *port);
+		    if_idx, &am1->fam_addr, am1->fam_family, port);
 		break;
-	}
-	case NPFCTL_BINAT: {
+
+	case (NPF_NATIN | NPF_NATOUT):
+		assert(am1 != NULL);
 		/*
 		 * Bi-directional NAT: a combination of inbound NAT and
 		 * outbound NAT policies.  Note that the translation address
 		 * is local IP and filter criteria is inverted accordingly.
 		 */
-		fam_addr_mask_t *tai = npfctl_get_singlefam(var2);
-		assert(tai != NULL);
-		if (ai->fam_family != AF_INET) {
-			yyerror("IPv6 NAT is not supported");
-		}
 		nat = npf_nat_create(NPF_NATIN, 0, if_idx,
-		    &tai->fam_addr, tai->fam_family, 0);
-		npfctl_build_ncode(nat, AF_INET, &op, fopts, true);
+		    &am1->fam_addr, am1->fam_family, 0);
+		npfctl_build_ncode(nat, family, &op, fopts, true);
 		npf_nat_insert(npf_conf, nat, NPF_PRI_NEXT);
 		/* FALLTHROUGH */
-	}
-	case NPFCTL_NAT: {
+
+	case NPF_NATOUT:
+		assert(am2 != NULL);
 		/*
 		 * Traditional NAPT: an outbound NAT policy with port.
-		 * If this is another hald for bi-directional NAT, then
+		 * If this is another half for bi-directional NAT, then
 		 * no port translation with mapping.
 		 */
-		nat = npf_nat_create(NPF_NATOUT, type == NPFCTL_NAT ?
+		nat = npf_nat_create(NPF_NATOUT, type == NPF_NATOUT ?
 		    (NPF_NAT_PORTS | NPF_NAT_PORTMAP) : 0,
-		    if_idx, &ai->fam_addr, ai->fam_family, 0);
+		    if_idx, &am2->fam_addr, am2->fam_family, 0);
 		break;
-	}
+
 	default:
 		assert(false);
 	}
-	npfctl_build_ncode(nat, AF_INET, &op, fopts, false);
+	npfctl_build_ncode(nat, family, &op, fopts, false);
 	npf_nat_insert(npf_conf, nat, NPF_PRI_NEXT);
 }
 
--- a/usr.sbin/npf/npfctl/npf_data.c	Mon Jun 25 00:43:40 2012 +0000
+++ b/usr.sbin/npf/npfctl/npf_data.c	Tue Jun 26 00:07:16 2012 +0000
@@ -1,4 +1,4 @@
-/*	$NetBSD: npf_data.c,v 1.10 2012/01/08 21:34:21 rmind Exp $	*/
+/*	$NetBSD: npf_data.c,v 1.10.2.1 2012/06/26 00:07:19 riz Exp $	*/
 
 /*-
  * Copyright (c) 2009-2012 The NetBSD Foundation, Inc.
@@ -31,7 +31,7 @@
  */
 
 #include <sys/cdefs.h>
-__RCSID("$NetBSD: npf_data.c,v 1.10 2012/01/08 21:34:21 rmind Exp $");
+__RCSID("$NetBSD: npf_data.c,v 1.10.2.1 2012/06/26 00:07:19 riz Exp $");
 
 #include <sys/types.h>
 #include <sys/null.h>
@@ -211,7 +211,7 @@
 
 /*
  * npfctl_parse_port_range: create a port-range variable.  Note that the
- * passed port numbers are in network byte order.
+ * passed port numbers should be in host byte order.
  */
 npfvar_t *
 npfctl_parse_port_range(in_port_t s, in_port_t e)
@@ -219,8 +219,8 @@
 	npfvar_t *vp = npfvar_create(".port_range");
 	port_range_t pr;
 
-	pr.pr_start = s;
-	pr.pr_end = e;
+	pr.pr_start = htons(s);
+	pr.pr_end = htons(e);
 
 	if (!npfvar_add_element(vp, NPFVAR_PORT_RANGE, &pr, sizeof(pr)))
 		goto out;
@@ -232,6 +232,44 @@
 }
 
 npfvar_t *
+npfctl_parse_port_range_variable(const char *v)
+{
+	npfvar_t *vp = npfvar_lookup(v);
+	size_t count = npfvar_get_count(vp);
+	npfvar_t *pvp = npfvar_create(".port_range");
+	port_range_t *pr;
+	in_port_t p;
+
+	for (size_t i = 0; i < count; i++) {
+		int type = npfvar_get_type(vp, i);
+		void *data = npfvar_get_data(vp, type, i);
+
+		switch (type) {
+		case NPFVAR_IDENTIFIER:
+		case NPFVAR_STRING:
+			p = npfctl_portno(data);
+			npfvar_add_elements(pvp, npfctl_parse_port_range(p, p));
+			break;
+		case NPFVAR_PORT_RANGE:
+			pr = data;
+			npfvar_add_element(pvp, NPFVAR_PORT_RANGE, pr,
+			    sizeof(*pr));
+			break;
+		case NPFVAR_NUM:
+			p = *(unsigned long *)data;
+			npfvar_add_elements(pvp, npfctl_parse_port_range(p, p));
+			break;
+		default:
+			yyerror("wrong variable '%s' type '%s' for port range",
+			    v, npfvar_type(type));
+			npfvar_destroy(pvp);
+			return NULL;
+		}
+	}
+	return pvp;
+}
+
+npfvar_t *
 npfctl_parse_iface(const char *ifname)
 {
 	npfvar_t *vp = npfvar_create(".iface");
@@ -311,7 +349,7 @@
 /*
  * npfctl_portno: convert port identifier (string) to a number.
  *
- * => Returns port number in network byte order.
+ * => Returns port number in host byte order.
  */
 in_port_t
 npfctl_portno(const char *port)
@@ -344,7 +382,7 @@
 	}
 out:
 	freeaddrinfo(rai);
-	return p;
+	return ntohs(p);
 }
 
 npfvar_t *
@@ -436,7 +474,7 @@
 }
 
 npfvar_t *
-npfctl_parse_icmp(uint8_t type, uint8_t code)
+npfctl_parse_icmp(int type, int code)
 {
 	npfvar_t *vp = npfvar_create(".icmp");
 
--- a/usr.sbin/npf/npfctl/npf_disassemble.c	Mon Jun 25 00:43:40 2012 +0000
+++ b/usr.sbin/npf/npfctl/npf_disassemble.c	Tue Jun 26 00:07:16 2012 +0000
@@ -1,4 +1,4 @@
-/*	$NetBSD: npf_disassemble.c,v 1.3.2.2 2012/04/03 17:22:54 riz Exp $	*/
+/*	$NetBSD: npf_disassemble.c,v 1.3.2.3 2012/06/26 00:07:20 riz Exp $	*/
 
 /*-
  * Copyright (c) 2012 The NetBSD Foundation, Inc.
@@ -30,15 +30,18 @@
  */
 
 #include <sys/cdefs.h>
-__RCSID("$NetBSD: npf_disassemble.c,v 1.3.2.2 2012/04/03 17:22:54 riz Exp $");
+__RCSID("$NetBSD: npf_disassemble.c,v 1.3.2.3 2012/06/26 00:07:20 riz Exp $");
 
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
+#include <errno.h>
+#include <assert.h>
 #include <err.h>
 #include <sys/socket.h>
 #include <netinet/in.h>
+#include <net/if.h>
 
 #include <util.h>
 
@@ -47,91 +50,127 @@
 
 #include "npfctl.h"
 
-#define ADVANCE(n, rv) \
-	do { \
-		if (len < sizeof(*pc) * (n)) { \
-			warnx("ran out of bytes"); \
-			return rv; \
-		} \
-		pc += (n); \
-		len -= sizeof(*pc) * (n); \
-	} while (/*CONSTCOND*/0)
+enum {
+	NPF_SHOW_SRCADDR,
+	NPF_SHOW_DSTADDR,
+	NPF_SHOW_SRCPORT,
+	NPF_SHOW_DSTPORT,
+	NPF_SHOW_ICMP,
+	NPF_SHOW_TCPF,
+	NPF_SHOW_COUNT,
+};
+
+struct nc_inf {
+	FILE *			ni_fp;
+	const uint32_t *	ni_buf;
+	size_t			ni_left;
+	const uint32_t *	ni_ipc;
+	const uint32_t *	ni_pc;
+
+	/* Jump target array, its size and current index. */
+	const uint32_t **	ni_targs;
+	size_t			ni_targsize;
+	size_t			ni_targidx;
+
+	/* Other meta-data. */
+	npfvar_t *		ni_vlist[NPF_SHOW_COUNT];
+	int			ni_proto;
+	bool			ni_srcdst;
+};
 
 static size_t
-npfctl_ncode_get_target(const uint32_t *pc, const uint32_t **t, size_t l)
+npfctl_ncode_get_target(const nc_inf_t *ni, const uint32_t *pc)
 {
-	for (size_t i = 0; i < l; i++)
-		if (t[i] == pc)
+	for (size_t i = 0; i < ni->ni_targidx; i++) {
+		if (ni->ni_targs[i] == pc)
 			return i;
-	return ~0;
+	}
+	return (size_t)-1;
 }
 
 static size_t
-npfctl_ncode_add_target(const uint32_t *pc, const uint32_t ***t, size_t *l,
-    size_t *m)
+npfctl_ncode_add_target(nc_inf_t *ni, const uint32_t *pc)
 {
-	size_t q = npfctl_ncode_get_target(pc, *t, *l);
+	size_t i = npfctl_ncode_get_target(ni, pc);
 
-	if (q != (size_t)~0)
-		return q;
+	/* If found, just return the index. */
+	if (i != (size_t)-1) {
+		return i;
+	}
 
-	if (*l <= *m) {
-		*m += 10;
-		*t = xrealloc(*t, *m * sizeof(**t));
+	/* Grow array, if needed, and add a new target. */
+	if (ni->ni_targidx == ni->ni_targsize) {
+		ni->ni_targsize += 16;
+		ni->ni_targs = xrealloc(ni->ni_targs,
+		    ni->ni_targsize * sizeof(uint32_t));
 	}
-	q = *l;
-	(*t)[(*l)++] = pc;
-	return q;
+	assert(ni->ni_targidx < ni->ni_targsize);
+	i = ni->ni_targidx++;
+	ni->ni_targs[i] = pc;
+	return i;
+}
+
+static void
+npfctl_ncode_add_vp(nc_inf_t *ni, char *buf, unsigned idx)
+{
+	npfvar_t *vl = ni->ni_vlist[idx];
+
+	if (vl == NULL) {
+		vl = npfvar_create(".list");
+		ni->ni_vlist[idx] = vl;
+	}
+	npfvar_t *vp = npfvar_create(".string");
+	npfvar_add_element(vp, NPFVAR_STRING, buf, strlen(buf) + 1);
+	npfvar_add_elements(vl, vp);
 }
 
 static const char *
-npfctl_ncode_operand(char *buf, size_t bufsiz, uint8_t op, const uint32_t *st,
-    const uint32_t *ipc, const uint32_t **pcv, size_t *lenv,
-    const uint32_t ***t, size_t *l, size_t *m)
+npfctl_ncode_operand(nc_inf_t *ni, char *buf, size_t bufsiz, uint8_t operand)
 {
-	const uint32_t *pc = *pcv;
-	size_t len = *lenv;
+	const uint32_t op = *ni->ni_pc;
 	struct sockaddr_storage ss;
+	unsigned advance;
 
-	switch (op) {
+	/* Advance by one is a default for most cases. */
+	advance = 1;
+
+	switch (operand) {
 	case NPF_OPERAND_NONE:
 		abort();
 
 	case NPF_OPERAND_REGISTER:
-		if (*pc & ~0x3) {
-			warnx("Bad register operand 0x%x at offset %td",
-			    *pc, pc - st);
+		if (op & ~0x3) {
+			warnx("invalid register operand 0x%x at offset %td",
+			    op, ni->ni_pc - ni->ni_buf);
 			return NULL;
 		}
-		snprintf(buf, bufsiz, "R%d", *pc);
-		ADVANCE(1, NULL);
+		snprintf(buf, bufsiz, "R%d", op);
 		break;
-				
+
 	case NPF_OPERAND_KEY:
-		snprintf(buf, bufsiz, "key=<0x%x>", *pc);
-		ADVANCE(1, NULL);
+		snprintf(buf, bufsiz, "key=<0x%x>", op);
 		break;
 
 	case NPF_OPERAND_VALUE:
-		snprintf(buf, bufsiz, "value=<0x%x>", *pc);
-		ADVANCE(1, NULL);
+		snprintf(buf, bufsiz, "value=<0x%x>", op);
 		break;
 
 	case NPF_OPERAND_SD:
-		if (*pc & ~0x1) {
-			warnx("Bad src/dst operand 0x%x at offset %td",
-			    *pc, pc - st);
+		if (op & ~0x1) {
+			warnx("invalid src/dst operand 0x%x at offset %td",
+			    op, ni->ni_pc - ni->ni_buf);
 			return NULL;
 		}
-		snprintf(buf, bufsiz, "%s", *pc == NPF_OPERAND_SD_SRC ?
-		    "SRC" : "DST");
-		ADVANCE(1, NULL);
+		bool srcdst = (op == NPF_OPERAND_SD_SRC);
+		if (ni) {
+			ni->ni_srcdst = srcdst;
+		}
+		snprintf(buf, bufsiz, "%s", srcdst ? "SRC" : "DST");
 		break;
 
 	case NPF_OPERAND_REL_ADDRESS:
-		snprintf(buf, bufsiz, "+%zu",
-		    npfctl_ncode_add_target(ipc + *pc, t, l, m));
-		ADVANCE(1, NULL);
+		snprintf(buf, bufsiz, "L%zu",
+		    npfctl_ncode_add_target(ni, ni->ni_ipc + op));
 		break;
 
 	case NPF_OPERAND_NET_ADDRESS4: {
@@ -139,10 +178,13 @@
 		sin->sin_len = sizeof(*sin);
 		sin->sin_family = AF_INET;
 		sin->sin_port = 0;
-		memcpy(&sin->sin_addr, pc, sizeof(sin->sin_addr));
-		sockaddr_snprintf(buf, bufsiz, "%a", (struct sockaddr *)(void *)
-		    sin);
-		ADVANCE(sizeof(sin->sin_addr) / sizeof(*pc), NULL);
+		memcpy(&sin->sin_addr, ni->ni_pc, sizeof(sin->sin_addr));
+		sockaddr_snprintf(buf, bufsiz, "%a", (struct sockaddr *)sin);
+		if (ni) {
+			npfctl_ncode_add_vp(ni, buf, ni->ni_srcdst ?
+			    NPF_SHOW_SRCADDR : NPF_SHOW_DSTADDR);
+		}
+		advance = sizeof(sin->sin_addr) / sizeof(op);
 		break;
 	}
 	case NPF_OPERAND_NET_ADDRESS6: {
@@ -150,114 +192,357 @@
 		sin6->sin6_len = sizeof(*sin6);
 		sin6->sin6_family = AF_INET6;
 		sin6->sin6_port = 0;
-		memcpy(&sin6->sin6_addr, pc, sizeof(sin6->sin6_addr));
-		sockaddr_snprintf(buf, bufsiz, "%a", (struct sockaddr *)(void *)
-		    sin6);
-		ADVANCE(sizeof(sin6->sin6_addr) / sizeof(*pc), NULL);
+		memcpy(&sin6->sin6_addr, ni->ni_pc, sizeof(sin6->sin6_addr));
+		sockaddr_snprintf(buf, bufsiz, "%a", (struct sockaddr *)sin6);
+		if (ni) {
+			npfctl_ncode_add_vp(ni, buf, ni->ni_srcdst ?
+			    NPF_SHOW_SRCADDR : NPF_SHOW_DSTADDR);
+		}
+		advance = sizeof(sin6->sin6_addr) / sizeof(op);
 		break;
 	}
 	case NPF_OPERAND_ETHER_TYPE:
-		snprintf(buf, bufsiz, "ether=0x%x", *pc);
-		ADVANCE(1, NULL);
+		snprintf(buf, bufsiz, "ether=0x%x", op);
 		break;
 
-	case NPF_OPERAND_SUBNET:
-		snprintf(buf, bufsiz, "/%d", *pc);
-		ADVANCE(1, NULL);
+	case NPF_OPERAND_SUBNET: {
+		snprintf(buf, bufsiz, "/%d", op);
+		if (ni) {
+			npfctl_ncode_add_vp(ni, buf, ni->ni_srcdst ?
+			    NPF_SHOW_SRCADDR : NPF_SHOW_DSTADDR);
+		}
 		break;
-
+	}
 	case NPF_OPERAND_LENGTH:
-		snprintf(buf, bufsiz, "length=%d", *pc);
-		ADVANCE(1, NULL);
+		snprintf(buf, bufsiz, "length=%d", op);
 		break;
 
 	case NPF_OPERAND_TABLE_ID:
-		snprintf(buf, bufsiz, "id=%d", *pc);
-		ADVANCE(1, NULL);
-		break;
-
-	case NPF_OPERAND_ICMP4_TYPE_CODE:
-		if (*pc & ~0xffff) {
-			warnx("Bad icmp/type operand 0x%x at offset %td",
-			    *pc, pc - st);
-			return NULL;
+		if (ni) {
+			snprintf(buf, bufsiz, "<%d>", op);
+			npfctl_ncode_add_vp(ni, buf, ni->ni_srcdst ?
+			    NPF_SHOW_SRCADDR : NPF_SHOW_DSTADDR);
 		}
-		snprintf(buf, bufsiz, "type=%d, code=%d", *pc >> 8,
-		    *pc & 0xff);
-		ADVANCE(1, NULL);
+		snprintf(buf, bufsiz, "id=%d", op);
 		break;
 
-	case NPF_OPERAND_TCP_FLAGS_MASK:
-		if (*pc & ~0xffff) {
-			warnx("Bad flags/mask operand 0x%x at offset %td",
-			    *pc, pc - st);
+	case NPF_OPERAND_ICMP4_TYPE_CODE: {
+		uint8_t type = (op & 31) ? op >> 8 : 0;
+		uint8_t code = (op & 30) ? op & 0xff : 0;
+
+		if (op & ~0xc000ffff) {
+			warnx("invalid icmp/type operand 0x%x at offset %td",
+			    op, ni->ni_pc - ni->ni_buf);
 			return NULL;
 		}
-		snprintf(buf, bufsiz, "type=%d, code=%d", *pc >> 8,
-		    *pc & 0xff);
-		ADVANCE(1, NULL);
+		snprintf(buf, bufsiz, "type=%d, code=%d", type, code);
+		if (ni) {
+			ni->ni_proto |= NC_MATCH_ICMP;
+			if (type || code) {
+				snprintf(buf, bufsiz,
+				    "icmp-type %d code %d", type, code);
+				npfctl_ncode_add_vp(ni, buf, NPF_SHOW_ICMP);
+			}
+		}
 		break;
+	}
+	case NPF_OPERAND_TCP_FLAGS_MASK: {
+		uint8_t tf = op >> 8, tf_mask = op & 0xff;
+		if (op & ~0xffff) {
+			warnx("invalid flags/mask operand 0x%x at offset %td",
+			    op, ni->ni_pc - ni->ni_buf);
+			return NULL;
+		}
+		snprintf(buf, bufsiz, "flags=0x%x, mask=%0xx", tf, tf_mask);
+		if (ni) {
+			ni->ni_proto |= NC_MATCH_TCP;
+			npfctl_ncode_add_vp(ni, buf, NPF_SHOW_TCPF);
+		}
+		break;
+	}
+	case NPF_OPERAND_PORT_RANGE: {
+		in_port_t p1 = ntohs(op >> 16), p2 = ntohs(op & 0xffff);
 
-	case NPF_OPERAND_PORT_RANGE:
-		snprintf(buf, bufsiz, "%d-%d", (*pc >> 16), *pc & 0xffff);
-		ADVANCE(1, NULL);
+		if (p1 == p2) {
+			snprintf(buf, bufsiz, "%d", p1);
+		} else {
+			snprintf(buf, bufsiz, "%d-%d", p1, p2);
+		}
+		if (ni) {
+			npfctl_ncode_add_vp(ni, buf, ni->ni_srcdst ?
+			    NPF_SHOW_SRCPORT : NPF_SHOW_DSTPORT);
+		}
 		break;
+	}
 	default:
-		warnx("invalid operand %d at offset %td", op, pc - st);
+		warnx("invalid operand %d at offset %td",
+		    operand, ni->ni_pc - ni->ni_buf);
 		return NULL;
 	}
 
-	*pcv = pc;
-	*lenv = len;
+	if (ni->ni_left < sizeof(op) * advance) {
+		warnx("ran out of bytes");
+		return NULL;
+	}
+	ni->ni_pc += advance;
+	ni->ni_left -= sizeof(op) * advance;
 	return buf;
 }
 
+nc_inf_t *
+npfctl_ncode_disinf(FILE *fp)
+{
+	nc_inf_t *ni = zalloc(sizeof(nc_inf_t));
+
+	memset(ni, 0, sizeof(nc_inf_t));
+	ni->ni_fp = fp;
+	return ni;
+}
+
 int
-npfctl_ncode_disassemble(FILE *fp, const void *v, size_t len)
+npfctl_ncode_disassemble(nc_inf_t *ni, const void *v, size_t len)
 {
-	const uint32_t *ipc, *pc = v;
-	const uint32_t *st = v;
-	const struct npf_instruction *ni;
-	char buf[256];
-	const uint32_t **targ;
-	size_t tlen, mlen, target;
+	FILE *fp = ni->ni_fp;
 	int error = -1;
 
-	targ = NULL;
-	mlen = tlen = 0;
-	while (len) {
-		/* Get the opcode */
-		if (*pc & ~0xff) {
-			warnx("bad opcode 0x%x at offset (%td)", *pc,
-			    pc - st);
+	ni->ni_buf = v;
+	ni->ni_left = len;
+	ni->ni_pc = v;
+
+	while (ni->ni_left) {
+		const struct npf_instruction *insn;
+		const uint32_t opcode = *ni->ni_pc;
+		size_t target;
+
+		/* Get the opcode. */
+		if (opcode & ~0xff) {
+			warnx("invalid opcode 0x%x at offset (%td)",
+			    opcode, ni->ni_pc - ni->ni_buf);
+			goto out;
+		}
+		insn = &npf_instructions[opcode];
+		if (insn->name == NULL) {
+			warnx("invalid opcode 0x%x at offset (%td)",
+			    opcode, ni->ni_pc - ni->ni_buf);
 			goto out;
 		}
-		ni = &npf_instructions[*pc];
-		if (ni->name == NULL) {
-			warnx("invalid opcode 0x%x at offset (%td)", *pc,
-			    pc - st);
-			goto out;
+
+		/*
+		 * Lookup target array and prefix with the label,
+		 * if this opcode is a jump target.
+		 */
+		ni->ni_ipc = ni->ni_pc;
+		target = npfctl_ncode_get_target(ni, ni->ni_pc);
+		if (fp) {
+			if (target != (size_t)-1) {
+				fprintf(fp, "L%zu:", target);
+			}
+			fprintf(fp, "\t%s", insn->name);
+		}
+		if (ni->ni_left < sizeof(opcode)) {
+			warnx("ran out of bytes");
+			return -1;
 		}
-		ipc = pc;
-		target = npfctl_ncode_get_target(pc, targ, tlen);
-		if (target != (size_t)~0)
-			printf("%zu:", target);
-		fprintf(fp, "\t%s", ni->name);
-		ADVANCE(1, -1);
-		for (size_t i = 0; i < __arraycount(ni->op); i++) {
+		ni->ni_left -= sizeof(opcode);
+		ni->ni_pc++;
+
+		for (size_t i = 0; i < __arraycount(insn->op); i++) {
+			const uint8_t o = insn->op[i];
 			const char *op;
-			if (ni->op[i] == NPF_OPERAND_NONE)
+			char buf[256];
+
+			if (o == NPF_OPERAND_NONE) {
 				break;
-			op = npfctl_ncode_operand(buf, sizeof(buf), ni->op[i],
-			    st, ipc, &pc, &len, &targ, &tlen, &mlen);
-			if (op == NULL)
+			}
+			op = npfctl_ncode_operand(ni, buf, sizeof(buf), o);
+			if (op == NULL) {
 				goto out;
-			fprintf(fp, "%s%s", i == 0 ? " " : ", ", op);
+			}
+			if (fp) {
+				fprintf(fp, "%s%s", i == 0 ? " " : ", ", op);
+			}
 		}
-		fprintf(fp, "\n");
+		if (fp) {
+			fprintf(fp, "\n");
+		}
 	}
 	error = 0;
 out:
-	free(targ);
+	free(ni->ni_targs);
 	return error;
 }
+
+static void
+npfctl_show_fromto(const char *name, npfvar_t *vl, bool showany)
+{
+	size_t count = npfvar_get_count(vl), last = count - 1;
+	bool one = (count == 1);
+
+	if (count == 0) {
+		if (showany) {
+			printf("%s any ", name);
+		}
+		return;
+	}
+	printf("%s%s ", name, one ? "" : " {");
+
+	for (size_t i = 0; i < count; i++) {
+		char *s = npfvar_get_data(vl, NPFVAR_STRING, i);
+		printf("%s%s ", s, i == last ? (one ? "" : " }") : ",");
+	}
+	npfvar_destroy(vl);
+}
+
+static bool
+npfctl_show_ncode(const void *nc, size_t len)
+{
+	nc_inf_t *ni = npfctl_ncode_disinf(NULL);
+	npfvar_t *vl;
+	bool any;
+
+	if (npfctl_ncode_disassemble(ni, nc, len) != 0) {
+		printf("<< ncode >> ");
+		return true;
+	}
+
+	switch (ni->ni_proto) {
+	case NC_MATCH_TCP:
+		printf("proto tcp ");
+		break;
+	case NC_MATCH_ICMP:
+		printf("proto icmp ");
+		if ((vl = ni->ni_vlist[NPF_SHOW_ICMP]) != NULL) {
+			printf("%s ", npfvar_expand_string(vl));
+			npfvar_destroy(vl);
+		}
+		break;
+	default:
+		break;
+	}
+
+	any = false;
+	if (ni->ni_vlist[NPF_SHOW_SRCADDR] || ni->ni_vlist[NPF_SHOW_SRCPORT]) {
+		npfctl_show_fromto("from", ni->ni_vlist[NPF_SHOW_SRCADDR], true);
+		npfctl_show_fromto("port", ni->ni_vlist[NPF_SHOW_SRCPORT], false);
+		any = true;
+	}
+	if (ni->ni_vlist[NPF_SHOW_DSTADDR] || ni->ni_vlist[NPF_SHOW_DSTPORT]) {
+		npfctl_show_fromto("to", ni->ni_vlist[NPF_SHOW_DSTADDR], true);
+		npfctl_show_fromto("port", ni->ni_vlist[NPF_SHOW_DSTPORT], false);
+		any = true;
+	}
+
+	free(ni);
+	return any;
+}
+
+#define	NPF_RSTICMP		(NPF_RULE_RETRST | NPF_RULE_RETICMP)
+
+static const struct attr_keyword_mapent {
+	uint32_t	mask;
+	uint32_t	flags;
+	const char *	onmatch;
+	const char *	nomatch;
+} attr_keyword_map[] = {
+	{ NPF_RULE_PASS,	NPF_RULE_PASS,	"pass",		"block"	},
+	{ NPF_RSTICMP,		NPF_RSTICMP,	"return",	NULL	},
+	{ NPF_RSTICMP,		NPF_RULE_RETRST,"return-rst",	NULL	},
+	{ NPF_RSTICMP,		NPF_RULE_RETICMP,"return-icmp",	NULL	},
+	{ NPF_RULE_KEEPSTATE,	NPF_RULE_KEEPSTATE,"stateful",	NULL	},
+	{ NPF_RULE_DIMASK,	NPF_RULE_IN,	"in",		NULL	},
+	{ NPF_RULE_DIMASK,	NPF_RULE_OUT,	"out",		NULL	},
+	{ NPF_RULE_FINAL,	NPF_RULE_FINAL,	"final",	NULL	},
+};
+
+static void
+npfctl_show_rule(nl_rule_t *nrl, unsigned nlevel)
+{
+	rule_group_t rg;
+	const char *rproc;
+	const void *nc;
+	size_t nclen;
+
+	_npf_rule_getinfo(nrl, &rg.rg_name, &rg.rg_attr, &rg.rg_ifnum);
+
+	/* Get the interface, if any. */
+	char ifnamebuf[IFNAMSIZ], *ifname = NULL;
+	if (rg.rg_ifnum) {
+		ifname = if_indextoname(rg.rg_ifnum, ifnamebuf);
+	}
+
+	/*
+	 * If zero level, then it is a group.
+	 */
+	if (nlevel == 0) {
+		static bool ingroup = false;
+		const char *rname = rg.rg_name;
+
+		if (ingroup) {
+			printf("}\n\n");
+		}
+		ingroup = true;
+		if (rg.rg_attr & NPF_RULE_DEFAULT) {
+			puts("group (default) {");
+			return;
+		}
+		printf("group (name \"%s\"", rname == NULL ? "" : rname);
+		if (ifname) {
+			printf(", interface %s", ifname);
+		}
+		puts(") {");
+		return;
+	}
+
+	/*
+	 * Rule case.  First, unparse the attributes.
+	 */
+	while (nlevel--) {
+		printf("\t");
+	}
+	for (unsigned i = 0; i < __arraycount(attr_keyword_map); i++) {
+		const struct attr_keyword_mapent *ak = &attr_keyword_map[i];
+
+		if ((rg.rg_attr & ak->mask) == ak->flags) {
+			printf("%s ", ak->onmatch);
+		} else if (ak->nomatch) {
+			printf("%s ", ak->nomatch);
+		}
+	}
+
+	if (ifname) {
+		printf("on %s ", ifname);
+	}
+
+	nc = _npf_rule_ncode(nrl, &nclen);
+	if (!nc || npfctl_show_ncode(nc, nclen)) {
+		printf("all ");
+	}
+
+	if ((rproc = _npf_rule_rproc(nrl)) != NULL) {
+		printf("apply \"%s\"", rproc);
+	}
+	puts("");
+}
+
+int
+npfctl_config_show(int fd)
+{
+	nl_config_t *ncf;
+	bool active, loaded;
+	int error = 0;
+
+	ncf = npf_config_retrieve(fd, &active, &loaded);
+	if (ncf == NULL) {
+		return errno;
+	}
+	printf("Filtering:\t%s\nConfiguration:\t%s\n\n",
+	    active ? "active" : "inactive",
+	    loaded ? "loaded" : "empty");
+
+	if (loaded) {
+		error = _npf_rule_foreach(ncf, npfctl_show_rule);
+		puts("}");
+	}
+	npf_config_destroy(ncf);
+	return error;
+}
--- a/usr.sbin/npf/npfctl/npf_ncgen.c	Mon Jun 25 00:43:40 2012 +0000
+++ b/usr.sbin/npf/npfctl/npf_ncgen.c	Tue Jun 26 00:07:16 2012 +0000
@@ -1,4 +1,4 @@
-/*	$NetBSD: npf_ncgen.c,v 1.7.2.1 2012/04/03 17:22:54 riz Exp $	*/
+/*	$NetBSD: npf_ncgen.c,v 1.7.2.2 2012/06/26 00:07:19 riz Exp $	*/
 
 /*-
  * Copyright (c) 2009-2012 The NetBSD Foundation, Inc.
@@ -34,7 +34,7 @@
  */
 
 #include <sys/cdefs.h>
-__RCSID("$NetBSD: npf_ncgen.c,v 1.7.2.1 2012/04/03 17:22:54 riz Exp $");
+__RCSID("$NetBSD: npf_ncgen.c,v 1.7.2.2 2012/06/26 00:07:19 riz Exp $");
 
 #include <stdlib.h>
 #include <stddef.h>
@@ -333,8 +333,8 @@
 
 	/* OP, code, type (2 words) */
 	*nc++ = NPF_OPCODE_ICMP4;
-	*nc++ = (type == -1 ? 0 : (1 << 31) & (type & 0xff << 8)) |
-		(code == -1 ? 0 : (1 << 31) & (code & 0xff));
+	*nc++ = (type == -1 ? 0 : (1 << 31) | ((type & 0xff) << 8)) |
+		(code == -1 ? 0 : (1 << 30) | (code & 0xff));
 
 	/* Comparison block (2 words). */
 	npfctl_ncgen_addjmp(ctx, &nc);
@@ -396,6 +396,8 @@
 		len -= sizeof(*op);
 	}
 #else
-	npfctl_ncode_disassemble(stdout, code, len);
+	nc_inf_t *ni = npfctl_ncode_disinf(stdout);
+	npfctl_ncode_disassemble(ni, code, len);
+	free(ni);
 #endif
 }
--- a/usr.sbin/npf/npfctl/npf_parse.y	Mon Jun 25 00:43:40 2012 +0000
+++ b/usr.sbin/npf/npfctl/npf_parse.y	Tue Jun 26 00:07:16 2012 +0000
@@ -1,4 +1,4 @@
-/*	$NetBSD: npf_parse.y,v 1.3.2.1 2012/04/03 17:22:53 riz Exp $	*/
+/*	$NetBSD: npf_parse.y,v 1.3.2.2 2012/06/26 00:07:19 riz Exp $	*/
 
 /*-
  * Copyright (c) 2011-2012 The NetBSD Foundation, Inc.
@@ -73,8 +73,9 @@
 %token			ALL
 %token			ANY
 %token			APPLY
-%token			ARROW
-%token			BINAT
+%token			ARROWBOTH
+%token			ARROWLEFT
+%token			ARROWRIGHT
 %token			BLOCK
 %token			CURLY_CLOSE
 %token			CURLY_OPEN
@@ -83,6 +84,7 @@
 %token			COMMA
 %token			DEFAULT
 %token			TDYNAMIC
+%token			TSTATIC
 %token			EQ
 %token			TFILE
 %token			FLAGS
@@ -95,9 +97,8 @@
 %token			INET
 %token			INET6
 %token			INTERFACE
-%token			KEEP
+%token			MAP
 %token			MINUS
-%token			NAT
 %token			NAME
 %token			ON
 %token			OUT
@@ -108,14 +109,13 @@
 %token			PROCEDURE
 %token			PROTO
 %token			FAMILY
-%token			QUICK
-%token			RDR
+%token			FINAL
 %token			RETURN
 %token			RETURNICMP
 %token			RETURNRST
 %token			SEPLINE
 %token			SLASH
-%token			STATE
+%token			STATEFUL
 %token			TABLE
 %token			TCP
 %token			TO
@@ -135,12 +135,13 @@
 
 %type	<str>		addr, iface_name, moduleargname, list_elem, table_store
 %type	<str>		opt_apply
-%type	<num>		ifindex, port, opt_quick, on_iface
+%type	<num>		ifindex, port, opt_final, on_iface
 %type	<num>		block_or_pass, rule_dir, block_opts, family, opt_family
-%type	<num>		opt_keep_state, icmp_type, table_type
+%type	<num>		opt_stateful, icmp_type, table_type, map_sd, map_type
 %type	<var>		addr_or_iface, port_range, icmp_type_and_code
 %type	<var>		filt_addr, addr_and_mask, tcp_flags, tcp_flags_and_mask
 %type	<var>		modulearg_opts, procs, proc_op, modulearg, moduleargs
+%type	<addrport>	mapseg
 %type	<filtopts>	filt_opts, all_or_filt_opts
 %type	<optproto>	opt_proto
 %type	<rulegroup>	group_attr, group_opt
@@ -148,6 +149,7 @@
 %union {
 	char *		str;
 	unsigned long	num;
+	addr_port_t	addrport;
 	filt_opts_t	filtopts;
 	npfvar_t *	var;
 	opt_proto_t	optproto;
@@ -168,7 +170,7 @@
 line
 	: def
 	| table
-	| nat
+	| map
 	| group
 	| rproc
 	|
@@ -213,6 +215,11 @@
 		npfvar_add_element(vp, NPFVAR_STRING, $1, strlen($1) + 1);
 		npfvar_add_elements(cvar, vp);
 	}
+	| NUM MINUS NUM
+	{
+		npfvar_t *vp = npfctl_parse_port_range($1, $3);
+		npfvar_add_elements(cvar, vp);
+	}
 	| NUM
 	{
 		npfvar_t *vp = npfvar_create(".num");
@@ -248,30 +255,34 @@
 	| TFILE STRING	{ $$ = $2; }
 	;
 
-nat
-	: natdef
-	| binatdef
-	| rdrdef
+map_sd
+	: TSTATIC	{ $$ = NPFCTL_NAT_STATIC; }
+	| TDYNAMIC	{ $$ = NPFCTL_NAT_DYNAMIC; }
+	|		{ $$ = NPFCTL_NAT_DYNAMIC; }
 	;
 
-natdef
-	: NAT ifindex filt_opts ARROW addr_or_iface
+map_type
+	: ARROWBOTH	{ $$ = NPF_NATIN | NPF_NATOUT; }
+	| ARROWLEFT	{ $$ = NPF_NATIN; }
+	| ARROWRIGHT	{ $$ = NPF_NATOUT; }
+	;
+
+mapseg
+	: addr_or_iface port_range
 	{
-		npfctl_build_nat(NPFCTL_NAT, $2, &$3, $5, NULL);
+		$$.ap_netaddr = $1;
+		$$.ap_portrange = $2;
 	}
 	;
 
-binatdef
-	: BINAT ifindex filt_opts ARROW addr_or_iface
+map
+	: MAP ifindex map_sd mapseg map_type mapseg PASS filt_opts
 	{
-		npfctl_build_nat(NPFCTL_BINAT, $2, &$3, $5, $3.fo_from);
+		npfctl_build_nat($3, $5, $2, &$4, &$6, &$8);
 	}
-	;
-
-rdrdef
-	: RDR ifindex filt_opts ARROW addr_or_iface port_range
+	| MAP ifindex map_sd mapseg map_type mapseg
 	{
-		npfctl_build_nat(NPFCTL_RDR, $2, &$3, $5, $6);
+		npfctl_build_nat($3, $5, $2, &$4, &$6, NULL);
 	}
 	;
 
@@ -416,15 +427,15 @@
 	;
 
 rule
-	: block_or_pass rule_dir opt_quick on_iface opt_family
-	  opt_proto all_or_filt_opts opt_keep_state opt_apply
+	: block_or_pass opt_stateful rule_dir opt_final on_iface opt_family
+	  opt_proto all_or_filt_opts opt_apply
 	{
 		/*
 		 * Arguments: attributes, interface index, address
 		 * family, protocol options, filter options.
 		 */
-		npfctl_build_rule($1 | $2 | $3 | $8, $4,
-		    $5, &$6, &$7, $9);
+		npfctl_build_rule($1 | $2 | $3 | $4, $5,
+		    $6, &$7, &$8, $9);
 	}
 	|
 	;
@@ -440,8 +451,8 @@
 	|			{ $$ = NPF_RULE_IN | NPF_RULE_OUT; }
 	;
 
-opt_quick
-	: QUICK			{ $$ = NPF_RULE_FINAL; }
+opt_final
+	: FINAL			{ $$ = NPF_RULE_FINAL; }
 	|			{ $$ = 0; }
 	;
 
@@ -486,16 +497,16 @@
 all_or_filt_opts
 	: ALL
 	{
-		$$.fo_from = NULL;
-		$$.fo_from_port_range = NULL;
-		$$.fo_to = NULL;
-		$$.fo_to_port_range = NULL;
+		$$.fo_from.ap_netaddr = NULL;
+		$$.fo_from.ap_portrange = NULL;
+		$$.fo_to.ap_netaddr = NULL;
+		$$.fo_to.ap_portrange = NULL;
 	}
 	| filt_opts	{ $$ = $1; }
 	;
 
-opt_keep_state
-	: KEEP STATE	{ $$ = NPF_RULE_KEEPSTATE; }
+opt_stateful
+	: STATEFUL	{ $$ = NPF_RULE_KEEPSTATE; }
 	|		{ $$ = 0; }
 	;
 
@@ -514,24 +525,24 @@
 filt_opts
 	: FROM filt_addr port_range TO filt_addr port_range
 	{
-		$$.fo_from = $2;
-		$$.fo_from_port_range = $3;
-		$$.fo_to = $5;
-		$$.fo_to_port_range = $6;
+		$$.fo_from.ap_netaddr = $2;
+		$$.fo_from.ap_portrange = $3;
+		$$.fo_to.ap_netaddr = $5;
+		$$.fo_to.ap_portrange = $6;
 	}
 	| FROM filt_addr port_range
 	{
-		$$.fo_from = $2;
-		$$.fo_from_port_range = $3;
-		$$.fo_to = NULL;
-		$$.fo_to_port_range = NULL;
+		$$.fo_from.ap_netaddr = $2;
+		$$.fo_from.ap_portrange = $3;
+		$$.fo_to.ap_netaddr = NULL;
+		$$.fo_to.ap_portrange = NULL;
 	}
 	| TO filt_addr port_range
 	{
-		$$.fo_from = NULL;
-		$$.fo_from_port_range = NULL;
-		$$.fo_to = $2;
-		$$.fo_to_port_range = $3;
+		$$.fo_from.ap_netaddr = NULL;
+		$$.fo_from.ap_portrange = NULL;
+		$$.fo_to.ap_netaddr = $2;
+		$$.fo_to.ap_portrange = $3;
 	}
 	;
 
@@ -569,7 +580,7 @@
 	| VAR_ID
 	{
 		npfvar_t *vp = npfvar_lookup($1);
-		const int type = npfvar_get_type(vp);
+		const int type = npfvar_get_type(vp, 0);
 
 		switch (type) {
 		case NPFVAR_VAR_ID:
@@ -602,10 +613,14 @@
 	{
 		$$ = npfctl_parse_port_range($2, $2);
 	}
-	| PORT port MINUS port	/* port from:to */
+	| PORT port MINUS port	/* port from-to */
 	{
 		$$ = npfctl_parse_port_range($2, $4);
 	}
+	| PORT VAR_ID
+	{
+		$$ = npfctl_parse_port_range_variable($2);
+	}
 	|
 	{
 		$$ = NULL;
@@ -613,13 +628,8 @@
 	;
 
 port
-	: NUM		{ $$ = htons($1); }
+	: NUM		{ $$ = $1; }
 	| IDENTIFIER	{ $$ = npfctl_portno($1); }
-	| VAR_ID
-	{
-		char *s = npfvar_expand_string(npfvar_lookup($1));
-		$$ = npfctl_portno(s);
-	}
 	;
 
 icmp_type_and_code
@@ -683,7 +693,7 @@
 	| VAR_ID
 	{
 		npfvar_t *vp = npfvar_lookup($1);
-		const int type = npfvar_get_type(vp);
+		const int type = npfvar_get_type(vp, 0);
 
 		switch (type) {
 		case NPFVAR_VAR_ID:
--- a/usr.sbin/npf/npfctl/npf_scan.l	Mon Jun 25 00:43:40 2012 +0000
+++ b/usr.sbin/npf/npfctl/npf_scan.l	Tue Jun 26 00:07:16 2012 +0000
@@ -1,4 +1,4 @@
-/*	$NetBSD: npf_scan.l,v 1.1 2012/01/08 21:34:21 rmind Exp $	*/
+/*	$NetBSD: npf_scan.l,v 1.1.2.1 2012/06/26 00:07:20 riz Exp $	*/
 
 /*-
  * Copyright (c) 2011-2012 The NetBSD Foundation, Inc.
@@ -53,14 +53,16 @@
 type			return TYPE;
 hash			return HASH;
 tree			return TREE;
+static			return TSTATIC;
 dynamic			return TDYNAMIC;
 file			return TFILE;
-nat			return NAT;
-binat			return BINAT;
-"->"			return ARROW;
+map			return MAP;
+"<->"			return ARROWBOTH;
+"<-"			return ARROWLEFT;
+"->"			return ARROWRIGHT;
 "-"			return MINUS;
-rdr			return RDR;
 procedure		return PROCEDURE;
+\\\n			yylineno++; yycolumn = 0;
 \n			yylineno++; yycolumn = 0; return SEPLINE;
 ;			return SEPLINE;
 name			return NAME;
@@ -72,10 +74,10 @@
 all			return ALL;
 block			return BLOCK;
 pass			return PASS;
-keep			return KEEP;
-state			return STATE;
+stateful		return STATEFUL;
 apply			return APPLY;
-quick			return QUICK;
+final			return FINAL;
+quick			return FINAL;
 on			return ON;
 inet6			return INET6;
 inet4			return INET;
--- a/usr.sbin/npf/npfctl/npf_var.c	Mon Jun 25 00:43:40 2012 +0000
+++ b/usr.sbin/npf/npfctl/npf_var.c	Tue Jun 26 00:07:16 2012 +0000
@@ -1,4 +1,4 @@
-/*	$NetBSD: npf_var.c,v 1.3 2012/01/15 00:49:48 rmind Exp $	*/
+/*	$NetBSD: npf_var.c,v 1.3.2.1 2012/06/26 00:07:20 riz Exp $	*/
 
 /*-
  * Copyright (c) 2011-2012 The NetBSD Foundation, Inc.
@@ -30,7 +30,7 @@
  */
 
 #include <sys/cdefs.h>
-__RCSID("$NetBSD: npf_var.c,v 1.3 2012/01/15 00:49:48 rmind Exp $");
+__RCSID("$NetBSD: npf_var.c,v 1.3.2.1 2012/06/26 00:07:20 riz Exp $");
 
 #include <stdlib.h>
 #include <string.h>
@@ -41,6 +41,7 @@
 
 typedef struct npf_element {
 	void *		e_data;
+	int		e_type;
 	struct npf_element *e_next;
 } npf_element_t;
 
@@ -61,6 +62,7 @@
 {
 	npfvar_t *vp = zalloc(sizeof(*vp));
 	vp->v_key = xstrdup(name);
+	var_num++;
 	return vp;
 }
 
@@ -87,7 +89,6 @@
 {
 	vp->v_next = var_list;
 	var_list = vp;
-	var_num++;
 }
 
 npfvar_t *
@@ -105,6 +106,7 @@
 	vp->v_count++;
 	el = zalloc(sizeof(*el));
 	el->e_data = zalloc(len);
+	el->e_type = type;
 	memcpy(el->e_data, data, len);
 
 	/* Preserve order of insertion. */
@@ -167,6 +169,7 @@
 	npfvar_free_elements(vp->v_elements);
 	free(vp->v_key);
 	free(vp);
+	var_num--;
 }
 
 char *
@@ -181,12 +184,6 @@
 	return vp ? vp->v_count : 0;
 }
 
-int
-npfvar_get_type(const npfvar_t *vp)
-{
-	return vp ? vp->v_type : -1;
-}
-
 static void *
 npfvar_get_data1(const npfvar_t *vp, int type, size_t idx, size_t level)
 {
@@ -224,6 +221,43 @@
 	return el->e_data;
 }
 
+static int
+npfvar_get_type1(const npfvar_t *vp, size_t idx, size_t level)
+{
+	npf_element_t *el;
+
+	if (level >= var_num) {
+		yyerror("variable loop for '%s'", vp->v_key);
+		return -1;
+	}
+
+	if (vp == NULL)
+		return -1;
+
+	if (vp->v_count <= idx) {
+		yyerror("variable '%s' has only %zu elements, requested %zu",
+		    vp->v_key, vp->v_count, idx);
+		return -1;
+	}
+
+	el = vp->v_elements;
+	while (idx--) {
+		el = el->e_next;
+	}
+
+	if (vp->v_type == NPFVAR_VAR_ID) {
+		npfvar_t *rvp = npfvar_lookup(el->e_data);
+		return npfvar_get_type1(rvp, 0, level + 1);
+	}
+	return el->e_type;
+}
+
+int
+npfvar_get_type(const npfvar_t *vp, size_t idx)
+{
+	return npfvar_get_type1(vp, idx, 0);
+}
+
 void *
 npfvar_get_data(const npfvar_t *vp, int type, size_t idx)
 {
--- a/usr.sbin/npf/npfctl/npf_var.h	Mon Jun 25 00:43:40 2012 +0000
+++ b/usr.sbin/npf/npfctl/npf_var.h	Tue Jun 26 00:07:16 2012 +0000
@@ -1,4 +1,4 @@
-/*	$NetBSD: npf_var.h,v 1.1 2012/01/08 21:34:21 rmind Exp $	*/
+/*	$NetBSD: npf_var.h,v 1.1.2.1 2012/06/26 00:07:20 riz Exp $	*/
 
 /*-
  * Copyright (c) 2011-2012 The NetBSD Foundation, Inc.
@@ -36,14 +36,14 @@
 #define	NPFVAR_IDENTIFIER	1
 #define	NPFVAR_VAR_ID		2
 #define NPFVAR_NUM		3
+#define NPFVAR_PORT_RANGE	4
 
 /* Note: primitive types are equivalent. */
-#define NPFVAR_PRIM		NPFVAR_NUM
-#define NPFVAR_TYPE(x)		(((x) & ~NPFVAR_PRIM) ? (x) : 0)
+#define NPFVAR_PRIM		NPFVAR_PORT_RANGE
+#define NPFVAR_TYPE(x)		(((x) > NPFVAR_PRIM) ? (x) : 0)
 
-#define	NPFVAR_TABLE		4
-#define	NPFVAR_FAM		5
-#define	NPFVAR_PORT_RANGE	6
+#define	NPFVAR_TABLE		5
+#define	NPFVAR_FAM		6
 #define	NPFVAR_TCPFLAG		7
 #define	NPFVAR_ICMP		8
 #define	NPFVAR_PROC_OP		9
@@ -69,7 +69,7 @@
 
 char *		npfvar_expand_string(const npfvar_t *);
 size_t		npfvar_get_count(const npfvar_t *);
-int		npfvar_get_type(const npfvar_t *);
+int		npfvar_get_type(const npfvar_t *, size_t);
 void *		npfvar_get_data(const npfvar_t *, int, size_t);
 
 #endif
--- a/usr.sbin/npf/npfctl/npfctl.c	Mon Jun 25 00:43:40 2012 +0000
+++ b/usr.sbin/npf/npfctl/npfctl.c	Tue Jun 26 00:07:16 2012 +0000
@@ -1,4 +1,4 @@
-/*	$NetBSD: npfctl.c,v 1.10 2012/02/05 00:37:13 rmind Exp $	*/
+/*	$NetBSD: npfctl.c,v 1.10.2.1 2012/06/26 00:07:20 riz Exp $	*/
 
 /*-
  * Copyright (c) 2009-2012 The NetBSD Foundation, Inc.
@@ -30,7 +30,7 @@
  */
 
 #include <sys/cdefs.h>
-__RCSID("$NetBSD: npfctl.c,v 1.10 2012/02/05 00:37:13 rmind Exp $");
+__RCSID("$NetBSD: npfctl.c,v 1.10.2.1 2012/06/26 00:07:20 riz Exp $");
 
 #include <sys/ioctl.h>
 #include <sys/stat.h>
@@ -50,14 +50,17 @@
 extern int		yyparse(void);
 extern void		yyrestart(FILE *);
 
-#define	NPFCTL_START		1
-#define	NPFCTL_STOP		2
-#define	NPFCTL_RELOAD		3
-#define	NPFCTL_FLUSH		4
-#define	NPFCTL_TABLE		5
-#define	NPFCTL_STATS		6
-#define	NPFCTL_SESSIONS_SAVE	7
-#define	NPFCTL_SESSIONS_LOAD	8
+enum {
+	NPFCTL_START,
+	NPFCTL_STOP,
+	NPFCTL_RELOAD,
+	NPFCTL_SHOWCONF,
+	NPFCTL_FLUSH,
+	NPFCTL_TABLE,
+	NPFCTL_STATS,
+	NPFCTL_SESSIONS_SAVE,
+	NPFCTL_SESSIONS_LOAD,
+};
 
 static struct operations_s {
 	const char *		cmd;
@@ -67,6 +70,7 @@
 	{	"start",		NPFCTL_START		},
 	{	"stop",			NPFCTL_STOP		},
 	{	"reload",		NPFCTL_RELOAD		},
+	{	"show",			NPFCTL_SHOWCONF,	},
 	{	"flush",		NPFCTL_FLUSH		},
 	/* Table */
 	{	"table",		NPFCTL_TABLE		},
@@ -263,6 +267,9 @@
 		npfctl_parsecfg(argc < 3 ? NPF_CONF_PATH : argv[2]);
 		ret = npfctl_config_send(fd);
 		break;
+	case NPFCTL_SHOWCONF:
+		ret = npfctl_config_show(fd);
+		break;
 	case NPFCTL_FLUSH:
 		ret = npf_config_flush(fd);
 		break;
@@ -331,14 +338,14 @@
 	cmd = argv[1];
 
 	if (strcmp(cmd, "debug") == 0) {
-		const char *cfg = argc > 2 ? argv[2] : "npf.conf";
+		const char *cfg = argc > 2 ? argv[2] : "/tmp/npf.conf";
 		npfctl_config_init(true);
 		npfctl_parsecfg(cfg);
 		npfctl_config_send(0);
 		return EXIT_SUCCESS;
 	}
 
-	/* Find and call the subroutine */
+	/* Find and call the subroutine. */
 	for (int n = 0; operations[n].cmd != NULL; n++) {
 		if (strcmp(cmd, operations[n].cmd) != 0)
 			continue;
--- a/usr.sbin/npf/npfctl/npfctl.h	Mon Jun 25 00:43:40 2012 +0000
+++ b/usr.sbin/npf/npfctl/npfctl.h	Tue Jun 26 00:07:16 2012 +0000
@@ -1,4 +1,4 @@
-/*	$NetBSD: npfctl.h,v 1.11.2.1 2012/04/03 17:22:54 riz Exp $	*/
+/*	$NetBSD: npfctl.h,v 1.11.2.2 2012/06/26 00:07:20 riz Exp $	*/
 
 /*-
  * Copyright (c) 2009-2012 The NetBSD Foundation, Inc.
@@ -57,11 +57,14 @@
 	in_port_t	pr_end;
 } port_range_t;
 
+typedef struct addr_port {
+	npfvar_t *	ap_netaddr;
+	npfvar_t *	ap_portrange;
+} addr_port_t;
+
 typedef struct filt_opts {
-	npfvar_t *	fo_from;
-	npfvar_t *	fo_from_port_range;
-	npfvar_t *	fo_to;
-	npfvar_t *	fo_to_port_range;
+	addr_port_t	fo_from;
+	addr_port_t	fo_to;
 } filt_opts_t;
 
 typedef struct opt_proto {
@@ -71,7 +74,7 @@
 
 typedef struct rule_group {
 	const char *	rg_name;
-	int		rg_attr;
+	uint32_t	rg_attr;
 	u_int		rg_ifnum;
 } rule_group_t;
 
@@ -100,9 +103,10 @@
 npfvar_t *	npfctl_parse_tcpflag(const char *);
 npfvar_t *	npfctl_parse_table_id(const char *);
 npfvar_t * 	npfctl_parse_icmpcode(const char *);
-npfvar_t * 	npfctl_parse_icmp(uint8_t, uint8_t);
+npfvar_t * 	npfctl_parse_icmp(int, int);
 npfvar_t *	npfctl_parse_iface(const char *);
 npfvar_t *	npfctl_parse_port_range(in_port_t, in_port_t);
+npfvar_t *	npfctl_parse_port_range_variable(const char *);
 npfvar_t *	npfctl_parse_fam_addr_mask(const char *, const char *,
 		    unsigned long *);
 fam_addr_mask_t *npfctl_parse_cidr(char *);
@@ -118,6 +122,7 @@
 
 #define	NC_MATCH_TCP		0x04
 #define	NC_MATCH_UDP		0x08
+#define	NC_MATCH_ICMP		0x10
 
 nc_ctx_t *	npfctl_ncgen_create(void);
 void *		npfctl_ncgen_complete(nc_ctx_t *, size_t *);
@@ -136,23 +141,31 @@
 void		npfctl_gennc_tcpfl(nc_ctx_t *, uint8_t, uint8_t);
 
 /*
+ * N-code disassembler.
+ */
+
+typedef struct nc_inf nc_inf_t;
+
+nc_inf_t *	npfctl_ncode_disinf(FILE *);
+int		npfctl_ncode_disassemble(nc_inf_t *, const void *, size_t);
+
+/*
  * Configuration building interface.
  */
 
-#define	NPFCTL_RDR		1
-#define	NPFCTL_BINAT		2
-#define	NPFCTL_NAT		3
+#define	NPFCTL_NAT_DYNAMIC	1
+#define	NPFCTL_NAT_STATIC	2
 
 void		npfctl_config_init(bool);
 int		npfctl_config_send(int);
+int		npfctl_config_show(int);
 
 void		npfctl_build_rproc(const char *, npfvar_t *);
 void		npfctl_build_group(const char *, int, u_int);
 void		npfctl_build_rule(int, u_int, sa_family_t,
 		    const opt_proto_t *, const filt_opts_t *, const char *);
-void		npfctl_build_nat(int, u_int, const filt_opts_t *,
-		    npfvar_t *, npfvar_t *);
+void		npfctl_build_nat(int, int, u_int, const addr_port_t *,
+		    const addr_port_t *, const filt_opts_t *);
 void		npfctl_build_table(const char *, u_int, const char *);
-int		npfctl_ncode_disassemble(FILE *, const void *, size_t);
 
 #endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr.sbin/npf/npftest/Makefile	Tue Jun 26 00:07:16 2012 +0000
@@ -0,0 +1,28 @@
+# $NetBSD$
+#
+# Public Domain
+#
+
+PROG=		npftest
+
+SRCS=		npftest.c npfstream.c
+CPPFLAGS+=	-I${.CURDIR}
+
+LIBNPFTEST!=	cd ${.CURDIR}/libnpftest && ${MAKE} -V .OBJDIR
+DPADD+=		${LIBNPFTEST}/libnpftest.a
+LDADD+=		-L${LIBNPFTEST} -lnpftest
+
+LDADD+=		-lrump -lrumpvfs -lrumpnet -lrumpnet_net -lrumpdev_npf
+
+LDADD+=		-lpcap -lprop -lpthread
+
+WARNS=		4
+NOMAN=		# no man page
+NOLINT=		# disabled (note: deliberately)
+
+SUBDIR+=	libnpftest
+
+${LIBNPFTEST}/libnpftest.a: all-libnpftest
+
+.include <bsd.subdir.mk>
+.include <bsd.prog.mk>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr.sbin/npf/npftest/libnpftest/Makefile	Tue Jun 26 00:07:16 2012 +0000
@@ -0,0 +1,27 @@
+# $NetBSD$
+#
+# Public Domain
+#
+
+RUMPTOP=	${.CURDIR}/../../../../sys/rump
+
+LIB=		npftest
+LIBISPRIVATE=	yes
+
+SRCS+=		npf_test_subr.c
+SRCS+=		npf_mbuf_subr.c
+
+SRCS+=		npf_nbuf_test.c
+SRCS+=		npf_processor_test.c
+SRCS+=		npf_table_test.c
+SRCS+=		npf_state_test.c
+
+CPPFLAGS+=	-D_NPF_TESTING
+CPPFLAGS+=	-I${.CURDIR}/../../../../sys/net/npf
+CPPFLAGS+=	-I${RUMPTOP}/librump/rumpkern
+
+WARNS=		4
+
+.include "${RUMPTOP}/Makefile.rump"
+.include <bsd.lib.mk>
+.include <bsd.klinks.mk>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr.sbin/npf/npftest/libnpftest/npf_mbuf_subr.c	Tue Jun 26 00:07:16 2012 +0000
@@ -0,0 +1,112 @@
+/*	$NetBSD$	*/
+
+/*
+ * NPF testing - helper routines.
+ *
+ * Public Domain.
+ */
+
+#include <sys/types.h>
+#include <sys/kmem.h>
+
+#include "npf_impl.h"
+#include "npf_test.h"
+
+struct mbuf *
+mbuf_getwithdata(const void *data, size_t len)
+{
+	struct mbuf *m;
+	void *dst;
+
+	m = m_gethdr(M_WAITOK, MT_HEADER);
+	assert(m != NULL);
+	dst = mtod(m, void *);
+	memcpy(dst, data, len);
+	m->m_len = len;
+	return m;
+}
+
+struct mbuf *
+mbuf_construct_ether(int proto)
+{
+	struct mbuf *m0, *m1;
+	struct ether_header *ethdr;
+
+	m0 = m_gethdr(M_WAITOK, MT_HEADER);
+	ethdr = mtod(m0, struct ether_header *);
+	ethdr->ether_type = htons(ETHERTYPE_IP);
+	m0->m_len = sizeof(struct ether_header);
+
+	m1 = mbuf_construct(proto);
+	m0->m_next = m1;
+	m1->m_next = NULL;
+	return m0;
+}
+
+struct mbuf *
+mbuf_construct(int proto)
+{
+	struct mbuf *m;
+	struct ip *iphdr;
+	struct tcphdr *th;
+	int size;
+
+	m = m_gethdr(M_WAITOK, MT_HEADER);
+	iphdr = mtod(m, struct ip *);
+
+	iphdr->ip_v = IPVERSION;
+	iphdr->ip_hl = sizeof(struct ip) >> 2;
+	iphdr->ip_off = 0;
+	iphdr->ip_ttl = 64;
+	iphdr->ip_p = proto;
+
+	size = sizeof(struct ip);
+
+	switch (proto) {
+	case IPPROTO_TCP:
+		th = (void *)(iphdr + 1);
+		th->th_off = sizeof(struct tcphdr) >> 2;
+		size += sizeof(struct tcphdr);
+		break;
+	case IPPROTO_UDP:
+		size += sizeof(struct udphdr);
+		break;
+	case IPPROTO_ICMP:
+		size += offsetof(struct icmp, icmp_data);
+		break;
+	}
+	iphdr->ip_len = htons(size);
+
+	m->m_len = size;
+	m->m_next = NULL;
+	return m;
+}
+
+void *
+mbuf_return_hdrs(struct mbuf *m, bool ether, struct ip **ip)
+{
+	struct ip *iphdr;
+
+	if (ether) {
+		struct mbuf *mn = m->m_next;
+		iphdr = mtod(mn, struct ip *);
+	} else {
+		iphdr = mtod(m, struct ip *);
+	}
+	*ip = iphdr;
+	return (void *)(iphdr + 1);
+}
+
+void
+mbuf_icmp_append(struct mbuf *m, struct mbuf *m_orig)
+{
+	struct ip *iphdr = mtod(m, struct ip *);
+	const size_t hlen = iphdr->ip_hl << 2;
+	struct icmp *ic = (struct icmp *)((uint8_t *)iphdr + hlen);
+	const size_t addlen = m_orig->m_len;
+
+	iphdr->ip_len = htons(ntohs(iphdr->ip_len) + addlen);
+	memcpy(&ic->icmp_ip, mtod(m_orig, struct ip *), addlen);
+	m->m_len += addlen;
+	m_freem(m_orig);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr.sbin/npf/npftest/libnpftest/npf_nbuf_test.c	Tue Jun 26 00:07:16 2012 +0000
@@ -0,0 +1,176 @@
+/*	$NetBSD: npf_nbuf_test.c,v 1.1.4.2 2012/06/26 00:07:18 riz Exp $	*/
+
+/*
+ * NPF nbuf interface test.
+ *
+ * Public Domain.
+ */
+
+#include <sys/types.h>
+#include <sys/kmem.h>
+
+#include "npf_impl.h"
+#include "npf_test.h"
+
+#define	MBUF_CHAIN_LEN		128
+
+CTASSERT((MBUF_CHAIN_LEN % sizeof(uint32_t)) == 0);
+
+static char *
+parse_nbuf_chain(void *nbuf, void *n_ptr)
+{
+	char *s = kmem_zalloc(MBUF_CHAIN_LEN + 1, KM_SLEEP);
+	int n, error;
+
+	for (n = 0; ; ) {
+		char d[4 + 1];
+
+		error = nbuf_fetch_datum(nbuf, n_ptr, sizeof(uint32_t), d);
+		if (error) {
+			return NULL;
+		}
+		d[sizeof(d) - 1] = '\0';
+		strcat(s, d);
+
+		if (n + sizeof(uint32_t) == MBUF_CHAIN_LEN) {
+			assert(nbuf_advance(&nbuf, n_ptr,
+			    sizeof(uint32_t) - 1));
+			break;
+		}
+		n_ptr = nbuf_advance(&nbuf, n_ptr, sizeof(uint32_t));
+		if (n_ptr == NULL) {
+			return NULL;
+		}
+		n += sizeof(uint32_t);
+	}
+	return s;
+}
+
+static char *
+mbuf_getstring(struct mbuf *m)
+{
+	char *s = kmem_zalloc(MBUF_CHAIN_LEN + 1, KM_SLEEP);
+	u_int tlen = 0;
+
+	while (m) {
+		int len = m->m_len;
+		char *d = m->m_data;
+		while (len--) {
+			s[tlen++] = *d++;
+		}
+		m = m->m_next;
+	}
+	return s;
+}
+
+static struct mbuf *
+mbuf_alloc_with_off(size_t off, int len)
+{
+	struct mbuf *m;
+
+	m = kmem_zalloc(sizeof(struct mbuf) + off + len, KM_SLEEP);
+	m->m_data = (char *)m + sizeof(struct mbuf) + off;
+	m->m_len = len;
+	return m;
+}
+
+/*
+ * Create an mbuf chain, each of 1 byte size.
+ */
+static struct mbuf *
+mbuf_bytesize(size_t clen)
+{
+	struct mbuf *m0 = NULL, *m = NULL;
+	u_int i, n;
+
+	/* Chain of clen (e.g. 128) mbufs, each storing 1 byte of data. */
+	for (i = 0, n = 0; i < clen; i++) {
+		/* Range of offset: 0 .. 15. */
+		m0 = mbuf_alloc_with_off(n & 0xf, 1);
+
+		/* Fill data with letters from 'a' to 'z'. */
+		memset(m0->m_data, 'a' + n, 1);
+		n = ('a' + n) != 'z' ? n + 1 : 0;
+
+		/* Next mbuf.. */
+		m0->m_next = m;
+		m = m0;
+	}
+	return m0;
+}
+
+/*
+ * Generate random amount of mbufs, with random offsets and lengths.
+ */
+static struct mbuf *
+mbuf_random_len(size_t chain_len)
+{
+	struct mbuf *m0 = NULL, *m = NULL;
+	u_int tlen = 0, n = 0;
+
+	while (tlen < chain_len) {
+		u_int off, len;
+		char *d;
+
+		/* Random offset and length range: 1 .. 16. */
+		off = (random() % 16) + 1;
+		len = (random() % 16) + 1;
+
+		/* Do not exceed 128 bytes of total length. */
+		if (tlen + len > chain_len) {
+			len = chain_len - tlen;
+		}
+		tlen += len;
+
+		/* Consistently fill data with letters from 'a' to 'z'. */
+		m0 = mbuf_alloc_with_off(off, len);
+		d = m0->m_data;
+		while (len--) {
+			*d++ = ('a' + n);
+			n = ('a' + n) != 'z' ? n + 1 : 0;
+		}
+
+		/* Next mbuf.. */
+		m0->m_next = m;
+		m = m0;
+	}
+	assert(tlen == chain_len);
+	return m0;
+}
+
+static bool
+validate_mbuf_data(struct mbuf *m, bool verbose, char *bufa, char *bufb)
+{
+	bool ret = (strcmp(bufa, bufb) == 0);
+
+	if (verbose) {
+		printf("Buffer A: %s\nBuffer B: %s\n", bufa, bufb);
+	}
+	/* XXX free m */
+	kmem_free(bufa, MBUF_CHAIN_LEN + 1);
+	kmem_free(bufb, MBUF_CHAIN_LEN + 1);
+	return ret;
+}
+
+bool
+npf_nbuf_test(bool verbose)
+{
+	struct mbuf *m1, *m2;
+	char *bufa, *bufb;
+
+	m1 = mbuf_random_len(MBUF_CHAIN_LEN);
+	bufa = mbuf_getstring(m1);
+	bufb = parse_nbuf_chain(m1, m1->m_data);
+	if (!validate_mbuf_data(m1, verbose, bufa, bufb)) {
+		return false;
+	}
+
+	m2 = mbuf_bytesize(MBUF_CHAIN_LEN);
+	bufa = mbuf_getstring(m2);
+	bufb = parse_nbuf_chain(m2, m2->m_data);
+	if (!validate_mbuf_data(m2, verbose, bufa, bufb)) {
+		return false;
+	}
+
+	return true;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr.sbin/npf/npftest/libnpftest/npf_processor_test.c	Tue Jun 26 00:07:16 2012 +0000
@@ -0,0 +1,163 @@
+/*	$NetBSD: npf_processor_test.c,v 1.1.4.2 2012/06/26 00:07:19 riz Exp $	*/
+
+/*
+ * NPF n-code processor test.
+ *
+ * Public Domain.
+ */
+
+#include <sys/types.h>
+
+#include "npf_impl.h"
+#include "npf_ncode.h"
+#include "npf_test.h"
+
+/*
+ * In network byte order:
+ *	192.168.2.0				== 0x0002a8c0
+ *	192.168.2.1				== 0x0102a8c0
+ *	192.168.2.100				== 0x6402a8c0
+ *	htons(ETHERTYPE_IP)			== 0x08
+ *	(htons(80) << 16) | htons(80)		== 0x50005000
+ *	(htons(80) << 16) | htons(15000)	== 0x5000983a
+ */
+
+static uint32_t nc_match[ ] __aligned(4) = {
+	NPF_OPCODE_CMP,		NPF_LAYER_3,	0,
+	NPF_OPCODE_BEQ,		0x0c,
+	NPF_OPCODE_ETHER,	0x00,		0x00,		0x08,
+	NPF_OPCODE_BEQ,		0x04,
+	NPF_OPCODE_RET,		0xff,
+	NPF_OPCODE_ADVR,	3,
+	NPF_OPCODE_IP4MASK,	0x01,		0x0002a8c0,	24,
+	NPF_OPCODE_BEQ,		0x04,
+	NPF_OPCODE_RET,		0xff,
+	NPF_OPCODE_TCP_PORTS,	0x00,		0x50005000,
+	NPF_OPCODE_BEQ,		0x04,
+	NPF_OPCODE_RET,		0xff,
+	NPF_OPCODE_RET,		0x00
+};
+
+static uint32_t nc_nmatch[ ] __aligned(4) = {
+	NPF_OPCODE_CMP,		NPF_LAYER_3,	0,
+	NPF_OPCODE_BEQ,		0x0c,
+	NPF_OPCODE_ETHER,	0x00,		0x00,		0x08,
+	NPF_OPCODE_BEQ,		0x04,
+	NPF_OPCODE_RET,		0xff,
+	NPF_OPCODE_ADVR,	3,
+	NPF_OPCODE_IP4MASK,	0x01,		0x0102a8c0,	32,
+	NPF_OPCODE_BEQ,		0x04,
+	NPF_OPCODE_RET,		0xff,
+	NPF_OPCODE_RET,		0x00
+};
+
+static uint32_t nc_rmatch[ ] __aligned(4) = {
+	NPF_OPCODE_MOVE,	offsetof(struct ip, ip_src),	1,
+	NPF_OPCODE_ADVR,	1,
+	NPF_OPCODE_LW,		sizeof(in_addr_t),		0,
+	NPF_OPCODE_CMP,		0x6402a8c0,			0,
+	NPF_OPCODE_BEQ,		0x04,
+	NPF_OPCODE_RET,		0xff,
+	NPF_OPCODE_MOVE,	sizeof(struct ip) - offsetof(struct ip, ip_src)
+				+ offsetof(struct tcphdr, th_sport),	1,
+	NPF_OPCODE_ADVR,	1,
+	NPF_OPCODE_LW,		2 * sizeof(in_port_t),		0,
+	NPF_OPCODE_CMP,		0x5000983a,			0,
+	NPF_OPCODE_BEQ,		0x04,
+	NPF_OPCODE_RET,		0xff,
+	NPF_OPCODE_RET,		0x01
+};
+
+static uint32_t nc_inval[ ] __aligned(4) = {
+	NPF_OPCODE_BEQ,		0x05,
+	NPF_OPCODE_RET,		0xff,
+	NPF_OPCODE_RET,		0x01
+};
+
+static void
+fill_packet(struct mbuf *m, bool ether)
+{
+	struct ip *ip;
+	struct tcphdr *th;
+
+	th = mbuf_return_hdrs(m, ether, &ip);
+	ip->ip_src.s_addr = inet_addr("192.168.2.100");
+	ip->ip_dst.s_addr = inet_addr("10.0.0.1");
+	th->th_sport = htons(15000);
+	th->th_dport = htons(80);
+}
+
+static bool
+validate_retcode(const char *msg, bool verbose, int ret, int expected)
+{
+	bool ok = (ret == expected);
+
+	if (verbose) {
+		printf("%-25s\t%-4d == %4d\t-> %s\n",
+		    msg, ret, expected, ok ? "ok" : "fail");
+	}
+	return ok;
+}
+
+bool
+npf_processor_test(bool verbose)
+{
+	npf_cache_t npc;
+	struct mbuf *m;
+	int errat, ret;
+
+	/* Layer 2 (Ethernet + IP + TCP). */
+	m = mbuf_construct_ether(IPPROTO_TCP);
+	fill_packet(m, true);
+	ret = npf_ncode_validate(nc_match, sizeof(nc_match), &errat);
+	if (!validate_retcode("Ether validation", verbose, ret, 0)) {
+		return false;
+	}
+	memset(&npc, 0, sizeof(npf_cache_t));
+	ret = npf_ncode_process(&npc, nc_match, m, NPF_LAYER_2);
+	if (!validate_retcode("Ether", verbose, ret, 0)) {
+		return false;
+	}
+	m_freem(m);
+
+	/* Layer 3 (IP + TCP). */
+	m = mbuf_construct(IPPROTO_TCP);
+	fill_packet(m, false);
+	memset(&npc, 0, sizeof(npf_cache_t));
+	ret = npf_ncode_process(&npc, nc_match, m, NPF_LAYER_3);
+	if (!validate_retcode("IPv4 mask 1", verbose, ret, 0)) {
+		return false;
+	}
+
+	/* Non-matching IPv4 case. */
+	ret = npf_ncode_validate(nc_nmatch, sizeof(nc_nmatch), &errat);
+	if (!validate_retcode("IPv4 mask 2 validation", verbose, ret, 0)) {
+		return false;
+	}
+	memset(&npc, 0, sizeof(npf_cache_t));
+	ret = npf_ncode_process(&npc, nc_nmatch, m, NPF_LAYER_3);
+	if (!validate_retcode("IPv4 mask 2", verbose, ret, 255)) {
+		return false;
+	}
+
+	/* Invalid n-code case. */
+	ret = npf_ncode_validate(nc_inval, sizeof(nc_inval), &errat);
+	if (!validate_retcode("Invalid n-code", verbose, ret, NPF_ERR_JUMP)) {
+		return false;
+	}
+
+	/* RISC-like insns. */
+	ret = npf_ncode_validate(nc_rmatch, sizeof(nc_rmatch), &errat);
+	if (!validate_retcode("RISC-like n-code validation", verbose, ret, 0)) {
+		return false;
+	}
+	memset(&npc, 0, sizeof(npf_cache_t));
+	ret = npf_ncode_process(&npc, nc_rmatch, m, NPF_LAYER_3);
+	if (!validate_retcode("RISC-like n-code", verbose, ret, 1)) {
+		return false;
+	}
+
+	m_freem(m);
+
+	return true;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr.sbin/npf/npftest/libnpftest/npf_state_test.c	Tue Jun 26 00:07:16 2012 +0000
@@ -0,0 +1,165 @@
+/*	$NetBSD: npf_state_test.c,v 1.1.2.2 2012/06/26 00:07:18 riz Exp $	*/
+
+/*
+ * NPF state tracking test.
+ *
+ * Public Domain.
+ */
+
+#include <sys/types.h>
+#include <sys/kmem.h>
+
+#include "npf_impl.h"
+#include "npf_test.h"
+
+typedef struct {
+	int		tcpfl;		/* TCP flags. */
+	int		tlen;		/* TCP data length. */
+	uint32_t	seq;		/* SEQ number. */
+	uint32_t	ack;		/* ACK number. */
+	uint32_t	win;		/* TCP Window. */
+	int		flags;		/* Direction et al. */
+} tcp_meta_t;
+
+#define	S	TH_SYN
+#define	A	TH_ACK
+#define	F	TH_FIN
+#define	OUT	0x1
+#define	IN	0x2
+#define	ERR	0x4
+#define	CLEAR	.flags = 0
+
+static const tcp_meta_t packet_sequence[] = {
+	/*
+	 *	TCP data	SEQ	ACK		WIN
+	 */
+
+	/* Out of order ACK. */
+	{ S,	0,		9999,	0,		4096,	OUT	},
+	{ S|A,	0,		9,	10000,		2048,	IN	},
+	{ A,	0,		10000,	10,		4096,	OUT	},
+	/* --- */
+	{ A,	0,		10,	10000,		2048,	IN	},
+	{ A,	1000,		10000,	10,		4096,	OUT	},
+	{ A,	1000,		11000,	10,		4096,	OUT	},
+	{ A,	0,		10,	12000,		2048,	IN	},
+	{ A,	0,		10,	13000,		2048,	IN	},
+	{ A,	1000,		12000,	10,		4096,	OUT	},
+	{ A,	0,		10,	11000,		1048,	IN	},
+	/* --- */
+	{ A,	1000,		14000,	10,		4096,	OUT	},
+	{ A,	0,		10,	13000,		2048,	IN	},
+	{ CLEAR },
+
+	/* Retransmission after out of order ACK and missing ACK. */
+	{ S,	0,		9999,	0,		1000,	OUT	},
+	{ S|A,	0,		9,	10000,		4000,	IN	},
+	{ A,	0,		10000,	10,		1000,	OUT	},
+	/* --- */
+	{ A,	1000,		10000,	10,		1000,	OUT	},
+	{ A,	0,		10,	11000,		4000,	IN	},
+	{ A,	1000,		11000,	10,		1000,	OUT	},
+	{ A,	1000,		12000,	10,		1000,	OUT	},
+	{ A,	1000,		13000,	10,		1000,	OUT	},
+	{ A,	1000,		14000,	10,		1000,	OUT	},
+	/* --- Assume the first was delayed; second was lost after us. */
+	{ A,	0,		10,	15000,		4000,	IN	},
+	{ A,	0,		10,	15000,		2000,	IN	},
+	/* --- */
+	{ A,	1000,		12000,	10,		1000,	OUT	},
+	{ CLEAR },
+
+	/* Out of window. */
+	{ S,	0,		9,	0,		8760,	OUT	},
+	{ S|A,	0,		9999,	10,		1000,	IN	},
+	{ A,	0,		10,	10000,		8760,	OUT	},
+	/* --- */
+	{ A,	1460,		10000,	10,		1000,	IN	},
+	{ A,	1460,		11460,	10,		1000,	IN	},
+	{ A,	0,		10,	12920,		8760,	OUT	},
+	{ A,	1460,		12920,	10,		1000,	IN	},
+	{ A,	0,		10,	14380,		8760,	OUT	},
+	{ A,	1460,		17300,	10,		1000,	IN	},
+	{ A,	0,		10,	14380,		8760,	OUT	},
+	{ A,	1460,		18760,	10,		1000,	IN	},
+	{ A,	0,		10,	14380,		8760,	OUT	},
+	{ A,	1460,		20220,	10,		1000,	IN	},
+	{ A,	0,		10,	14380,		8760,	OUT	},
+	{ A,	1460,		21680,	10,		1000,	IN	},
+	{ A,	0,		10,	14380,		8760,	OUT	},
+	/* --- */
+	{ A,	1460,		14380,	10,		1000,	IN	},
+	{ A,	1460,		23140,	10,		1000,	IN|ERR	},
+	{ CLEAR },
+
+};
+
+#undef S
+#undef A
+#undef F
+
+static struct mbuf *
+construct_packet(const tcp_meta_t *p)
+{
+	struct mbuf *m = mbuf_construct(IPPROTO_TCP);
+	struct ip *ip;
+	struct tcphdr *th;
+
+	th = mbuf_return_hdrs(m, false, &ip);
+
+	/* Imitate TCP payload, set TCP sequence numbers, flags and window. */
+	ip->ip_len = htons(sizeof(struct ip) + sizeof(struct tcphdr) + p->tlen);
+	th->th_seq = htonl(p->seq);
+	th->th_ack = htonl(p->ack);
+	th->th_flags = p->tcpfl;
+	th->th_win = htons(p->win);
+	return m;
+}
+
+static bool
+process_packet(const int i, npf_state_t *nst, bool *snew)
+{
+	const tcp_meta_t *p = &packet_sequence[i];
+	npf_cache_t npc = { .npc_info = 0 };
+	nbuf_t *nbuf;
+	int ret;
+
+	if (p->flags == 0) {
+		npf_state_destroy(nst);
+		*snew = true;
+		return true;
+	}
+
+	nbuf = (nbuf_t *)construct_packet(p);
+	ret = npf_cache_all(&npc, nbuf);
+	KASSERT((ret & NPC_IPFRAG) == 0);
+
+	if (*snew) {
+		ret = npf_state_init(&npc, nbuf, nst);
+		KASSERT(ret == true);
+		*snew = false;
+	}
+	ret = npf_state_inspect(&npc, nbuf, nst, p->flags == OUT);
+	m_freem(nbuf);
+
+	return ret ? true : (p->flags & ERR) != 0;
+}
+
+bool
+npf_state_test(bool verbose)
+{
+	npf_state_t nst;
+	bool snew = true;
+
+	for (u_int i = 0; i < __arraycount(packet_sequence); i++) {
+		if (process_packet(i, &nst, &snew)) {
+			continue;
+		}
+		if (verbose) {
+			printf("Failed on packet %d, state dump:\n", i);
+			npf_state_dump(&nst);
+		}
+		return false;
+	}
+	return true;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr.sbin/npf/npftest/libnpftest/npf_table_test.c	Tue Jun 26 00:07:16 2012 +0000
@@ -0,0 +1,117 @@
+/*	$NetBSD: npf_table_test.c,v 1.2.2.2 2012/06/26 00:07:18 riz Exp $	*/
+
+/*
+ * NPF tableset test.
+ *
+ * Public Domain.
+ */
+
+#include <sys/types.h>
+
+#include "npf_impl.h"
+#include "npf_test.h"
+
+static const char *ip_list[] = {
+	"192.168.1.1",
+	"10.0.0.1",
+	"192.168.2.1",
+	"10.1.0.1",
+	"192.168.100.253",
+	"10.0.5.1",
+	"192.168.128.127",
+	"10.0.0.2",
+};
+
+#define	HASH_TID		1
+#define	TREE_TID		2
+
+bool
+npf_table_test(bool verbose)
+{
+	npf_addr_t addr_storage, *addr = &addr_storage;
+	npf_tableset_t *tblset;
+	npf_table_t *t1, *t2;
+	int error;
+	u_int i;
+
+	npf_tableset_sysinit();
+
+	tblset = npf_tableset_create();
+	assert(tblset != NULL);
+
+	/* Table ID 1, using hash table with 256 lists. */
+	t1 = npf_table_create(HASH_TID, NPF_TABLE_HASH, 256);
+	assert(t1 != NULL);
+	error = npf_tableset_insert(tblset, t1);
+	assert(error == 0);
+
+	/* Check for double-insert. */
+	error = npf_tableset_insert(tblset, t1);
+	assert(error != 0);
+
+	/* Table ID 2, using RB-tree. */
+	t2 = npf_table_create(TREE_TID, NPF_TABLE_TREE, 0);
+	assert(t2 != NULL);
+	error = npf_tableset_insert(tblset, t2);
+	assert(error == 0);
+
+	/* Fill both tables with IP addresses. */
+	for (i = 0; i < __arraycount(ip_list); i++) {
+		addr->s6_addr32[0] = inet_addr(ip_list[i]);
+
+		error = npf_table_add_cidr(tblset, HASH_TID, addr, 32);
+		assert(error == 0);
+		error = npf_table_add_cidr(tblset, HASH_TID, addr, 32);
+		assert(error != 0);
+
+		error = npf_table_add_cidr(tblset, TREE_TID, addr, 32);
+		assert(error == 0);
+		error = npf_table_add_cidr(tblset, TREE_TID, addr, 32);
+		assert(error != 0);
+	}
+
+	/* Attempt to add duplicates - should fail. */
+	addr->s6_addr32[0] = inet_addr(ip_list[0]);
+
+	error = npf_table_add_cidr(tblset, HASH_TID, addr, 32);
+	assert(error != 0);
+
+	error = npf_table_add_cidr(tblset, TREE_TID, addr, 32);
+	assert(error != 0);
+
+	/* Reference checks. */
+	t1 = npf_table_get(tblset, HASH_TID);
+	assert(t1 != NULL);
+	npf_table_put(t1);
+
+	t2 = npf_table_get(tblset, TREE_TID);
+	assert(t2 != NULL);
+	npf_table_put(t2);
+
+	/* Match (validate) each IP entry. */
+	for (i = 0; i < __arraycount(ip_list); i++) {
+		addr->s6_addr32[0] = inet_addr(ip_list[i]);
+
+		error = npf_table_match_addr(tblset, HASH_TID, addr);
+		assert(error == 0);
+
+		error = npf_table_match_addr(tblset, TREE_TID, addr);
+		assert(error == 0);
+	}
+
+	/* Remove all entries. */
+	for (i = 0; i < __arraycount(ip_list); i++) {
+		addr->s6_addr32[0] = inet_addr(ip_list[i]);
+
+		error = npf_table_rem_cidr(tblset, HASH_TID, addr, 32);
+		assert(error == 0);
+
+		error = npf_table_rem_cidr(tblset, TREE_TID, addr, 32);
+		assert(error == 0);
+	}
+
+	npf_tableset_destroy(tblset);
+	npf_tableset_sysfini();
+
+	return true;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr.sbin/npf/npftest/libnpftest/npf_test.h	Tue Jun 26 00:07:16 2012 +0000
@@ -0,0 +1,42 @@
+/*	$NetBSD$	*/
+
+/*
+ * Public Domain.
+ */
+
+#ifndef _LIB_NPF_TEST_H_
+#define _LIB_NPF_TEST_H_
+
+#include <sys/types.h>
+#include <sys/mbuf.h>
+
+#include <netinet/in_systm.h>
+#include <netinet/in.h>
+#include <netinet6/in6.h>
+
+#include <netinet/ip.h>
+#include <netinet/ip6.h>
+#include <netinet/tcp.h>
+#include <netinet/udp.h>
+#include <netinet/ip_icmp.h>
+
+#include <net/if.h>
+#include <net/if_ether.h>
+#include <net/ethertypes.h>
+
+int		npf_test_load(const void *);
+int		npf_test_handlepkt(const void *, size_t, unsigned,
+		    bool, int64_t *);
+
+struct mbuf *	mbuf_getwithdata(const void *, size_t);
+struct mbuf *	mbuf_construct_ether(int);
+struct mbuf *	mbuf_construct(int);
+void *		mbuf_return_hdrs(struct mbuf *, bool, struct ip **);
+void		mbuf_icmp_append(struct mbuf *, struct mbuf *);
+
+bool		npf_nbuf_test(bool);
+bool		npf_processor_test(bool);
+bool		npf_table_test(bool);
+bool		npf_state_test(bool);
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr.sbin/npf/npftest/libnpftest/npf_test_subr.c	Tue Jun 26 00:07:16 2012 +0000
@@ -0,0 +1,74 @@
+/*	$NetBSD: npf_test_subr.c,v 1.1.2.2 2012/06/26 00:07:19 riz Exp $	*/
+
+/*
+ * NPF initialisation and handler routines.
+ *
+ * Public Domain.
+ */
+
+#include <sys/types.h>
+#include <net/if.h>
+#include <net/if_types.h>
+
+#include "npf_impl.h"
+#include "npf_test.h"
+
+/* State of the current stream. */
+static npf_state_t	cstream_state;
+static void *		cstream_ptr;
+static bool		cstream_retval;
+
+int
+npf_test_load(const void *xml)
+{
+	prop_dictionary_t npf_dict = prop_dictionary_internalize(xml);
+	return npfctl_reload(0, npf_dict);
+}
+
+/*
+ * State sampler - this routine is called from inside of NPF state engine.
+ */
+void
+npf_state_sample(npf_state_t *nst, bool retval)
+{
+	/* Pointer will serve as an ID. */
+	cstream_ptr = nst;
+	memcpy(&cstream_state, nst, sizeof(npf_state_t));
+	cstream_retval = retval;
+}
+
+int
+npf_test_handlepkt(const void *data, size_t len, unsigned idx,
+    bool forw, int64_t *result)
+{
+	ifnet_t ifp = { .if_index = idx };
+	struct mbuf *m;
+	int i = 0, error;
+
+	m = mbuf_getwithdata(data, len);
+	error = npf_packet_handler(NULL, &m, &ifp, forw ? PFIL_OUT : PFIL_IN);
+	if (error) {
+		assert(m == NULL);
+		return error;
+	}
+	assert(m != NULL);
+	m_freem(m);
+
+	const int di = forw ? NPF_FLOW_FORW : NPF_FLOW_BACK;
+	npf_tcpstate_t *fstate = &cstream_state.nst_tcpst[di];
+	npf_tcpstate_t *tstate = &cstream_state.nst_tcpst[!di];
+
+	result[i++] = (intptr_t)cstream_ptr;
+	result[i++] = cstream_retval;
+	result[i++] = cstream_state.nst_state;
+
+	result[i++] = fstate->nst_end;
+	result[i++] = fstate->nst_maxend;
+	result[i++] = fstate->nst_maxwin;
+
+	result[i++] = tstate->nst_end;
+	result[i++] = tstate->nst_maxend;
+	result[i++] = tstate->nst_maxwin;
+
+	return 0;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr.sbin/npf/npftest/npfstream.c	Tue Jun 26 00:07:16 2012 +0000
@@ -0,0 +1,117 @@
+/*	$NetBSD: npfstream.c,v 1.1.2.2 2012/06/26 00:07:17 riz Exp $	*/
+
+/*
+ * NPF stream processor.
+ *
+ * Public Domain.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <string.h>
+#include <inttypes.h>
+#include <err.h>
+#include <pcap.h>
+
+#include <arpa/inet.h>
+
+#include <net/if.h>
+#include <net/ethertypes.h>
+#include <net/if_ether.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/ip.h>
+#include <netinet/tcp.h>
+
+#include <rump/rump.h>
+
+#include "npftest.h"
+
+static struct in_addr	initial_ip;
+static int		snd_packet_no = 0;
+static int		rcv_packet_no = 0;
+
+static void
+process_tcpip(const void *data, size_t len, FILE *fp, unsigned idx)
+{
+	const struct ether_header *eth = data;
+	const struct ip *ip;
+	const struct tcphdr *th;
+	unsigned hlen, tcpdlen;
+	int error, packetno;
+	tcp_seq seq;
+	bool forw;
+
+	if (ntohs(eth->ether_type) != ETHERTYPE_IP) {
+		errx(EXIT_FAILURE, "process_tcpip: not IP protocol (%d)",
+		    eth->ether_type);
+	}
+	ip = (const struct ip *)(eth + 1);
+	hlen = ip->ip_hl << 2;
+	th = (const struct tcphdr *)((const uint8_t *)ip + hlen);
+
+	tcpdlen = ntohs(ip->ip_len) - hlen - (th->th_off << 2);
+	if (th->th_flags & TH_SYN) {
+		tcpdlen++;
+	}
+	if (th->th_flags & TH_FIN) {
+		tcpdlen++;
+	}
+	seq = ntohl(th->th_seq);
+
+	if (snd_packet_no == 0) {
+		memcpy(&initial_ip, &ip->ip_src, sizeof(struct in_addr));
+	}
+
+	forw = (initial_ip.s_addr == ip->ip_src.s_addr);
+	packetno = forw ? ++snd_packet_no : ++rcv_packet_no;
+
+	int64_t result[9];
+	memset(result, 0, sizeof(result));
+
+	len = ntohs(ip->ip_len);
+	error = rumpns_npf_test_handlepkt(ip, len, idx, forw, result);
+
+	fprintf(fp, "%s%2x %5d %3d %11u %11u %11u %11u %12lx",
+	    forw ? ">" : "<", (th->th_flags & (TH_SYN | TH_ACK | TH_FIN)),
+	    packetno, error, (u_int)seq, (u_int)ntohl(th->th_ack),
+	    (u_int)(seq + tcpdlen), ntohs(th->th_win), (uintptr_t)result[0]);
+
+	for (unsigned i = 1; i < __arraycount(result); i++) {
+		fprintf(fp, "%11" PRIu64 " ", result[i]);
+	}
+	fputs("\n", fp);
+}
+
+int
+process_stream(const char *input, const char *output, unsigned idx)
+{
+	pcap_t *pcap;
+	char pcap_errbuf[PCAP_ERRBUF_SIZE];
+	struct pcap_pkthdr *phdr;
+	const uint8_t *data;
+	FILE *fp;
+
+	pcap = pcap_open_offline(input, pcap_errbuf);
+	if (pcap == NULL) {
+		errx(EXIT_FAILURE, "pcap_open_offline failed: %s", pcap_errbuf);
+	}
+	fp = output ? fopen(output, "w") : stdout;
+	if (fp == NULL) {
+		err(EXIT_FAILURE, "fopen");
+	}
+	fprintf(fp, "#   %5s %3s %11s %11s %11s %11s %11s %11s %11s\n",
+	    "No", "Err", "Seq", "Ack", "TCP Len", "Win",
+	    "Stream", "RetVal", "State");
+	while (pcap_next_ex(pcap, &phdr, &data) > 0) {
+		if (phdr->len != phdr->caplen) {
+			warnx("process_stream: truncated packet");
+		}
+		process_tcpip(data, phdr->caplen, fp, idx);
+	}
+	pcap_close(pcap);
+	fclose(fp);
+
+	return 0;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr.sbin/npf/npftest/npftest.c	Tue Jun 26 00:07:16 2012 +0000
@@ -0,0 +1,200 @@
+/*	$NetBSD: npftest.c,v 1.3.2.2 2012/06/26 00:07:17 riz Exp $	*/
+
+/*
+ * NPF testing framework.
+ *
+ * Public Domain.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <string.h>
+#include <unistd.h>
+#include <assert.h>
+#include <fcntl.h>
+#include <err.h>
+
+#include <sys/ioctl.h>
+#include <net/if.h>
+#include <arpa/inet.h>
+
+#include <rump/rump.h>
+#include <rump/rump_syscalls.h>
+
+#include "npftest.h"
+
+static bool verbose, quiet;
+
+static void
+usage(void)
+{
+	printf("usage: %s: [ -q | -v ] [ -c <config> ] "
+	    "[ -i 'interfaces' ] < -b | -t | -s file >\n"
+	    "\t-b: benchmark\n"
+	    "\t-t: regression test\n"
+	    "\t-c <config>: NPF configuration file\n"
+	    "\t-i 'interfaces': interfaces to create\n"
+	    "\t-q: quiet mode\n"
+	    "\t-s <file>: pcap stream\n"
+	    "\t-v: verbose mode\n",
+	    getprogname());
+	exit(EXIT_FAILURE);
+}
+
+static void
+result(const char *testcase, bool ok)
+{
+	if (!quiet) {
+		printf("NPF %-10s\t%s\n", testcase, ok ? "OK" : "fail");
+	}
+	if (verbose) {
+		puts("-----");
+	}
+	if (!ok) {
+		exit(EXIT_FAILURE);
+	}
+}
+
+#if 0
+static void
+construct_interfaces(char *ifs)
+{
+	char *ifname, *addr, *mask, *sptr;
+
+	/*
+	 * Format: ifname0[,ip0,mask1];ifname1,...
+	 */
+	ifname = strtok_r(ifs, ";", &sptr);
+	while (ifname) {
+		/* Address and netmask. */
+		addr = strchr(ifname, ',');
+		if (addr) {
+			*addr++ = '\0';
+		}
+		mask = strchr(addr, ',');
+		if (mask) {
+			*mask++ = '\0';
+		}
+
+		/* Construct; next.. */
+		setup_rump_if(ifname, addr, mask);
+		ifname = strtok_r(NULL, ";", &sptr);
+	}
+}
+#endif
+
+static void
+load_npf_config(const char *config)
+{
+	prop_dictionary_t npf_dict;
+	void *xml;
+	int error;
+
+	npf_dict = prop_dictionary_internalize_from_file(config);
+	if (!npf_dict) {
+		err(EXIT_FAILURE, "prop_dictionary_internalize_from_file");
+	}
+	xml = prop_dictionary_externalize(npf_dict);
+	prop_object_release(npf_dict);
+
+	error = rumpns_npf_test_load(xml);
+	if (error) {
+		errx(EXIT_FAILURE, "npf_test_load: %s\n", strerror(error));
+	}
+	free(xml);
+
+	if (verbose) {
+		printf("Loaded NPF config at '%s'\n", config);
+	}
+}
+
+int
+main(int argc, char **argv)
+{
+	bool benchmark, test, ok;
+	char *config, *interfaces, *stream;
+	int ch;
+
+	benchmark = false;
+	test = false;
+
+	config = NULL;
+	interfaces = NULL;
+	stream = NULL;
+
+	verbose = false;
+	quiet = false;
+
+	while ((ch = getopt(argc, argv, "bqvc:i:s:t")) != -1) {
+		switch (ch) {
+		case 'b':
+			benchmark = true;
+			break;
+		case 'q':
+			quiet = true;
+			break;
+		case 'v':
+			verbose = true;
+			break;
+		case 'c':
+			config = optarg;
+			break;
+		case 'i':
+			interfaces = optarg;
+			break;
+		case 's':
+			stream = optarg;
+			break;
+		case 't':
+			test = true;
+			break;
+		default:
+			usage();
+		}
+	}
+
+	/* Either benchmark or test. */
+	if (benchmark == test && (!stream || !interfaces)) {
+		usage();
+	}
+
+	/* XXX rn_init */
+	extern int rumpns_max_keylen;
+	rumpns_max_keylen = 1;
+
+	rump_init();
+	rump_schedule();
+
+	if (config) {
+		load_npf_config(config);
+	}
+
+	if (test) {
+		ok = rumpns_npf_nbuf_test(verbose);
+		result("nbuf", ok);
+
+		ok = rumpns_npf_processor_test(verbose);
+		result("processor", ok);
+
+		ok = rumpns_npf_table_test(verbose);
+		result("table", ok);
+
+		ok = rumpns_npf_state_test(verbose);
+		result("state", ok);
+	}
+
+	if (stream) {
+		unsigned idx = if_nametoindex(interfaces);
+		if (idx == 0) {
+			err(EXIT_FAILURE, "if_nametoindex");
+		} else if (verbose) {
+			printf("Interface %s index %u\n", interfaces, idx);
+		}
+		process_stream(stream, NULL, idx);
+	}
+
+	rump_unschedule();
+
+	return EXIT_SUCCESS;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr.sbin/npf/npftest/npftest.h	Tue Jun 26 00:07:16 2012 +0000
@@ -0,0 +1,24 @@
+/*	$NetBSD$	*/
+
+/*
+ * Public Domain.
+ */
+
+#ifndef _NPF_TEST_H_
+#define _NPF_TEST_H_
+
+#include <stdint.h>
+#include <stdbool.h>
+
+int		rumpns_npf_test_load(const void *);
+int		rumpns_npf_test_handlepkt(const void *, size_t,
+		    unsigned, bool, int64_t *);
+
+bool		rumpns_npf_nbuf_test(bool);
+bool		rumpns_npf_processor_test(bool);
+bool		rumpns_npf_table_test(bool);
+bool		rumpns_npf_state_test(bool);
+
+int		process_stream(const char *, const char *, unsigned);
+
+#endif