NPF: trunk
authorrmind <rmind@NetBSD.org>
Sat, 09 Feb 2013 03:35:31 +0000
branchtrunk
changeset 216594 21d80b5345a3
parent 216593 d7eae14567c0
child 216595 44da59de0c7d
NPF: - Implement dynamic NPF rules. Controlled through npf(3) library of via npfctl rule command. A rule can be removed using a unique identifier, returned on addition, or using a key which is SHA1 hash of the rule. Adjust npftest and add a regression test. - Improvements to rule inspection mechanism. - Initial BPF support as an alternative to n-code. - Minor fixes; bump the version.
lib/libnpf/npf.3
lib/libnpf/npf.c
lib/libnpf/npf.h
sys/modules/npf/Makefile
sys/net/npf/files.npf
sys/net/npf/npf.c
sys/net/npf/npf.h
sys/net/npf/npf_alg.c
sys/net/npf/npf_alg_icmp.c
sys/net/npf/npf_conf.c
sys/net/npf/npf_ctl.c
sys/net/npf/npf_handler.c
sys/net/npf/npf_impl.h
sys/net/npf/npf_inet.c
sys/net/npf/npf_instr.c
sys/net/npf/npf_nat.c
sys/net/npf/npf_ncode.h
sys/net/npf/npf_processor.c
sys/net/npf/npf_rproc.c
sys/net/npf/npf_ruleset.c
sys/net/npf/npf_sendpkt.c
sys/net/npf/npf_session.c
sys/net/npf/npf_state.c
sys/net/npf/npf_tableset.c
sys/rump/net/lib/libnpf/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_disassemble.c
usr.sbin/npf/npfctl/npf_parse.y
usr.sbin/npf/npfctl/npf_scan.l
usr.sbin/npf/npfctl/npfctl.8
usr.sbin/npf/npfctl/npfctl.c
usr.sbin/npf/npfctl/npfctl.h
usr.sbin/npf/npftest/Makefile
usr.sbin/npf/npftest/README
usr.sbin/npf/npftest/libnpftest/npf_rule_test.c
usr.sbin/npf/npftest/npftest.conf
--- a/lib/libnpf/npf.3	Sat Feb 09 02:49:36 2013 +0000
+++ b/lib/libnpf/npf.3	Sat Feb 09 03:35:31 2013 +0000
@@ -1,6 +1,6 @@
-.\"	$NetBSD: npf.3,v 1.7 2012/12/24 00:35:56 wiz Exp $
+.\"	$NetBSD: npf.3,v 1.8 2013/02/09 03:35:33 rmind Exp $
 .\"
-.\" Copyright (c) 2011-2012 The NetBSD Foundation, Inc.
+.\" Copyright (c) 2011-2013 The NetBSD Foundation, Inc.
 .\" All rights reserved.
 .\"
 .\" This material is based upon work partially supported by The
@@ -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 December 23, 2012
+.Dd January 5, 2013
 .Dt NPF 3
 .Os
 .Sh NAME
@@ -50,12 +50,15 @@
 .Ft nl_rule_t *
 .Fn npf_rule_create "char *name" "uint32_t attr" "u_int if_idx"
 .Ft int
-.Fn npf_rule_setcode "nl_rule_t *rl" "int type" "const void *code" "size_t sz"
+.Fn npf_rule_setcode "nl_rule_t *rl" "int type" "const void *code" "size_t len"
+.Ft int
+.Fn npf_rule_setkey "nl_rule_t *rl" "int type" "const void *code" "size_t len"
 .Ft bool
 .Fn npf_rule_exists_p "nl_config_t *ncf" "const char *name"
 .Ft int
-.Fn npf_rule_insert "nl_config_t *ncf" " nl_rule_t *parent" \
-"nl_rule_t *rl" "pri_t pri"
+.Fn npf_rule_insert "nl_config_t *ncf" " nl_rule_t *parent" "nl_rule_t *rl"
+.Ft int
+.Fn npf_rule_setprio "nl_rule_t *rl" "pri_t pri"
 .Ft int
 .Fn npf_rule_setproc "nl_config_t *ncf" "nl_rule_t *rl" "const char *name"
 .Ft void
@@ -123,10 +126,6 @@
 Decision of this rule is "pass".
 If this attribute is not
 specified, then packet "block" (drop) is the default.
-.It Dv NPF_RULE_DEFAULT
-This a default rule in the ruleset.
-There can only be a
-single rule having this attribute set in the ruleset.
 .It Dv NPF_RULE_FINAL
 Indicates that on rule match, further processing of the
 ruleset should be stopped and this rule applied instantly.
@@ -150,21 +149,31 @@
 .Xr if_nametoindex 3 .
 Zero indicates any interface.
 .\" ---
-.It Fn npf_rule_setcode "rl" "type" "code" "sz"
+.It Fn npf_rule_setcode "rl" "type" "code" "len"
 Assign compiled code for the rule specified by
 .Fa rl ,
 used for filter criteria.
 Pointer to the binary code is specified by
 .Fa code ,
 and size of the memory area by
-.Fa sz .
+.Fa len .
 Type of the code is specified by
 .Fa type .
 Currently, only n-code is supported and
-.Dv NPF_CODE_NCODE
+.Dv NPF_CODE_NC
 should be passed.
 .\" ---
-.It Fn npf_rule_insert "ncf" "parent" "rl" "pri"
+.It Fn npf_rule_setkey "rl" "type" "key" "len"
+Assign a key for the rule specified by
+.Fa rl .
+Binary key is specified by
+.Fa key ,
+and its size by
+.Fa len .
+The size shall not exceed
+.Dv NPF_RULE_MAXKEYLEN .
+.\" ---
+.It Fn npf_rule_insert "ncf" "parent" "rl"
 Insert the rule into the set of parent rule specified by
 .Fa parent .
 If value of
@@ -172,15 +181,26 @@
 is
 .Dv NULL ,
 then insert into the main ruleset.
+.\" ---
+.It Fn npf_rule_setprio "rl" "pri"
+Set priority to the rule.
+Negative priorities are invalid.
 .Pp
 Priority is the order of the rule in the ruleset.
 Lower value means first to process, higher value - last to process.
-If multiple rules have the same priority - order is unspecified.
-A special constant
-.Dv NPF_PRI_NEXT
-may be passed to use the value of last used priority incremented by 1.
+If multiple rules are inserted with the same priority,
+the order is unspecified.
+.Pp
+The special constants
+.Dv NPF_PRI_FIRST
+and
+.Dv NPF_PRI_LAST
+can be passed to indicate that the rule should be inserted into the
+beginning or the end of the priority level 0 in the ruleset.
+All rules inserted using these constants will have the priority 0
+assigned and will share this level in the ordered way.
 .It Fn npf_rule_setproc "ncf" "rl" "name"
-Set procedure for the specified rule.
+Set a procedure for the specified rule.
 .It Fn npf_rule_destroy "rl"
 Destroy the given rule.
 .El
--- a/lib/libnpf/npf.c	Sat Feb 09 02:49:36 2013 +0000
+++ b/lib/libnpf/npf.c	Sat Feb 09 03:35:31 2013 +0000
@@ -1,7 +1,7 @@
-/*	$NetBSD: npf.c,v 1.15 2012/12/23 21:01:05 rmind Exp $	*/
+/*	$NetBSD: npf.c,v 1.16 2013/02/09 03:35:33 rmind Exp $	*/
 
 /*-
- * Copyright (c) 2010-2012 The NetBSD Foundation, Inc.
+ * Copyright (c) 2010-2013 The NetBSD Foundation, Inc.
  * All rights reserved.
  *
  * This material is based upon work partially supported by The
@@ -30,7 +30,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: npf.c,v 1.15 2012/12/23 21:01:05 rmind Exp $");
+__KERNEL_RCSID(0, "$NetBSD: npf.c,v 1.16 2013/02/09 03:35:33 rmind Exp $");
 
 #include <sys/types.h>
 #include <netinet/in_systm.h>
@@ -54,9 +54,6 @@
 	prop_array_t		ncf_rproc_list;
 	prop_array_t		ncf_table_list;
 	prop_array_t		ncf_nat_list;
-	/* Priority counters. */
-	pri_t			ncf_rule_pri;
-	pri_t			ncf_nat_pri;
 	/* Debug information. */
 	prop_dictionary_t	ncf_debug;
 	/* Error report. */
@@ -83,6 +80,8 @@
 	prop_dictionary_t	nxt_dict;
 };
 
+static prop_array_t	_npf_ruleset_transform(prop_array_t);
+
 /*
  * CONFIGURATION INTERFACE.
  */
@@ -101,9 +100,6 @@
 	ncf->ncf_table_list = prop_array_create();
 	ncf->ncf_nat_list = prop_array_create();
 
-	ncf->ncf_rule_pri = 1;
-	ncf->ncf_nat_pri = 1;
-
 	ncf->ncf_plist = NULL;
 	ncf->ncf_flush = false;
 
@@ -115,6 +111,7 @@
 {
 	const char *plist = ncf->ncf_plist;
 	prop_dictionary_t npf_dict;
+	prop_array_t rlset;
 	int error = 0;
 
 	npf_dict = prop_dictionary_create();
@@ -122,7 +119,15 @@
 		return ENOMEM;
 	}
 	prop_dictionary_set_uint32(npf_dict, "version", NPF_VERSION);
-	prop_dictionary_set(npf_dict, "rules", ncf->ncf_rules_list);
+
+	rlset = _npf_ruleset_transform(ncf->ncf_rules_list);
+	if (rlset == NULL) {
+		prop_object_release(npf_dict);
+		return ENOMEM;
+	}
+	prop_dictionary_set(npf_dict, "rules", rlset);
+	prop_object_release(rlset);
+
 	prop_dictionary_set(npf_dict, "rprocs", ncf->ncf_rproc_list);
 	prop_dictionary_set(npf_dict, "tables", ncf->ncf_table_list);
 	prop_dictionary_set(npf_dict, "translation", ncf->ncf_nat_list);
@@ -205,9 +210,9 @@
 	prop_dictionary_get_uint32(ncf->ncf_err,
 	    "source-line", &ne->ne_source_line);
 	prop_dictionary_get_int32(ncf->ncf_err,
-	    "ncode-error", &ne->ne_ncode_error);
+	    "code-error", &ne->ne_ncode_error);
 	prop_dictionary_get_int32(ncf->ncf_err,
-	    "ncode-errat", &ne->ne_ncode_errat);
+	    "code-errat", &ne->ne_ncode_errat);
 }
 
 void
@@ -254,6 +259,110 @@
 }
 
 /*
+ * DYNAMIC RULESET INTERFACE.
+ */
+
+int
+npf_ruleset_add(int fd, const char *rname, nl_rule_t *rl, uintptr_t *id)
+{
+	prop_dictionary_t rldict = rl->nrl_dict;
+	prop_dictionary_t ret;
+	uint64_t id64;
+	int error;
+
+	prop_dictionary_set_cstring(rldict, "ruleset-name", rname);
+	prop_dictionary_set_uint32(rldict, "command", NPF_CMD_RULE_ADD);
+	error = prop_dictionary_sendrecv_ioctl(rldict, fd, IOC_NPF_RULE, &ret);
+	if (!error) {
+		prop_dictionary_get_uint64(ret, "id", &id64);
+		*id = (uintptr_t)id64;
+	}
+	return error;
+}
+
+int
+npf_ruleset_remove(int fd, const char *rname, uintptr_t id)
+{
+	prop_dictionary_t rldict;
+
+	rldict = prop_dictionary_create();
+	if (rldict == NULL) {
+		return ENOMEM;
+	}
+	prop_dictionary_set_cstring(rldict, "ruleset-name", rname);
+	prop_dictionary_set_uint32(rldict, "command", NPF_CMD_RULE_REMOVE);
+	__CTASSERT(sizeof(uintptr_t) <= sizeof(uint64_t));
+	prop_dictionary_set_uint64(rldict, "id", (uint64_t)id);
+	return prop_dictionary_send_ioctl(rldict, fd, IOC_NPF_RULE);
+}
+
+int
+npf_ruleset_remkey(int fd, const char *rname, const void *key, size_t len)
+{
+	prop_dictionary_t rldict;
+	prop_data_t keyobj;
+
+	rldict = prop_dictionary_create();
+	if (rldict == NULL) {
+		return ENOMEM;
+	}
+	prop_dictionary_set_cstring(rldict, "ruleset-name", rname);
+	prop_dictionary_set_uint32(rldict, "command", NPF_CMD_RULE_REMKEY);
+
+	keyobj = prop_data_create_data(key, len);
+	if (keyobj == NULL) {
+		prop_object_release(rldict);
+		return ENOMEM;
+	}
+	prop_dictionary_set(rldict, "key", keyobj);
+	prop_object_release(keyobj);
+
+	return prop_dictionary_send_ioctl(rldict, fd, IOC_NPF_RULE);
+}
+
+/*
+ * _npf_ruleset_transform: transform the ruleset representing nested
+ * rules with lists into an array.
+ */
+
+static void
+_npf_ruleset_transform1(prop_array_t rlset, prop_array_t rules)
+{
+	prop_object_iterator_t it;
+	prop_dictionary_t rldict;
+	prop_array_t subrlset;
+
+	it = prop_array_iterator(rules);
+	while ((rldict = prop_object_iterator_next(it)) != NULL) {
+		unsigned idx;
+
+		/* Add rules to the array (reference is retained). */
+		prop_array_add(rlset, rldict);
+
+		subrlset = prop_dictionary_get(rldict, "subrules");
+		if (subrlset) {
+			/* Process subrules recursively. */
+			_npf_ruleset_transform1(rlset, subrlset);
+			/* Add the skip-to position. */
+			idx = prop_array_count(rlset);
+			prop_dictionary_set_uint32(rldict, "skip-to", idx);
+			prop_dictionary_remove(rldict, "subrules");
+		}
+	}
+	prop_object_iterator_release(it);
+}
+
+static prop_array_t
+_npf_ruleset_transform(prop_array_t rlset)
+{
+	prop_array_t nrlset;
+
+	nrlset = prop_array_create();
+	_npf_ruleset_transform1(nrlset, rlset);
+	return nrlset;
+}
+
+/*
  * NPF EXTENSION INTERFACE.
  */
 
@@ -322,55 +431,84 @@
 }
 
 int
