Pull up following revision(s) (requested by rmind in ticket #485): netbsd-6
authorriz <riz@NetBSD.org>
Mon, 13 Aug 2012 17:49:51 +0000
branchnetbsd-6
changeset 256071 dc68fa1a737d
parent 256070 fa25996360ed
child 256072 18120dcd9726
Pull up following revision(s) (requested by rmind in ticket #485): lib/libnpf/npf.c: revision 1.11 sys/net/npf/npf_session.c: revision 1.17 sys/modules/npf/Makefile: revision 1.10 usr.sbin/npf/npftest/npftest.c: revision 1.4 usr.sbin/npf/npftest/README: revision 1.1 sys/net/npf/npf_tableset.c: revision 1.14 usr.sbin/npf/npftest/npftest.h: revision 1.4 lib/libnpf/npf.h: revision 1.10 sys/net/npf/npf_ruleset.c: revision 1.14 usr.sbin/npf/npfctl/npf_data.c: revision 1.18 usr.sbin/npf/npftest/npftest.conf: revision 1.1 sys/net/npf/npf_handler.c: revision 1.21 sys/net/npf/npf_impl.h: revision 1.21 usr.sbin/npf/npfctl/npfctl.c: revision 1.18 usr.sbin/npf/npftest/libnpftest/npf_nat_test.c: revision 1.1 usr.sbin/npf/npfctl/npf_build.c: revision 1.13 usr.sbin/npf/npftest/libnpftest/npf_rule_test.c: revision 1.1 usr.sbin/npf/npftest/npfstream.c: revision 1.3 usr.sbin/npf/npftest/libnpftest/Makefile: revision 1.4 usr.sbin/npf/npfctl/npfctl.h: revision 1.19 sys/net/npf/npf_nat.c: revision 1.16 sys/net/npf/npf_state.c: revision 1.11 usr.sbin/npf/npftest/libnpftest/npf_test_subr.c: revision 1.3 usr.sbin/npf/npftest/libnpftest/npf_test.h: revision 1.5 usr.sbin/npf/npfctl/npf_parse.y: revision 1.12 - Extend npftest: add ruleset inspection testing from the config generated by npfctl debug functionality. Auto-create npftest interfaces for this. - NPF sessions: combine protocol and interface into a separate substructure, share between the entries and thus fix the handling of them. Constify. - npftest: add regression tests for NAT policies. - npf_build_nat: simplify and fix bi-NAT regression. - Bump yacc stack size for npfctl.
lib/libnpf/npf.c
lib/libnpf/npf.h
sys/modules/npf/Makefile
sys/net/npf/npf_handler.c
sys/net/npf/npf_impl.h
sys/net/npf/npf_nat.c
sys/net/npf/npf_ruleset.c
sys/net/npf/npf_session.c
sys/net/npf/npf_state.c
sys/net/npf/npf_tableset.c
usr.sbin/npf/npfctl/npf_build.c
usr.sbin/npf/npfctl/npf_data.c
usr.sbin/npf/npfctl/npf_parse.y
usr.sbin/npf/npfctl/npfctl.c
usr.sbin/npf/npfctl/npfctl.h
usr.sbin/npf/npftest/README
usr.sbin/npf/npftest/libnpftest/Makefile
usr.sbin/npf/npftest/libnpftest/npf_nat_test.c
usr.sbin/npf/npftest/libnpftest/npf_rule_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.conf
usr.sbin/npf/npftest/npftest.h
--- a/lib/libnpf/npf.c	Mon Aug 13 17:09:49 2012 +0000
+++ b/lib/libnpf/npf.c	Mon Aug 13 17:49:51 2012 +0000
@@ -1,4 +1,4 @@
-/*	$NetBSD: npf.c,v 1.7.2.3 2012/07/16 22:13:25 riz Exp $	*/
+/*	$NetBSD: npf.c,v 1.7.2.4 2012/08/13 17:49:51 riz Exp $	*/
 
 /*-
  * Copyright (c) 2010-2012 The NetBSD Foundation, Inc.
@@ -30,11 +30,12 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: npf.c,v 1.7.2.3 2012/07/16 22:13:25 riz Exp $");
+__KERNEL_RCSID(0, "$NetBSD: npf.c,v 1.7.2.4 2012/08/13 17:49:51 riz Exp $");
 
 #include <sys/types.h>
 #include <netinet/in_systm.h>
 #include <netinet/in.h>
+#include <net/if.h>
 #include <prop/proplib.h>
 
 #include <stdlib.h>
@@ -56,6 +57,8 @@
 	/* Priority counters. */
 	pri_t			ncf_rule_pri;
 	pri_t			ncf_nat_pri;
+	/* Debug information. */
+	prop_dictionary_t	ncf_debug;
 	/* Error report. */
 	prop_dictionary_t	ncf_err;
 	/* Custom file to externalise property-list. */
@@ -113,6 +116,9 @@
 	if (npf_dict == NULL) {
 		return ENOMEM;
 	}
+	if (ncf->ncf_debug) {
+		prop_dictionary_set(npf_dict, "debug", ncf->ncf_debug);
+	}
 	prop_dictionary_set(npf_dict, "rules", ncf->ncf_rules_list);
 	prop_dictionary_set(npf_dict, "rprocs", ncf->ncf_rproc_list);
 	prop_dictionary_set(npf_dict, "tables", ncf->ncf_table_list);
@@ -213,6 +219,9 @@
 	if (ncf->ncf_err) {
 		prop_object_release(ncf->ncf_err);
 	}
+	if (ncf->ncf_debug) {
+		prop_object_release(ncf->ncf_debug);
+	}
 	free(ncf);
 }
 
@@ -753,3 +762,63 @@
 	prop_object_release(sdict);
 	return error;
 }
+
+static prop_dictionary_t
+_npf_debug_initonce(nl_config_t *ncf)
+{
+	if (!ncf->ncf_debug) {
+		prop_array_t iflist = prop_array_create();
+		ncf->ncf_debug = prop_dictionary_create();
+		prop_dictionary_set(ncf->ncf_debug, "interfaces", iflist);
+		prop_object_release(iflist);
+	}
+	return ncf->ncf_debug;
+}
+
+void
+_npf_debug_addif(nl_config_t *ncf, struct ifaddrs *ifa, u_int if_idx)
+{
+	prop_dictionary_t ifdict, dbg = _npf_debug_initonce(ncf);
+	prop_array_t iflist = prop_dictionary_get(dbg, "interfaces");
+
+	if (_npf_prop_array_lookup(iflist, "name", ifa->ifa_name)) {
+		return;
+	}
+
+	ifdict = prop_dictionary_create();
+	prop_dictionary_set_cstring(ifdict, "name", ifa->ifa_name);
+	prop_dictionary_set_uint32(ifdict, "flags", ifa->ifa_flags);
+	if (!if_idx) {
+		if_idx = if_nametoindex(ifa->ifa_name);
+	}
+	prop_dictionary_set_uint32(ifdict, "idx", if_idx);
+
+	const struct sockaddr *sa = ifa->ifa_addr;
+	npf_addr_t addr;
+	size_t alen = 0;
+
+	switch (sa ? sa->sa_family : -1) {
+	case AF_INET: {
+		const struct sockaddr_in *sin = (const void *)sa;
+		alen = sizeof(sin->sin_addr);
+		memcpy(&addr, &sin->sin_addr, alen);
+		break;
+	}
+	case AF_INET6: {
+		const struct sockaddr_in6 *sin6 = (const void *)sa;
+		alen = sizeof(sin6->sin6_addr);
+		memcpy(&addr, &sin6->sin6_addr, alen);
+		break;
+	}
+	default:
+		break;
+	}
+
+	if (alen) {
+		prop_data_t addrdata = prop_data_create_data(&addr, alen);
+		prop_dictionary_set(ifdict, "addr", addrdata);
+		prop_object_release(addrdata);
+	}
+	prop_array_add(iflist, ifdict);
+	prop_object_release(ifdict);
+}
--- a/lib/libnpf/npf.h	Mon Aug 13 17:09:49 2012 +0000
+++ b/lib/libnpf/npf.h	Mon Aug 13 17:49:51 2012 +0000
@@ -1,4 +1,4 @@
-/*	$NetBSD: npf.h,v 1.6.2.3 2012/07/16 22:13:25 riz Exp $	*/
+/*	$NetBSD: npf.h,v 1.6.2.4 2012/08/13 17:49:52 riz Exp $	*/
 
 /*-
  * Copyright (c) 2011-2012 The NetBSD Foundation, Inc.
@@ -107,6 +107,9 @@
 int		npf_sessions_recv(int, const char *);
 
 #ifdef _NPF_PRIVATE
+
+#include <ifaddrs.h>
+
 void		_npf_config_error(nl_config_t *, nl_error_t *);
 void		_npf_config_setsubmit(nl_config_t *, const char *);
 int		_npf_rule_foreach(nl_config_t *, nl_rule_callback_t);
@@ -120,6 +123,8 @@
 int		_npf_rproc_setnorm(nl_rproc_t *, bool, bool, u_int, u_int);
 int		_npf_rproc_setlog(nl_rproc_t *, u_int);
 void		_npf_table_foreach(nl_config_t *, nl_table_callback_t);
+
+void		_npf_debug_addif(nl_config_t *, struct ifaddrs *, u_int);
 #endif
 
 __END_DECLS
--- a/sys/modules/npf/Makefile	Mon Aug 13 17:09:49 2012 +0000
+++ b/sys/modules/npf/Makefile	Mon Aug 13 17:49:51 2012 +0000
@@ -1,4 +1,4 @@
-# $NetBSD: Makefile,v 1.9 2012/02/06 23:30:14 rmind Exp $
+# $NetBSD: Makefile,v 1.9.2.1 2012/08/13 17:49:52 riz Exp $
 
 .include "../Makefile.inc"
 
@@ -9,7 +9,8 @@
 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+=		npf_session.c npf_state.c npf_state_tcp.c
+SRCS+=		npf_tableset.c npf_tableset_ptree.c
 
 CPPFLAGS+=	-DINET6
 
--- a/sys/net/npf/npf_handler.c	Mon Aug 13 17:09:49 2012 +0000
+++ b/sys/net/npf/npf_handler.c	Mon Aug 13 17:49:51 2012 +0000
@@ -1,4 +1,4 @@
-/*	$NetBSD: npf_handler.c,v 1.13.2.4 2012/07/16 22:13:26 riz Exp $	*/
+/*	$NetBSD: npf_handler.c,v 1.13.2.5 2012/08/13 17:49:52 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.4 2012/07/16 22:13:26 riz Exp $");
+__KERNEL_RCSID(0, "$NetBSD: npf_handler.c,v 1.13.2.5 2012/08/13 17:49:52 riz Exp $");
 
 #include <sys/types.h>
 #include <sys/param.h>
@@ -142,7 +142,7 @@
 	}
 
 	/* Inspect the list of sessions. */
-	se = npf_session_inspect(&npc, nbuf, di, &error);
+	se = npf_session_inspect(&npc, nbuf, ifp, di, &error);
 
 	/* If "passing" session found - skip the ruleset inspection. */
 	if (se && npf_session_pass(se, &rp)) {
@@ -193,7 +193,7 @@
 	 * session.  It will be released on session destruction.
 	 */
 	if ((retfl & NPF_RULE_STATEFUL) != 0 && !se) {
-		se = npf_session_establish(&npc, nbuf, di);
+		se = npf_session_establish(&npc, nbuf, ifp, di);
 		if (se) {
 			npf_session_setpass(se, rp);
 		}
--- a/sys/net/npf/npf_impl.h	Mon Aug 13 17:09:49 2012 +0000
+++ b/sys/net/npf/npf_impl.h	Mon Aug 13 17:49:51 2012 +0000
@@ -1,4 +1,4 @@
-/*	$NetBSD: npf_impl.h,v 1.10.2.6 2012/07/25 20:45:23 jdc Exp $	*/
+/*	$NetBSD: npf_impl.h,v 1.10.2.7 2012/08/13 17:49:52 riz Exp $	*/
 
 /*-
  * Copyright (c) 2009-2012 The NetBSD Foundation, Inc.
@@ -124,9 +124,9 @@
 
 #if defined(_NPF_TESTING)
 void		npf_state_sample(npf_state_t *, bool);
-#define	NPF_TCP_STATE_SAMPLE(n, r)	npf_state_sample(n, r)
+#define	NPF_STATE_SAMPLE(n, r)	npf_state_sample(n, r)
 #else
-#define	NPF_TCP_STATE_SAMPLE(n, r)
+#define	NPF_STATE_SAMPLE(n, r)
 #endif
 
 /*
@@ -248,7 +248,7 @@
 void		npf_ruleset_freealg(npf_ruleset_t *, npf_alg_t *);
 
 npf_rule_t *	npf_ruleset_inspect(npf_cache_t *, nbuf_t *, npf_ruleset_t *,
-		    ifnet_t *, const int, const int);
+		    const ifnet_t *, const int, const int);
 int		npf_rule_apply(npf_cache_t *, nbuf_t *, npf_rule_t *, int *);
 
 /* Rule interface. */
@@ -273,8 +273,10 @@
 void		sess_htable_destroy(npf_sehash_t *);
 void		sess_htable_reload(npf_sehash_t *);
 
-npf_session_t *	npf_session_inspect(npf_cache_t *, nbuf_t *, const int, int *);
-npf_session_t *	npf_session_establish(const npf_cache_t *, nbuf_t *, const int);
+npf_session_t *	npf_session_inspect(npf_cache_t *, nbuf_t *,
+		    const ifnet_t *, const int, int *);
+npf_session_t *	npf_session_establish(const npf_cache_t *, nbuf_t *,
+		    const ifnet_t *, const int);
 void		npf_session_release(npf_session_t *);
 void		npf_session_expire(npf_session_t *);
 bool		npf_session_pass(const npf_session_t *, npf_rproc_t **);
@@ -305,7 +307,7 @@
 void		npf_nat_freealg(npf_natpolicy_t *, npf_alg_t *);
 
 int		npf_do_nat(npf_cache_t *, npf_session_t *, nbuf_t *,
-		    ifnet_t *, const int);
+		    const ifnet_t *, const int);
 void		npf_nat_expire(npf_nat_t *);
 void		npf_nat_getorig(npf_nat_t *, npf_addr_t **, in_port_t *);
 void		npf_nat_gettrans(npf_nat_t *, npf_addr_t **, in_port_t *);
--- a/sys/net/npf/npf_nat.c	Mon Aug 13 17:09:49 2012 +0000
+++ b/sys/net/npf/npf_nat.c	Mon Aug 13 17:49:51 2012 +0000
@@ -1,4 +1,4 @@
-/*	$NetBSD: npf_nat.c,v 1.10.2.4 2012/07/16 22:13:27 riz Exp $	*/
+/*	$NetBSD: npf_nat.c,v 1.10.2.5 2012/08/13 17:49:52 riz Exp $	*/
 
 /*-
  * Copyright (c) 2010-2012 The NetBSD Foundation, Inc.
@@ -76,7 +76,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: npf_nat.c,v 1.10.2.4 2012/07/16 22:13:27 riz Exp $");
+__KERNEL_RCSID(0, "$NetBSD: npf_nat.c,v 1.10.2.5 2012/08/13 17:49:52 riz Exp $");
 
 #include <sys/param.h>
 #include <sys/types.h>
@@ -417,7 +417,8 @@
  * npf_nat_inspect: inspect packet against NAT ruleset and return a policy.
  */
 static npf_natpolicy_t *
-npf_nat_inspect(npf_cache_t *npc, nbuf_t *nbuf, ifnet_t *ifp, const int di)
+npf_nat_inspect(npf_cache_t *npc, nbuf_t *nbuf, const ifnet_t *ifp,
+    const int di)
 {
 	npf_ruleset_t *rlset;
 	npf_natpolicy_t *np;
@@ -582,7 +583,7 @@
  */
 int
 npf_do_nat(npf_cache_t *npc, npf_session_t *se, nbuf_t *nbuf,
-    ifnet_t *ifp, const int di)
+    const ifnet_t *ifp, const int di)
 {
 	npf_session_t *nse = NULL;
 	npf_natpolicy_t *np;
@@ -643,7 +644,7 @@
 	 * stream depends on other, stateless filtering rules.
 	 */
 	if (se == NULL) {
-		nse = npf_session_establish(npc, nbuf, di);
+		nse = npf_session_establish(npc, nbuf, ifp, di);
 		if (nse == NULL) {
 			error = ENOMEM;
 			goto out;
--- a/sys/net/npf/npf_ruleset.c	Mon Aug 13 17:09:49 2012 +0000
+++ b/sys/net/npf/npf_ruleset.c	Mon Aug 13 17:49:51 2012 +0000
@@ -1,4 +1,4 @@
-/*	$NetBSD: npf_ruleset.c,v 1.10.2.3 2012/07/16 22:13:26 riz Exp $	*/
+/*	$NetBSD: npf_ruleset.c,v 1.10.2.4 2012/08/13 17:49:52 riz Exp $	*/
 
 /*-
  * Copyright (c) 2009-2012 The NetBSD Foundation, Inc.
@@ -34,7 +34,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: npf_ruleset.c,v 1.10.2.3 2012/07/16 22:13:26 riz Exp $");
+__KERNEL_RCSID(0, "$NetBSD: npf_ruleset.c,v 1.10.2.4 2012/08/13 17:49:52 riz Exp $");
 
 #include <sys/param.h>
 #include <sys/types.h>
@@ -354,7 +354,7 @@
  */
 npf_rule_t *
 npf_ruleset_inspect(npf_cache_t *npc, nbuf_t *nbuf, npf_ruleset_t *mainrlset,
-    ifnet_t *ifp, const int di, const int layer)
+    const ifnet_t *ifp, const int di, const int layer)
 {
 	const int di_mask = (di & PFIL_IN) ? NPF_RULE_IN : NPF_RULE_OUT;
 	npf_ruleset_t *rlset = mainrlset;
--- a/sys/net/npf/npf_session.c	Mon Aug 13 17:09:49 2012 +0000
+++ b/sys/net/npf/npf_session.c	Mon Aug 13 17:49:51 2012 +0000
@@ -1,4 +1,4 @@
-/*	$NetBSD: npf_session.c,v 1.10.4.5 2012/07/25 20:45:24 jdc Exp $	*/
+/*	$NetBSD: npf_session.c,v 1.10.4.6 2012/08/13 17:49:52 riz Exp $	*/
 
 /*-
  * Copyright (c) 2010-2012 The NetBSD Foundation, Inc.
@@ -80,7 +80,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: npf_session.c,v 1.10.4.5 2012/07/25 20:45:24 jdc Exp $");
+__KERNEL_RCSID(0, "$NetBSD: npf_session.c,v 1.10.4.6 2012/08/13 17:49:52 riz Exp $");
 
 #include <sys/param.h>
 #include <sys/types.h>
@@ -107,18 +107,24 @@
  * WARNING: update npf_session_restore() when adding fields.
  */
 
+struct npf_secomid;
+typedef struct npf_secomid npf_secomid_t;
+
 typedef struct {
-	/* Session entry node and backpointer to the actual session. */
+	/* Session entry node and back-pointer to the actual session. */
 	rb_node_t		se_rbnode;
-	npf_session_t *		se_backptr;
+	union {
+		npf_session_t *	se_backptr;
+		void *		se_common_id;
+	};
 	/* Size of the addresses. */
 	int			se_alen;
 	/* Source and destination addresses. */
 	npf_addr_t		se_src_addr;
 	npf_addr_t		se_dst_addr;
 	/* Source and destination ports (TCP / UDP) or generic IDs. */
-	uint32_t		se_src_id;
-	uint32_t		se_dst_id;
+	uint16_t		se_src_id;
+	uint16_t		se_dst_id;
 } npf_sentry_t;
 
 struct npf_session {
@@ -128,8 +134,12 @@
 	/* Entry in the session hash or G/C list. */
 	LIST_ENTRY(npf_session)	s_list;
 	u_int			s_refcnt;
-	/* Session type.  Supported: TCP, UDP, ICMP. */
-	int			s_type;
+	/* Protocol and interface (common IDs). */
+	struct npf_secomid {
+		uint16_t	proto;
+		uint16_t	if_idx;
+	} s_common_id;
+	/* Flags and the protocol state. */
 	int			s_flags;
 	npf_state_t		s_state;
 	/* Association of rule procedure data. */
@@ -228,7 +238,8 @@
 
 /*
  * Session hash table and RB-tree helper routines.
- * Order: (src.id, dst.id, src.addr, dst.addr), where (node1 < node2).
+ * The order is (src.id, dst.id, src.addr, dst.addr, common_id),
+ * where (node1 < node2) shall be negative.
  */
 
 static signed int
@@ -240,7 +251,7 @@
 	int ret;
 
 	/*
-	 * Ports are the main criteria and are first.
+	 * Ports are expected to vary most, therefore they are first.
 	 */
 	if (sen1->se_src_id != sen2->se_src_id) {
 		return (sen1->se_src_id < sen2->se_src_id) ? -1 : 1;
@@ -248,6 +259,7 @@
 	if (sen1->se_dst_id != sen2->se_dst_id) {
 		return (sen1->se_dst_id < sen2->se_dst_id) ? -1 : 1;
 	}
+
 	/*
 	 * Note that hash should minimise differentiation on addresses.
 	 */
@@ -260,7 +272,10 @@
 	if ((ret = memcmp(&sen1->se_dst_addr, &sen2->se_dst_addr, sz)) != 0) {
 		return ret;
 	}
-	return 0;
+
+	const npf_secomid_t *id1 = &sen1->se_backptr->s_common_id;
+	const npf_secomid_t *id2 = ctx ? ctx : &sen2->se_backptr->s_common_id;
+	return memcmp(id1, id2, sizeof(npf_secomid_t));
 }
 
 static signed int
@@ -270,7 +285,7 @@
 	const npf_sentry_t * const sen2 = key;
 
 	KASSERT(sen1->se_alen != 0 && sen2->se_alen != 0);
-	return sess_rbtree_cmp_nodes(NULL, sen1, sen2);
+	return sess_rbtree_cmp_nodes(sen2->se_common_id, sen1, sen2);
 }
 
 static const rb_tree_ops_t sess_rbtree_ops = {
@@ -281,13 +296,17 @@
 };
 
 static inline npf_sehash_t *
-sess_hash_bucket(npf_sehash_t *stbl, const int proto, npf_sentry_t *sen)
+sess_hash_bucket(npf_sehash_t *stbl, const npf_secomid_t *scid,
+    const npf_sentry_t *sen)
 {
 	const int sz = sen->se_alen;
 	uint32_t hash, mix;
 
-	/* Sum protocol and both addresses (for both directions). */
-	mix = proto + npf_addr_sum(sz, &sen->se_src_addr, &sen->se_dst_addr);
+	/*
+	 * Sum protocol, interface and both addresses (for both directions).
+	 */
+	mix = scid->proto + scid->if_idx;
+	mix += npf_addr_sum(sz, &sen->se_src_addr, &sen->se_dst_addr);
 	hash = hash32_buf(&mix, sizeof(uint32_t), HASH32_BUF_INIT);
 	return &stbl[hash & SESS_HASH_MASK];
 }
@@ -434,12 +453,13 @@
 }
 
 /*
- * npf_session_inspect: look if there is an established session (connection).
+ * npf_session_inspect: lookup for an established session (connection).
  *
  * => If found, we will hold a reference for caller.
  */
 npf_session_t *
-npf_session_inspect(npf_cache_t *npc, nbuf_t *nbuf, const int di, int *error)
+npf_session_inspect(npf_cache_t *npc, nbuf_t *nbuf, const ifnet_t *ifp,
+    const int di, int *error)
 {
 	npf_sehash_t *sh;
 	npf_sentry_t *sen;
@@ -473,7 +493,7 @@
 	}
 
 	/* Note: take protocol from the key. */
-	const int proto = npf_cache_ipproto(key);
+	const u_int proto = npf_cache_ipproto(key);
 
 	switch (proto) {
 	case IPPROTO_TCP: {
@@ -515,10 +535,19 @@
 	senkey.se_alen = key->npc_alen;
 
 	/*
+	 * Note: this is a special case where we use common ID pointer
+	 * to pass the structure for the key comparator.
+	 */
+	npf_secomid_t scid;
+	memset(&scid, 0, sizeof(npf_secomid_t));
+	scid = (npf_secomid_t){ .proto = proto, .if_idx = ifp->if_index };
+	senkey.se_common_id = &scid;
+
+	/*
 	 * Get a hash bucket from the cached key data.
 	 * Pre-check if there are any entries in the hash table.
 	 */
-	sh = sess_hash_bucket(sess_hashtbl, proto, &senkey);
+	sh = sess_hash_bucket(sess_hashtbl, &scid, &senkey);
 	if (sh->sh_count == 0) {
 		return NULL;
 	}
@@ -531,6 +560,8 @@
 		return NULL;
 	}
 	se = sen->se_backptr;
+	KASSERT(se->s_common_id.proto == proto);
+	KASSERT(se->s_common_id.if_idx == ifp->if_index);
 	flags = se->s_flags;
 
 	/* Check if session is active and not expired. */
@@ -539,7 +570,7 @@
 		return NULL;
 	}
 
-	/* Match session entry and packet directions. */
+	/* Match directions of the session entry and the packet. */
 	const bool sforw = (sen == &se->s_forw_entry);
 	const bool pforw = (flags & PFIL_ALL) == di;
 	if (__predict_false(sforw != pforw)) {
@@ -569,14 +600,15 @@
  * => Session will be activated on the first reference release.
  */
 npf_session_t *
-npf_session_establish(const npf_cache_t *npc, nbuf_t *nbuf, const int di)
+npf_session_establish(const npf_cache_t *npc, nbuf_t *nbuf,
+    const ifnet_t *ifp, const int di)
 {
 	const struct tcphdr *th;
 	const struct udphdr *uh;
 	npf_sentry_t *fw, *bk;
 	npf_sehash_t *sh;
 	npf_session_t *se;
-	int proto, alen;
+	u_int proto, alen;
 	bool ok;
 
 	/*
@@ -604,6 +636,12 @@
 	se->s_rproc = NULL;
 	se->s_nat = NULL;
 
+	/* Initialize protocol state. */
+	if (!npf_state_init(npc, nbuf, &se->s_state)) {
+		ok = false;
+		goto out;
+	}
+
 	/* Unique IDs: IP addresses.  Setup "forwards" entry first. */
 	KASSERT(npf_iscached(npc, NPC_IP46));
 	alen = npc->npc_alen;
@@ -611,15 +649,11 @@
 	memcpy(&fw->se_src_addr, npc->npc_srcip, alen);
 	memcpy(&fw->se_dst_addr, npc->npc_dstip, alen);
 
-	/* Initialize protocol state. */
-	if (!npf_state_init(npc, nbuf, &se->s_state)) {
-		ok = false;
-		goto out;
-	}
-
-	/* Procotol. */
+	/* Protocol and interface. */
 	proto = npf_cache_ipproto(npc);
-	se->s_type = proto;
+	memset(&se->s_common_id, 0, sizeof(npf_secomid_t));
+	se->s_common_id.proto = proto;
+	se->s_common_id.if_idx = ifp->if_index;
 
 	switch (proto) {
 	case IPPROTO_TCP:
@@ -679,8 +713,8 @@
 	/*
 	 * Insert the session and both entries into the tree.
 	 */
-	sh = sess_hash_bucket(sess_hashtbl, se->s_type, fw);
-	KASSERT(sh == sess_hash_bucket(sess_hashtbl, se->s_type, bk));
+	sh = sess_hash_bucket(sess_hashtbl, &se->s_common_id, fw);
+	KASSERT(sh == sess_hash_bucket(sess_hashtbl, &se->s_common_id, bk));
 
 	rw_enter(&sh->sh_lock, RW_WRITER);
 	ok = (rb_tree_insert_node(&sh->sh_tree, fw) == fw);
@@ -759,7 +793,7 @@
 	 * never happen in practice, though.
 	 */
 	sen = &se->s_back_entry;
-	sh = sess_hash_bucket(sess_hashtbl, se->s_type, sen);
+	sh = sess_hash_bucket(sess_hashtbl, &se->s_common_id, sen);
 
 	rw_enter(&sh->sh_lock, RW_WRITER);
 	rb_tree_remove_node(&sh->sh_tree, sen);
@@ -784,7 +818,7 @@
 			sen->se_src_id = tport;
 		}
 	}
-	sh = sess_hash_bucket(sess_hashtbl, se->s_type, sen);
+	sh = sess_hash_bucket(sess_hashtbl, &se->s_common_id, sen);
 
 	/* Insert into the new bucket. */
 	rw_enter(&sh->sh_lock, RW_WRITER);
@@ -878,7 +912,8 @@
 static inline bool
 npf_session_expired(const npf_session_t *se, const struct timespec *tsnow)
 {
-	const int etime = npf_state_etime(&se->s_state, se->s_type);
+	const u_int proto = se->s_common_id.proto;
+	const int etime = npf_state_etime(&se->s_state, proto);
 	struct timespec tsdiff;
 
 	if (__predict_false(se->s_flags & SE_EXPIRE)) {
@@ -1120,11 +1155,11 @@
 
 	/*
 	 * Find a hash bucket and insert each entry.
-	 * Warning: must reset back pointer.
+	 * Warning: must reset back pointers.
 	 */
 	fw = &se->s_forw_entry;
 	fw->se_backptr = se;
-	fsh = sess_hash_bucket(stbl, se->s_type, fw);
+	fsh = sess_hash_bucket(stbl, &se->s_common_id, fw);
 	if (rb_tree_insert_node(&fsh->sh_tree, fw) != fw) {
 		error = EINVAL;
 		goto out;
@@ -1133,7 +1168,7 @@
 
 	bk = &se->s_back_entry;
 	bk->se_backptr = se;
-	bsh = sess_hash_bucket(stbl, se->s_type, bk);
+	bsh = sess_hash_bucket(stbl, &se->s_common_id, bk);
 	if (rb_tree_insert_node(&bsh->sh_tree, bk) != bk) {
 		rb_tree_remove_node(&fsh->sh_tree, fw);
 		error = EINVAL;
@@ -1173,15 +1208,16 @@
 		RB_TREE_FOREACH(sen, &sh->sh_tree) {
 			struct timespec tsdiff;
 			struct in_addr ip;
-			int etime;
+			int proto, etime;
 
 			se = sen->se_backptr;
+			proto = se->s_common_id.proto;
 			timespecsub(&tsnow, &se->s_atime, &tsdiff);
-			etime = npf_state_etime(&se->s_state, se->s_type);
+			etime = npf_state_etime(&se->s_state, proto);
 
 			printf("    %p[%p]: %s proto %d flags 0x%x tsdiff %d "
 			    "etime %d\n", sen, se, sen == &se->s_forw_entry ?
-			    "forw" : "back",  se->s_type, se->s_flags,
+			    "forw" : "back", proto, se->s_flags,
 			    (int)tsdiff.tv_sec, etime);
 			memcpy(&ip, &sen->se_src_addr, sizeof(ip));
 			printf("\tsrc (%s, %d) ",
--- a/sys/net/npf/npf_state.c	Mon Aug 13 17:09:49 2012 +0000
+++ b/sys/net/npf/npf_state.c	Mon Aug 13 17:49:51 2012 +0000
@@ -1,4 +1,4 @@
-/*	$NetBSD: npf_state.c,v 1.6.4.4 2012/07/25 20:45:24 jdc Exp $	*/
+/*	$NetBSD: npf_state.c,v 1.6.4.5 2012/08/13 17:49:52 riz Exp $	*/
 
 /*-
  * Copyright (c) 2010-2012 The NetBSD Foundation, Inc.
@@ -34,7 +34,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: npf_state.c,v 1.6.4.4 2012/07/25 20:45:24 jdc Exp $");
+__KERNEL_RCSID(0, "$NetBSD: npf_state.c,v 1.6.4.5 2012/08/13 17:49:52 riz Exp $");
 
 #include <sys/param.h>
 #include <sys/systm.h>
@@ -107,7 +107,7 @@
 	default:
 		ret = false;
 	}
-	NPF_TCP_STATE_SAMPLE(nst, ret);
+	NPF_STATE_SAMPLE(nst, ret);
 	return ret;
 }
 
@@ -148,7 +148,7 @@
 	default:
 		ret = false;
 	}
-	NPF_TCP_STATE_SAMPLE(nst, ret);
+	NPF_STATE_SAMPLE(nst, ret);
 	mutex_exit(&nst->nst_lock);
 
 	return ret;
--- a/sys/net/npf/npf_tableset.c	Mon Aug 13 17:09:49 2012 +0000
+++ b/sys/net/npf/npf_tableset.c	Mon Aug 13 17:49:51 2012 +0000
@@ -1,4 +1,4 @@
-/*	$NetBSD: npf_tableset.c,v 1.9.2.4 2012/07/16 22:13:25 riz Exp $	*/
+/*	$NetBSD: npf_tableset.c,v 1.9.2.5 2012/08/13 17:49:52 riz Exp $	*/
 
 /*-
  * Copyright (c) 2009-2012 The NetBSD Foundation, Inc.
@@ -38,7 +38,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: npf_tableset.c,v 1.9.2.4 2012/07/16 22:13:25 riz Exp $");
+__KERNEL_RCSID(0, "$NetBSD: npf_tableset.c,v 1.9.2.5 2012/08/13 17:49:52 riz Exp $");
 
 #include <sys/param.h>
 #include <sys/types.h>
@@ -55,7 +55,7 @@
 #include "npf_impl.h"
 
 /*
- * Table entry, table and key-wrapper structures.
+ * Table structures.
  */
 
 struct npf_tblent {
@@ -76,7 +76,7 @@
 	u_int			t_refcnt;
 	/* Table ID. */
 	u_int			t_id;
-	/* The storage type can be: 1. Hash 2. RB-tree. */
+	/* The storage type can be: a) hash b) tree. */
 	int			t_type;
 	struct npf_hashl *	t_hashl;
 	u_long			t_hashmask;
@@ -95,7 +95,7 @@
 {
 
 	tblent_cache = pool_cache_init(sizeof(npf_tblent_t), coherency_unit,
-	    0, 0, "npftenpl", NULL, IPL_NONE, NULL, NULL, NULL);
+	    0, 0, "npftblpl", NULL, IPL_NONE, NULL, NULL, NULL);
 }
 
 void
--- a/usr.sbin/npf/npfctl/npf_build.c	Mon Aug 13 17:09:49 2012 +0000
+++ b/usr.sbin/npf/npfctl/npf_build.c	Mon Aug 13 17:49:51 2012 +0000
@@ -1,4 +1,4 @@
-/*	$NetBSD: npf_build.c,v 1.4.2.5 2012/07/25 20:45:23 jdc Exp $	*/
+/*	$NetBSD: npf_build.c,v 1.4.2.6 2012/08/13 17:49:52 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.5 2012/07/25 20:45:23 jdc Exp $");
+__RCSID("$NetBSD: npf_build.c,v 1.4.2.6 2012/08/13 17:49:52 riz Exp $");
 
 #include <sys/types.h>
 #include <sys/ioctl.h>
@@ -63,14 +63,13 @@
 }
 
 int
-npfctl_config_send(int fd)
+npfctl_config_send(int fd, const char *out)
 {
 	int error;
 
-	if (!fd) {
-		const char *outconf = "/tmp/npf.plist";
-		_npf_config_setsubmit(npf_conf, outconf);
-		printf("\nSaving to %s\n", outconf);
+	if (out) {
+		_npf_config_setsubmit(npf_conf, out);
+		printf("\nSaving to %s\n", out);
 	}
 	if (!defgroup_set) {
 		errx(EXIT_FAILURE, "default group was not defined");
@@ -85,6 +84,24 @@
 	return error;
 }
 
+unsigned long
+npfctl_debug_addif(const char *ifname)
+{
+	char tname[] = "npftest";
+	const size_t tnamelen = sizeof(tname) - 1;
+
+	if (!npf_debug || strncmp(ifname, tname, tnamelen) != 0) {
+		return 0;
+	}
+	struct ifaddrs ifa = {
+		.ifa_name = __UNCONST(ifname),
+		.ifa_flags = 0
+	};
+	unsigned long if_idx = atol(ifname + tnamelen) + 1;
+	_npf_debug_addif(npf_conf, &ifa, if_idx);
+	return if_idx;
+}
+
 bool
 npfctl_table_exists_p(const char *id)
 {
@@ -386,9 +403,6 @@
 
 		if (log) {
 			u_int if_idx = npfctl_find_ifindex(aval);
-			if (!if_idx) {
-				yyerror("unknown interface '%s'", aval);
-			}
 			_npf_rproc_setlog(rp, if_idx);
 			return;
 		}
@@ -484,18 +498,71 @@
 }
 
 /*
- * npfctl_build_nat: create a NAT policy of a specified type with a
- * given filter options.
+ * npfctl_build_onenat: create a single NAT policy of a specified
+ * type with a given filter options.
+ */
+static void
+npfctl_build_nat(int type, u_int if_idx, sa_family_t family,
+    const addr_port_t *ap, const filt_opts_t *fopts, bool binat)
+{
+	const opt_proto_t op = { .op_proto = -1, .op_opts = NULL };
+	fam_addr_mask_t *am;
+	in_port_t port;
+	nl_nat_t *nat;
+
+	if (!ap->ap_netaddr) {
+		yyerror("%s network segment is not specified",
+		    type == NPF_NATIN ? "inbound" : "outbound");
+	}
+	am = npfctl_get_singlefam(ap->ap_netaddr);
+	if (am->fam_family != family) {
+		yyerror("IPv6 NAT is not supported");
+	}
+
+	switch (type) {
+	case NPF_NATOUT:
+		/*
+		 * Outbound NAT (or source NAT) policy, usually used for the
+		 * traditional NAPT.  If it is a half for bi-directional NAT,
+		 * then no port translation with mapping.
+		 */
+		nat = npf_nat_create(NPF_NATOUT, !binat ?
+		    (NPF_NAT_PORTS | NPF_NAT_PORTMAP) : 0,
+		    if_idx, &am->fam_addr, am->fam_family, 0);
+		break;
+	case NPF_NATIN:
+		/*
+		 * Inbound NAT (or destination NAT).  Unless bi-NAT, a port
+		 * must be specified, since it has to be redirection.
+		 */
+		port = 0;
+		if (!binat) {
+			if (!ap->ap_portrange) {
+				yyerror("inbound port is not specified");
+			}
+			port = npfctl_get_singleport(ap->ap_portrange);
+		}
+		nat = npf_nat_create(NPF_NATIN, !binat ? NPF_NAT_PORTS : 0,
+		    if_idx, &am->fam_addr, am->fam_family, port);
+		break;
+	default:
+		assert(false);
+	}
+
+	npfctl_build_ncode(nat, family, &op, fopts, false);
+	npf_nat_insert(npf_conf, nat, NPF_PRI_NEXT);
+}
+
+/*
+ * npfctl_build_nat: validate and create NAT policies.
  */
 void
-npfctl_build_nat(int sd, int type, u_int if_idx, const addr_port_t *ap1,
+npfctl_build_natseg(int sd, int type, u_int if_idx, const addr_port_t *ap1,
     const addr_port_t *ap2, const filt_opts_t *fopts)
 {
-	const opt_proto_t op = { .op_proto = -1, .op_opts = NULL };
-	fam_addr_mask_t *am1 = NULL, *am2 = NULL;
+	sa_family_t af = AF_INET;
 	filt_opts_t imfopts;
-	sa_family_t family;
-	nl_nat_t *nat;
+	bool binat;
 
 	if (sd == NPFCTL_NAT_STATIC) {
 		yyerror("static NAT is not yet supported");
@@ -503,89 +570,33 @@
 	assert(sd == NPFCTL_NAT_DYNAMIC);
 	assert(if_idx != 0);
 
-	family = AF_INET;
-
-	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 != family) {
-			yyerror("IPv6 NAT is not supported");
-		}
-		assert(am1 != NULL);
-	}
-
-	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);
-	}
+	/*
+	 * Bi-directional NAT is a combination of inbound NAT and outbound
+	 * NAT policies.  Note that the translation address is local IP and
+	 * the filter criteria is inverted accordingly.
+	 */
+	binat = (NPF_NATIN | NPF_NATOUT) == type;
 
 	/*
-	 * If filter criteria is not specified explicitly, apply implicit
+	 * If the filter criteria is not specified explicitly, apply implicit
 	 * filtering according to the given network segements.
+	 *
+	 * Note: filled below, depending on the type.
 	 */
 	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 NPF_NATIN:
-		assert(am1 != NULL);
-		/*
-		 * Redirection: an inbound NAT with a specific port.
-		 */
-		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, &am1->fam_addr, am1->fam_family, port);
-		break;
-
-	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.
-		 */
-		nat = npf_nat_create(NPF_NATIN, 0, if_idx,
-		    &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 NPF_NATOUT:
-		assert(am2 != NULL);
-		/*
-		 * Traditional NAPT: an outbound NAT policy with port.
-		 * If this is another half for bi-directional NAT, then
-		 * no port translation with mapping.
-		 */
-		nat = npf_nat_create(NPF_NATOUT, type == NPF_NATOUT ?
-		    (NPF_NAT_PORTS | NPF_NAT_PORTMAP) : 0,
-		    if_idx, &am2->fam_addr, am2->fam_family, 0);
-		break;
-
-	default:
-		assert(false);
+	if (type & NPF_NATIN) {
+		memset(&imfopts, 0, sizeof(filt_opts_t));
+		memcpy(&imfopts.fo_to, ap2, sizeof(addr_port_t));
+		npfctl_build_nat(NPF_NATIN, if_idx, af, ap1, fopts, binat);
 	}
-	npfctl_build_ncode(nat, family, &op, fopts, false);
-	npf_nat_insert(npf_conf, nat, NPF_PRI_NEXT);
+	if (type & NPF_NATOUT) {
+		memset(&imfopts, 0, sizeof(filt_opts_t));
+		memcpy(&imfopts.fo_from, ap1, sizeof(addr_port_t));
+		npfctl_build_nat(NPF_NATOUT, if_idx, af, ap2, fopts, binat);
+	}
 }
 
 /*
--- a/usr.sbin/npf/npfctl/npf_data.c	Mon Aug 13 17:09:49 2012 +0000
+++ b/usr.sbin/npf/npfctl/npf_data.c	Mon Aug 13 17:49:51 2012 +0000
@@ -1,4 +1,4 @@
-/*	$NetBSD: npf_data.c,v 1.10.2.4 2012/07/25 20:45:23 jdc Exp $	*/
+/*	$NetBSD: npf_data.c,v 1.10.2.5 2012/08/13 17:49:52 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.2.4 2012/07/25 20:45:23 jdc Exp $");
+__RCSID("$NetBSD: npf_data.c,v 1.10.2.5 2012/08/13 17:49:52 riz Exp $");
 
 #include <sys/types.h>
 #include <sys/null.h>
@@ -60,7 +60,15 @@
 unsigned long
 npfctl_find_ifindex(const char *ifname)
 {
-	return if_nametoindex(ifname);
+	unsigned long if_idx = if_nametoindex(ifname);
+
+	if (!if_idx) {
+		if ((if_idx = npfctl_debug_addif(ifname)) != 0) {
+			return if_idx;
+		}
+		yyerror("unknown interface '%s'", ifname);
+	}
+	return if_idx;
 }
 
 static bool
@@ -308,6 +316,7 @@
 
 		if (!npfvar_add_element(vp, NPFVAR_FAM, &fam, sizeof(fam)))
 			goto out;
+
 	}
 	if (!gotif) {
 		yyerror("interface '%s' not found", ifname);
@@ -560,8 +569,8 @@
 npfvar_t *
 npfctl_parse_icmp(int proto, int type, int code)
 {
-	npfvar_t *vp=npfvar_create(".icmp");
-	int      varnum;
+	npfvar_t *vp = npfvar_create(".icmp");
+	int varnum;
 
 	switch (proto) {
 	case IPPROTO_ICMP:
@@ -585,4 +594,3 @@
 	npfvar_destroy(vp);
 	return NULL;
 }
-
--- a/usr.sbin/npf/npfctl/npf_parse.y	Mon Aug 13 17:09:49 2012 +0000
+++ b/usr.sbin/npf/npfctl/npf_parse.y	Mon Aug 13 17:49:51 2012 +0000
@@ -1,4 +1,4 @@
-/*	$NetBSD: npf_parse.y,v 1.3.2.5 2012/07/25 20:45:23 jdc Exp $	*/
+/*	$NetBSD: npf_parse.y,v 1.3.2.6 2012/08/13 17:49:52 riz Exp $	*/
 
 /*-
  * Copyright (c) 2011-2012 The NetBSD Foundation, Inc.
@@ -38,6 +38,8 @@
 
 #include "npfctl.h"
 
+#define	YYSTACKSIZE	4096
+
 const char *		yyfilename;
 
 extern int		yylineno, yycolumn;
@@ -277,11 +279,11 @@
 map
 	: MAP ifindex map_sd mapseg map_type mapseg PASS filt_opts
 	{
-		npfctl_build_nat($3, $5, $2, &$4, &$6, &$8);
+		npfctl_build_natseg($3, $5, $2, &$4, &$6, &$8);
 	}
 	| MAP ifindex map_sd mapseg map_type mapseg
 	{
-		npfctl_build_nat($3, $5, $2, &$4, &$6, NULL);
+		npfctl_build_natseg($3, $5, $2, &$4, &$6, NULL);
 	}
 	;
 
--- a/usr.sbin/npf/npfctl/npfctl.c	Mon Aug 13 17:09:49 2012 +0000
+++ b/usr.sbin/npf/npfctl/npfctl.c	Mon Aug 13 17:49:51 2012 +0000
@@ -1,4 +1,4 @@
-/*	$NetBSD: npfctl.c,v 1.10.2.4 2012/07/25 20:45:23 jdc Exp $	*/
+/*	$NetBSD: npfctl.c,v 1.10.2.5 2012/08/13 17:49:52 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.2.4 2012/07/25 20:45:23 jdc Exp $");
+__RCSID("$NetBSD: npfctl.c,v 1.10.2.5 2012/08/13 17:49:52 riz Exp $");
 
 #include <sys/ioctl.h>
 #include <sys/stat.h>
@@ -347,7 +347,7 @@
 	case NPFCTL_RELOAD:
 		npfctl_config_init(false);
 		npfctl_parsecfg(argc < 3 ? NPF_CONF_PATH : argv[2]);
-		ret = npfctl_config_send(fd);
+		ret = npfctl_config_send(fd, NULL);
 		if (ret) {
 			errx(EXIT_FAILURE, "ioctl: %s", strerror(ret));
 		}
@@ -398,9 +398,11 @@
 
 	if (strcmp(cmd, "debug") == 0) {
 		const char *cfg = argc > 2 ? argv[2] : "/etc/npf.conf";
+		const char *out = argc > 3 ? argv[3] : "/tmp/npf.plist";
+
 		npfctl_config_init(true);
 		npfctl_parsecfg(cfg);
-		npfctl_config_send(0);
+		npfctl_config_send(0, out);
 		return EXIT_SUCCESS;
 	}
 
--- a/usr.sbin/npf/npfctl/npfctl.h	Mon Aug 13 17:09:49 2012 +0000
+++ b/usr.sbin/npf/npfctl/npfctl.h	Mon Aug 13 17:49:51 2012 +0000
@@ -1,4 +1,4 @@
-/*	$NetBSD: npfctl.h,v 1.11.2.5 2012/07/25 20:45:23 jdc Exp $	*/
+/*	$NetBSD: npfctl.h,v 1.11.2.6 2012/08/13 17:49:52 riz Exp $	*/
 
 /*-
  * Copyright (c) 2009-2012 The NetBSD Foundation, Inc.
@@ -161,14 +161,15 @@
 #define	NPFCTL_NAT_STATIC	2
 
 void		npfctl_config_init(bool);
-int		npfctl_config_send(int);
+int		npfctl_config_send(int, const char *);
 int		npfctl_config_show(int);
+unsigned long	npfctl_debug_addif(const char *);
 
 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, int, u_int, const addr_port_t *,
+void		npfctl_build_natseg(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 *);
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr.sbin/npf/npftest/README	Mon Aug 13 17:49:51 2012 +0000
@@ -0,0 +1,29 @@
+$NetBSD: README,v 1.1.2.2 2012/08/13 17:49:52 riz Exp $
+
+npftest - a tool for regression testing and debugging NPF.
+It uses RUMP framework to run NPF kernel module in the userspace.
+
+---
+
+Test:
+
+npfctl debug npftest.conf /tmp/npf.plist
+npftest -c /tmp/npf.plist -t
+
+Stream:
+
+tcpdump -w stream.pcap -i $INTERFACE "host $HOST and tcp"
+npfctl debug
+npftest -c /tmp/npf.plist -s stream.pcap -o stream_npf_data.txt
+
+---
+
+Update RUMP libraries once the kernel side has been changed.  Hint:
+
+cd src/sys/net/npf
+sudo make includes
+
+cd src/sys/rump/dev/lib/libnpf
+make distclean
+MKDEBUG=yes MKDEBUGLIB=yes DBG=-g make -j8
+sudo MKDEBUG=yes MKDEBUGLIB=yes DBG=-g make install
--- a/usr.sbin/npf/npftest/libnpftest/Makefile	Mon Aug 13 17:09:49 2012 +0000
+++ b/usr.sbin/npf/npftest/libnpftest/Makefile	Mon Aug 13 17:49:51 2012 +0000
@@ -15,6 +15,8 @@
 SRCS+=		npf_processor_test.c
 SRCS+=		npf_table_test.c
 SRCS+=		npf_state_test.c
+SRCS+=		npf_rule_test.c
+SRCS+=		npf_nat_test.c
 
 CPPFLAGS+=	-D_NPF_TESTING
 CPPFLAGS+=	-I${.CURDIR}/../../../../sys/net/npf
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr.sbin/npf/npftest/libnpftest/npf_nat_test.c	Mon Aug 13 17:49:51 2012 +0000
@@ -0,0 +1,210 @@
+/*	$NetBSD: npf_nat_test.c,v 1.1.2.2 2012/08/13 17:49:53 riz Exp $	*/
+
+/*
+ * NPF NAT test.
+ *
+ * Public Domain.
+ */
+
+#include <sys/types.h>
+
+#include "npf_impl.h"
+#include "npf_test.h"
+
+#define	IFNAME_EXT	"npftest0"
+#define	IFNAME_INT	"npftest1"
+
+#define	LOCAL_IP1	"10.1.1.1"
+#define	LOCAL_IP2	"10.1.1.2"
+
+/* Note: RFC 5737 compliant addresses. */
+#define	PUB_IP1		"192.0.2.1"
+#define	PUB_IP2		"192.0.2.2"
+#define	REMOTE_IP1	"192.0.2.3"
+#define	REMOTE_IP2	"192.0.2.4"
+
+#define	RESULT_PASS	0
+#define	RESULT_BLOCK	ENETUNREACH
+
+#define	NPF_BINAT	(NPF_NATIN | NPF_NATOUT)
+
+static const struct test_case {
+	const char *	src;
+	in_port_t	sport;
+	const char *	dst;
+	in_port_t	dport;
+	int		ttype;
+	const char *	ifname;
+	int		di;
+	int		ret;
+	const char *	taddr;
+	in_port_t	tport;
+} test_cases[] = {
+
+	/*
+	 * Traditional NAPT (outbound NAT):
+	 *	map $ext_if dynamic $local_net -> $pub_ip1
+	 */
+	{
+		LOCAL_IP1,	15000,		REMOTE_IP1,	7000,
+		NPF_NATOUT,	IFNAME_EXT,	PFIL_OUT,
+		RESULT_PASS,	PUB_IP1,	53472
+	},
+	{
+		LOCAL_IP1,	15000,		REMOTE_IP1,	7000,
+		NPF_NATOUT,	IFNAME_EXT,	PFIL_OUT,
+		RESULT_PASS,	PUB_IP1,	53472
+	},
+	{
+		LOCAL_IP1,	15000,		REMOTE_IP1,	7000,
+		NPF_NATOUT,	IFNAME_EXT,	PFIL_IN,
+		RESULT_BLOCK,	NULL,		0
+	},
+	{
+		REMOTE_IP1,	7000,		LOCAL_IP1,	15000,
+		NPF_NATOUT,	IFNAME_EXT,	PFIL_IN,
+		RESULT_BLOCK,	NULL,		0
+	},
+	{
+		REMOTE_IP1,	7000,		PUB_IP1,	53472,
+		NPF_NATOUT,	IFNAME_INT,	PFIL_IN,
+		RESULT_BLOCK,	NULL,		0
+	},
+	{
+		REMOTE_IP1,	7000,		PUB_IP1,	53472,
+		NPF_NATOUT,	IFNAME_EXT,	PFIL_IN,
+		RESULT_PASS,	LOCAL_IP1,	15000
+	},
+
+	/*
+	 * NAT redirect (inbound NAT):
+	 *	map $ext_if dynamic $local_ip1 port 8000 <- $pub_ip1 port 8000
+	 */
+	{
+		REMOTE_IP2,	16000,		PUB_IP1,	8000,
+		NPF_NATIN,	IFNAME_EXT,	PFIL_IN,
+		RESULT_PASS,	LOCAL_IP1,	6000
+	},
+	{
+		LOCAL_IP1,	6000,		REMOTE_IP2,	16000,
+		NPF_NATIN,	IFNAME_EXT,	PFIL_OUT,
+		RESULT_PASS,	PUB_IP1,	8000
+	},
+
+	/*
+	 * Bi-directional NAT (inbound + outbound NAT):
+	 *	map $ext_if dynamic $local_ip2 <-> $pub_ip2
+	 */
+	{
+		REMOTE_IP2,	17000,		PUB_IP2,	9000,
+		NPF_BINAT,	IFNAME_EXT,	PFIL_IN,
+		RESULT_PASS,	LOCAL_IP2,	9000
+	},
+	{
+		LOCAL_IP2,	9000,		REMOTE_IP2,	17000,
+		NPF_BINAT,	IFNAME_EXT,	PFIL_OUT,
+		RESULT_PASS,	PUB_IP2,	9000
+	},
+	{
+		LOCAL_IP2,	18000,		REMOTE_IP2,	9000,
+		NPF_BINAT,	IFNAME_EXT,	PFIL_OUT,
+		RESULT_PASS,	PUB_IP2,	18000
+	},
+	{
+		REMOTE_IP2,	9000,		PUB_IP2,	18000,
+		NPF_BINAT,	IFNAME_EXT,	PFIL_IN,
+		RESULT_PASS,	LOCAL_IP2,	18000
+	},
+
+};
+
+static bool
+nmatch_addr(const char *saddr, const struct in_addr *addr2)
+{
+	const in_addr_t addr1 = inet_addr(saddr);
+	return memcmp(&addr1, &addr2->s_addr, sizeof(in_addr_t)) != 0;
+}
+
+static bool
+checkresult(bool verbose, unsigned i, struct mbuf *m, int error)
+{
+	const struct test_case *t = &test_cases[i];
+	npf_cache_t npc = { .npc_info = 0 };
+
+	if (verbose) {
+		printf("packet %d (expected %d ret %d)\n", i+1, t->ret, error);
+	}
+	if (error) {
+		return error == t->ret;
+	}
+	if (!npf_cache_all(&npc, m)) {
+		printf("error: could not fetch the packet data");
+		return false;
+	}
+
+	const struct ip *ip = &npc.npc_ip.v4;
+	const struct udphdr *uh = &npc.npc_l4.udp;
+
+	if (verbose) {
+		printf("\tpost-translation: src %s (%d)",
+		    inet_ntoa(ip->ip_src), ntohs(uh->uh_sport));
+		printf(" dst %s (%d)\n",
+		    inet_ntoa(ip->ip_dst), ntohs(uh->uh_dport));
+	}
+
+	const bool forw = t->di == PFIL_OUT;
+	const char *saddr = forw ? t->taddr : t->src;
+	const char *daddr = forw ? t->dst : t->taddr;
+	in_addr_t sport = forw ? t->tport : t->sport;
+	in_addr_t dport = forw ? t->dport : t->tport;
+
+	bool defect = false;
+	defect |= nmatch_addr(saddr, &ip->ip_src);
+	defect |= sport != ntohs(uh->uh_sport);
+	defect |= nmatch_addr(daddr, &ip->ip_dst);
+	defect |= dport != ntohs(uh->uh_dport);
+
+	return !defect && error == t->ret;
+}
+
+static struct mbuf *
+fill_packet(const struct test_case *t)
+{
+	struct mbuf *m;
+	struct ip *ip;
+	struct udphdr *uh;
+
+	m = mbuf_construct(IPPROTO_UDP);
+	uh = mbuf_return_hdrs(m, false, &ip);
+	ip->ip_src.s_addr = inet_addr(t->src);
+	ip->ip_dst.s_addr = inet_addr(t->dst);
+	uh->uh_sport = htons(t->sport);
+	uh->uh_dport = htons(t->dport);
+	return m;
+}
+
+bool
+npf_nat_test(bool verbose)
+{
+	for (unsigned i = 0; i < __arraycount(test_cases); i++) {
+		const struct test_case *t = &test_cases[i];
+		ifnet_t *ifp = ifunit(t->ifname);
+		struct mbuf *m = fill_packet(t);
+		int error;
+		bool ret;
+
+		if (ifp == NULL) {
+			printf("Interface %s is not configured.\n", t->ifname);
+			return false;
+		}
+		error = npf_packet_handler(NULL, &m, ifp, t->di);
+		ret = checkresult(verbose, i, m, error);
+		if (m) {
+			m_freem(m);
+		}
+		if (!ret) {
+			return false;
+		}
+	}
+	return true;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr.sbin/npf/npftest/libnpftest/npf_rule_test.c	Mon Aug 13 17:49:51 2012 +0000
@@ -0,0 +1,130 @@
+/*	$NetBSD: npf_rule_test.c,v 1.1.2.2 2012/08/13 17:49:53 riz Exp $	*/
+
+/*
+ * NPF ruleset test.
+ *
+ * Public Domain.
+ */
+
+#include <sys/types.h>
+
+#include "npf_impl.h"
+#include "npf_test.h"
+
+#define	IFNAME_EXT	"npftest0"
+#define	IFNAME_INT	"npftest1"
+
+#define	RESULT_PASS	0
+#define	RESULT_BLOCK	ENETUNREACH
+
+static const struct test_case {
+	const char *	src;
+	const char *	dst;
+	const char *	ifname;
+	int		di;
+	int		stateful_ret;
+	int		ret;
+} test_cases[] = {
+
+	/* Stateful pass. */
+	{
+		.src = "10.1.1.1",		.dst = "10.1.1.2",
+		.ifname = IFNAME_INT,		.di = PFIL_OUT,
+		.stateful_ret = RESULT_PASS,	.ret = RESULT_PASS
+	},
+	{
+		.src = "10.1.1.2",		.dst = "10.1.1.1",
+		.ifname = IFNAME_INT,		.di = PFIL_IN,
+		.stateful_ret = RESULT_PASS,	.ret = RESULT_BLOCK
+	},
+
+	/* Pass forwards stream only. */
+	{
+		.src = "10.1.1.1",		.dst = "10.1.1.3",
+		.ifname = IFNAME_INT,		.di = PFIL_OUT,
+		.stateful_ret = RESULT_PASS,	.ret = RESULT_PASS
+	},
+	{
+		.src = "10.1.1.3",		.dst = "10.1.1.1",
+		.ifname = IFNAME_INT,		.di = PFIL_IN,
+		.stateful_ret = RESULT_BLOCK,	.ret = RESULT_BLOCK
+	},
+
+	/* Block. */
+	{	.src = "10.1.1.1",		.dst = "10.1.1.4",
+		.ifname = IFNAME_INT,		.di = PFIL_OUT,
+		.stateful_ret = RESULT_BLOCK,	.ret = RESULT_BLOCK
+	},
+
+};
+
+static struct mbuf *
+fill_packet(const struct test_case *t)
+{
+	struct mbuf *m;
+	struct ip *ip;
+	struct udphdr *uh;
+
+	m = mbuf_construct(IPPROTO_UDP);
+	uh = mbuf_return_hdrs(m, false, &ip);
+	ip->ip_src.s_addr = inet_addr(t->src);
+	ip->ip_dst.s_addr = inet_addr(t->dst);
+	uh->uh_sport = htons(9000);
+	uh->uh_dport = htons(9000);
+	return m;
+}
+
+static int
+npf_rule_raw_test(bool verbose, struct mbuf *m, ifnet_t *ifp, int di)
+{
+	npf_cache_t npc = { .npc_info = 0 };
+	npf_rule_t *rl;
+	int retfl, error;
+
+	npf_core_enter();
+	rl = npf_ruleset_inspect(&npc, m, npf_core_ruleset(),
+	    ifp, di, NPF_LAYER_3);
+	if (rl) {
+		if (verbose) {
+			npf_rulenc_dump(rl);
+		}
+		error = npf_rule_apply(&npc, m, rl, &retfl);
+	} else {
+		npf_core_exit();
+		error = ENOENT;
+	}
+	return error;
+}
+
+bool
+npf_rule_test(bool verbose)
+{
+	for (unsigned i = 0; i < __arraycount(test_cases); i++) {
+		const struct test_case *t = &test_cases[i];
+		ifnet_t *ifp = ifunit(t->ifname);
+		struct mbuf *m = fill_packet(t);
+		int serror, error;
+
+		if (ifp == NULL) {
+			printf("Interface %s is not configured.\n", t->ifname);
+			return false;
+		}
+
+		error = npf_rule_raw_test(verbose, m, ifp, t->di);
+		serror = npf_packet_handler(NULL, &m, ifp, t->di);
+
+		if (m) {
+			m_freem(m);
+		}
+
+		if (verbose) {
+			printf("Rule test %d, expected %d (stateful) and %d \n"
+			    "-> returned %d and %d.\n",
+			    i + 1, t->stateful_ret, t->ret, serror, error);
+		}
+		if (serror != t->stateful_ret || error != t->ret) {
+			return false;
+		}
+	}
+	return true;
+}
--- a/usr.sbin/npf/npftest/libnpftest/npf_test.h	Mon Aug 13 17:09:49 2012 +0000
+++ b/usr.sbin/npf/npftest/libnpftest/npf_test.h	Mon Aug 13 17:49:51 2012 +0000
@@ -25,6 +25,8 @@
 #include <net/ethertypes.h>
 
 int		npf_test_load(const void *);
+unsigned	npf_test_addif(const char *, unsigned, bool);
+unsigned	npf_test_getif(const char *);
 int		npf_test_handlepkt(const void *, size_t, unsigned,
 		    bool, int64_t *);
 
@@ -40,4 +42,7 @@
 bool		npf_table_test(bool);
 bool		npf_state_test(bool);
 
+bool		npf_rule_test(bool);
+bool		npf_nat_test(bool);
+
 #endif
--- a/usr.sbin/npf/npftest/libnpftest/npf_test_subr.c	Mon Aug 13 17:09:49 2012 +0000
+++ b/usr.sbin/npf/npftest/libnpftest/npf_test_subr.c	Mon Aug 13 17:49:51 2012 +0000
@@ -1,4 +1,4 @@
-/*	$NetBSD: npf_test_subr.c,v 1.1.2.3 2012/07/25 20:45:24 jdc Exp $	*/
+/*	$NetBSD: npf_test_subr.c,v 1.1.2.4 2012/08/13 17:49:53 riz Exp $	*/
 
 /*
  * NPF initialisation and handler routines.
@@ -25,6 +25,32 @@
 	return npfctl_reload(0, npf_dict);
 }
 
+unsigned
+npf_test_addif(const char *ifname, unsigned if_idx, bool verbose)
+{
+	ifnet_t *ifp = if_alloc(IFT_OTHER);
+
+	/*
+	 * This is a "fake" interface with explicitly set index.
+	 */
+	strlcpy(ifp->if_xname, ifname, sizeof(ifp->if_xname));
+	if (verbose) {
+		printf("+ Interface %s\n", ifp->if_xname);
+	}
+	ifp->if_dlt = DLT_NULL;
+	if_attach(ifp);
+	ifp->if_index = if_idx;
+	if_alloc_sadl(ifp);
+	return if_idx;
+}
+
+unsigned
+npf_test_getif(const char *ifname)
+{
+	ifnet_t *ifp = ifunit(ifname);
+	return ifp ? ifp->if_index : 0;
+}
+
 /*
  * State sampler - this routine is called from inside of NPF state engine.
  */
--- a/usr.sbin/npf/npftest/npfstream.c	Mon Aug 13 17:09:49 2012 +0000
+++ b/usr.sbin/npf/npftest/npfstream.c	Mon Aug 13 17:49:51 2012 +0000
@@ -1,4 +1,4 @@
-/*	$NetBSD: npfstream.c,v 1.1.2.3 2012/07/25 20:45:24 jdc Exp $	*/
+/*	$NetBSD: npfstream.c,v 1.1.2.4 2012/08/13 17:49:52 riz Exp $	*/
 
 /*
  * NPF stream processor.
@@ -44,10 +44,10 @@
 	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 *)((const char *)data + 4);
+	} else {
+		ip = (const struct ip *)(eth + 1);
 	}
-	ip = (const struct ip *)(eth + 1);
 	hlen = ip->ip_hl << 2;
 	th = (const struct tcphdr *)((const uint8_t *)ip + hlen);
 
--- a/usr.sbin/npf/npftest/npftest.c	Mon Aug 13 17:09:49 2012 +0000
+++ b/usr.sbin/npf/npftest/npftest.c	Mon Aug 13 17:49:51 2012 +0000
@@ -1,4 +1,4 @@
-/*	$NetBSD: npftest.c,v 1.3.2.2 2012/06/26 00:07:17 riz Exp $	*/
+/*	$NetBSD: npftest.c,v 1.3.2.3 2012/08/13 17:49:52 riz Exp $	*/
 
 /*
  * NPF testing framework.
@@ -30,13 +30,13 @@
 usage(void)
 {
 	printf("usage: %s: [ -q | -v ] [ -c <config> ] "
-	    "[ -i 'interfaces' ] < -b | -t | -s file >\n"
+	    "[ -i <interface> ] < -b | -t | -s file >\n"
 	    "\t-b: benchmark\n"
 	    "\t-t: regression test\n"
+	    "\t-s <file>: pcap stream\n"
 	    "\t-c <config>: NPF configuration file\n"
-	    "\t-i 'interfaces': interfaces to create\n"
+	    "\t-i <interface>: primary interface\n"
 	    "\t-q: quiet mode\n"
-	    "\t-s <file>: pcap stream\n"
 	    "\t-v: verbose mode\n",
 	    getprogname());
 	exit(EXIT_FAILURE);
@@ -56,48 +56,48 @@
 	}
 }
 
-#if 0
 static void
-construct_interfaces(char *ifs)
+load_npf_config_ifs(prop_dictionary_t dbg_dict)
 {
-	char *ifname, *addr, *mask, *sptr;
+	prop_dictionary_t ifdict;
+	prop_object_iterator_t it;
+	prop_array_t iflist;
 
-	/*
-	 * 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';
-		}
+	iflist = prop_dictionary_get(dbg_dict, "interfaces");
+	it = prop_array_iterator(iflist);
+	while ((ifdict = prop_object_iterator_next(it)) != NULL) {
+		const char *ifname;
+		unsigned if_idx;
 
-		/* Construct; next.. */
-		setup_rump_if(ifname, addr, mask);
-		ifname = strtok_r(NULL, ";", &sptr);
+		prop_dictionary_get_cstring_nocopy(ifdict, "name", &ifname);
+		prop_dictionary_get_uint32(ifdict, "idx", &if_idx);
+		(void)rumpns_npf_test_addif(ifname, if_idx, verbose);
 	}
+	prop_object_iterator_release(it);
 }
-#endif
 
 static void
 load_npf_config(const char *config)
 {
-	prop_dictionary_t npf_dict;
+	prop_dictionary_t npf_dict, dbg_dict;
 	void *xml;
 	int error;
 
+	/* Read the configuration from the specified file. */
 	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);
+
+	/* Inspect the debug data.  Create the interfaces, if any. */
+	dbg_dict = prop_dictionary_get(npf_dict, "debug");
+	if (dbg_dict) {
+		load_npf_config_ifs(dbg_dict);
+	}
 	prop_object_release(npf_dict);
 
+	/* Pass the XML configuration for NPF kernel component to load. */
 	error = rumpns_npf_test_load(xml);
 	if (error) {
 		errx(EXIT_FAILURE, "npf_test_load: %s\n", strerror(error));
@@ -109,18 +109,27 @@
 	}
 }
 
+/*
+ * Need to override for cprng_fast32(), since RUMP uses arc4random() for it.
+ */
+uint32_t
+arc4random(void)
+{
+	return random();
+}
+
 int
 main(int argc, char **argv)
 {
 	bool benchmark, test, ok;
-	char *config, *interfaces, *stream;
-	int ch;
+	char *config, *interface, *stream;
+	int idx = -1, ch;
 
 	benchmark = false;
 	test = false;
 
 	config = NULL;
-	interfaces = NULL;
+	interface = NULL;
 	stream = NULL;
 
 	verbose = false;
@@ -141,7 +150,7 @@
 			config = optarg;
 			break;
 		case 'i':
-			interfaces = optarg;
+			interface = optarg;
 			break;
 		case 's':
 			stream = optarg;
@@ -154,8 +163,11 @@
 		}
 	}
 
-	/* Either benchmark or test. */
-	if (benchmark == test && (!stream || !interfaces)) {
+	/*
+	 * Either benchmark or test.  If stream analysis, then the interface
+	 * is needed as well.
+	 */
+	if (benchmark == test && (stream && !interface)) {
 		usage();
 	}
 
@@ -169,6 +181,11 @@
 	if (config) {
 		load_npf_config(config);
 	}
+	if (interface && (idx = rumpns_npf_test_getif(interface)) == 0) {
+		errx(EXIT_FAILURE, "failed to find the interface");
+	}
+
+	srandom(1);
 
 	if (test) {
 		ok = rumpns_npf_nbuf_test(verbose);
@@ -184,13 +201,15 @@
 		result("state", ok);
 	}
 
+	if (test && config) {
+		ok = rumpns_npf_rule_test(verbose);
+		result("rule", ok);
+
+		ok = rumpns_npf_nat_test(verbose);
+		result("nat", 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);
 	}
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr.sbin/npf/npftest/npftest.conf	Mon Aug 13 17:49:51 2012 +0000
@@ -0,0 +1,41 @@
+# $NetBSD: npftest.conf,v 1.1.2.2 2012/08/13 17:49:52 riz Exp $
+
+$ext_if = "npftest0"
+$int_if = "npftest1"
+
+#
+# RFC 5737
+#
+
+$pub_ip1 = 192.0.2.1
+$pub_ip2 = 192.0.2.2
+
+$local_ip1 = 10.1.1.1
+$local_ip2 = 10.1.1.2
+$local_ip3 = 10.1.1.3
+$local_ip4 = 10.1.1.4
+
+$local_net = { 10.1.1.0/24 }
+$ports = { 8000, 9000 }
+
+map $ext_if dynamic $local_ip2 <-> $pub_ip2
+map $ext_if dynamic $local_net -> $pub_ip1
+map $ext_if dynamic $local_ip1 port 6000 <- $pub_ip1 port 8000
+
+group (interface $ext_if) {
+	pass stateful out final proto tcp flags S/SA all
+	pass stateful out final from $local_net
+	pass stateful in final to any port $ports
+	pass stateful in final proto icmp all
+	block all
+}
+
+group (interface $int_if) {
+	pass stateful out final to $local_ip2
+	pass out final to $local_ip3
+	block final to $local_ip4
+}
+
+group (default) {
+	block all
+}
--- a/usr.sbin/npf/npftest/npftest.h	Mon Aug 13 17:09:49 2012 +0000
+++ b/usr.sbin/npf/npftest/npftest.h	Mon Aug 13 17:49:51 2012 +0000
@@ -11,6 +11,8 @@
 #include <stdbool.h>
 
 int		rumpns_npf_test_load(const void *);
+unsigned	rumpns_npf_test_addif(const char *, unsigned, bool);
+unsigned	rumpns_npf_test_getif(const char *);
 int		rumpns_npf_test_handlepkt(const void *, size_t,
 		    unsigned, bool, int64_t *);
 
@@ -19,6 +21,9 @@
 bool		rumpns_npf_table_test(bool);
 bool		rumpns_npf_state_test(bool);
 
+bool		rumpns_npf_rule_test(bool);
+bool		rumpns_npf_nat_test(bool);
+
 int		process_stream(const char *, const char *, unsigned);
 
 #endif