-npf_rule_setcode(nl_rule_t *rl, int type, const void *code, size_t sz)
+npf_rule_setcode(nl_rule_t *rl, int type, const void *code, size_t len)
 {
 	prop_dictionary_t rldict = rl->nrl_dict;
 	prop_data_t cdata;
 
-	if (type != NPF_CODE_NCODE) {
+	switch (type) {
+	case NPF_CODE_NC:
+	case NPF_CODE_BPF:
+		break;
+	default:
 		return ENOTSUP;
 	}
-	cdata = prop_data_create_data(code, sz);
-	if (cdata == NULL) {
+	prop_dictionary_set_uint32(rldict, "code-type", type);
+	if ((cdata = prop_data_create_data(code, len)) == NULL) {
 		return ENOMEM;
 	}
-	prop_dictionary_set(rldict, "ncode", cdata);
+	prop_dictionary_set(rldict, "code", cdata);
 	prop_object_release(cdata);
 	return 0;
 }
 
 int
-npf_rule_setproc(nl_config_t *ncf, nl_rule_t *rl, const char *name)
+npf_rule_setkey(nl_rule_t *rl, const void *key, size_t len)
+{
+	prop_dictionary_t rldict = rl->nrl_dict;
+	prop_data_t kdata;
+
+	if ((kdata = prop_data_create_data(key, len)) == NULL) {
+		return ENOMEM;
+	}
+	prop_dictionary_set(rldict, "key", kdata);
+	prop_object_release(kdata);
+	return 0;
+}
+
+int
+npf_rule_setprio(nl_rule_t *rl, pri_t pri)
 {
 	prop_dictionary_t rldict = rl->nrl_dict;
 
-	if (!npf_rproc_exists_p(ncf, name)) {
-		return ENOENT;
-	}
+	prop_dictionary_set_int32(rldict, "priority", pri);
+	return 0;
+}
+
+int
+npf_rule_setproc(nl_rule_t *rl, const char *name)
+{
+	prop_dictionary_t rldict = rl->nrl_dict;
+
 	prop_dictionary_set_cstring(rldict, "rproc", name);
 	return 0;
 }
 
+void *
+npf_rule_export(nl_rule_t *rl, size_t *length)
+{
+	prop_dictionary_t rldict = rl->nrl_dict;
+	void *xml;
+
+	if ((xml = prop_dictionary_externalize(rldict)) == NULL) {
+		return NULL;
+	}
+	*length = strlen(xml);
+	return xml;
+}
+
 bool
 npf_rule_exists_p(nl_config_t *ncf, const char *name)
 {
-
 	return _npf_prop_array_lookup(ncf->ncf_rules_list, "name", name);
 }
 
 int
-npf_rule_insert(nl_config_t *ncf, nl_rule_t *parent, nl_rule_t *rl, pri_t pri)
+npf_rule_insert(nl_config_t *ncf, nl_rule_t *parent, nl_rule_t *rl)
 {
 	prop_dictionary_t rldict = rl->nrl_dict;
 	prop_array_t rlset;
 
-	if (pri == NPF_PRI_NEXT) {
-		pri = ncf->ncf_rule_pri++;
-	} else if (ncf) {
-		ncf->ncf_rule_pri = pri + 1;
-	}
-	prop_dictionary_set_int32(rldict, "priority", pri);
-
 	if (parent) {
 		prop_dictionary_t pdict = parent->nrl_dict;
 		rlset = prop_dictionary_get(pdict, "subrules");
@@ -387,10 +525,12 @@
 }
 
 static int
-_npf_rule_foreach1(prop_array_t rules, unsigned nlevel, nl_rule_callback_t func)
+_npf_rule_foreach1(prop_array_t rules, nl_rule_callback_t func)
 {
 	prop_dictionary_t rldict;
 	prop_object_iterator_t it;
+	unsigned reduce[16], n;
+	unsigned nlevel;
 
 	if (!rules || prop_object_type(rules) != PROP_TYPE_ARRAY) {
 		return ENOENT;
@@ -399,19 +539,25 @@
 	if (it == NULL) {
 		return ENOMEM;
 	}
+
+	nlevel = 0;
+	reduce[nlevel] = 0;
+	n = 0;
+
 	while ((rldict = prop_object_iterator_next(it)) != NULL) {
-		prop_array_t subrules;
-		nl_rule_t nrl;
-
-		nrl.nrl_dict = rldict;
-		(*func)(&nrl, nlevel);
+		nl_rule_t nrl = { .nrl_dict = rldict };
+		uint32_t skipto = 0;
 
-		subrules = prop_dictionary_get(rldict, "subrules");
-		if (!subrules) {
-			continue;
+		prop_dictionary_get_uint32(rldict, "skip-to", &skipto);
+		(*func)(&nrl, nlevel);
+		if (skipto) {
+			nlevel++;
+			reduce[nlevel] = skipto;
 		}
-		(void)_npf_rule_foreach1(subrules, nlevel + 1, func);
-		prop_object_release(subrules);
+		if (reduce[nlevel] == ++n) {
+			assert(nlevel > 0);
+			nlevel--;
+		}
 	}
 	prop_object_iterator_release(it);
 	return 0;
@@ -420,8 +566,7 @@
 int
 _npf_rule_foreach(nl_config_t *ncf, nl_rule_callback_t func)
 {
-
-	return _npf_rule_foreach1(ncf->ncf_rules_list, 0, func);
+	return _npf_rule_foreach1(ncf->ncf_rules_list, func);
 }
 
 pri_t
@@ -442,7 +587,7 @@
 _npf_rule_ncode(nl_rule_t *nrl, size_t *size)
 {
 	prop_dictionary_t rldict = nrl->nrl_dict;
-	prop_object_t obj = prop_dictionary_get(rldict, "ncode");
+	prop_object_t obj = prop_dictionary_get(rldict, "code");
 	*size = prop_data_size(obj);
 	return prop_data_data_nocopy(obj);
 }
@@ -595,12 +740,7 @@
 {
 	prop_dictionary_t rldict = nt->nrl_dict;
 
-	if (pri == NPF_PRI_NEXT) {
-		pri = ncf->ncf_nat_pri++;
-	} else {
-		ncf->ncf_nat_pri = pri + 1;
-	}
-	prop_dictionary_set_int32(rldict, "priority", pri);
+	prop_dictionary_set_int32(rldict, "priority", NPF_PRI_LAST);
 	prop_array_add(ncf->ncf_nat_list, rldict);
 	return 0;
 }
@@ -608,8 +748,7 @@
 int
 _npf_nat_foreach(nl_config_t *ncf, nl_rule_callback_t func)
 {
-
-	return _npf_rule_foreach1(ncf->ncf_nat_list, 0, func);
+	return _npf_rule_foreach1(ncf->ncf_nat_list, func);
 }
 
 void
@@ -764,20 +903,6 @@
  */
 
 int
-npf_update_rule(int fd, const char *rname __unused, nl_rule_t *rl)
-{
-	prop_dictionary_t rldict = rl->nrl_dict, errdict = NULL;
-	int error;
-
-	error = prop_dictionary_sendrecv_ioctl(rldict, fd,
-	    IOC_NPF_UPDATE_RULE, &errdict);
-	if (errdict) {
-		prop_object_release(errdict);
-	}
-	return error;
-}
-
-int
 npf_sessions_recv(int fd, const char *fpath)
 {
 	prop_dictionary_t sdict;
--- a/lib/libnpf/npf.h	Sat Feb 09 02:49:36 2013 +0000
+++ b/lib/libnpf/npf.h	Sat Feb 09 03:35:31 2013 +0000
@@ -1,7 +1,7 @@
-/*	$NetBSD: npf.h,v 1.12 2012/12/23 21:01:05 rmind Exp $	*/
+/*	$NetBSD: npf.h,v 1.13 2013/02/09 03:35:33 rmind Exp $	*/
 
 /*-
- * Copyright (c) 2011-2012 The NetBSD Foundation, Inc.
+ * Copyright (c) 2011-2013 The NetBSD Foundation, Inc.
  * All rights reserved.
  *
  * This material is based upon work partially supported by The
@@ -41,6 +41,7 @@
 struct nl_rule;
 struct nl_rproc;
 struct nl_table;
+struct nl_ext;
 
 typedef struct nl_config	nl_config_t;
 typedef struct nl_rule		nl_rule_t;
@@ -70,11 +71,6 @@
 
 #endif
 
-#define	NPF_CODE_NCODE		1
-#define	NPF_CODE_BPF		2
-
-#define	NPF_PRI_NEXT		(-1)
-
 #define	NPF_MAX_TABLE_ID	(16)
 
 nl_config_t *	npf_config_create(void);
@@ -83,15 +79,22 @@
 nl_config_t *	npf_config_retrieve(int, bool *, bool *);
 int		npf_config_flush(int);
 
+int		npf_ruleset_add(int, const char *, nl_rule_t *, uintptr_t *);
+int		npf_ruleset_remove(int, const char *, uintptr_t);
+int		npf_ruleset_remkey(int, const char *, const void *, size_t);
+
 nl_ext_t *	npf_ext_construct(const char *name);
 void		npf_ext_param_u32(nl_ext_t *, const char *, uint32_t);
 void		npf_ext_param_bool(nl_ext_t *, const char *, bool);
 
 nl_rule_t *	npf_rule_create(const char *, uint32_t, u_int);
 int		npf_rule_setcode(nl_rule_t *, int, const void *, size_t);
-int		npf_rule_setproc(nl_config_t *, nl_rule_t *, const char *);
+int		npf_rule_setprio(nl_rule_t *, pri_t);
+int		npf_rule_setproc(nl_rule_t *, const char *);
+int		npf_rule_setkey(nl_rule_t *, const void *, size_t);
 bool		npf_rule_exists_p(nl_config_t *, const char *);
-int		npf_rule_insert(nl_config_t *, nl_rule_t *, nl_rule_t *, pri_t);
+int		npf_rule_insert(nl_config_t *, nl_rule_t *, nl_rule_t *);
+void *		npf_rule_export(nl_rule_t *, size_t *);
 void		npf_rule_destroy(nl_rule_t *);
 
 nl_rproc_t *	npf_rproc_create(const char *);
@@ -113,7 +116,6 @@
 
 #include <ifaddrs.h>
 
-int		npf_update_rule(int, const char *, nl_rule_t *);
 int		npf_sessions_send(int, const char *);
 int		npf_sessions_recv(int, const char *);
 
--- a/sys/modules/npf/Makefile	Sat Feb 09 02:49:36 2013 +0000
+++ b/sys/modules/npf/Makefile	Sat Feb 09 03:35:31 2013 +0000
@@ -1,4 +1,4 @@
-# $NetBSD: Makefile,v 1.11 2012/09/16 13:47:42 rmind Exp $
+# $NetBSD: Makefile,v 1.12 2013/02/09 03:35:33 rmind Exp $
 
 .include "../Makefile.inc"
 
@@ -6,7 +6,7 @@
 
 KMOD=		npf
 
-SRCS=		npf.c npf_alg.c npf_ctl.c npf_handler.c
+SRCS=		npf.c npf_alg.c npf_conf.c npf_ctl.c npf_handler.c
 SRCS+=		npf_inet.c npf_instr.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
--- a/sys/net/npf/files.npf	Sat Feb 09 02:49:36 2013 +0000
+++ b/sys/net/npf/files.npf	Sat Feb 09 03:35:31 2013 +0000
@@ -1,4 +1,4 @@
-# $NetBSD: files.npf,v 1.9 2012/12/10 00:32:23 rmind Exp $
+# $NetBSD: files.npf,v 1.10 2013/02/09 03:35:31 rmind Exp $
 #
 # Public Domain.
 #
@@ -11,6 +11,7 @@
 
 # Core
 file	net/npf/npf.c				npf
+file	net/npf/npf_conf.c			npf
 file	net/npf/npf_ctl.c			npf
 file	net/npf/npf_handler.c			npf
 file	net/npf/npf_instr.c			npf
--- a/sys/net/npf/npf.c	Sat Feb 09 02:49:36 2013 +0000
+++ b/sys/net/npf/npf.c	Sat Feb 09 03:35:31 2013 +0000
@@ -1,7 +1,7 @@
-/*	$NetBSD: npf.c,v 1.14 2012/10/29 02:27:11 rmind Exp $	*/
+/*	$NetBSD: npf.c,v 1.15 2013/02/09 03:35:31 rmind Exp $	*/
 
 /*-
- * Copyright (c) 2009-2012 The NetBSD Foundation, Inc.
+ * Copyright (c) 2009-2013 The NetBSD Foundation, Inc.
  * All rights reserved.
  *
  * This material is based upon work partially supported by The
@@ -34,7 +34,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: npf.c,v 1.14 2012/10/29 02:27:11 rmind Exp $");
+__KERNEL_RCSID(0, "$NetBSD: npf.c,v 1.15 2013/02/09 03:35:31 rmind Exp $");
 
 #include <sys/param.h>
 #include <sys/types.h>
@@ -67,19 +67,8 @@
 static int	npf_dev_poll(dev_t, int, lwp_t *);
 static int	npf_dev_read(dev_t, struct uio *, int);
 
-typedef struct {
-	npf_ruleset_t *		n_rules;
-	npf_tableset_t *	n_tables;
-	npf_ruleset_t *		n_nat_rules;
-	prop_dictionary_t	n_dict;
-	bool			n_default_pass;
-} npf_core_t;
-
-static void	npf_core_destroy(npf_core_t *);
 static int	npfctl_stats(void *);
 
-static krwlock_t		npf_lock		__cacheline_aligned;
-static npf_core_t *		npf_core		__cacheline_aligned;
 static percpu_t *		npf_stats_percpu	__read_mostly;
 static struct sysctllog *	npf_sysctl		__read_mostly;
 
@@ -94,12 +83,8 @@
 #ifdef _MODULE
 	devmajor_t bmajor = NODEVMAJOR, cmajor = NODEVMAJOR;
 #endif
-	npf_ruleset_t *rset, *nset;
-	npf_tableset_t *tset;
-	prop_dictionary_t dict;
 	int error = 0;
 
-	rw_init(&npf_lock);
 	npf_stats_percpu = percpu_alloc(NPF_STATS_SIZE);
 	npf_sysctl = NULL;
 
@@ -110,12 +95,7 @@
 	npf_ext_sysinit();
 
 	/* Load empty configuration. */
-	dict = prop_dictionary_create();
-	rset = npf_ruleset_create();
-	tset = npf_tableset_create();
-	nset = npf_ruleset_create();
-	npf_reload(dict, rset, tset, nset, true);
-	KASSERT(npf_core != NULL);
+	npf_config_init();
 
 #ifdef _MODULE
 	/* Attach /dev/npf device. */
@@ -140,7 +120,7 @@
 
 	/* Flush all sessions, destroy configuration (ruleset, etc). */
 	npf_session_tracking(false);
-	npf_core_destroy(npf_core);
+	npf_config_fini();
 
 	/* Finally, safe to destroy the subsystems. */
 	npf_ext_sysfini();
@@ -153,7 +133,6 @@
 		sysctl_teardown(&npf_sysctl);
 	}
 	percpu_free(npf_stats_percpu, NPF_STATS_SIZE);
-	rw_destroy(&npf_lock);
 
 	return 0;
 }
@@ -219,22 +198,15 @@
 	}
 
 	switch (cmd) {
-	case IOC_NPF_VERSION:
-		*(int *)data = NPF_VERSION;
-		error = 0;
+	case IOC_NPF_TABLE:
+		error = npfctl_table(data);
 		break;
-	case IOC_NPF_SWITCH:
-		error = npfctl_switch(data);
-		break;
-	case IOC_NPF_RELOAD:
-		error = npfctl_reload(cmd, data);
+	case IOC_NPF_RULE:
+		error = npfctl_rule(cmd, data);
 		break;
 	case IOC_NPF_GETCONF:
 		error = npfctl_getconf(cmd, data);
 		break;
-	case IOC_NPF_TABLE:
-		error = npfctl_table(data);
-		break;
 	case IOC_NPF_STATS:
 		error = npfctl_stats(data);
 		break;
@@ -244,8 +216,15 @@
 	case IOC_NPF_SESSIONS_LOAD:
 		error = npfctl_sessions_load(cmd, data);
 		break;
-	case IOC_NPF_UPDATE_RULE:
-		error = npfctl_update_rule(cmd, data);
+	case IOC_NPF_SWITCH:
+		error = npfctl_switch(data);
+		break;
+	case IOC_NPF_RELOAD:
+		error = npfctl_reload(cmd, data);
+		break;
+	case IOC_NPF_VERSION:
+		*(int *)data = NPF_VERSION;
+		error = 0;
 		break;
 	default:
 		error = ENOTTY;
@@ -268,110 +247,6 @@
 	return ENOTSUP;
 }
 
-/*
- * NPF core loading/reloading/unloading mechanism.
- */
-
-static void
-npf_core_destroy(npf_core_t *nc)
-{
-
-	prop_object_release(nc->n_dict);
-	npf_ruleset_destroy(nc->n_rules);
-	npf_ruleset_destroy(nc->n_nat_rules);
-	npf_tableset_destroy(nc->n_tables);
-	kmem_free(nc, sizeof(npf_core_t));
-}
-
-/*
- * npf_reload: atomically load new ruleset, tableset and NAT policies.
- * Then destroy old (unloaded) structures.
- */
-void
-npf_reload(prop_dictionary_t dict, npf_ruleset_t *rset,
-    npf_tableset_t *tset, npf_ruleset_t *nset, bool flush)
-{
-	npf_core_t *nc, *onc;
-
-	/* Setup a new core structure. */
-	nc = kmem_zalloc(sizeof(npf_core_t), KM_SLEEP);
-	nc->n_rules = rset;
-	nc->n_tables = tset;
-	nc->n_nat_rules = nset;
-	nc->n_dict = dict;
-	nc->n_default_pass = flush;
-
-	/* Lock and load the core structure. */
-	rw_enter(&npf_lock, RW_WRITER);
-	onc = atomic_swap_ptr(&npf_core, nc);
-	if (onc) {
-		/* Reload only the static tables. */
-		npf_tableset_reload(tset, onc->n_tables);
-		/* Reload only the necessary NAT policies. */
-		npf_ruleset_natreload(nset, onc->n_nat_rules);
-	}
-	/* Unlock.  Everything goes "live" now. */
-	rw_exit(&npf_lock);
-
-	if (onc) {
-		/* Destroy unloaded structures. */
-		npf_core_destroy(onc);
-	}
-}
-
-void
-npf_core_enter(void)
-{
-	rw_enter(&npf_lock, RW_READER);
-}
-
-npf_ruleset_t *
-npf_core_ruleset(void)
-{
-	KASSERT(rw_lock_held(&npf_lock));
-	return npf_core->n_rules;
-}
-
-npf_ruleset_t *
-npf_core_natset(void)
-{
-	KASSERT(rw_lock_held(&npf_lock));
-	return npf_core->n_nat_rules;
-}
-
-npf_tableset_t *
-npf_core_tableset(void)
-{
-	KASSERT(rw_lock_held(&npf_lock));
-	return npf_core->n_tables;
-}
-
-void
-npf_core_exit(void)
-{
-	rw_exit(&npf_lock);
-}
-
-bool
-npf_core_locked(void)
-{
-	return rw_lock_held(&npf_lock);
-}
-
-prop_dictionary_t
-npf_core_dict(void)
-{
-	KASSERT(rw_lock_held(&npf_lock));
-	return npf_core->n_dict;
-}
-
-bool
-npf_default_pass(void)
-{
-	KASSERT(rw_lock_held(&npf_lock));
-	return npf_core->n_default_pass;
-}
-
 bool
 npf_autounload_p(void)
 {
--- a/sys/net/npf/npf.h	Sat Feb 09 02:49:36 2013 +0000
+++ b/sys/net/npf/npf.h	Sat Feb 09 03:35:31 2013 +0000
@@ -1,7 +1,7 @@
-/*	$NetBSD: npf.h,v 1.25 2012/12/24 19:05:42 rmind Exp $	*/
+/*	$NetBSD: npf.h,v 1.26 2013/02/09 03:35:31 rmind Exp $	*/
 
 /*-
- * Copyright (c) 2009-2012 The NetBSD Foundation, Inc.
+ * Copyright (c) 2009-2013 The NetBSD Foundation, Inc.
  * All rights reserved.
  *
  * This material is based upon work partially supported by The
@@ -45,7 +45,7 @@
 #include <netinet/in_systm.h>
 #include <netinet/in.h>
 
-#define	NPF_VERSION		8
+#define	NPF_VERSION		9
 
 /*
  * Public declarations and definitions.
@@ -119,24 +119,9 @@
 static inline bool
 npf_iscached(const npf_cache_t *npc, const int inf)
 {
-
 	return __predict_true((npc->npc_info & inf) != 0);
 }
 
-static inline int
-npf_cache_ipproto(const npf_cache_t *npc)
-{
-	KASSERT(npf_iscached(npc, NPC_IP46));
-	return npc->npc_proto;
-}
-
-static inline u_int
-npf_cache_hlen(const npf_cache_t *npc)
-{
-	KASSERT(npf_iscached(npc, NPC_IP46));
-	return npc->npc_hlen;
-}
-
 /*
  * Network buffer interface.
  */
@@ -199,15 +184,30 @@
 
 /* Rule attributes. */
 #define	NPF_RULE_PASS			0x0001
-#define	NPF_RULE_DEFAULT		0x0002
+#define	NPF_RULE_GROUP			0x0002
 #define	NPF_RULE_FINAL			0x0004
 #define	NPF_RULE_STATEFUL		0x0008
 #define	NPF_RULE_RETRST			0x0010
 #define	NPF_RULE_RETICMP		0x0020
+#define	NPF_RULE_DYNAMIC		0x0040
+
+#define	NPF_DYNAMIC_GROUP		(NPF_RULE_GROUP | NPF_RULE_DYNAMIC)
 
 #define	NPF_RULE_IN			0x10000000
 #define	NPF_RULE_OUT			0x20000000
 #define	NPF_RULE_DIMASK			(NPF_RULE_IN | NPF_RULE_OUT)
+#define	NPF_RULE_FORW			0x40000000
+
+#define	NPF_RULE_MAXNAMELEN		64
+#define	NPF_RULE_MAXKEYLEN		32
+
+/* Priority values. */
+#define	NPF_PRI_FIRST			(-2)
+#define	NPF_PRI_LAST			(-1)
+
+/* Types of code. */
+#define	NPF_CODE_NC			1
+#define	NPF_CODE_BPF			2
 
 /* Address translation types and flags. */
 #define	NPF_NATIN			1
@@ -228,13 +228,24 @@
 #define	PACKET_TAG_NPF			10
 
 /*
- * IOCTL structures.
+ * Rule commands (non-ioctl).
  */
 
-#define	NPF_IOCTL_TBLENT_LOOKUP		0
-#define	NPF_IOCTL_TBLENT_ADD		1
-#define	NPF_IOCTL_TBLENT_REM		2
-#define	NPF_IOCTL_TBLENT_LIST		3
+#define	NPF_CMD_RULE_ADD		1
+#define	NPF_CMD_RULE_INSERT		2
+#define	NPF_CMD_RULE_REMOVE		3
+#define	NPF_CMD_RULE_REMKEY		4
+#define	NPF_CMD_RULE_FLUSH		5
+
+/*
+ * NPF ioctl(2): table commands and structures.
+ */
+
+#define	NPF_CMD_TABLE_LOOKUP		1
+#define	NPF_CMD_TABLE_ADD		2
+#define	NPF_CMD_TABLE_REMOVE		3
+#define	NPF_CMD_TABLE_LIST		4
+#define	NPF_CMD_TABLE_FLUSH		5
 
 typedef struct npf_ioctl_ent {
 	int			alen;
@@ -248,7 +259,7 @@
 } npf_ioctl_buf_t;
 
 typedef struct npf_ioctl_table {
-	int			nct_action;
+	int			nct_cmd;
 	u_int			nct_tid;
 	union {
 		npf_ioctl_ent_t	ent;
@@ -256,6 +267,24 @@
 	} nct_data;
 } npf_ioctl_table_t;
 
+/*
+ * IOCTL operations.
+ */
+
+#define	IOC_NPF_VERSION		_IOR('N', 100, int)
+#define	IOC_NPF_SWITCH		_IOW('N', 101, int)
+#define	IOC_NPF_RELOAD		_IOWR('N', 102, struct plistref)
+#define	IOC_NPF_TABLE		_IOW('N', 103, struct npf_ioctl_table)
+#define	IOC_NPF_STATS		_IOW('N', 104, void *)
+#define	IOC_NPF_SESSIONS_SAVE	_IOR('N', 105, struct plistref)
+#define	IOC_NPF_SESSIONS_LOAD	_IOW('N', 106, struct plistref)
+#define	IOC_NPF_RULE		_IOWR('N', 107, struct plistref)
+#define	IOC_NPF_GETCONF		_IOR('N', 108, struct plistref)
+
+/*
+ * Statistics counters.
+ */
+
 typedef enum {
 	/* Packets passed. */
 	NPF_STAT_PASS_DEFAULT,
@@ -292,18 +321,4 @@
 
 #define	NPF_STATS_SIZE		(sizeof(uint64_t) * NPF_STATS_COUNT)
 
-/*
- * IOCTL operations.
- */
-
-#define	IOC_NPF_VERSION		_IOR('N', 100, int)
-#define	IOC_NPF_SWITCH		_IOW('N', 101, int)
-#define	IOC_NPF_RELOAD		_IOWR('N', 102, struct plistref)
-#define	IOC_NPF_TABLE		_IOW('N', 103, struct npf_ioctl_table)
-#define	IOC_NPF_STATS		_IOW('N', 104, void *)
-#define	IOC_NPF_SESSIONS_SAVE	_IOR('N', 105, struct plistref)
-#define	IOC_NPF_SESSIONS_LOAD	_IOW('N', 106, struct plistref)
-#define	IOC_NPF_UPDATE_RULE	_IOWR('N', 107, struct plistref)
-#define	IOC_NPF_GETCONF		_IOR('N', 108, struct plistref)
-
 #endif	/* _NPF_NET_H_ */
--- a/sys/net/npf/npf_alg.c	Sat Feb 09 02:49:36 2013 +0000
+++ b/sys/net/npf/npf_alg.c	Sat Feb 09 03:35:31 2013 +0000
@@ -1,4 +1,4 @@
-/*	$NetBSD: npf_alg.c,v 1.6 2012/12/24 19:05:42 rmind Exp $	*/
+/*	$NetBSD: npf_alg.c,v 1.7 2013/02/09 03:35:31 rmind Exp $	*/
 
 /*-
  * Copyright (c) 2010 The NetBSD Foundation, Inc.
@@ -34,7 +34,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: npf_alg.c,v 1.6 2012/12/24 19:05:42 rmind Exp $");
+__KERNEL_RCSID(0, "$NetBSD: npf_alg.c,v 1.7 2013/02/09 03:35:31 rmind Exp $");
 
 #include <sys/param.h>
 #include <sys/types.h>
@@ -79,8 +79,6 @@
 
 /*
  * npf_alg_register: register application-level gateway.
- *
- * XXX: Protected by module lock, but unify serialisation later.
  */
 npf_alg_t *
 npf_alg_register(npf_alg_func_t mfunc, npf_alg_func_t tfunc,
@@ -107,15 +105,14 @@
 int
 npf_alg_unregister(npf_alg_t *alg)
 {
-
 	mutex_enter(&nat_alg_lock);
 	LIST_REMOVE(alg, na_entry);
 	pserialize_perform(nat_alg_psz);
 	mutex_exit(&nat_alg_lock);
 
-	npf_core_enter();
-	npf_ruleset_freealg(npf_core_natset(), alg);
-	npf_core_exit();
+	npf_config_enter();
+	npf_ruleset_freealg(npf_config_natset(), alg);
+	npf_config_exit();
 
 	kmem_free(alg, sizeof(npf_alg_t));
 	return 0;
--- a/sys/net/npf/npf_alg_icmp.c	Sat Feb 09 02:49:36 2013 +0000
+++ b/sys/net/npf/npf_alg_icmp.c	Sat Feb 09 03:35:31 2013 +0000
@@ -1,4 +1,4 @@
-/*	$NetBSD: npf_alg_icmp.c,v 1.14 2012/12/24 19:05:42 rmind Exp $	*/
+/*	$NetBSD: npf_alg_icmp.c,v 1.15 2013/02/09 03:35:31 rmind Exp $	*/
 
 /*-
  * Copyright (c) 2010 The NetBSD Foundation, Inc.
@@ -34,7 +34,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: npf_alg_icmp.c,v 1.14 2012/12/24 19:05:42 rmind Exp $");
+__KERNEL_RCSID(0, "$NetBSD: npf_alg_icmp.c,v 1.15 2013/02/09 03:35:31 rmind Exp $");
 
 #include <sys/param.h>
 #include <sys/module.h>
@@ -117,7 +117,7 @@
 static bool
 npfa_icmp_match(npf_cache_t *npc, nbuf_t *nbuf, npf_nat_t *nt, int di)
 {
-	const int proto = npf_cache_ipproto(npc);
+	const int proto = npc->npc_proto;
 	const struct ip *ip = npc->npc_ip.v4;
 	in_port_t dport;
 
@@ -254,7 +254,7 @@
 
 	/* Advance to ICMP header. */
 	nbuf_reset(nbuf);
-	if (!nbuf_advance(nbuf, npf_cache_hlen(npc), 0)) {
+	if (!nbuf_advance(nbuf, npc->npc_hlen, 0)) {
 		return false;
 	}
 	enpc->npc_info = 0;
@@ -310,7 +310,7 @@
 	#define	SWAP(type, x, y) { type tmp = x; x = y; y = tmp; }
 	SWAP(npf_addr_t *, enpc.npc_srcip, enpc.npc_dstip);
 
-	switch (npf_cache_ipproto(&enpc)) {
+	switch (enpc.npc_proto) {
 	case IPPROTO_TCP:
 		l4.th.th_sport = enpc.npc_l4.tcp->th_dport;
 		l4.th.th_dport = enpc.npc_l4.tcp->th_sport;
@@ -371,7 +371,7 @@
 	 * checksum for these changes in the embedded packet.  While data
 	 * is not rewritten in the cache, save IP and TCP/UDP checksums.
 	 */
-	const int proto = npf_cache_ipproto(&enpc);
+	const int proto = enpc.npc_proto;
 	uint16_t ipcksum = 0, l4cksum = 0;
 	npf_addr_t *addr;
 	in_port_t port;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sys/net/npf/npf_conf.c	Sat Feb 09 03:35:31 2013 +0000
@@ -0,0 +1,236 @@
+/*	$NetBSD: npf_conf.c,v 1.1 2013/02/09 03:35:31 rmind Exp $	*/
+
+/*-
+ * Copyright (c) 2013 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This material is based upon work partially supported by The
+ * NetBSD Foundation under a contract with Mindaugas Rasiukevicius.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * NPF config loading mechanism.
+ *
+ * There are few main operations on the config:
+ * 1) Read access which is primarily from the npf_packet_handler() et al.
+ * 2) Write access on particular set, mainly rule or table updates.
+ * 3) Deletion of the config, which is done during the reload operation.
+ *
+ * Synchronisation
+ *
+ *	For (1) case, passive serialisation is used to allow concurrent
+ *	access to the configuration set (ruleset, etc).  It guarantees
+ *	that the config will not be destroyed while accessing it.
+ *
+ *	Writers, i.e. cases (2) and (3) use mutual exclusion and when
+ *	necessary writer-side barrier of the passive serialisation.
+ */
+
+#include <sys/cdefs.h>
+__KERNEL_RCSID(0, "$NetBSD: npf_conf.c,v 1.1 2013/02/09 03:35:31 rmind Exp $");
+
+#include <sys/param.h>
+#include <sys/types.h>
+
+#include <sys/atomic.h>
+#include <sys/kmem.h>
+#include <sys/pserialize.h>
+#include <sys/mutex.h>
+
+#include "npf_impl.h"
+
+typedef struct {
+	npf_ruleset_t *		n_rules;
+	npf_tableset_t *	n_tables;
+	npf_ruleset_t *		n_nat_rules;
+	npf_rprocset_t *	n_rprocs;
+	prop_dictionary_t	n_dict;
+	bool			n_default_pass;
+} npf_config_t;
+
+static npf_config_t *		npf_config		__cacheline_aligned;
+static kmutex_t			npf_config_lock		__cacheline_aligned;
+static pserialize_t		npf_config_psz		__cacheline_aligned;
+
+void
+npf_config_init(void)
+{
+	prop_dictionary_t dict;
+	npf_ruleset_t *rlset, *nset;
+	npf_rprocset_t *rpset;
+	npf_tableset_t *tset;
+
+	mutex_init(&npf_config_lock, MUTEX_DEFAULT, IPL_SOFTNET);
+	npf_config_psz = pserialize_create();
+
+	/* Load the empty configuration. */
+	dict = prop_dictionary_create();
+	tset = npf_tableset_create();
+	rpset = npf_rprocset_create();
+	rlset = npf_ruleset_create(0);
+	nset = npf_ruleset_create(0);
+	npf_config_reload(dict, rlset, tset, nset, rpset, true);
+	KASSERT(npf_config != NULL);
+}
+
+static void
+npf_config_destroy(npf_config_t *nc)
+{
+	prop_object_release(nc->n_dict);
+	npf_ruleset_destroy(nc->n_rules);
+	npf_ruleset_destroy(nc->n_nat_rules);
+	npf_rprocset_destroy(nc->n_rprocs);
+	npf_tableset_destroy(nc->n_tables);
+	kmem_free(nc, sizeof(npf_config_t));
+}
+
+void
+npf_config_fini(void)
+{
+	npf_config_destroy(npf_config);
+	pserialize_destroy(npf_config_psz);
+	mutex_destroy(&npf_config_lock);
+}
+
+/*
+ * npf_config_reload: the main routine performing configuration reload.
+ * Performs the necessary synchronisation and destroys the old config.
+ */
+void
+npf_config_reload(prop_dictionary_t dict, npf_ruleset_t *rset,
+    npf_tableset_t *tset, npf_ruleset_t *nset, npf_rprocset_t *rpset,
+    bool flush)
+{
+	npf_config_t *nc, *onc;
+
+	nc = kmem_zalloc(sizeof(npf_config_t), KM_SLEEP);
+	nc->n_rules = rset;
+	nc->n_tables = tset;
+	nc->n_nat_rules = nset;
+	nc->n_rprocs = rpset;
+	nc->n_dict = dict;
+	nc->n_default_pass = flush;
+
+	/*
+	 * Acquire the lock and perform the first phase:
+	 * - Scan and use existing dynamic tables, reload only static.
+	 * - Scan and use matching NAT policies to preserve the connections.
+	 */
+	mutex_enter(&npf_config_lock);
+	if ((onc = npf_config) != NULL) {
+		npf_ruleset_reload(rset, onc->n_rules);
+		npf_tableset_reload(tset, onc->n_tables);
+		npf_ruleset_natreload(nset, onc->n_nat_rules);
+	}
+
+	/*
+	 * Set the new config and release the lock.
+	 */
+	membar_sync();
+	npf_config = nc;
+	if (onc == NULL) {
+		/* Initial load, done. */
+		mutex_exit(&npf_config_lock);
+		return;
+	}
+
+	/* Synchronise: drain all references. */
+	pserialize_perform(npf_config_psz);
+	mutex_exit(&npf_config_lock);
+
+	/* Finally, it is safe to destroy the old config. */
+	npf_config_destroy(onc);
+}
+
+/*
+ * Writer-side exclusive locking.
+ */
+
+void
+npf_config_enter(void)
+{
+	mutex_enter(&npf_config_lock);
+}
+
+void
+npf_config_exit(void)
+{
+	mutex_exit(&npf_config_lock);
+}
+
+bool
+npf_config_locked_p(void)
+{
+	return mutex_owned(&npf_config_lock);
+}
+
+/*
+ * Reader-side synchronisation routines.
+ */
+
+int
+npf_config_read_enter(void)
+{
+	return pserialize_read_enter();
+}
+
+void
+npf_config_read_exit(int s)
+{
+	pserialize_read_exit(s);
+}
+
+/*
+ * Accessors.
+ */
+
+npf_ruleset_t *
+npf_config_ruleset(void)
+{
+	return npf_config->n_rules;
+}
+
+npf_ruleset_t *
+npf_config_natset(void)
+{
+	return npf_config->n_nat_rules;
+}
+
+npf_tableset_t *
+npf_config_tableset(void)
+{
+	return npf_config->n_tables;
+}
+
+prop_dictionary_t
+npf_config_dict(void)
+{
+	return npf_config->n_dict;
+}
+
+bool
+npf_default_pass(void)
+{
+	return npf_config->n_default_pass;
+}
--- a/sys/net/npf/npf_ctl.c	Sat Feb 09 02:49:36 2013 +0000
+++ b/sys/net/npf/npf_ctl.c	Sat Feb 09 03:35:31 2013 +0000
@@ -1,7 +1,7 @@
-/*	$NetBSD: npf_ctl.c,v 1.20 2012/12/23 21:01:03 rmind Exp $	*/
+/*	$NetBSD: npf_ctl.c,v 1.21 2013/02/09 03:35:31 rmind Exp $	*/
 
 /*-
- * Copyright (c) 2009-2012 The NetBSD Foundation, Inc.
+ * Copyright (c) 2009-2013 The NetBSD Foundation, Inc.
  * All rights reserved.
  *
  * This material is based upon work partially supported by The
@@ -37,10 +37,12 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: npf_ctl.c,v 1.20 2012/12/23 21:01:03 rmind Exp $");
+__KERNEL_RCSID(0, "$NetBSD: npf_ctl.c,v 1.21 2013/02/09 03:35:31 rmind Exp $");
 
 #include <sys/param.h>
 #include <sys/conf.h>
+#include <sys/kmem.h>
+#include <net/bpf.h>
 
 #include <prop/proplib.h>
 
@@ -159,30 +161,12 @@
 }
 
 static npf_rproc_t *
-npf_mk_rproc(prop_array_t rprocs, const char *rpname)
+npf_mk_singlerproc(prop_dictionary_t rpdict)
 {
 	prop_object_iterator_t it;
-	prop_dictionary_t rpdict, extdict;
+	prop_dictionary_t extdict;
 	prop_array_t extlist;
 	npf_rproc_t *rp;
-	const char *name;
-	uint64_t rpval;
-
-	it = prop_array_iterator(rprocs);
-	while ((rpdict = prop_object_iterator_next(it)) != NULL) {
-		prop_dictionary_get_cstring_nocopy(rpdict, "name", &name);
-		KASSERT(name != NULL);
-		if (strcmp(rpname, name) == 0)
-			break;
-	}
-	prop_object_iterator_release(it);
-	if (!rpdict) {
-		return NULL;
-	}
-	CTASSERT(sizeof(uintptr_t) <= sizeof(uint64_t));
-	if (prop_dictionary_get_uint64(rpdict, "rproc-ptr", &rpval)) {
-		return (npf_rproc_t *)(uintptr_t)rpval;
-	}
 
 	extlist = prop_dictionary_get(rpdict, "extcalls");
 	if (prop_object_type(extlist) != PROP_TYPE_ARRAY) {
@@ -190,11 +174,14 @@
 	}
 
 	rp = npf_rproc_create(rpdict);
-	if (!rp) {
+	if (rp == NULL) {
 		return NULL;
 	}
+
 	it = prop_array_iterator(extlist);
 	while ((extdict = prop_object_iterator_next(it)) != NULL) {
+		const char *name;
+
 		if (!prop_dictionary_get_cstring_nocopy(extdict,
 		    "name", &name) || npf_ext_construct(name, rp, extdict)) {
 			npf_rproc_release(rp);
@@ -203,182 +190,160 @@
 		}
 	}
 	prop_object_iterator_release(it);
-
-	if (rp) {
-		rpval = (uint64_t)(uintptr_t)rp;
-		prop_dictionary_set_uint64(rpdict, "rproc-ptr", rpval);
-	}
 	return rp;
 }
 
 static int __noinline
-npf_mk_ncode(prop_object_t obj, void **code, size_t *csize,
+npf_mk_rprocs(npf_rprocset_t *rpset, prop_array_t rprocs,
+    prop_dictionary_t errdict)
+{
+	prop_object_iterator_t it;
+	prop_dictionary_t rpdict;
+	int error = 0;
+
+	it = prop_array_iterator(rprocs);
+	while ((rpdict = prop_object_iterator_next(it)) != NULL) {
+		npf_rproc_t *rp;
+
+		if ((rp = npf_mk_singlerproc(rpdict)) == NULL) {
+			NPF_ERR_DEBUG(errdict);
+			error = EINVAL;
+			break;
+		}
+		npf_rprocset_insert(rpset, rp);
+	}
+	prop_object_iterator_release(it);
+	return error;
+}
+
+static int __noinline
+npf_mk_code(prop_object_t obj, int type, void **code, size_t *csize,
     prop_dictionary_t errdict)
 {
-	const void *ncptr;
-	int nc_err, errat;
-	size_t nc_size;
-	void *nc;
+	const void *cptr;
+	int cerr, errat;
+	size_t clen;
+	void *bc;
 
-	/*
-	 * Allocate, copy and validate n-code. XXX: Inefficient.
-	 */
-	ncptr = prop_data_data_nocopy(obj);
-	nc_size = prop_data_size(obj);
-	if (ncptr == NULL || nc_size > NPF_NCODE_LIMIT) {
+	cptr = prop_data_data_nocopy(obj);
+	if (cptr == NULL || (clen = prop_data_size(obj)) == 0) {
 		NPF_ERR_DEBUG(errdict);
-		return ERANGE;
-	}
-	nc = npf_ncode_alloc(nc_size);
-	if (nc == NULL) {
-		NPF_ERR_DEBUG(errdict);
-		return ENOMEM;
-	}
-	memcpy(nc, ncptr, nc_size);
-	nc_err = npf_ncode_validate(nc, nc_size, &errat);
-	if (nc_err) {
-		npf_ncode_free(nc, nc_size);
-		prop_dictionary_set_int32(errdict, "ncode-error", nc_err);
-		prop_dictionary_set_int32(errdict, "ncode-errat", errat);
 		return EINVAL;
 	}
-	*code = nc;
-	*csize = nc_size;
+
+	switch (type) {
+	case NPF_CODE_NC:
+		if (clen > NPF_NCODE_LIMIT) {
+			NPF_ERR_DEBUG(errdict);
+			return ERANGE;
+		}
+		if ((cerr = npf_ncode_validate(cptr, clen, &errat)) != 0) {
+			prop_dictionary_set_int32(errdict, "code-error", cerr);
+			prop_dictionary_set_int32(errdict, "code-errat", errat);
+			return EINVAL;
+		}
+		break;
+	case NPF_CODE_BPF:
+		if (!bpf_validate(cptr, clen)) {
+			return EINVAL;
+		}
+		break;
+	default:
+		return ENOTSUP;
+	}
+
+	bc = kmem_alloc(clen, KM_SLEEP);
+	memcpy(bc, cptr, clen);
+
+	*code = bc;
+	*csize = clen;
 	return 0;
 }
 
 static int __noinline
-npf_mk_singlerule(prop_dictionary_t rldict, prop_array_t rps, npf_rule_t **rl,
-    prop_dictionary_t errdict)
+npf_mk_singlerule(prop_dictionary_t rldict, npf_rprocset_t *rpset,
+    npf_rule_t **rlret, prop_dictionary_t errdict)
 {
-	const char *rnm;
-	npf_rproc_t *rp;
+	npf_rule_t *rl;
+	const char *rname;
 	prop_object_t obj;
-	size_t nc_size;
-	void *nc;
-	int p, error;
+	int p, error = 0;
 
 	/* Rule - dictionary. */
 	if (prop_object_type(rldict) != PROP_TYPE_DICTIONARY) {
 		NPF_ERR_DEBUG(errdict);
 		return EINVAL;
 	}
+	if ((rl = npf_rule_alloc(rldict)) == NULL) {
+		NPF_ERR_DEBUG(errdict);
+		return EINVAL;
+	}
 
-	/* Make the rule procedure, if any. */
-	if (rps && prop_dictionary_get_cstring_nocopy(rldict, "rproc", &rnm)) {
-		rp = npf_mk_rproc(rps, rnm);
-		if (rp == NULL) {
+	/* Assign rule procedure, if any. */
+	if (prop_dictionary_get_cstring_nocopy(rldict, "rproc", &rname)) {
+		npf_rproc_t *rp;
+
+		if (rpset == NULL) {
+			error = EINVAL;
+			goto err;
+		}
+		if ((rp = npf_rprocset_lookup(rpset, rname)) == NULL) {
 			NPF_ERR_DEBUG(errdict);
 			error = EINVAL;
 			goto err;
 		}
-	} else {
-		rp = NULL;
+		npf_rule_setrproc(rl, rp);
 	}
 
-	error = 0;
-	obj = prop_dictionary_get(rldict, "ncode");
-	if (obj) {
-		/* N-code (binary data). */
-		error = npf_mk_ncode(obj, &nc, &nc_size, errdict);
+	/* Filter code (binary data). */
+	if ((obj = prop_dictionary_get(rldict, "code")) != NULL) {
+		int type;
+		size_t len;
+		void *code;
+
+		prop_dictionary_get_int32(rldict, "code-type", &type);
+		error = npf_mk_code(obj, type, &code, &len, errdict);
 		if (error) {
 			goto err;
 		}
-	} else {
-		/* No n-code. */
-		nc = NULL;
-		nc_size = 0;
+		npf_rule_setcode(rl, type, code, len);
 	}
 
-	/* Finally, allocate and return the rule. */
-	*rl = npf_rule_alloc(rldict, rp, nc, nc_size);
-	KASSERT(*rl != NULL);
+	*rlret = rl;
 	return 0;
 err:
-	if (rp) {
-		npf_rproc_release(rp);
-	}
+	npf_rule_free(rl);
 	prop_dictionary_get_int32(rldict, "priority", &p); /* XXX */
 	prop_dictionary_set_int32(errdict, "id", p);
 	return error;
 }
 
 static int __noinline
-npf_mk_subrules(npf_ruleset_t *rlset, prop_array_t rules, prop_array_t rprocs,
+npf_mk_rules(npf_ruleset_t *rlset, prop_array_t rules, npf_rprocset_t *rpset,
     prop_dictionary_t errdict)
 {
 	prop_object_iterator_t it;
 	prop_dictionary_t rldict;
-	int error = 0;
+	int error;
 
 	if (prop_object_type(rules) != PROP_TYPE_ARRAY) {
 		NPF_ERR_DEBUG(errdict);
 		return EINVAL;
 	}
+
+	error = 0;
 	it = prop_array_iterator(rules);
 	while ((rldict = prop_object_iterator_next(it)) != NULL) {
-		npf_rule_t *rl;
-		error = npf_mk_singlerule(rldict, rprocs, &rl, errdict);
+		npf_rule_t *rl = NULL;
+
+		/* Generate a single rule. */
+		error = npf_mk_singlerule(rldict, rpset, &rl, errdict);
 		if (error) {
 			break;
 		}
 		npf_ruleset_insert(rlset, rl);
 	}
 	prop_object_iterator_release(it);
-	return error;
-}
-
-static int __noinline
-npf_mk_rules(npf_ruleset_t *rlset, prop_array_t rules, prop_array_t rprocs,
-    prop_dictionary_t errdict)
-{
-	prop_object_iterator_t it;
-	prop_dictionary_t rldict, rpdict;
-	int error;
-
-	/* Rule procedures and the ruleset - arrays. */
-	if (prop_object_type(rprocs) != PROP_TYPE_ARRAY ||
-	    prop_object_type(rules) != PROP_TYPE_ARRAY) {
-		NPF_ERR_DEBUG(errdict);
-		return EINVAL;
-	}
-
-	it = prop_array_iterator(rprocs);
-	while ((rpdict = prop_object_iterator_next(it)) != NULL) {
-		if (prop_dictionary_get(rpdict, "rproc-ptr")) {
-			prop_object_iterator_release(it);
-			NPF_ERR_DEBUG(errdict);
-			return EINVAL;
-		}
-	}
-	prop_object_iterator_release(it);
-
-	error = 0;
-	it = prop_array_iterator(rules);
-	while ((rldict = prop_object_iterator_next(it)) != NULL) {
-		prop_array_t subrules;
-		npf_ruleset_t *rlsetsub;
-		npf_rule_t *rl;
-
-		/* Generate a single rule. */
-		error = npf_mk_singlerule(rldict, rprocs, &rl, errdict);
-		if (error) {
-			break;
-		}
-		npf_ruleset_insert(rlset, rl);
-
-		/* Check for sub-rules and generate, if any. */
-		subrules = prop_dictionary_get(rldict, "subrules");
-		if (subrules == NULL) {
-			/* No subrules, next.. */
-			continue;
-		}
-		rlsetsub = npf_rule_subset(rl);
-		error = npf_mk_subrules(rlsetsub, subrules, rprocs, errdict);
-		if (error)
-			break;
-	}
-	prop_object_iterator_release(it);
 	/*
 	 * Note: in a case of error, caller will free the ruleset.
 	 */
@@ -402,8 +367,8 @@
 	error = 0;
 	it = prop_array_iterator(natlist);
 	while ((natdict = prop_object_iterator_next(it)) != NULL) {
+		npf_rule_t *rl = NULL;
 		npf_natpolicy_t *np;
-		npf_rule_t *rl;
 
 		/* NAT policy - dictionary. */
 		if (prop_object_type(natdict) != PROP_TYPE_DICTIONARY) {
@@ -456,9 +421,11 @@
 	prop_dictionary_t npf_dict, errdict;
 	prop_array_t natlist, tables, rprocs, rules;
 	npf_tableset_t *tblset = NULL;
+	npf_rprocset_t *rpset = NULL;
 	npf_ruleset_t *rlset = NULL;
 	npf_ruleset_t *nset = NULL;
 	uint32_t ver = 0;
+	size_t nitems;
 	bool flush;
 	int error;
 
@@ -480,8 +447,10 @@
 	}
 
 	/* NAT policies. */
-	nset = npf_ruleset_create();
 	natlist = prop_dictionary_get(npf_dict, "translation");
+	nitems = prop_array_count(natlist);
+
+	nset = npf_ruleset_create(nitems);
 	error = npf_mk_natlist(nset, natlist, errdict);
 	if (error) {
 		goto fail;
@@ -495,11 +464,20 @@
 		goto fail;
 	}
 
-	/* Rules and rule procedures. */
-	rlset = npf_ruleset_create();
+	/* Rule procedures. */
+	rpset = npf_rprocset_create();
 	rprocs = prop_dictionary_get(npf_dict, "rprocs");
+	error = npf_mk_rprocs(rpset, rprocs, errdict);
+	if (error) {
+		goto fail;
+	}
+
+	/* Rules. */
 	rules = prop_dictionary_get(npf_dict, "rules");
-	error = npf_mk_rules(rlset, rules, rprocs, errdict);
+	nitems = prop_array_count(rules);
+
+	rlset = npf_ruleset_create(nitems);
+	error = npf_mk_rules(rlset, rules, rpset, errdict);
 	if (error) {
 		goto fail;
 	}
@@ -508,29 +486,32 @@
 	prop_dictionary_get_bool(npf_dict, "flush", &flush);
 
 	/*
-	 * Finally - reload ruleset, tableset and NAT policies.
-	 * Operation will be performed as a single transaction.
+	 * Finally - perform the reload.
 	 */
-	npf_reload(npf_dict, rlset, tblset, nset, flush);
+	npf_config_reload(npf_dict, rlset, tblset, nset, rpset, flush);
 
 	/* Turn on/off session tracking accordingly. */
 	npf_session_tracking(!flush);
 
 	/* Done.  Since data is consumed now, we shall not destroy it. */
 	tblset = NULL;
+	rpset = NULL;
 	rlset = NULL;
 	nset = NULL;
 fail:
 	/*
 	 * Note: destroy rulesets first, to drop references to the tableset.
 	 */
-	KASSERT(error == 0 || (nset || rlset || tblset));
+	KASSERT(error == 0 || (nset || rpset || rlset || tblset));
 	if (nset) {
 		npf_ruleset_destroy(nset);
 	}
 	if (rlset) {
 		npf_ruleset_destroy(rlset);
 	}
+	if (rpset) {
+		npf_rprocset_destroy(rpset);
+	}
 	if (tblset) {
 		npf_tableset_destroy(tblset);
 	}
@@ -548,6 +529,100 @@
 	return error;
 }
 
+/*
+ * npfctl_rule: add or remove dynamic rules in the specified ruleset.
+ */
+int
+npfctl_rule(u_long cmd, void *data)
+{
+	struct plistref *pref = data;
+	prop_dictionary_t npf_rule, retdict = NULL;
+	npf_ruleset_t *rlset;
+	npf_rule_t *rl = NULL;
+	const char *ruleset_name;
+	uint32_t rcmd = 0;
+	int error;
+
+	error = prop_dictionary_copyin_ioctl(pref, cmd, &npf_rule);
+	if (error) {
+		return error;
+	}
+	prop_dictionary_get_uint32(npf_rule, "command", &rcmd);
+	if (!prop_dictionary_get_cstring_nocopy(npf_rule,
+	    "ruleset-name", &ruleset_name)) {
+		return EINVAL;
+	}
+
+	if (rcmd == NPF_CMD_RULE_ADD) {
+		if ((rl = npf_rule_alloc(npf_rule)) == NULL) {
+			return EINVAL;
+		}
+		retdict = prop_dictionary_create();
+		prop_dictionary_set_uint64(retdict, "id",
+		    (uint64_t)(uintptr_t)rl);
+	}
+
+	npf_config_enter();
+	rlset = npf_config_ruleset();
+
+	switch (rcmd) {
+	case NPF_CMD_RULE_ADD: {
+		if ((error = npf_ruleset_add(rlset, ruleset_name, rl)) == 0) {
+			/* Success. */
+			rl = NULL;
+		}
+		break;
+	}
+	case NPF_CMD_RULE_REMOVE: {
+		uint64_t id64;
+
+		CTASSERT(sizeof(uintptr_t) <= sizeof(uint64_t));
+		if (!prop_dictionary_get_uint64(npf_rule, "id", &id64)) {
+			error = EINVAL;
+			break;
+		}
+		rl = npf_ruleset_remove(rlset, ruleset_name, (uintptr_t)id64);
+		if (rl == NULL) {
+			error = ENOENT;
+		}
+		break;
+	}
+	case NPF_CMD_RULE_REMKEY: {
+		prop_object_t obj = prop_dictionary_get(npf_rule, "key");
+		const void *key = prop_data_data_nocopy(obj);
+		size_t len = prop_data_size(obj);
+
+		if (len == 0 || len > NPF_RULE_MAXKEYLEN) {
+			error = EINVAL;
+			break;
+		}
+		rl = npf_ruleset_remkey(rlset, ruleset_name, key, len);
+		if (rl == NULL) {
+			error = ENOENT;
+		}
+		break;
+	}
+	default:
+		error = EINVAL;
+		break;
+	}
+	npf_config_exit();
+
+	if (rl) {
+		npf_rule_free(rl);
+	}
+	if (retdict) {
+		prop_object_release(npf_rule);
+		prop_dictionary_copyout_ioctl(pref, cmd, retdict);
+		prop_object_release(retdict);
+	}
+	return error;
+}
+
+/*
+ * npfctl_getconf: return the config dictionary as it was submitted.
+ * Additionally, indicate whether the ruleset is currently active.
+ */
 int
 npfctl_getconf(u_long cmd, void *data)
 {
@@ -555,61 +630,12 @@
 	prop_dictionary_t npf_dict;
 	int error;
 
-	npf_core_enter();
-	npf_dict = npf_core_dict();
+	npf_config_enter();
+	npf_dict = npf_config_dict();
 	prop_dictionary_set_bool(npf_dict, "active", npf_pfil_registered_p());
 	error = prop_dictionary_copyout_ioctl(pref, cmd, npf_dict);
-	npf_core_exit();
-
-	return error;
-}
-
-/*
- * npfctl_update_rule: reload a specific rule identified by the name.
- */
-int
-npfctl_update_rule(u_long cmd, void *data)
-{
-	struct plistref *pref = data;
-	prop_dictionary_t dict, errdict;
-	prop_array_t subrules;
-	prop_object_t obj;
-	npf_ruleset_t *rlset;
-	const char *name;
-	int error;
-
-	/* Retrieve and construct the rule. */
-	error = prop_dictionary_copyin_ioctl(pref, cmd, &dict);
-	if (error) {
-		return error;
-	}
+	npf_config_exit();
 
-	/* Dictionary for error reporting. */
-	errdict = prop_dictionary_create();
-
-	/* Create the ruleset and construct sub-rules. */
-	rlset = npf_ruleset_create();
-	subrules = prop_dictionary_get(dict, "subrules");
-	error = npf_mk_subrules(rlset, subrules, NULL, errdict);
-	if (error) {
-		goto out;
-	}
-
-	/* Lookup the rule by name, and replace its subset (sub-rules). */
-	obj = prop_dictionary_get(dict, "name");
-	name = prop_string_cstring_nocopy(obj);
-	if (npf_ruleset_replace(name, rlset) == NULL) {
-		/* Not found. */
-		error = ENOENT;
-out:		/* Error path. */
-		npf_ruleset_destroy(rlset);
-	}
-	prop_object_release(dict);
-
-	/* Error report. */
-	prop_dictionary_set_int32(errdict, "errno", error);
-	prop_dictionary_copyout_ioctl(pref, cmd, errdict);
-	prop_object_release(errdict);
 	return error;
 }
 
@@ -668,23 +694,24 @@
 	 */
 	selist = prop_dictionary_get(sesdict, "session-list");
 	if (prop_object_type(selist) != PROP_TYPE_ARRAY) {
-		error = EINVAL;
-		goto fail;
+		prop_object_release(selist);
+		return EINVAL;
 	}
 
 	/* Create a session hash table. */
 	sehasht = sess_htable_create();
 	if (sehasht == NULL) {
-		error = ENOMEM;
-		goto fail;
+		prop_object_release(selist);
+		return ENOMEM;
 	}
 
 	/*
-	 * Iterate through and construct each session.
+	 * Iterate through and construct each session.  Note: acquire the
+	 * config lock as we access NAT policies during the restore.
 	 */
 	error = 0;
+	npf_config_enter();
 	it = prop_array_iterator(selist);
-	npf_core_enter();
 	while ((sedict = prop_object_iterator_next(it)) != NULL) {
 		/* Session - dictionary. */
 		if (prop_object_type(sedict) != PROP_TYPE_DICTIONARY) {
@@ -697,11 +724,12 @@
 			goto fail;
 		}
 	}
-	npf_core_exit();
 	sess_htable_reload(sehasht);
 fail:
+	npf_config_exit();
+	prop_object_iterator_release(it);
 	prop_object_release(selist);
-	if (error && sehasht) {
+	if (error) {
 		/* Destroy session table. */
 		sess_htable_destroy(sehasht);
 	}
@@ -720,24 +748,25 @@
 	npf_tableset_t *tblset;
 	int error;
 
-	npf_core_enter(); /* XXXSMP */
-	tblset = npf_core_tableset();
-	switch (nct->nct_action) {
-	case NPF_IOCTL_TBLENT_LOOKUP:
+	//npf_config_ref();
+	tblset = npf_config_tableset();
+
+	switch (nct->nct_cmd) {
+	case NPF_CMD_TABLE_LOOKUP:
 		error = npf_table_lookup(tblset, nct->nct_tid,
 		    nct->nct_data.ent.alen, &nct->nct_data.ent.addr);
 		break;
-	case NPF_IOCTL_TBLENT_ADD:
+	case NPF_CMD_TABLE_ADD:
 		error = npf_table_insert(tblset, nct->nct_tid,
 		    nct->nct_data.ent.alen, &nct->nct_data.ent.addr,
 		    nct->nct_data.ent.mask);
 		break;
-	case NPF_IOCTL_TBLENT_REM:
+	case NPF_CMD_TABLE_REMOVE:
 		error = npf_table_remove(tblset, nct->nct_tid,
 		    nct->nct_data.ent.alen, &nct->nct_data.ent.addr,
 		    nct->nct_data.ent.mask);
 		break;
-	case NPF_IOCTL_TBLENT_LIST:
+	case NPF_CMD_TABLE_LIST:
 		error = npf_table_list(tblset, nct->nct_tid,
 		    nct->nct_data.buf.buf, nct->nct_data.buf.len);
 		break;
@@ -745,6 +774,7 @@
 		error = EINVAL;
 		break;
 	}
-	npf_core_exit(); /* XXXSMP */
+	//npf_config_unref();
+
 	return error;
 }
--- a/sys/net/npf/npf_handler.c	Sat Feb 09 02:49:36 2013 +0000
+++ b/sys/net/npf/npf_handler.c	Sat Feb 09 03:35:31 2013 +0000
@@ -1,7 +1,7 @@
-/*	$NetBSD: npf_handler.c,v 1.25 2013/01/20 18:45:56 rmind Exp $	*/
+/*	$NetBSD: npf_handler.c,v 1.26 2013/02/09 03:35:31 rmind Exp $	*/
 
 /*-
- * Copyright (c) 2009-2012 The NetBSD Foundation, Inc.
+ * Copyright (c) 2009-2013 The NetBSD Foundation, Inc.
  * All rights reserved.
  *
  * This material is based upon work partially supported by The
@@ -34,7 +34,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: npf_handler.c,v 1.25 2013/01/20 18:45:56 rmind Exp $");
+__KERNEL_RCSID(0, "$NetBSD: npf_handler.c,v 1.26 2013/02/09 03:35:31 rmind Exp $");
 
 #include <sys/types.h>
 #include <sys/param.h>
@@ -61,6 +61,10 @@
 static struct pfil_head *	npf_ph_inet = NULL;
 static struct pfil_head *	npf_ph_inet6 = NULL;
 
+#ifndef INET6
+#define ip6_reass_packet(x, y)	ENOTSUP
+#endif
+
 /*
  * npf_ifhook: hook handling interface changes.
  */
@@ -84,17 +88,14 @@
 		struct ip *ip = nbuf_dataptr(nbuf);
 		error = ip_reass_packet(mp, ip);
 	} else if (npf_iscached(npc, NPC_IP6)) {
-#ifdef INET6
 		/*
 		 * Note: ip6_reass_packet() offset is the start of
 		 * the fragment header.
 		 */
-		const u_int hlen = npf_cache_hlen(npc);
-		error = ip6_reass_packet(mp, hlen);
+		error = ip6_reass_packet(mp, npc->npc_hlen);
 		if (error && *mp == NULL) {
 			memset(nbuf, 0, sizeof(nbuf_t));
 		}
-#endif
 	}
 	if (error) {
 		npf_stats_inc(NPF_STAT_REASSFAIL);
@@ -131,7 +132,6 @@
 	nbuf_t nbuf;
 	npf_cache_t npc;
 	npf_session_t *se;
-	npf_ruleset_t *rlset;
 	npf_rule_t *rl;
 	npf_rproc_t *rp;
 	int error, retfl;
@@ -165,7 +165,7 @@
 		}
 	}
 
-	/* Inspect the list of sessions. */
+	/* Inspect the list of sessions (if found, acquires a reference). */
 	se = npf_session_inspect(&npc, &nbuf, di, &error);
 
 	/* If "passing" session found - skip the ruleset inspection. */
@@ -181,14 +181,15 @@
 	}
 
 	/* Acquire the lock, inspect the ruleset using this packet. */
-	npf_core_enter();
-	rlset = npf_core_ruleset();
+	int slock = npf_config_read_enter();
+	npf_ruleset_t *rlset = npf_config_ruleset();
+
 	rl = npf_ruleset_inspect(&npc, &nbuf, rlset, di, NPF_LAYER_3);
 	if (rl == NULL) {
-		bool default_pass = npf_default_pass();
-		npf_core_exit();
+		const bool pass = npf_default_pass();
+		npf_config_read_exit(slock);
 
-		if (default_pass) {
+		if (pass) {
 			npf_stats_inc(NPF_STAT_PASS_DEFAULT);
 			goto pass;
 		}
@@ -203,8 +204,10 @@
 	KASSERT(rp == NULL);
 	rp = npf_rule_getrproc(rl);
 
-	/* Apply the rule, release the lock. */
-	error = npf_rule_apply(&npc, &nbuf, rl, &retfl);
+	/* Conclude with the rule and release the lock. */
+	error = npf_rule_conclude(rl, &retfl);
+	npf_config_read_exit(slock);
+
 	if (error) {
 		npf_stats_inc(NPF_STAT_BLOCK_RULESET);
 		goto block;
@@ -212,15 +215,17 @@
 	npf_stats_inc(NPF_STAT_PASS_RULESET);
 
 	/*
-	 * Establish a "pass" session, if required.  Just proceed, if session
-	 * creation fails (e.g. due to unsupported protocol).
-	 *
-	 * Note: the reference on the rule procedure is transfered to the
-	 * session.  It will be released on session destruction.
+	 * Establish a "pass" session, if required.  Just proceed,
+	 * if session creation fails (e.g. due to unsupported protocol).
 	 */
 	if ((retfl & NPF_RULE_STATEFUL) != 0 && !se) {
 		se = npf_session_establish(&npc, &nbuf, di);
 		if (se) {
+			/*
+			 * Note: the reference on the rule procedure is
+			 * transfered to the session.  It will be released
+			 * on session destruction.
+			 */
 			npf_session_setpass(se, rp);
 		}
 	}
--- a/sys/net/npf/npf_impl.h	Sat Feb 09 02:49:36 2013 +0000
+++ b/sys/net/npf/npf_impl.h	Sat Feb 09 03:35:31 2013 +0000
@@ -1,7 +1,7 @@
-/*	$NetBSD: npf_impl.h,v 1.25 2012/12/24 19:05:43 rmind Exp $	*/
+/*	$NetBSD: npf_impl.h,v 1.26 2013/02/09 03:35:32 rmind Exp $	*/
 
 /*-
- * Copyright (c) 2009-2012 The NetBSD Foundation, Inc.
+ * Copyright (c) 2009-2013 The NetBSD Foundation, Inc.
  * All rights reserved.
  *
  * This material is based upon work partially supported by The
@@ -70,12 +70,14 @@
 
 struct npf_ruleset;
 struct npf_rule;
+struct npf_rprocset;
 struct npf_nat;
 struct npf_session;
 
 typedef struct npf_ruleset	npf_ruleset_t;
 typedef struct npf_rule		npf_rule_t;
 typedef struct npf_nat		npf_nat_t;
+typedef struct npf_rprocset	npf_rprocset_t;
 typedef struct npf_alg		npf_alg_t;
 typedef struct npf_natpolicy	npf_natpolicy_t;
 typedef struct npf_session	npf_session_t;
@@ -122,18 +124,23 @@
  * INTERFACES.
  */
 
-/* NPF control, statistics, etc. */
-void		npf_core_enter(void);
-npf_ruleset_t *	npf_core_ruleset(void);
-npf_ruleset_t *	npf_core_natset(void);
-npf_tableset_t *npf_core_tableset(void);
-void		npf_core_exit(void);
-bool		npf_core_locked(void);
+/* NPF config, statistics, etc. */
+void		npf_config_init(void);
+void		npf_config_fini(void);
+
+void		npf_config_enter(void);
+void		npf_config_exit(void);
+bool		npf_config_locked_p(void);
+int		npf_config_read_enter(void);
+void		npf_config_read_exit(int);
+
+void		npf_config_reload(prop_dictionary_t, npf_ruleset_t *,
+		    npf_tableset_t *, npf_ruleset_t *, npf_rprocset_t *, bool);
+npf_ruleset_t *	npf_config_ruleset(void);
+npf_ruleset_t *	npf_config_natset(void);
+npf_tableset_t *npf_config_tableset(void);
+prop_dictionary_t npf_config_dict(void);
 bool		npf_default_pass(void);
-prop_dictionary_t npf_core_dict(void);
-
-void		npf_reload(prop_dictionary_t, npf_ruleset_t *,
-		    npf_tableset_t *, npf_ruleset_t *, bool);
 
 void		npflogattach(int);
 void		npflogdetach(void);
@@ -142,7 +149,7 @@
 int		npfctl_getconf(u_long, void *);
 int		npfctl_sessions_save(u_long, void *);
 int		npfctl_sessions_load(u_long, void *);
-int		npfctl_update_rule(u_long, void *);
+int		npfctl_rule(u_long, void *);
 int		npfctl_table(void *);
 
 void		npf_stats_inc(npf_stats_t);
@@ -214,23 +221,29 @@
 int		npf_table_list(npf_tableset_t *, u_int, void *, size_t);
 
 /* Ruleset interface. */
-npf_ruleset_t *	npf_ruleset_create(void);
+npf_ruleset_t *	npf_ruleset_create(size_t);
 void		npf_ruleset_destroy(npf_ruleset_t *);
 void		npf_ruleset_insert(npf_ruleset_t *, npf_rule_t *);
+void		npf_ruleset_reload(npf_ruleset_t *, npf_ruleset_t *);
 void		npf_ruleset_natreload(npf_ruleset_t *, npf_ruleset_t *);
 npf_rule_t *	npf_ruleset_matchnat(npf_ruleset_t *, npf_natpolicy_t *);
 npf_rule_t *	npf_ruleset_sharepm(npf_ruleset_t *, npf_natpolicy_t *);
-npf_rule_t *	npf_ruleset_replace(const char *, npf_ruleset_t *);
 void		npf_ruleset_freealg(npf_ruleset_t *, npf_alg_t *);
 
+int		npf_ruleset_add(npf_ruleset_t *, const char *, npf_rule_t *);
+npf_rule_t *	npf_ruleset_remove(npf_ruleset_t *, const char *, uintptr_t);
+npf_rule_t *	npf_ruleset_remkey(npf_ruleset_t *, const char *,
+		    const void *, size_t);
+
 npf_rule_t *	npf_ruleset_inspect(npf_cache_t *, nbuf_t *,
 		    const npf_ruleset_t *, const int, const int);
-int		npf_rule_apply(npf_cache_t *, nbuf_t *, npf_rule_t *, int *);
+int		npf_rule_conclude(const npf_rule_t *, int *);
 
 /* Rule interface. */
-npf_rule_t *	npf_rule_alloc(prop_dictionary_t, npf_rproc_t *, void *, size_t);
+npf_rule_t *	npf_rule_alloc(prop_dictionary_t);
+void		npf_rule_setcode(npf_rule_t *, int, void *, size_t);
+void		npf_rule_setrproc(npf_rule_t *, npf_rproc_t *);
 void		npf_rule_free(npf_rule_t *);
-npf_ruleset_t *	npf_rule_subset(npf_rule_t *);
 npf_natpolicy_t *npf_rule_getnat(const npf_rule_t *);
 void		npf_rule_setnat(npf_rule_t *, npf_natpolicy_t *);
 npf_rproc_t *	npf_rule_getrproc(npf_rule_t *);
@@ -240,6 +253,11 @@
 int		npf_ext_construct(const char *,
 		    npf_rproc_t *, prop_dictionary_t);
 
+npf_rprocset_t *npf_rprocset_create(void);
+void		npf_rprocset_destroy(npf_rprocset_t *);
+npf_rproc_t *	npf_rprocset_lookup(npf_rprocset_t *, const char *);
+void		npf_rprocset_insert(npf_rprocset_t *, npf_rproc_t *);
+
 npf_rproc_t *	npf_rproc_create(prop_dictionary_t);
 void		npf_rproc_acquire(npf_rproc_t *);
 void		npf_rproc_release(npf_rproc_t *);
--- a/sys/net/npf/npf_inet.c	Sat Feb 09 02:49:36 2013 +0000
+++ b/sys/net/npf/npf_inet.c	Sat Feb 09 03:35:31 2013 +0000
@@ -1,4 +1,4 @@
-/*	$NetBSD: npf_inet.c,v 1.20 2012/12/24 23:11:25 rmind Exp $	*/
+/*	$NetBSD: npf_inet.c,v 1.21 2013/02/09 03:35:32 rmind Exp $	*/
 
 /*-
  * Copyright (c) 2009-2012 The NetBSD Foundation, Inc.
@@ -39,7 +39,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: npf_inet.c,v 1.20 2012/12/24 23:11:25 rmind Exp $");
+__KERNEL_RCSID(0, "$NetBSD: npf_inet.c,v 1.21 2013/02/09 03:35:32 rmind Exp $");
 
 #include <sys/param.h>
 #include <sys/types.h>
@@ -199,7 +199,7 @@
 
 	if (npf_iscached(npc, NPC_IP4)) {
 		const struct ip *ip = npc->npc_ip.v4;
-		return ntohs(ip->ip_len) - npf_cache_hlen(npc) - thlen;
+		return ntohs(ip->ip_len) - npc->npc_hlen - thlen;
 	} else if (npf_iscached(npc, NPC_IP6)) {
 		const struct ip6_hdr *ip6 = npc->npc_ip.v6;
 		return ntohs(ip6->ip6_plen) - thlen;
@@ -231,7 +231,7 @@
 	KASSERT(topts_len <= MAX_TCPOPTLEN);
 
 	/* First step: IP and TCP header up to options. */
-	step = npf_cache_hlen(npc) + sizeof(struct tcphdr);
+	step = npc->npc_hlen + sizeof(struct tcphdr);
 	nbuf_reset(nbuf);
 next:
 	if ((nptr = nbuf_advance(nbuf, step, 1)) == NULL) {
@@ -521,7 +521,7 @@
 bool
 npf_rwrport(const npf_cache_t *npc, int di, const in_port_t port)
 {
-	const int proto = npf_cache_ipproto(npc);
+	const int proto = npc->npc_proto;
 	in_port_t *oport;
 
 	KASSERT(npf_iscached(npc, NPC_TCP) || npf_iscached(npc, NPC_UDP));
@@ -546,7 +546,7 @@
 npf_rwrcksum(const npf_cache_t *npc, const int di,
     const npf_addr_t *addr, const in_port_t port)
 {
-	const int proto = npf_cache_ipproto(npc);
+	const int proto = npc->npc_proto;
 	const int alen = npc->npc_alen;
 	npf_addr_t *oaddr;
 	uint16_t *ocksum;
--- a/sys/net/npf/npf_instr.c	Sat Feb 09 02:49:36 2013 +0000
+++ b/sys/net/npf/npf_instr.c	Sat Feb 09 03:35:31 2013 +0000
@@ -1,4 +1,4 @@
-/*	$NetBSD: npf_instr.c,v 1.15 2012/12/24 19:05:43 rmind Exp $	*/
+/*	$NetBSD: npf_instr.c,v 1.16 2013/02/09 03:35:32 rmind Exp $	*/
 
 /*-
  * Copyright (c) 2009-2012 The NetBSD Foundation, Inc.
@@ -34,7 +34,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: npf_instr.c,v 1.15 2012/12/24 19:05:43 rmind Exp $");
+__KERNEL_RCSID(0, "$NetBSD: npf_instr.c,v 1.16 2013/02/09 03:35:32 rmind Exp $");
 
 #include <sys/param.h>
 #include <sys/types.h>
@@ -105,7 +105,7 @@
 	if (alen && npc->npc_alen != alen) {
 		return -1;
 	}
-	return (proto != 0xff && npf_cache_ipproto(npc) != proto) ? -1 : 0;
+	return (proto != 0xff && npc->npc_proto != proto) ? -1 : 0;
 }
 
 /*
@@ -114,11 +114,10 @@
 int
 npf_match_table(const npf_cache_t *npc, int sd, u_int tid)
 {
-	npf_tableset_t *tblset = npf_core_tableset();
+	npf_tableset_t *tblset = npf_config_tableset();
 	const npf_addr_t *addr = sd ? npc->npc_srcip : npc->npc_dstip;
 	const int alen = npc->npc_alen;
 
-	KASSERT(npf_core_locked());
 	KASSERT(npf_iscached(npc, NPC_IP46));
 
 	/* Match address against NPF table. */
--- a/sys/net/npf/npf_nat.c	Sat Feb 09 02:49:36 2013 +0000
+++ b/sys/net/npf/npf_nat.c	Sat Feb 09 03:35:31 2013 +0000
@@ -1,7 +1,7 @@
-/*	$NetBSD: npf_nat.c,v 1.18 2012/12/24 19:05:44 rmind Exp $	*/
+/*	$NetBSD: npf_nat.c,v 1.19 2013/02/09 03:35:32 rmind Exp $	*/
 
 /*-
- * Copyright (c) 2010-2012 The NetBSD Foundation, Inc.
+ * Copyright (c) 2010-2013 The NetBSD Foundation, Inc.
  * All rights reserved.
  *
  * This material is based upon work partially supported by The
@@ -30,8 +30,8 @@
  */
 
 /*
- * NPF network address port translation (NAPT).
- * Described in RFC 2663, RFC 3022.  Commonly just "NAT".
+ * NPF network address port translation (NAPT) and other forms of NAT.
+ * Described in RFC 2663, RFC 3022, etc.
  *
  * Overview
  *
@@ -76,7 +76,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: npf_nat.c,v 1.18 2012/12/24 19:05:44 rmind Exp $");
+__KERNEL_RCSID(0, "$NetBSD: npf_nat.c,v 1.19 2013/02/09 03:35:32 rmind Exp $");
 
 #include <sys/param.h>
 #include <sys/types.h>
@@ -87,6 +87,7 @@
 #include <sys/kmem.h>
 #include <sys/mutex.h>
 #include <sys/pool.h>
+#include <sys/proc.h>
 #include <sys/cprng.h>
 
 #include <net/pfil.h>
@@ -117,6 +118,7 @@
  */
 struct npf_natpolicy {
 	LIST_HEAD(, npf_nat)	n_nat_list;
+	volatile u_int		n_refcnt;
 	kmutex_t		n_lock;
 	kcondvar_t		n_cv;
 	npf_portmap_t *		n_portmap;
@@ -258,6 +260,11 @@
 	}
 	mutex_exit(&np->n_lock);
 
+	/* All references should be going away. */
+	while (np->n_refcnt) {
+		kpause("npfgcnat", false, 1, NULL);
+	}
+
 	/* Destroy the port map, on last reference. */
 	if (pm && --pm->p_refcnt == 0) {
 		KASSERT((np->n_flags & NPF_NAT_PORTMAP) != 0);
@@ -415,26 +422,25 @@
 
 /*
  * npf_nat_inspect: inspect packet against NAT ruleset and return a policy.
+ *
+ * => Acquire a reference on the policy, if found.
  */
 static npf_natpolicy_t *
 npf_nat_inspect(npf_cache_t *npc, nbuf_t *nbuf, const int di)
 {
-	npf_ruleset_t *rlset;
+	int slock = npf_config_read_enter();
+	npf_ruleset_t *rlset = npf_config_natset();
 	npf_natpolicy_t *np;
 	npf_rule_t *rl;
 
-	npf_core_enter();
-	rlset = npf_core_natset();
 	rl = npf_ruleset_inspect(npc, nbuf, rlset, di, NPF_LAYER_3);
 	if (rl == NULL) {
-		npf_core_exit();
+		npf_config_read_exit(slock);
 		return NULL;
 	}
 	np = npf_rule_getnat(rl);
-	if (np == NULL) {
-		npf_core_exit();
-		return NULL;
-	}
+	atomic_inc_uint(&np->n_refcnt);
+	npf_config_read_exit(slock);
 	return np;
 }
 
@@ -444,7 +450,7 @@
 static npf_nat_t *
 npf_nat_create(npf_cache_t *npc, npf_natpolicy_t *np)
 {
-	const int proto = npf_cache_ipproto(npc);
+	const int proto = npc->npc_proto;
 	npf_nat_t *nt;
 
 	KASSERT(npf_iscached(npc, NPC_IP46));
@@ -511,7 +517,7 @@
 npf_nat_translate(npf_cache_t *npc, nbuf_t *nbuf, npf_nat_t *nt,
     const bool forw, const int di)
 {
-	const int proto = npf_cache_ipproto(npc);
+	const int proto = npc->npc_proto;
 	const npf_natpolicy_t *np = nt->nt_natpolicy;
 	const npf_addr_t *addr;
 	in_port_t port;
@@ -618,7 +624,7 @@
 
 	/*
 	 * Inspect the packet for a NAT policy, if there is no session.
-	 * Note: acquires the lock (releases, if not found).
+	 * Note: acquires a reference if found.
 	 */
 	np = npf_nat_inspect(npc, nbuf, di);
 	if (np == NULL) {
@@ -628,17 +634,14 @@
 	forw = true;
 
 	/*
-	 * Create a new NAT entry.  Note: it is safe to unlock, since the
-	 * NAT policy wont be desotroyed while there are list entries, which
-	 * are removed only on session expiration.  Currently, NAT entry is
-	 * not yet associated with any session.
+	 * Create a new NAT entry (not yet associated with any session).
+	 * We will consume the reference on success (release on error).
 	 */
 	nt = npf_nat_create(npc, np);
 	if (nt == NULL) {
-		npf_core_exit();
+		atomic_dec_uint(&np->n_refcnt);
 		return ENOMEM;
 	}
-	npf_core_exit();
 	new = true;
 
 	/* Determine whether any ALG matches. */
@@ -743,6 +746,7 @@
 	if (LIST_EMPTY(&np->n_nat_list)) {
 		cv_broadcast(&np->n_cv);
 	}
+	atomic_dec_uint(&np->n_refcnt);
 	mutex_exit(&np->n_lock);
 
 	/* Free structure, increase the counter. */
@@ -824,7 +828,8 @@
 	}
 
 	/* Match if there is an existing NAT policy. */
-	rl = npf_ruleset_matchnat(npf_core_natset(), __UNCONST(onp));
+	KASSERT(npf_config_locked_p());
+	rl = npf_ruleset_matchnat(npf_config_natset(), __UNCONST(onp));
 	if (rl == NULL) {
 		return NULL;
 	}
--- a/sys/net/npf/npf_ncode.h	Sat Feb 09 02:49:36 2013 +0000
+++ b/sys/net/npf/npf_ncode.h	Sat Feb 09 03:35:31 2013 +0000
@@ -1,4 +1,4 @@
-/*	$NetBSD: npf_ncode.h,v 1.10 2012/07/19 21:52:29 spz Exp $	*/
+/*	$NetBSD: npf_ncode.h,v 1.11 2013/02/09 03:35:32 rmind Exp $	*/
 
 /*-
  * Copyright (c) 2009-2010 The NetBSD Foundation, Inc.
@@ -45,12 +45,8 @@
 /*
  * N-code processing, validation & building.
  */
-void *	npf_ncode_alloc(size_t);
-void	npf_ncode_free(void *, size_t);
-
 int	npf_ncode_process(npf_cache_t *, const void *, nbuf_t *, const int);
 int	npf_ncode_validate(const void *, size_t, int *);
-
 #endif
 
 /* Error codes. */
--- a/sys/net/npf/npf_processor.c	Sat Feb 09 02:49:36 2013 +0000
+++ b/sys/net/npf/npf_processor.c	Sat Feb 09 03:35:31 2013 +0000
@@ -1,4 +1,4 @@
-/*	$NetBSD: npf_processor.c,v 1.14 2013/01/20 18:45:56 rmind Exp $	*/
+/*	$NetBSD: npf_processor.c,v 1.15 2013/02/09 03:35:32 rmind Exp $	*/
 
 /*-
  * Copyright (c) 2009-2010 The NetBSD Foundation, Inc.
@@ -50,7 +50,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: npf_processor.c,v 1.14 2013/01/20 18:45:56 rmind Exp $");
+__KERNEL_RCSID(0, "$NetBSD: npf_processor.c,v 1.15 2013/02/09 03:35:32 rmind Exp $");
 
 #include <sys/param.h>
 #include <sys/types.h>
@@ -104,20 +104,6 @@
 	return (const uint32_t *)iptr + n;
 }
 
-void *
-npf_ncode_alloc(size_t sz)
-{
-
-	return kmem_alloc(sz, KM_SLEEP);
-}
-
-void
-npf_ncode_free(void *nc, size_t sz)
-{
-
-	kmem_free(nc, sz);
-}
-
 /*
  * npf_ncode_process: process n-code using data of the specified packet.
  *
--- a/sys/net/npf/npf_rproc.c	Sat Feb 09 02:49:36 2013 +0000
+++ b/sys/net/npf/npf_rproc.c	Sat Feb 09 03:35:31 2013 +0000
@@ -1,7 +1,7 @@
-/*	$NetBSD: npf_rproc.c,v 1.5 2013/01/20 18:45:56 rmind Exp $	*/
+/*	$NetBSD: npf_rproc.c,v 1.6 2013/02/09 03:35:32 rmind Exp $	*/
 
 /*-
- * Copyright (c) 2009-2012 The NetBSD Foundation, Inc.
+ * Copyright (c) 2009-2013 The NetBSD Foundation, Inc.
  * All rights reserved.
  *
  * This material is based upon work partially supported by The
@@ -54,18 +54,26 @@
 	unsigned		ext_refcnt;
 } npf_ext_t;
 
+struct npf_rprocset {
+	LIST_HEAD(, npf_rproc)	rps_list;
+};
+
 #define	RPROC_NAME_LEN		32
 #define	RPROC_EXT_COUNT		16
 
 struct npf_rproc {
-	/* Name, reference count and flags. */
-	char			rp_name[RPROC_NAME_LEN];
+	/* Flags and reference count. */
+	uint32_t		rp_flags;
 	u_int			rp_refcnt;
-	uint32_t		rp_flags;
+
 	/* Associated extensions and their metadata . */
 	unsigned		rp_ext_count;
 	npf_ext_t *		rp_ext[RPROC_EXT_COUNT];
 	void *			rp_ext_meta[RPROC_EXT_COUNT];
+
+	/* Name of the procedure and list entry. */
+	char			rp_name[RPROC_NAME_LEN];
+	LIST_ENTRY(npf_rproc)	rp_entry;
 };
 
 static LIST_HEAD(, npf_ext)	ext_list	__cacheline_aligned;
@@ -188,6 +196,52 @@
  * Rule procedure management.
  */
 
+npf_rprocset_t *
+npf_rprocset_create(void)
+{
+	npf_rprocset_t *rpset;
+
+	rpset = kmem_zalloc(sizeof(npf_rprocset_t), KM_SLEEP);
+	LIST_INIT(&rpset->rps_list);
+	return rpset;
+}
+
+void
+npf_rprocset_destroy(npf_rprocset_t *rpset)
+{
+	npf_rproc_t *rp;
+
+	while ((rp = LIST_FIRST(&rpset->rps_list)) != NULL) {
+		LIST_REMOVE(rp, rp_entry);
+		npf_rproc_release(rp);
+	}
+	kmem_free(rpset, sizeof(npf_rprocset_t));
+}
+
+/*
+ * npf_rproc_lookup: find a rule procedure by the name.
+ */
+npf_rproc_t *
+npf_rprocset_lookup(npf_rprocset_t *rpset, const char *name)
+{
+	npf_rproc_t *rp;
+
+	LIST_FOREACH(rp, &rpset->rps_list, rp_entry) {
+		if (strncmp(rp->rp_name, name, RPROC_NAME_LEN) == 0)
+			break;
+	}
+	return rp;
+}
+
+/*
+ * npf_rproc_insert: insert a new rule procedure into the set.
+ */
+void
+npf_rprocset_insert(npf_rprocset_t *rpset, npf_rproc_t *rp)
+{
+	LIST_INSERT_HEAD(&rpset->rps_list, rp, rp_entry);
+}
+
 /*
  * npf_rproc_create: construct a new rule procedure, lookup and associate
  * the extension calls with it.
@@ -216,7 +270,6 @@
 void
 npf_rproc_acquire(npf_rproc_t *rp)
 {
-
 	atomic_inc_uint(&rp->rp_refcnt);
 }
 
--- a/sys/net/npf/npf_ruleset.c	Sat Feb 09 02:49:36 2013 +0000
+++ b/sys/net/npf/npf_ruleset.c	Sat Feb 09 03:35:31 2013 +0000
@@ -1,7 +1,7 @@
-/*	$NetBSD: npf_ruleset.c,v 1.16 2013/01/20 18:45:56 rmind Exp $	*/
+/*	$NetBSD: npf_ruleset.c,v 1.17 2013/02/09 03:35:32 rmind Exp $	*/
 
 /*-
- * Copyright (c) 2009-2012 The NetBSD Foundation, Inc.
+ * Copyright (c) 2009-2013 The NetBSD Foundation, Inc.
  * All rights reserved.
  *
  * This material is based upon work partially supported by The
@@ -34,95 +34,267 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: npf_ruleset.c,v 1.16 2013/01/20 18:45:56 rmind Exp $");
+__KERNEL_RCSID(0, "$NetBSD: npf_ruleset.c,v 1.17 2013/02/09 03:35:32 rmind Exp $");
 
 #include <sys/param.h>
 #include <sys/types.h>
 
 #include <sys/kmem.h>
 #include <sys/queue.h>
+#include <sys/mbuf.h>
 #include <sys/types.h>
 
+#include <net/bpf.h>
 #include <net/pfil.h>
 #include <net/if.h>
 
 #include "npf_ncode.h"
 #include "npf_impl.h"
 
-/* Ruleset structure (queue and default rule). */
 struct npf_ruleset {
-	TAILQ_HEAD(, npf_rule)	rs_queue;
-	npf_rule_t *		rs_default;
+	/* List of all rules and dynamic (i.e. named) rules. */
+	LIST_HEAD(, npf_rule)	rs_all;
+	LIST_HEAD(, npf_rule)	rs_dynamic;
+
+	/* Number of array slots and active rules. */
+	u_int			rs_slots;
+	u_int			rs_nitems;
+
+	/* Array of ordered rules. */
+	npf_rule_t *		rs_rules[];
 };
 
-#define	NPF_RNAME_LEN		16
+struct npf_rule {
+	/* Attributes, interface and skip slot. */
+	uint32_t		r_attr;
+	u_int			r_ifid;
+	u_int			r_skip_to;
 
-/* Rule structure. */
-struct npf_rule {
-	/* Rule name (optional) and list entry. */
-	char			r_name[NPF_RNAME_LEN];
-	TAILQ_ENTRY(npf_rule)	r_entry;
-	/* Optional: sub-ruleset, NAT policy. */
-	npf_ruleset_t		r_subset;
+	/* Code to process, if any. */
+	int			r_type;
+	void *			r_code;
+	size_t			r_clen;
+
+	/* NAT policy (optional), rule procedure and subset. */
 	npf_natpolicy_t *	r_natp;
-	/* Rule priority: (highest) 0, 1, 2 ... n (lowest). */
+	npf_rproc_t *		r_rproc;
+
+	/* Rule priority: (highest) 1, 2 ... n (lowest). */
 	pri_t			r_priority;
-	/* N-code to process. */
-	void *			r_ncode;
-	size_t			r_nc_size;
-	/* Attributes of this rule. */
-	uint32_t		r_attr;
-	/* Interface. */
-	u_int			r_ifid;
-	/* Rule procedure data. */
-	npf_rproc_t *		r_rproc;
+
+	/*
+	 * Dynamic group: subset queue and a dynamic group list entry.
+	 * Dynamic rule: entry and the parent rule (the group).
+	 */
+	union {
+		TAILQ_HEAD(npf_ruleq, npf_rule) r_subset;
+		TAILQ_ENTRY(npf_rule)	r_entry;
+	} /* C11 */;
+	union {
+		LIST_ENTRY(npf_rule)	r_dentry;
+		npf_rule_t *		r_parent;
+	} /* C11 */;
+
+	/* Rule name and all-list entry. */
+	char			r_name[NPF_RULE_MAXNAMELEN];
+	LIST_ENTRY(npf_rule)	r_aentry;
+
+	/* Key (optional). */
+	uint8_t			r_key[NPF_RULE_MAXKEYLEN];
 };
 
+#define	NPF_DYNAMIC_GROUP_P(attr) \
+    (((attr) & NPF_DYNAMIC_GROUP) == NPF_DYNAMIC_GROUP)
+
 npf_ruleset_t *
-npf_ruleset_create(void)
+npf_ruleset_create(size_t slots)
 {
+	size_t len = offsetof(npf_ruleset_t, rs_rules[slots]);
 	npf_ruleset_t *rlset;
 
-	rlset = kmem_zalloc(sizeof(npf_ruleset_t), KM_SLEEP);
-	TAILQ_INIT(&rlset->rs_queue);
+	rlset = kmem_zalloc(len, KM_SLEEP);
+	rlset->rs_slots = slots;
+	LIST_INIT(&rlset->rs_dynamic);
+	LIST_INIT(&rlset->rs_all);
 	return rlset;
 }
 
+static void
+npf_ruleset_unlink(npf_ruleset_t *rlset, npf_rule_t *rl)
+{
+	if (NPF_DYNAMIC_GROUP_P(rl->r_attr)) {
+		LIST_REMOVE(rl, r_dentry);
+	}
+	if ((rl->r_attr & NPF_DYNAMIC_GROUP) == NPF_RULE_DYNAMIC) {
+		npf_rule_t *rg = rl->r_parent;
+		TAILQ_REMOVE(&rg->r_subset, rl, r_entry);
+	}
+	LIST_REMOVE(rl, r_aentry);
+}
+
 void
 npf_ruleset_destroy(npf_ruleset_t *rlset)
 {
+	size_t len = offsetof(npf_ruleset_t, rs_rules[rlset->rs_slots]);
 	npf_rule_t *rl;
 
-	while ((rl = TAILQ_FIRST(&rlset->rs_queue)) != NULL) {
-		TAILQ_REMOVE(&rlset->rs_queue, rl, r_entry);
+	while ((rl = LIST_FIRST(&rlset->rs_all)) != NULL) {
+		npf_ruleset_unlink(rlset, rl);
 		npf_rule_free(rl);
 	}
-	kmem_free(rlset, sizeof(npf_ruleset_t));
+	KASSERT(LIST_EMPTY(&rlset->rs_dynamic));
+	kmem_free(rlset, len);
 }
 
 /*
  * npf_ruleset_insert: insert the rule into the specified ruleset.
- *
- * Note: multiple rules at the same priority are allowed.
  */
 void
 npf_ruleset_insert(npf_ruleset_t *rlset, npf_rule_t *rl)
 {
-	npf_rule_t *it;
+	u_int n = rlset->rs_nitems;
+
+	KASSERT(n < rlset->rs_slots);
+
+	LIST_INSERT_HEAD(&rlset->rs_all, rl, r_aentry);
+	if (NPF_DYNAMIC_GROUP_P(rl->r_attr)) {
+		LIST_INSERT_HEAD(&rlset->rs_dynamic, rl, r_dentry);
+	}
+
+	rlset->rs_rules[n] = rl;
+	rlset->rs_nitems++;
 
-	if (rl->r_attr & NPF_RULE_DEFAULT) {
-		rlset->rs_default = rl;
-		return;
+	if (rl->r_skip_to < ++n) {
+		rl->r_skip_to = n;
 	}
-	TAILQ_FOREACH(it, &rlset->rs_queue, r_entry) {
-		/* Rule priority: (highest) 0, 1, 2, 4 ... n (lowest). */
-		if (it->r_priority > rl->r_priority)
+}
+
+static npf_rule_t *
+npf_ruleset_lookup(npf_ruleset_t *rlset, const char *name)
+{
+	npf_rule_t *rl;
+
+	KASSERT(npf_config_locked_p());
+
+	LIST_FOREACH(rl, &rlset->rs_dynamic, r_dentry) {
+		KASSERT(NPF_DYNAMIC_GROUP_P(rl->r_attr));
+		if (strncmp(rl->r_name, name, NPF_RULE_MAXNAMELEN) == 0)
 			break;
 	}
-	if (it == NULL) {
-		TAILQ_INSERT_TAIL(&rlset->rs_queue, rl, r_entry);
-	} else {
-		TAILQ_INSERT_BEFORE(it, rl, r_entry);
+	return rl;
+}
+
+int
+npf_ruleset_add(npf_ruleset_t *rlset, const char *rname, npf_rule_t *rl)
+{
+	npf_rule_t *rg, *it;
+	pri_t priocmd;
+
+	rg = npf_ruleset_lookup(rlset, rname);
+	if (rg == NULL) {
+		return ENOENT;
+	}
+
+	/* Dynamic rule. */
+	rl->r_attr |= NPF_RULE_DYNAMIC;
+	rl->r_parent = rg;
+
+	/*
+	 * Rule priority: (highest) 1, 2 ... n (lowest).
+	 * Negative priority indicates an operation and is reset to zero.
+	 */
+	if ((priocmd = rl->r_priority) < 0) {
+		rl->r_priority = 0;
+	}
+
+	switch (priocmd) {
+	case NPF_PRI_FIRST:
+		TAILQ_FOREACH(it, &rg->r_subset, r_entry) {
+			if (rl->r_priority <= it->r_priority)
+				break;
+		}
+		if (it) {
+			TAILQ_INSERT_BEFORE(it, rl, r_entry);
+		} else {
+			TAILQ_INSERT_HEAD(&rg->r_subset, rl, r_entry);
+		}
+		break;
+	case NPF_PRI_LAST:
+	default:
+		TAILQ_FOREACH(it, &rg->r_subset, r_entry) {
+			if (rl->r_priority < it->r_priority)
+				break;
+		}
+		if (it) {
+			TAILQ_INSERT_BEFORE(it, rl, r_entry);
+		} else {
+			TAILQ_INSERT_TAIL(&rg->r_subset, rl, r_entry);
+		}
+		break;
+	}
+
+	/* Finally, add into the all-list. */
+	LIST_INSERT_HEAD(&rlset->rs_all, rl, r_aentry);
+	return 0;
+}
+
+npf_rule_t *
+npf_ruleset_remove(npf_ruleset_t *rlset, const char *rname, uintptr_t id)
+{
+	npf_rule_t *rg, *rl;
+
+	if ((rg = npf_ruleset_lookup(rlset, rname)) == NULL) {
+		return NULL;
+	}
+	TAILQ_FOREACH(rl, &rg->r_subset, r_entry) {
+		/* Compare ID.  On match, remove and return. */
+		if ((uintptr_t)rl == id) {
+			npf_ruleset_unlink(rlset, rl);
+			break;
+		}
+	}
+	return rl;
+}
+
+npf_rule_t *
+npf_ruleset_remkey(npf_ruleset_t *rlset, const char *rname,
+    const void *key, size_t len)
+{
+	npf_rule_t *rg, *rl;
+
+	KASSERT(len && len <= NPF_RULE_MAXKEYLEN);
+
+	if ((rg = npf_ruleset_lookup(rlset, rname)) == NULL) {
+		return NULL;
+	}
+	/* Find the last in the list. */
+	TAILQ_FOREACH_REVERSE(rl, &rg->r_subset, npf_ruleq, r_entry) {
+		/* Compare the key.  On match, remove and return. */
+		if (memcmp(rl->r_key, key, len) == 0) {
+			npf_ruleset_unlink(rlset, rl);
+			break;
+		}
+	}
+	return rl;
+}
+
+/*
+ * npf_ruleset_reload: share the dynamic rules.
+ *
+ * => Active ruleset should be exclusively locked.
+ */
+void
+npf_ruleset_reload(npf_ruleset_t *nrlset, npf_ruleset_t *arlset)
+{
+	npf_rule_t *rl, *arl;
+
+	KASSERT(npf_config_locked_p());
+
+	LIST_FOREACH(rl, &nrlset->rs_dynamic, r_dentry) {
+		if ((arl = npf_ruleset_lookup(arlset, rl->r_name)) == NULL) {
+			continue;
+		}
+		memcpy(&rl->r_subset, &arl->r_subset, sizeof(rl->r_subset));
 	}
 }
 
@@ -135,7 +307,7 @@
 	npf_rule_t *rl;
 
 	/* Find a matching NAT policy in the old ruleset. */
-	TAILQ_FOREACH(rl, &rlset->rs_queue, r_entry) {
+	LIST_FOREACH(rl, &rlset->rs_all, r_aentry) {
 		if (npf_nat_matchpolicy(rl->r_natp, mnp))
 			break;
 	}
@@ -149,7 +321,7 @@
 	npf_rule_t *rl;
 
 	/* Find a matching NAT policy in the old ruleset. */
-	TAILQ_FOREACH(rl, &rlset->rs_queue, r_entry) {
+	LIST_FOREACH(rl, &rlset->rs_all, r_aentry) {
 		/*
 		 * NAT policy might not yet be set during the creation of
 		 * the ruleset (in such case, rule is for our policy), or
@@ -172,13 +344,10 @@
 npf_ruleset_freealg(npf_ruleset_t *rlset, npf_alg_t *alg)
 {
 	npf_rule_t *rl;
-
-	KASSERT(npf_core_locked());
+	npf_natpolicy_t *np;
 
-	TAILQ_FOREACH(rl, &rlset->rs_queue, r_entry) {
-		npf_natpolicy_t *np = rl->r_natp;
-
-		if (np != NULL) {
+	LIST_FOREACH(rl, &rlset->rs_all, r_aentry) {
+		if ((np = rl->r_natp) != NULL) {
 			npf_nat_freealg(np, alg);
 		}
 	}
@@ -196,10 +365,8 @@
 	npf_natpolicy_t *np, *anp;
 	npf_rule_t *rl, *arl;
 
-	KASSERT(npf_core_locked());
-
 	/* Scan a new NAT ruleset against NAT policies in old ruleset. */
-	TAILQ_FOREACH(rl, &nrlset->rs_queue, r_entry) {
+	LIST_FOREACH(rl, &nrlset->rs_all, r_aentry) {
 		np = rl->r_natp;
 		arl = npf_ruleset_matchnat(arlset, np);
 		if (arl == NULL) {
@@ -216,30 +383,21 @@
 
 /*
  * npf_rule_alloc: allocate a rule and copy n-code from user-space.
- *
- * => N-code should be validated by the caller.
  */
 npf_rule_t *
-npf_rule_alloc(prop_dictionary_t rldict, npf_rproc_t *rp,
-   void *nc, size_t nc_size)
+npf_rule_alloc(prop_dictionary_t rldict)
 {
 	npf_rule_t *rl;
 	const char *rname;
-	int errat __unused;
 
 	/* Allocate a rule structure. */
 	rl = kmem_zalloc(sizeof(npf_rule_t), KM_SLEEP);
-	TAILQ_INIT(&rl->r_subset.rs_queue);
+	TAILQ_INIT(&rl->r_subset);
 	rl->r_natp = NULL;
 
-	/* N-code. */
-	KASSERT(nc == NULL || npf_ncode_validate(nc, nc_size, &errat) == 0);
-	rl->r_ncode = nc;
-	rl->r_nc_size = nc_size;
-
 	/* Name (optional) */
 	if (prop_dictionary_get_cstring_nocopy(rldict, "name", &rname)) {
-		strlcpy(rl->r_name, rname, NPF_RNAME_LEN);
+		strlcpy(rl->r_name, rname, NPF_RULE_MAXNAMELEN);
 	} else {
 		rl->r_name[0] = '\0';
 	}
@@ -249,13 +407,45 @@
 	prop_dictionary_get_int32(rldict, "priority", &rl->r_priority);
 	prop_dictionary_get_uint32(rldict, "interface", &rl->r_ifid);
 
-	/* Rule procedure. */
-	if (rp) {
-		npf_rproc_acquire(rp);
+	/* Get the skip-to index.  No need to validate it. */
+	prop_dictionary_get_uint32(rldict, "skip-to", &rl->r_skip_to);
+
+	/* Key (optional). */
+	prop_object_t obj = prop_dictionary_get(rldict, "key");
+	const void *key = prop_data_data_nocopy(obj);
+
+	if (key) {
+		size_t len = prop_data_size(obj);
+		if (len > NPF_RULE_MAXKEYLEN) {
+			kmem_free(rl, sizeof(npf_rule_t));
+			return NULL;
+		}
+		memcpy(rl->r_key, key, len);
 	}
-	rl->r_rproc = rp;
+	return rl;
+}
 
-	return rl;
+/*
+ * npf_rule_setcode: assign filter code to the rule.
+ *
+ * => The code should be validated by the caller.
+ */
+void
+npf_rule_setcode(npf_rule_t *rl, const int type, void *code, size_t size)
+{
+	rl->r_type = type;
+	rl->r_code = code;
+	rl->r_clen = size;
+}
+
+/*
+ * npf_rule_setrproc: assign a rule procedure and hold a reference on it.
+ */
+void
+npf_rule_setrproc(npf_rule_t *rl, npf_rproc_t *rp)
+{
+	npf_rproc_acquire(rp);
+	rl->r_rproc = rp;
 }
 
 /*
@@ -275,31 +465,23 @@
 		/* Release rule procedure. */
 		npf_rproc_release(rp);
 	}
-	if (rl->r_ncode) {
+	if (rl->r_code) {
 		/* Free n-code. */
-		npf_ncode_free(rl->r_ncode, rl->r_nc_size);
+		kmem_free(rl->r_code, rl->r_clen);
 	}
 	kmem_free(rl, sizeof(npf_rule_t));
 }
 
 /*
- * npf_rule_subset: return sub-ruleset, if any.
  * npf_rule_getrproc: acquire a reference and return rule procedure, if any.
  * npf_rule_getnat: get NAT policy assigned to the rule.
  */
 
-npf_ruleset_t *
-npf_rule_subset(npf_rule_t *rl)
-{
-	return &rl->r_subset;
-}
-
 npf_rproc_t *
 npf_rule_getrproc(npf_rule_t *rl)
 {
 	npf_rproc_t *rp = rl->r_rproc;
 
-	KASSERT(npf_core_locked());
 	if (rp) {
 		npf_rproc_acquire(rp);
 	}
@@ -324,24 +506,69 @@
 	rl->r_natp = np;
 }
 
-npf_rule_t *
-npf_ruleset_replace(const char *name, npf_ruleset_t *rlset)
+/*
+ * npf_rule_inspect: match the interface, direction and run the filter code.
+ * Returns true if rule matches, false otherise.
+ */
+static inline bool
+npf_rule_inspect(npf_cache_t *npc, nbuf_t *nbuf, const npf_rule_t *rl,
+    const int di_mask, const int layer)
 {
-	npf_ruleset_t orlset;
-	npf_rule_t *rl;
+	const ifnet_t *ifp = nbuf->nb_ifp;
+	const void *code;
+
+	/* Match the interface. */
+	if (rl->r_ifid && rl->r_ifid != ifp->if_index) {
+		return false;
+	}
+
+	/* Match the direction. */
+	if ((rl->r_attr & NPF_RULE_DIMASK) != NPF_RULE_DIMASK) {
+		if ((rl->r_attr & di_mask) == 0)
+			return false;
+	}
+
+	/* Execute the code, if any. */
+	if ((code = rl->r_code) == NULL) {
+		return true;
+	}
 
-	npf_core_enter(); /* XXX */
-	rlset = npf_core_ruleset();
-	TAILQ_FOREACH(rl, &rlset->rs_queue, r_entry) {
-		if (rl->r_name[0] == '\0')
+	switch (rl->r_type) {
+	case NPF_CODE_NC:
+		return npf_ncode_process(npc, code, nbuf, layer) == 0;
+	case NPF_CODE_BPF: {
+		struct mbuf *m = nbuf_head_mbuf(nbuf);
+		size_t pktlen = m_length(m);
+		return bpf_filter(code, (unsigned char *)m, pktlen, 0) != 0;
+	}
+	default:
+		KASSERT(false);
+	}
+	return false;
+}
+
+/*
+ * npf_rule_reinspect: re-inspect the dynamic rule by iterating its list.
+ * This is only for the dynamic rules.  Subrules cannot have nested rules.
+ */
+static npf_rule_t *
+npf_rule_reinspect(npf_cache_t *npc, nbuf_t *nbuf, const npf_rule_t *drl,
+    const int di_mask, const int layer)
+{
+	npf_rule_t *final_rl = NULL, *rl;
+
+	KASSERT(NPF_DYNAMIC_GROUP_P(drl->r_attr));
+
+	TAILQ_FOREACH(rl, &drl->r_subset, r_entry) {
+		if (!npf_rule_inspect(npc, nbuf, rl, di_mask, layer)) {
 			continue;
-		if (strncmp(rl->r_name, name, NPF_RNAME_LEN))
-			continue;
-		memcpy(&orlset, &rl->r_subset, sizeof(npf_ruleset_t));
-		break;
+		}
+		if (rl->r_attr & NPF_RULE_FINAL) {
+			return rl;
+		}
+		final_rl = rl;
 	}
-	npf_core_exit();
-	return rl;
+	return final_rl;
 }
 
 /*
@@ -354,53 +581,57 @@
  */
 npf_rule_t *
 npf_ruleset_inspect(npf_cache_t *npc, nbuf_t *nbuf,
-    const npf_ruleset_t *mainrlset, const int di, const int layer)
+    const npf_ruleset_t *rlset, const int di, const int layer)
 {
-	const ifnet_t *ifp = nbuf->nb_ifp;
 	const int di_mask = (di & PFIL_IN) ? NPF_RULE_IN : NPF_RULE_OUT;
-	const npf_ruleset_t *rlset = mainrlset;
-	npf_rule_t *final_rl = NULL, *rl;
-	bool defed = false;
+	const u_int nitems = rlset->rs_nitems;
+	npf_rule_t *final_rl = NULL;
+	u_int n = 0;
+
+	KASSERT(((di & PFIL_IN) != 0) ^ ((di & PFIL_OUT) != 0));
 
-	KASSERT(ifp != NULL);
-	KASSERT(npf_core_locked());
-	KASSERT(((di & PFIL_IN) != 0) ^ ((di & PFIL_OUT) != 0));
-again:
-	TAILQ_FOREACH(rl, &rlset->rs_queue, r_entry) {
+	while (n < nitems) {
+		npf_rule_t *rl = rlset->rs_rules[n];
+		const u_int skip_to = rl->r_skip_to;
+		const uint32_t attr = rl->r_attr;
+
 		KASSERT(!nbuf_flag_p(nbuf, NBUF_DATAREF_RESET));
 		KASSERT(!final_rl || rl->r_priority >= final_rl->r_priority);
+		KASSERT(n < skip_to);
 
-		/* Match the interface. */
-		if (rl->r_ifid && rl->r_ifid != ifp->if_index) {
+		/* Group is a barrier: return a matching if found any. */
+		if ((attr & NPF_RULE_GROUP) != 0 && final_rl) {
+			break;
+		}
+
+		/* Main inspection of the rule. */
+		if (!npf_rule_inspect(npc, nbuf, rl, di_mask, layer)) {
+			n = skip_to;
 			continue;
 		}
-		/* Match the direction. */
-		if ((rl->r_attr & NPF_RULE_DIMASK) != NPF_RULE_DIMASK) {
-			if ((rl->r_attr & di_mask) == 0)
-				continue;
+
+		if (NPF_DYNAMIC_GROUP_P(attr)) {
+			/*
+			 * If this is a dynamic rule, re-inspect the subrules.
+			 * If it has any matching rule, then it is final.
+			 */
+			rl = npf_rule_reinspect(npc, nbuf, rl, di_mask, layer);
+			if (rl != NULL) {
+				final_rl = rl;
+				break;
+			}
+		} else if ((attr & NPF_RULE_GROUP) == 0) {
+			/*
+			 * Groups themselves are not matching.
+			 */
+			final_rl = rl;
 		}
-		/* Process the n-code, if any. */
-		const void *nc = rl->r_ncode;
-		if (nc && npf_ncode_process(npc, nc, nbuf, layer)) {
-			continue;
-		}
+
 		/* Set the matching rule and check for "final". */
-		final_rl = rl;
-		if (rl->r_attr & NPF_RULE_FINAL) {
+		if (attr & NPF_RULE_FINAL) {
 			break;
 		}
-	}
-
-	/* If no final rule, then - default. */
-	if (final_rl == NULL && !defed) {
-		final_rl = mainrlset->rs_default;
-		defed = true;
-	}
-	/* Inspect the sub-ruleset, if any. */
-	if (final_rl && !TAILQ_EMPTY(&final_rl->r_subset.rs_queue)) {
-		rlset = &final_rl->r_subset;
-		final_rl = NULL;
-		goto again;
+		n++;
 	}
 
 	KASSERT(!nbuf_flag_p(nbuf, NBUF_DATAREF_RESET));
@@ -408,25 +639,16 @@
 }
 
 /*
- * npf_rule_apply: apply the rule and return appropriate value.
+ * npf_rule_conclude: return decision and the flags for conclusion.
  *
  * => Returns ENETUNREACH if "block" and 0 if "pass".
- * => Releases the ruleset lock.
  */
 int
-npf_rule_apply(npf_cache_t *npc, nbuf_t *nbuf, npf_rule_t *rl, int *retfl)
+npf_rule_conclude(const npf_rule_t *rl, int *retfl)
 {
-	int error;
-
-	KASSERT(npf_core_locked());
-
 	/* If not passing - drop the packet. */
-	error = (rl->r_attr & NPF_RULE_PASS) ? 0 : ENETUNREACH;
-
 	*retfl = rl->r_attr;
-	npf_core_exit();
-
-	return error;
+	return (rl->r_attr & NPF_RULE_PASS) ? 0 : ENETUNREACH;
 }
 
 #if defined(DDB) || defined(_NPF_TESTING)
@@ -434,8 +656,8 @@
 void
 npf_rulenc_dump(const npf_rule_t *rl)
 {
-	const uint32_t *op = rl->r_ncode;
-	size_t n = rl->r_nc_size;
+	const uint32_t *op = rl->r_code;
+	size_t n = rl->r_clen;
 
 	while (n) {
 		printf("\t> |0x%02x|\n", (uint32_t)*op);
--- a/sys/net/npf/npf_sendpkt.c	Sat Feb 09 02:49:36 2013 +0000
+++ b/sys/net/npf/npf_sendpkt.c	Sat Feb 09 03:35:31 2013 +0000
@@ -1,4 +1,4 @@
-/*	$NetBSD: npf_sendpkt.c,v 1.13 2012/12/24 19:05:44 rmind Exp $	*/
+/*	$NetBSD: npf_sendpkt.c,v 1.14 2013/02/09 03:35:32 rmind 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.13 2012/12/24 19:05:44 rmind Exp $");
+__KERNEL_RCSID(0, "$NetBSD: npf_sendpkt.c,v 1.14 2013/02/09 03:35:32 rmind Exp $");
 
 #include <sys/param.h>
 #include <sys/types.h>
@@ -200,7 +200,7 @@
 	if (!npf_iscached(npc, NPC_IP46) || !npf_iscached(npc, NPC_LAYER4)) {
 		return false;
 	}
-	switch (npf_cache_ipproto(npc)) {
+	switch (npc->npc_proto) {
 	case IPPROTO_TCP:
 		if (retfl & NPF_RULE_RETRST) {
 			(void)npf_return_tcp(npc);
--- a/sys/net/npf/npf_session.c	Sat Feb 09 02:49:36 2013 +0000
+++ b/sys/net/npf/npf_session.c	Sat Feb 09 03:35:31 2013 +0000
@@ -1,4 +1,4 @@
-/*	$NetBSD: npf_session.c,v 1.20 2013/01/20 18:45:56 rmind Exp $	*/
+/*	$NetBSD: npf_session.c,v 1.21 2013/02/09 03:35:32 rmind 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.20 2013/01/20 18:45:56 rmind Exp $");
+__KERNEL_RCSID(0, "$NetBSD: npf_session.c,v 1.21 2013/02/09 03:35:32 rmind Exp $");
 
 #include <sys/param.h>
 #include <sys/types.h>
@@ -461,7 +461,7 @@
 npf_session_lookup(const npf_cache_t *npc, const nbuf_t *nbuf,
     const int di, bool *forw)
 {
-	const u_int proto = npf_cache_ipproto(npc);
+	const u_int proto = npc->npc_proto;
 	const ifnet_t *ifp = nbuf->nb_ifp;
 	npf_sentry_t senkey, *sen;
 	npf_session_t *se;
@@ -669,7 +669,7 @@
 	memcpy(&fw->se_dst_addr, npc->npc_dstip, alen);
 
 	/* Protocol and interface. */
-	proto = npf_cache_ipproto(npc);
+	proto = npc->npc_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;
@@ -886,12 +886,15 @@
 void
 npf_session_setpass(npf_session_t *se, npf_rproc_t *rp)
 {
-
 	KASSERT((se->s_flags & SE_ACTIVE) == 0);
 	KASSERT(se->s_refcnt > 0);
 	KASSERT(se->s_rproc == NULL);
 
-	/* No need for atomic since the session is not yet active. */
+	/*
+	 * No need for atomic since the session is not yet active.
+	 * If rproc is set, the caller transfers its reference to us,
+	 * which will be released on npf_session_destroy().
+	 */
 	se->s_flags |= SE_PASS;
 	se->s_rproc = rp;
 }
--- a/sys/net/npf/npf_state.c	Sat Feb 09 02:49:36 2013 +0000
+++ b/sys/net/npf/npf_state.c	Sat Feb 09 03:35:31 2013 +0000
@@ -1,4 +1,4 @@
-/*	$NetBSD: npf_state.c,v 1.13 2012/12/24 19:05:45 rmind Exp $	*/
+/*	$NetBSD: npf_state.c,v 1.14 2013/02/09 03:35:32 rmind 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.13 2012/12/24 19:05:45 rmind Exp $");
+__KERNEL_RCSID(0, "$NetBSD: npf_state.c,v 1.14 2013/02/09 03:35:32 rmind Exp $");
 
 #include <sys/param.h>
 #include <sys/systm.h>
@@ -94,7 +94,7 @@
 bool
 npf_state_init(npf_cache_t *npc, nbuf_t *nbuf, npf_state_t *nst)
 {
-	const int proto = npf_cache_ipproto(npc);
+	const int proto = npc->npc_proto;
 	bool ret;
 
 	KASSERT(npf_iscached(npc, NPC_IP46));
@@ -139,7 +139,7 @@
 npf_state_inspect(npf_cache_t *npc, nbuf_t *nbuf,
     npf_state_t *nst, const bool forw)
 {
-	const int proto = npf_cache_ipproto(npc);
+	const int proto = npc->npc_proto;
 	const int di = forw ? NPF_FLOW_FORW : NPF_FLOW_BACK;
 	bool ret;
 
--- a/sys/net/npf/npf_tableset.c	Sat Feb 09 02:49:36 2013 +0000
+++ b/sys/net/npf/npf_tableset.c	Sat Feb 09 03:35:31 2013 +0000
@@ -1,4 +1,4 @@
-/*	$NetBSD: npf_tableset.c,v 1.16 2012/12/04 19:28:16 rmind Exp $	*/
+/*	$NetBSD: npf_tableset.c,v 1.17 2013/02/09 03:35:32 rmind Exp $	*/
 
 /*-
  * Copyright (c) 2009-2012 The NetBSD Foundation, Inc.
@@ -41,7 +41,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: npf_tableset.c,v 1.16 2012/12/04 19:28:16 rmind Exp $");
+__KERNEL_RCSID(0, "$NetBSD: npf_tableset.c,v 1.17 2013/02/09 03:35:32 rmind Exp $");
 
 #include <sys/param.h>
 #include <sys/types.h>
@@ -132,7 +132,7 @@
 	 */
 	for (tid = 0; tid < NPF_TABLE_SLOTS; tid++) {
 		t = tblset[tid];
-		if (t && --t->t_refcnt == 0) {
+		if (t && atomic_dec_uint_nv(&t->t_refcnt) == 0) {
 			npf_table_destroy(t);
 		}
 	}
@@ -153,8 +153,8 @@
 	KASSERT((u_int)tid < NPF_TABLE_SLOTS);
 
 	if (tblset[tid] == NULL) {
+		atomic_inc_uint(&t->t_refcnt);
 		tblset[tid] = t;
-		t->t_refcnt++;
 		error = 0;
 	} else {
 		error = EEXIST;
@@ -180,8 +180,16 @@
 		if (t->t_nitems || t->t_type != ot->t_type) {
 			continue;
 		}
+
+		/*
+		 * Acquire a reference since the table has to be kept
+		 * in the old tableset.
+		 */
+		atomic_inc_uint(&ot->t_refcnt);
 		ntset[i] = ot;
-		ot->t_refcnt++;
+
+		/* Only reference, never been visible. */
+		t->t_refcnt--;
 		npf_table_destroy(t);
 	}
 }
@@ -270,6 +278,7 @@
 void
 npf_table_destroy(npf_table_t *t)
 {
+	KASSERT(t->t_refcnt == 0);
 
 	switch (t->t_type) {
 	case NPF_TABLE_HASH:
--- a/sys/rump/net/lib/libnpf/Makefile	Sat Feb 09 02:49:36 2013 +0000
+++ b/sys/rump/net/lib/libnpf/Makefile	Sat Feb 09 03:35:31 2013 +0000
@@ -1,4 +1,4 @@
-#	$NetBSD: Makefile,v 1.3 2012/11/21 11:03:13 pooka Exp $
+#	$NetBSD: Makefile,v 1.4 2013/02/09 03:35:34 rmind Exp $
 #
 # Public Domain.
 #
@@ -7,7 +7,7 @@
 
 LIB=	rumpnet_npf
 
-SRCS=	npf.c npf_alg.c npf_ctl.c npf_handler.c
+SRCS=	npf.c npf_alg.c npf_conf.c npf_ctl.c npf_handler.c
 SRCS+=	npf_inet.c npf_instr.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
@@ -15,7 +15,7 @@
 
 SRCS+=	npf_alg_icmp.c
 
-SRCS+=	npf_ext_log.c npf_ext_normalise.c
+SRCS+=	npf_ext_log.c npf_ext_normalise.c npf_ext_rndblock.c
 
 SRCS+=	component.c
 
--- a/usr.sbin/npf/npfctl/Makefile	Sat Feb 09 02:49:36 2013 +0000
+++ b/usr.sbin/npf/npfctl/Makefile	Sat Feb 09 03:35:31 2013 +0000
@@ -1,4 +1,4 @@
-# $NetBSD: Makefile,v 1.9 2012/11/01 03:21:49 christos Exp $
+# $NetBSD: Makefile,v 1.10 2013/02/09 03:35:32 rmind Exp $
 
 BINDIR=/sbin
 PROG=		npfctl
@@ -11,7 +11,7 @@
 SRCS+=		npf_scan.l npf_parse.y
 YHEADER=	1
 
-LDADD+=		-lnpf -lprop -lutil -ly
+LDADD+=		-lnpf -lprop -lcrypto -lutil -ly
 DPADD+=		${LIBNPF} ${LIBPROP} ${LIBUTIL}
 
 WARNS=		5
--- a/usr.sbin/npf/npfctl/npf.conf.5	Sat Feb 09 02:49:36 2013 +0000
+++ b/usr.sbin/npf/npfctl/npf.conf.5	Sat Feb 09 03:35:31 2013 +0000
@@ -1,6 +1,6 @@
-.\"    $NetBSD: npf.conf.5,v 1.26 2012/12/23 21:01:04 rmind Exp $
+.\"    $NetBSD: npf.conf.5,v 1.27 2013/02/09 03:35:32 rmind Exp $
 .\"
-.\" Copyright (c) 2009-2012 The NetBSD Foundation, Inc.
+.\" Copyright (c) 2009-2013 The NetBSD Foundation, Inc.
 .\" All rights reserved.
 .\"
 .\" This material is based upon work partially supported by The
@@ -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 December 23, 2012
+.Dd January 11, 2013
 .Dt NPF.CONF 5
 .Os
 .Sh NAME
@@ -203,7 +203,7 @@
 therefore it does not strictly represent the full syntax, which
 is more flexible.
 .Bd -literal
-; Syntax of a single line.  Lines can be separated by LF (\n) or
+; Syntax of a single line.  Lines can be separated by LF (\\n) or
 ; a semicolon.  Comments start with a hash (#) character.
 
 syntax		= var-def | table-def | map | group | rproc | comment
--- a/usr.sbin/npf/npfctl/npf_build.c	Sat Feb 09 02:49:36 2013 +0000
+++ b/usr.sbin/npf/npfctl/npf_build.c	Sat Feb 09 03:35:31 2013 +0000
@@ -1,7 +1,7 @@
-/*	$NetBSD: npf_build.c,v 1.17 2012/12/23 21:01:04 rmind Exp $	*/
+/*	$NetBSD: npf_build.c,v 1.18 2013/02/09 03:35:32 rmind Exp $	*/
 
 /*-
- * Copyright (c) 2011-2012 The NetBSD Foundation, Inc.
+ * Copyright (c) 2011-2013 The NetBSD Foundation, Inc.
  * All rights reserved.
  *
  * This material is based upon work partially supported by The
@@ -34,7 +34,7 @@
  */
 
 #include <sys/cdefs.h>
-__RCSID("$NetBSD: npf_build.c,v 1.17 2012/12/23 21:01:04 rmind Exp $");
+__RCSID("$NetBSD: npf_build.c,v 1.18 2013/02/09 03:35:32 rmind Exp $");
 
 #include <sys/types.h>
 #include <sys/ioctl.h>
@@ -47,20 +47,25 @@
 
 #include "npfctl.h"
 
+#define	MAX_RULE_NESTING	16
+
 static nl_config_t *		npf_conf = NULL;
-static nl_rule_t *		current_group = NULL;
 static bool			npf_debug = false;
-static bool			defgroup_set = false;
+static nl_rule_t *		the_rule = NULL;
+
+static nl_rule_t *		current_group[MAX_RULE_NESTING];
+static unsigned			rule_nesting_level = 0;
+static nl_rule_t *		defgroup = NULL;
 
 void
 npfctl_config_init(bool debug)
 {
-
 	npf_conf = npf_config_create();
 	if (npf_conf == NULL) {
 		errx(EXIT_FAILURE, "npf_config_create failed");
 	}
 	npf_debug = debug;
+	memset(current_group, 0, sizeof(current_group));
 }
 
 int
@@ -72,9 +77,10 @@
 		_npf_config_setsubmit(npf_conf, out);
 		printf("\nSaving to %s\n", out);
 	}
-	if (!defgroup_set) {
+	if (!defgroup) {
 		errx(EXIT_FAILURE, "default group was not defined");
 	}
+	npf_rule_insert(npf_conf, NULL, defgroup);
 	error = npf_config_submit(npf_conf, fd);
 	if (error) {
 		nl_error_t ne;
@@ -91,6 +97,12 @@
 	return npf_conf;
 }
 
+nl_rule_t *
+npfctl_rule_ref(void)
+{
+	return the_rule;
+}
+
 unsigned long
 npfctl_debug_addif(const char *ifname)
 {
@@ -374,7 +386,7 @@
 	}
 	assert(code && len > 0);
 
-	if (npf_rule_setcode(rl, NPF_CODE_NCODE, code, len) == -1) {
+	if (npf_rule_setcode(rl, NPF_CODE_NC, code, len) == -1) {
 		errx(EXIT_FAILURE, "npf_rule_setcode failed");
 	}
 	free(code);
@@ -438,34 +450,51 @@
 }
 
 /*
- * npfctl_build_group: create a group, insert into the global ruleset
- * and update the current group pointer.
+ * npfctl_build_group: create a group, insert into the global ruleset,
+ * update the current group pointer and increase the nesting level.
  */
 void
-npfctl_build_group(const char *name, int attr, u_int if_idx)
+npfctl_build_group(const char *name, int attr, u_int if_idx, bool def)
 {
 	const int attr_di = (NPF_RULE_IN | NPF_RULE_OUT);
 	nl_rule_t *rl;
 
-	if (attr & NPF_RULE_DEFAULT) {
-		if (defgroup_set) {
-			yyerror("multiple default groups are not valid");
-		}
-		defgroup_set = true;
-		attr |= attr_di;
-
-	} else if ((attr & attr_di) == 0) {
+	if (def || (attr & attr_di) == 0) {
 		attr |= attr_di;
 	}
 
-	rl = npf_rule_create(name, attr | NPF_RULE_FINAL, if_idx);
-	npf_rule_insert(npf_conf, NULL, rl, NPF_PRI_NEXT);
-	current_group = rl;
+	rl = npf_rule_create(name, attr | NPF_RULE_GROUP, if_idx);
+	npf_rule_setprio(rl, NPF_PRI_LAST);
+	if (def) {
+		if (defgroup) {
+			yyerror("multiple default groups are not valid");
+		}
+		if (rule_nesting_level) {
+			yyerror("default group can only be at the top level");
+		}
+		defgroup = rl;
+	} else {
+		nl_rule_t *cg = current_group[rule_nesting_level];
+		npf_rule_insert(npf_conf, cg, rl);
+	}
+
+	/* Set the current group and increase the nesting level. */
+	if (rule_nesting_level >= MAX_RULE_NESTING) {
+		yyerror("rule nesting limit reached");
+	}
+	current_group[++rule_nesting_level] = rl;
+}
+
+void
+npfctl_build_group_end(void)
+{
+	assert(rule_nesting_level > 0);
+	current_group[rule_nesting_level--] = NULL;
 }
 
 /*
  * npfctl_build_rule: create a rule, build n-code from filter options,
- * if any, and insert into the ruleset of current group.
+ * if any, and insert into the ruleset of current group, or set the rule.
  */
 void
 npfctl_build_rule(int attr, u_int if_idx, sa_family_t family,
@@ -475,11 +504,23 @@
 
 	rl = npf_rule_create(NULL, attr, if_idx);
 	npfctl_build_ncode(rl, family, op, fopts, false);
-	if (rproc && npf_rule_setproc(npf_conf, rl, rproc) != 0) {
-		yyerror("rule procedure '%s' is not defined", rproc);
+	if (rproc) {
+		npf_rule_setproc(rl, rproc);
 	}
-	assert(current_group != NULL);
-	npf_rule_insert(npf_conf, current_group, rl, NPF_PRI_NEXT);
+
+	if (npf_conf) {
+		nl_rule_t *cg = current_group[rule_nesting_level];
+
+		if (rproc && !npf_rproc_exists_p(npf_conf, rproc)) {
+			yyerror("rule procedure '%s' is not defined", rproc);
+		}
+		assert(cg != NULL);
+		npf_rule_setprio(rl, NPF_PRI_LAST);
+		npf_rule_insert(npf_conf, cg, rl);
+	} else {
+		/* We have parsed a single rule - set it. */
+		the_rule = rl;
+	}
 }
 
 /*
@@ -535,7 +576,7 @@
 	}
 
 	npfctl_build_ncode(nat, family, &op, fopts, false);
-	npf_nat_insert(npf_conf, nat, NPF_PRI_NEXT);
+	npf_nat_insert(npf_conf, nat, NPF_PRI_LAST);
 }
 
 /*
--- a/usr.sbin/npf/npfctl/npf_disassemble.c	Sat Feb 09 02:49:36 2013 +0000
+++ b/usr.sbin/npf/npfctl/npf_disassemble.c	Sat Feb 09 03:35:31 2013 +0000
@@ -1,4 +1,4 @@
-/*	$NetBSD: npf_disassemble.c,v 1.14 2013/02/01 05:40:07 spz Exp $	*/
+/*	$NetBSD: npf_disassemble.c,v 1.15 2013/02/09 03:35:32 rmind Exp $	*/
 
 /*-
  * Copyright (c) 2012 The NetBSD Foundation, Inc.
@@ -35,7 +35,7 @@
  * FIXME: config generation should be redesigned..
  */
 #include <sys/cdefs.h>
-__RCSID("$NetBSD: npf_disassemble.c,v 1.14 2013/02/01 05:40:07 spz Exp $");
+__RCSID("$NetBSD: npf_disassemble.c,v 1.15 2013/02/09 03:35:32 rmind Exp $");
 
 #include <stdio.h>
 #include <stdlib.h>
@@ -561,15 +561,25 @@
 	{ NPF_RULE_FINAL,	NPF_RULE_FINAL,	"final",	NULL	},
 };
 
+static int rules_seen = 0;
+
+/*
+ * FIXME: This mess needs a complete rewrite..
+ */
+
 static void
 npfctl_show_rule(nl_rule_t *nrl, unsigned nlevel)
 {
+	static int grouplvl = -1;
 	rule_group_t rg;
 	const char *rproc;
 	const void *nc;
 	size_t nclen;
+	u_int n;
 
+	memset(&rg, 0, sizeof(rg));
 	_npf_rule_getinfo(nrl, &rg.rg_name, &rg.rg_attr, &rg.rg_ifnum);
+	rules_seen++;
 
 	/* Get the interface, if any. */
 	char ifnamebuf[IFNAMSIZ], *ifname = NULL;
@@ -577,18 +587,23 @@
 		ifname = if_indextoname(rg.rg_ifnum, ifnamebuf);
 	}
 
-	/*
-	 * If zero level, then it is a group.
-	 */
-	if (nlevel == 0) {
-		static bool ingroup = false;
+	if (grouplvl >= 0 && (unsigned)grouplvl >= nlevel) {
+		for (n = 0; n < nlevel; n++) {
+			printf("\t");
+		}
+		printf("}\n\n");
+		grouplvl--;
+	}
+	for (n = 0; n < nlevel; n++) {
+		printf("\t");
+	}
+
+	if (rg.rg_attr & NPF_RULE_GROUP) {
 		const char *rname = rg.rg_name;
 
-		if (ingroup) {
-			printf("}\n\n");
-		}
-		ingroup = true;
-		if (rg.rg_attr & NPF_RULE_DEFAULT) {
+		grouplvl = nlevel;
+		if (rg.rg_attr == (NPF_RULE_GROUP| NPF_RULE_IN | NPF_RULE_OUT)
+		    && rname == NULL && rg.rg_ifnum == 0) {
 			puts("group (default) {");
 			return;
 		}
@@ -603,9 +618,6 @@
 	/*
 	 * 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];
 
@@ -723,7 +735,8 @@
 		puts("");
 		if (!error) {
 			error = _npf_rule_foreach(ncf, npfctl_show_rule);
-			puts("}");
+			if (rules_seen)
+				puts("}");
 		}
 	}
 	npf_config_destroy(ncf);
--- a/usr.sbin/npf/npfctl/npf_parse.y	Sat Feb 09 02:49:36 2013 +0000
+++ b/usr.sbin/npf/npfctl/npf_parse.y	Sat Feb 09 03:35:31 2013 +0000
@@ -1,4 +1,4 @@
-/*	$NetBSD: npf_parse.y,v 1.17 2012/11/26 20:34:28 rmind Exp $	*/
+/*	$NetBSD: npf_parse.y,v 1.18 2013/02/09 03:35:33 rmind Exp $	*/
 
 /*-
  * Copyright (c) 2011-2012 The NetBSD Foundation, Inc.
@@ -40,6 +40,7 @@
 
 #define	YYSTACKSIZE	4096
 
+int			yyparsetarget;
 const char *		yyfilename;
 
 extern int		yylineno, yycolumn;
@@ -75,6 +76,14 @@
 	exit(EXIT_FAILURE);
 }
 
+#define	CHECK_PARSER_FILE				\
+	if (yyparsetarget != NPFCTL_PARSE_FILE)		\
+		yyerror("rule must be in the group");
+
+#define	CHECK_PARSER_STRING				\
+	if (yyparsetarget != NPFCTL_PARSE_STRING)	\
+		yyerror("invalid rule syntax");
+
 %}
 
 %token			ALL
@@ -118,6 +127,7 @@
 %token			PROTO
 %token			FAMILY
 %token			FINAL
+%token			FORW
 %token			RETURN
 %token			RETURNICMP
 %token			RETURNRST
@@ -170,7 +180,8 @@
 %%
 
 input
-	: lines
+	: { CHECK_PARSER_FILE	} lines
+	| { CHECK_PARSER_STRING	} rule
 	;
 
 lines
@@ -361,9 +372,15 @@
 group
 	: GROUP PAR_OPEN group_attr PAR_CLOSE
 	{
-		npfctl_build_group($3.rg_name, $3.rg_attr, $3.rg_ifnum);
+		/* Build a group.  Increases the nesting level. */
+		npfctl_build_group($3.rg_name, $3.rg_attr,
+		    $3.rg_ifnum, $3.rg_default);
 	}
-	  ruleset
+	  ruleset_block
+	{
+		/* Decrease the nesting level. */
+		npfctl_build_group_end();
+	}
 	;
 
 group_attr
@@ -385,6 +402,9 @@
 		if ($1.rg_ifnum) {
 			$$.rg_ifnum = $1.rg_ifnum;
 		}
+		if ($1.rg_default) {
+			$$.rg_default = $1.rg_default;
+		}
 	}
 	| group_opt		{ $$ = $1; }
 	;
@@ -392,51 +412,58 @@
 group_opt
 	: DEFAULT
 	{
-		$$.rg_name = NULL;
-		$$.rg_ifnum = 0;
-		$$.rg_attr = NPF_RULE_DEFAULT;
+		memset(&$$, 0, sizeof(rule_group_t));
+		$$.rg_default = true;
 	}
 	| NAME STRING
 	{
+		memset(&$$, 0, sizeof(rule_group_t));
 		$$.rg_name = $2;
-		$$.rg_ifnum = 0;
-		$$.rg_attr = 0;
 	}
 	| INTERFACE ifindex
 	{
-		$$.rg_name = NULL;
+		memset(&$$, 0, sizeof(rule_group_t));
 		$$.rg_ifnum = $2;
-		$$.rg_attr = 0;
+	}
+	| TDYNAMIC
+	{
+		memset(&$$, 0, sizeof(rule_group_t));
+		$$.rg_attr = NPF_RULE_DYNAMIC;
+	}
+	| FORW
+	{
+		memset(&$$, 0, sizeof(rule_group_t));
+		$$.rg_attr = NPF_RULE_FORW;
 	}
 	| rule_dir
 	{
-		$$.rg_name = NULL;
-		$$.rg_ifnum = 0;
+		memset(&$$, 0, sizeof(rule_group_t));
 		$$.rg_attr = $1;
 	}
 	;
 
-ruleset
-	: CURLY_OPEN rules CURLY_CLOSE
+ruleset_block
+	: CURLY_OPEN ruleset CURLY_CLOSE
+	| /* Empty (for a dynamic ruleset). */
 	;
 
-rules
-	: rule SEPLINE rules
-	| rule
+ruleset
+	: rule_group SEPLINE ruleset
+	| rule_group
 	;
 
+rule_group
+	: rule
+	| group
+	|
+
 rule
 	: block_or_pass opt_stateful rule_dir opt_final on_ifindex
 	  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 | $4, $5,
 		    $6, &$7, &$8, $9);
 	}
-	|
 	;
 
 block_or_pass
--- a/usr.sbin/npf/npfctl/npf_scan.l	Sat Feb 09 02:49:36 2013 +0000
+++ b/usr.sbin/npf/npfctl/npf_scan.l	Sat Feb 09 03:35:31 2013 +0000
@@ -1,4 +1,4 @@
-/*	$NetBSD: npf_scan.l,v 1.9 2012/11/26 20:34:28 rmind Exp $	*/
+/*	$NetBSD: npf_scan.l,v 1.10 2013/02/09 03:35:33 rmind Exp $	*/
 
 /*-
  * Copyright (c) 2011-2012 The NetBSD Foundation, Inc.
@@ -41,6 +41,42 @@
 
 #define	YY_USER_ACTION	yycolumn += yyleng;
 
+extern int		yyparsetarget;
+extern int		yylineno;
+extern const char *	yyfilename;
+extern int		yyparse(void);
+extern void		yyrestart(FILE *);
+
+void
+npfctl_parse_file(const char *name)
+{
+	FILE *fp;
+
+	fp = fopen(name, "r");
+	if (fp == NULL) {
+		err(EXIT_FAILURE, "open '%s'", name);
+	}
+	yyparsetarget = NPFCTL_PARSE_FILE;
+	yyrestart(fp);
+	yylineno = 1;
+	yycolumn = 0;
+	yyfilename = name;
+	yyparse();
+	fclose(fp);
+}
+
+void
+npfctl_parse_string(const char *str)
+{
+	YY_BUFFER_STATE bs;
+
+	yyparsetarget = NPFCTL_PARSE_STRING;
+	bs = yy_scan_string(str);
+	yyfilename = "stdin";
+	yyparse();
+	yy_delete_buffer(bs);
+}
+
 %}
 
 %option noyywrap nounput noinput
@@ -71,6 +107,7 @@
 default			return DEFAULT;
 in			return IN;
 out			return OUT;
+forw			return FORW;
 interface		return INTERFACE;
 all			return ALL;
 block			return BLOCK;
@@ -93,6 +130,7 @@
 return-rst		return RETURNRST;
 return-icmp		return RETURNICMP;
 return			return RETURN;
+ruleset			return GROUP;
 from			return FROM;
 to			return TO;
 port			return PORT;
--- a/usr.sbin/npf/npfctl/npfctl.8	Sat Feb 09 02:49:36 2013 +0000
+++ b/usr.sbin/npf/npfctl/npfctl.8	Sat Feb 09 03:35:31 2013 +0000
@@ -1,6 +1,6 @@
-.\"	$NetBSD: npfctl.8,v 1.11 2012/12/10 02:26:04 rmind Exp $
+.\"	$NetBSD: npfctl.8,v 1.12 2013/02/09 03:35:33 rmind Exp $
 .\"
-.\" Copyright (c) 2009-2012 The NetBSD Foundation, Inc.
+.\" Copyright (c) 2009-2013 The NetBSD Foundation, Inc.
 .\" All rights reserved.
 .\"
 .\" This material is based upon work partially supported by The
@@ -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 December 10, 2012
+.Dd January 11, 2013
 .Dt NPFCTL 8
 .Os
 .Sh NAME
@@ -85,6 +85,28 @@
 .Pa /etc/npf.conf
 will be used unless a file is specified by
 .Ar path .
+.\" ---
+.It Ic rule Ar name Ic add Aq rule-syntax
+Add a rule to a dynamic ruleset specified by
+.Ar name .
+On success, returns a unique identifier which can be used to remove
+the rule with
+.Ic rem-id
+command.
+.It Ic rule Ar name Ic rem Aq rule-syntax
+Remove a rule from a dynamic ruleset specified by
+.Ar name .
+This method uses SHA1 hash computed on a rule to identify it.
+Although very unlikely, it is subject to hash collisions.
+For a fully reliable and more efficient method, it is recommended to use 
+.Ic rem-id
+command.
+.It Ic rule Ar name Ic rem-id Aq id
+Remove a rule specified by unique
+.Ar id
+from a dynamic ruleset specified by
+.Ar name .
+.\" ---
 .It Ic table Ar tid Ic add Aq Ar addr/mask
 In table
 .Ar tid ,
@@ -107,6 +129,7 @@
 List all entries in the currently loaded table specified by
 .Ar tid .
 This operation is expensive and should be used with caution.
+.\" ---
 .It Ic sess-save
 Save all active sessions.
 The data will be stored in the
--- a/usr.sbin/npf/npfctl/npfctl.c	Sat Feb 09 02:49:36 2013 +0000
+++ b/usr.sbin/npf/npfctl/npfctl.c	Sat Feb 09 03:35:31 2013 +0000
@@ -1,7 +1,7 @@
-/*	$NetBSD: npfctl.c,v 1.28 2013/02/01 05:40:07 spz Exp $	*/
+/*	$NetBSD: npfctl.c,v 1.29 2013/02/09 03:35:33 rmind Exp $	*/
 
 /*-
- * Copyright (c) 2009-2012 The NetBSD Foundation, Inc.
+ * Copyright (c) 2009-2013 The NetBSD Foundation, Inc.
  * All rights reserved.
  *
  * This material is based upon work partially supported by The
@@ -30,7 +30,7 @@
  */
 
 #include <sys/cdefs.h>
-__RCSID("$NetBSD: npfctl.c,v 1.28 2013/02/01 05:40:07 spz Exp $");
+__RCSID("$NetBSD: npfctl.c,v 1.29 2013/02/09 03:35:33 rmind Exp $");
 
 #include <sys/ioctl.h>
 #include <sys/stat.h>
@@ -44,12 +44,11 @@
 #include <unistd.h>
 #include <errno.h>
 
+#include <openssl/sha.h>
+
 #include "npfctl.h"
 
-extern int		yylineno, yycolumn;
-extern const char *	yyfilename;
-extern int		yyparse(void);
-extern void		yyrestart(FILE *);
+extern void		npf_yyparse_string(const char *);
 
 enum {
 	NPFCTL_START,
@@ -59,6 +58,7 @@
 	NPFCTL_FLUSH,
 	NPFCTL_VALIDATE,
 	NPFCTL_TABLE,
+	NPFCTL_RULE,
 	NPFCTL_STATS,
 	NPFCTL_SESSIONS_SAVE,
 	NPFCTL_SESSIONS_LOAD,
@@ -77,6 +77,8 @@
 	{	"valid",		NPFCTL_VALIDATE		},
 	/* Table */
 	{	"table",		NPFCTL_TABLE		},
+	/* Rule */
+	{	"rule",			NPFCTL_RULE		},
 	/* Stats */
 	{	"stats",		NPFCTL_STATS		},
 	/* Sessions */
@@ -86,6 +88,27 @@
 	{	NULL,			0			}
 };
 
+static bool
+join(char *buf, size_t buflen, int count, char **args)
+{
+	char *s = buf, *p = NULL;
+
+	for (int i = 0; i < count; i++) {
+		size_t len;
+
+		p = stpncpy(s, args[i], buflen);
+		len = p - s + 1;
+		if (len >= buflen) {
+			return false;
+		}
+		buflen -= len;
+		*p = ' ';
+		s = p + 1;
+	}
+	*p = '\0';
+	return true;
+}
+
 __dead static void
 usage(void)
 {
@@ -95,7 +118,10 @@
 	    "usage:\t%s [ start | stop | reload | flush | show | stats ]\n",
 	    progname);
 	fprintf(stderr,
-	    "\t%s ( sess-save | sess-load )\n",
+	    "\t%s rule \"rule-name\" { add | rem } <rule-syntax>\n",
+	    progname);
+	fprintf(stderr,
+	    "\t%s rule \"rule-name\" rem-id <rule-id>\n",
 	    progname);
 	fprintf(stderr,
 	    "\t%s table <tid> { add | rem | test } <address/mask>\n",
@@ -103,27 +129,12 @@
 	fprintf(stderr,
 	    "\t%s table <tid> { list | flush }\n",
 	    progname);
-
+	fprintf(stderr,
+	    "\t%s ( sess-save | sess-load )\n",
+	    progname);
 	exit(EXIT_FAILURE);
 }
 
-static void
-npfctl_parsecfg(const char *cfg)
-{
-	FILE *fp;
-
-	fp = fopen(cfg, "r");
-	if (fp == NULL) {
-		err(EXIT_FAILURE, "open '%s'", cfg);
-	}
-	yyrestart(fp);
-	yylineno = 1;
-	yycolumn = 0;
-	yyfilename = cfg;
-	yyparse();
-	fclose(fp);
-}
-
 static int
 npfctl_print_stats(int fd)
 {
@@ -257,10 +268,11 @@
 		const char *	cmd;
 		int		action;
 	} tblops[] = {
-		{ "add",	NPF_IOCTL_TBLENT_ADD		},
-		{ "rem",	NPF_IOCTL_TBLENT_REM		},
-		{ "test",	NPF_IOCTL_TBLENT_LOOKUP		},
-		{ "list",	NPF_IOCTL_TBLENT_LIST		},
+		{ "add",	NPF_CMD_TABLE_ADD		},
+		{ "rem",	NPF_CMD_TABLE_REMOVE		},
+		{ "del",	NPF_CMD_TABLE_REMOVE		},
+		{ "test",	NPF_CMD_TABLE_LOOKUP		},
+		{ "list",	NPF_CMD_TABLE_LIST		},
 		{ NULL,		0				}
 	};
 	npf_ioctl_table_t nct;
@@ -278,20 +290,20 @@
 		if (strcmp(cmd, tblops[n].cmd) != 0) {
 			continue;
 		}
-		nct.nct_action = tblops[n].action;
+		nct.nct_cmd = tblops[n].action;
 		break;
 	}
 	if (tblops[n].cmd == NULL) {
 		errx(EXIT_FAILURE, "invalid command '%s'", cmd);
 	}
-	if (nct.nct_action != NPF_IOCTL_TBLENT_LIST) {
+	if (nct.nct_cmd != NPF_CMD_TABLE_LIST) {
 		if (argc < 3) {
 			usage();
 		}
 		arg = argv[2];
 	}
 again:
-	if (nct.nct_action == NPF_IOCTL_TBLENT_LIST) {
+	if (nct.nct_cmd == NPF_CMD_TABLE_LIST) {
 		nct.nct_data.buf.buf = ecalloc(1, buflen);
 		nct.nct_data.buf.len = buflen;
 	} else {
@@ -316,7 +328,7 @@
 	case EINVAL:
 		errx(EXIT_FAILURE, "invalid address, mask or table ID");
 	case ENOMEM:
-		if (nct.nct_action == NPF_IOCTL_TBLENT_LIST) {
+		if (nct.nct_cmd == NPF_CMD_TABLE_LIST) {
 			/* XXX */
 			free(nct.nct_data.buf.buf);
 			buflen <<= 1;
@@ -327,7 +339,7 @@
 		err(EXIT_FAILURE, "ioctl");
 	}
 
-	if (nct.nct_action == NPF_IOCTL_TBLENT_LIST) {
+	if (nct.nct_cmd == NPF_CMD_TABLE_LIST) {
 		npf_ioctl_ent_t *ent = nct.nct_data.buf.buf;
 		char *buf;
 
@@ -342,23 +354,124 @@
 		free(nct.nct_data.buf.buf);
 	} else {
 		printf("%s: %s\n", getprogname(),
-		    nct.nct_action == NPF_IOCTL_TBLENT_LOOKUP ?
+		    nct.nct_cmd == NPF_CMD_TABLE_LOOKUP ?
 		    "matching entry found" : "success");
 	}
 	exit(EXIT_SUCCESS);
 }
 
+static nl_rule_t *
+npfctl_parse_rule(int argc, char **argv)
+{
+	char rule_string[1024];
+	nl_rule_t *rl;
+
+	/* Get the rule string and parse it. */
+	if (!join(rule_string, sizeof(rule_string), argc, argv)) {
+		errx(EXIT_FAILURE, "command too long");
+	}
+	npfctl_parse_string(rule_string);
+	if ((rl = npfctl_rule_ref()) == NULL) {
+		errx(EXIT_FAILURE, "could not parse the rule");
+	}
+	return rl;
+}
+
+static void
+npfctl_generate_key(nl_rule_t *rl, void *key)
+{
+	void *meta;
+	size_t len;
+
+	if ((meta = npf_rule_export(rl, &len)) == NULL) {
+		errx(EXIT_FAILURE, "error generating rule key");
+	}
+	__CTASSERT(NPF_RULE_MAXKEYLEN >= SHA_DIGEST_LENGTH);
+	memset(key, 0, NPF_RULE_MAXKEYLEN);
+	SHA1(meta, len, key);
+	free(meta);
+}
+
+__dead static void
+npfctl_rule(int fd, int argc, char **argv)
+{
+	static const struct ruleops_s {
+		const char *	cmd;
+		int		action;
+	} ruleops[] = {
+		{ "add",	NPF_CMD_RULE_ADD		},
+		{ "rem",	NPF_CMD_RULE_REMKEY		},
+		{ "del",	NPF_CMD_RULE_REMKEY		},
+		{ "rem-id",	NPF_CMD_RULE_REMOVE		},
+		{ NULL,		0				}
+	};
+	uint8_t key[NPF_RULE_MAXKEYLEN];
+	const char *ruleset_name = argv[0];
+	const char *cmd = argv[1];
+	int error, action = 0;
+	uintptr_t rule_id;
+	nl_rule_t *rl;
+
+	for (int n = 0; ruleops[n].cmd != NULL; n++) {
+		if (strcmp(cmd, ruleops[n].cmd) == 0) {
+			action = ruleops[n].action;
+			break;
+		}
+	}
+
+	if (!action || argc < 3) {
+		usage();
+	}
+	argc -= 2;
+	argv += 2;
+
+	switch (action) {
+	case NPF_CMD_RULE_ADD:
+		rl = npfctl_parse_rule(argc, argv);
+		npfctl_generate_key(rl, key);
+		npf_rule_setkey(rl, key, sizeof(key));
+		error = npf_ruleset_add(fd, ruleset_name, rl, &rule_id);
+		break;
+	case NPF_CMD_RULE_REMKEY:
+		rl = npfctl_parse_rule(argc, argv);
+		npfctl_generate_key(rl, key);
+		error = npf_ruleset_remkey(fd, ruleset_name, key, sizeof(key));
+		break;
+	case NPF_CMD_RULE_REMOVE:
+		rule_id = (uintptr_t)strtoull(argv[0], NULL, 16);
+		error = npf_ruleset_remove(fd, ruleset_name, rule_id);
+		break;
+	default:
+		assert(false);
+	}
+
+	switch (error) {
+	case 0:
+		/* Success. */
+		break;
+	case ENOENT:
+		errx(EXIT_FAILURE, "ruleset \"%s\" or the specified rule in "
+		    "it not found", ruleset_name);
+		break;
+	default:
+		errx(EXIT_FAILURE, "rule operation: %s", strerror(error));
+	}
+	if (action == NPF_CMD_RULE_ADD) {
+		printf("OK %" PRIXPTR "\n", rule_id);
+	}
+	exit(EXIT_SUCCESS);
+}
+
 static void
 npfctl(int action, int argc, char **argv)
 {
-	int fd, ret, ver, boolval;
+	int fd, ver, boolval, ret = 0;
 
 	fd = open(NPF_DEV_PATH, O_RDONLY);
 	if (fd == -1) {
 		err(EXIT_FAILURE, "cannot open '%s'", NPF_DEV_PATH);
 	}
-	ret = ioctl(fd, IOC_NPF_VERSION, &ver);
-	if (ret == -1) {
+	if (ioctl(fd, IOC_NPF_VERSION, &ver) == -1) {
 		err(EXIT_FAILURE, "ioctl");
 	}
 	if (ver != NPF_VERSION) {
@@ -366,6 +479,7 @@
 		    "incompatible NPF interface version (%d, kernel %d)\n"
 		    "Hint: update userland?", NPF_VERSION, ver);
 	}
+
 	switch (action) {
 	case NPFCTL_START:
 		boolval = true;
@@ -377,7 +491,7 @@
 		break;
 	case NPFCTL_RELOAD:
 		npfctl_config_init(false);
-		npfctl_parsecfg(argc < 3 ? NPF_CONF_PATH : argv[2]);
+		npfctl_parse_file(argc < 3 ? NPF_CONF_PATH : argv[2]);
 		ret = npfctl_config_send(fd, NULL);
 		if (ret) {
 			errx(EXIT_FAILURE, "ioctl: %s", strerror(ret));
@@ -391,7 +505,7 @@
 		break;
 	case NPFCTL_VALIDATE:
 		npfctl_config_init(false);
-		npfctl_parsecfg(argc < 3 ? NPF_CONF_PATH : argv[2]);
+		npfctl_parse_file(argc < 3 ? NPF_CONF_PATH : argv[2]);
 		ret = npfctl_config_show(0);
 		break;
 	case NPFCTL_TABLE:
@@ -401,6 +515,13 @@
 		argv += 2;
 		npfctl_table(fd, argc, argv);
 		break;
+	case NPFCTL_RULE:
+		if ((argc -= 2) < 2) {
+			usage();
+		}
+		argv += 2;
+		npfctl_rule(fd, argc, argv);
+		break;
 	case NPFCTL_STATS:
 		ret = npfctl_print_stats(fd);
 		break;
@@ -438,7 +559,7 @@
 		const char *out = argc > 3 ? argv[3] : "/tmp/npf.plist";
 
 		npfctl_config_init(true);
-		npfctl_parsecfg(cfg);
+		npfctl_parse_file(cfg);
 		npfctl_config_send(0, out);
 		return EXIT_SUCCESS;
 	}
--- a/usr.sbin/npf/npfctl/npfctl.h	Sat Feb 09 02:49:36 2013 +0000
+++ b/usr.sbin/npf/npfctl/npfctl.h	Sat Feb 09 03:35:31 2013 +0000
@@ -1,9 +1,12 @@
-/*	$NetBSD: npfctl.h,v 1.24 2012/12/10 02:26:04 rmind Exp $	*/
+/*	$NetBSD: npfctl.h,v 1.25 2013/02/09 03:35:33 rmind Exp $	*/
 
 /*-
- * Copyright (c) 2009-2012 The NetBSD Foundation, Inc.
+ * Copyright (c) 2009-2013 The NetBSD Foundation, Inc.
  * All rights reserved.
  *
+ * This material is based upon work partially supported by The
+ * NetBSD Foundation under a contract with Mindaugas Rasiukevicius.
+ *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
  * are met:
@@ -85,6 +88,7 @@
 	const char *	rg_name;
 	uint32_t	rg_attr;
 	u_int		rg_ifnum;
+	bool		rg_default;
 } rule_group_t;
 
 typedef struct proc_call {
@@ -97,7 +101,11 @@
 	const char *	pp_value;
 } proc_param_t;
 
+enum { NPFCTL_PARSE_FILE, NPFCTL_PARSE_STRING };
+
 void		yyerror(const char *, ...) __printflike(1, 2) __dead;
+void		npfctl_parse_file(const char *);
+void		npfctl_parse_string(const char *);
 
 void		npfctl_print_error(const nl_error_t *);
 char *		npfctl_print_addrmask(int, npf_addr_t *, npf_netmask_t);
@@ -179,10 +187,12 @@
 int		npfctl_config_send(int, const char *);
 nl_config_t *	npfctl_config_ref(void);
 int		npfctl_config_show(int);
+nl_rule_t *	npfctl_rule_ref(void);
 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_group(const char *, int, u_int, bool);
+void		npfctl_build_group_end(void);
 void		npfctl_build_rule(int, u_int, sa_family_t,
 		    const opt_proto_t *, const filt_opts_t *, const char *);
 void		npfctl_build_natseg(int, int, u_int, const addr_port_t *,
--- a/usr.sbin/npf/npftest/Makefile	Sat Feb 09 02:49:36 2013 +0000
+++ b/usr.sbin/npf/npftest/Makefile	Sat Feb 09 03:35:31 2013 +0000
@@ -13,6 +13,7 @@
 LDADD+=		-L${LIBNPFTEST} -lnpftest
 
 LDADD+=		-lrump -lrumpvfs -lrumpnet -lrumpnet_net -lrumpnet_npf
+LDADD+=		-lrumpdev_bpf
 
 LDADD+=		-lpcap -lprop -lpthread
 
--- a/usr.sbin/npf/npftest/README	Sat Feb 09 02:49:36 2013 +0000
+++ b/usr.sbin/npf/npftest/README	Sat Feb 09 03:35:31 2013 +0000
@@ -1,4 +1,4 @@
-$NetBSD: README,v 1.3 2012/12/24 19:05:46 rmind Exp $
+$NetBSD: README,v 1.4 2013/02/09 03:35:33 rmind Exp $
 
 npftest - a tool for regression testing and debugging NPF.
 It uses RUMP framework to run NPF kernel module in the userspace.
@@ -16,6 +16,8 @@
 npfctl debug
 npftest -c /tmp/npf.plist -s stream.pcap -o stream_npf_data.txt
 
+Preferably, use MALLOC_OPTIONS="AJ" and/or other facilities.
+
 ---
 
 Update RUMP libraries once the kernel side has been changed.  Hint:
--- a/usr.sbin/npf/npftest/libnpftest/npf_rule_test.c	Sat Feb 09 02:49:36 2013 +0000
+++ b/usr.sbin/npf/npftest/libnpftest/npf_rule_test.c	Sat Feb 09 03:35:31 2013 +0000
@@ -1,4 +1,4 @@
-/*	$NetBSD: npf_rule_test.c,v 1.3 2012/12/24 19:05:48 rmind Exp $	*/
+/*	$NetBSD: npf_rule_test.c,v 1.4 2013/02/09 03:35:33 rmind Exp $	*/
 
 /*
  * NPF ruleset test.
@@ -85,30 +85,57 @@
 	nbuf_init(&nbuf, m, ifp);
 	npf_cache_all(&npc, &nbuf);
 
-	npf_core_enter();
-	rl = npf_ruleset_inspect(&npc, &nbuf, npf_core_ruleset(),
+	int slock = npf_config_read_enter();
+	rl = npf_ruleset_inspect(&npc, &nbuf, npf_config_ruleset(),
 	    di, NPF_LAYER_3);
 	if (rl) {
 		if (verbose) {
 			npf_rulenc_dump(rl);
 		}
-		error = npf_rule_apply(&npc, &nbuf, rl, &retfl);
+		error = npf_rule_conclude(rl, &retfl);
 	} else {
-		npf_core_exit();
 		error = ENOENT;
 	}
+	npf_config_read_exit(slock);
 	return error;
 }
 
+static int
+npf_test_first(bool verbose)
+{
+	const struct test_case *t = &test_cases[0];
+	ifnet_t *ifp = ifunit(t->ifname);
+	int error;
+
+	struct mbuf *m = fill_packet(t);
+	error = npf_rule_raw_test(verbose, m, ifp, t->di);
+	m_freem(m);
+	return error;
+}
+
+static npf_rule_t *
+npf_blockall_rule(void)
+{
+	prop_dictionary_t rldict;
+
+	rldict = prop_dictionary_create();
+	prop_dictionary_set_uint32(rldict, "attributes",
+	    NPF_RULE_IN | NPF_RULE_OUT);
+	return npf_rule_alloc(rldict);
+}
+
 bool
 npf_rule_test(bool verbose)
 {
+	npf_ruleset_t *rlset;
+	npf_rule_t *rl;
 	bool fail = false;
+	int error;
 
 	for (unsigned i = 0; i < __arraycount(test_cases); i++) {
 		const struct test_case *t = &test_cases[i];
 		ifnet_t *ifp = ifunit(t->ifname);
-		int serror, error;
+		int serror;
 
 		if (ifp == NULL) {
 			printf("Interface %s is not configured.\n", t->ifname);
@@ -130,5 +157,27 @@
 		}
 		fail |= (serror != t->stateful_ret || error != t->ret);
 	}
+
+	error = npf_test_first(verbose);
+	assert(error == RESULT_PASS);
+
+	npf_config_enter();
+	rlset = npf_config_ruleset();
+
+	rl = npf_blockall_rule();
+	error = npf_ruleset_add(rlset, "test-rules", rl);
+	fail |= error != 0;
+
+	error = npf_test_first(verbose);
+	fail |= (error != RESULT_BLOCK);
+
+	rl = npf_ruleset_remove(rlset, "test-rules", (uintptr_t)rl);
+	fail |= (rl == NULL);
+
+	npf_config_exit();
+
+	error = npf_test_first(verbose);
+	fail |= (error != RESULT_PASS);
+
 	return !fail;
 }
--- a/usr.sbin/npf/npftest/npftest.conf	Sat Feb 09 02:49:36 2013 +0000
+++ b/usr.sbin/npf/npftest/npftest.conf	Sat Feb 09 03:35:31 2013 +0000
@@ -1,4 +1,4 @@
-# $NetBSD: npftest.conf,v 1.1 2012/08/12 03:35:14 rmind Exp $
+# $NetBSD: npftest.conf,v 1.2 2013/02/09 03:35:33 rmind Exp $
 
 $ext_if = "npftest0"
 $int_if = "npftest1"
@@ -31,6 +31,7 @@
 }
 
 group (interface $int_if) {
+	ruleset (name "test-rules", dynamic)
 	pass stateful out final to $local_ip2
 	pass out final to $local_ip3
 	block final to $local_ip4