- Convert NPF to use BPF byte-code by default. Compile BPF byte-code in trunk
authorrmind <rmind@NetBSD.org>
Thu, 19 Sep 2013 01:04:45 +0000
branchtrunk
changeset 221210 4cff8ccc954f
parent 221209 d3dcee9b62da
child 221211 00ce35cabce2
- Convert NPF to use BPF byte-code by default. Compile BPF byte-code in npfctl(8) and generate separate marks to describe the filter criteria. - Rewrite 'npfctl show' functionality and fix some of the bugs. - npftest: add a test for BPF COP. - Bump NPF_VERSION.
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_bpf.c
sys/net/npf/npf_ctl.c
sys/net/npf/npf_impl.h
sys/net/npf/npf_ruleset.c
usr.sbin/npf/npfctl/Makefile
usr.sbin/npf/npfctl/npf_bpf_comp.c
usr.sbin/npf/npfctl/npf_build.c
usr.sbin/npf/npfctl/npf_data.c
usr.sbin/npf/npfctl/npf_disassemble.c
usr.sbin/npf/npfctl/npf_parse.y
usr.sbin/npf/npfctl/npf_show.c
usr.sbin/npf/npfctl/npf_var.h
usr.sbin/npf/npfctl/npfctl.c
usr.sbin/npf/npfctl/npfctl.h
usr.sbin/npf/npftest/libnpftest/Makefile
usr.sbin/npf/npftest/libnpftest/npf_bpf_test.c
usr.sbin/npf/npftest/libnpftest/npf_nat_test.c
usr.sbin/npf/npftest/libnpftest/npf_rule_test.c
usr.sbin/npf/npftest/libnpftest/npf_test.h
usr.sbin/npf/npftest/npftest.c
usr.sbin/npf/npftest/npftest.h
--- a/lib/libnpf/npf.c	Thu Sep 19 00:58:11 2013 +0000
+++ b/lib/libnpf/npf.c	Thu Sep 19 01:04:45 2013 +0000
@@ -1,4 +1,4 @@
-/*	$NetBSD: npf.c,v 1.19 2013/03/20 00:29:46 christos Exp $	*/
+/*	$NetBSD: npf.c,v 1.20 2013/09/19 01:04:46 rmind Exp $	*/
 
 /*-
  * Copyright (c) 2010-2013 The NetBSD Foundation, Inc.
@@ -30,7 +30,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: npf.c,v 1.19 2013/03/20 00:29:46 christos Exp $");
+__KERNEL_RCSID(0, "$NetBSD: npf.c,v 1.20 2013/09/19 01:04:46 rmind Exp $");
 
 #include <sys/types.h>
 #include <netinet/in_systm.h>
@@ -47,23 +47,6 @@
 #define	_NPF_PRIVATE
 #include "npf.h"
 
-struct nl_config {
-	/* Rules, translations, tables, procedures. */
-	prop_dictionary_t	ncf_dict;
-	prop_array_t		ncf_alg_list;
-	prop_array_t		ncf_rules_list;
-	prop_array_t		ncf_rproc_list;
-	prop_array_t		ncf_table_list;
-	prop_array_t		ncf_nat_list;
-	/* Debug information. */
-	prop_dictionary_t	ncf_debug;
-	/* Error report. */
-	prop_dictionary_t	ncf_err;
-	/* Custom file to externalise property-list. */
-	const char *		ncf_plist;
-	bool			ncf_flush;
-};
-
 struct nl_rule {
 	prop_dictionary_t	nrl_dict;
 };
@@ -85,6 +68,37 @@
 	prop_dictionary_t	nxt_dict;
 };
 
+struct nl_config {
+	/* Rules, translations, tables, procedures. */
+	prop_dictionary_t	ncf_dict;
+	prop_array_t		ncf_alg_list;
+	prop_array_t		ncf_rules_list;
+	prop_array_t		ncf_rproc_list;
+	prop_array_t		ncf_table_list;
+	prop_array_t		ncf_nat_list;
+
+	/* Iterators. */
+	prop_object_iterator_t	ncf_rule_iter;
+	unsigned		ncf_reduce[16];
+	unsigned		ncf_nlevel;
+	unsigned		ncf_counter;
+	nl_rule_t		ncf_cur_rule;
+
+	prop_object_iterator_t	ncf_table_iter;
+	nl_table_t		ncf_cur_table;
+
+	prop_object_iterator_t	ncf_rproc_iter;
+	nl_rproc_t		ncf_cur_rproc;
+
+	/* Error report and debug information. */
+	prop_dictionary_t	ncf_err;
+	prop_dictionary_t	ncf_debug;
+
+	/* Custom file to externalise property-list. */
+	const char *		ncf_plist;
+	bool			ncf_flush;
+};
+
 static prop_array_t	_npf_ruleset_transform(prop_array_t);
 
 /*
@@ -131,9 +145,10 @@
 		prop_object_release(npf_dict);
 		return ENOMEM;
 	}
-	prop_dictionary_set(npf_dict, "rules", rlset);
-	prop_object_release(rlset);
+	prop_object_release(ncf->ncf_rules_list);
+	ncf->ncf_rules_list = rlset;
 
+	prop_dictionary_set(npf_dict, "rules", ncf->ncf_rules_list);
 	prop_dictionary_set(npf_dict, "algs", ncf->ncf_alg_list);
 	prop_dictionary_set(npf_dict, "rprocs", ncf->ncf_rproc_list);
 	prop_dictionary_set(npf_dict, "tables", ncf->ncf_table_list);
@@ -150,16 +165,16 @@
 		prop_object_release(npf_dict);
 		return error;
 	}
-
-	error = prop_dictionary_sendrecv_ioctl(npf_dict, fd,
-	    IOC_NPF_RELOAD, &ncf->ncf_err);
-	if (error) {
-		prop_object_release(npf_dict);
-		assert(ncf->ncf_err == NULL);
-		return error;
+	if (fd) {
+		error = prop_dictionary_sendrecv_ioctl(npf_dict, fd,
+		    IOC_NPF_RELOAD, &ncf->ncf_err);
+		if (error) {
+			prop_object_release(npf_dict);
+			assert(ncf->ncf_err == NULL);
+			return error;
+		}
+		prop_dictionary_get_int32(ncf->ncf_err, "errno", &error);
 	}
-
-	prop_dictionary_get_int32(ncf->ncf_err, "errno", &error);
 	prop_object_release(npf_dict);
 	return error;
 }
@@ -226,7 +241,6 @@
 void
 npf_config_destroy(nl_config_t *ncf)
 {
-
 	if (!ncf->ncf_dict) {
 		prop_object_release(ncf->ncf_alg_list);
 		prop_object_release(ncf->ncf_rules_list);
@@ -246,7 +260,6 @@
 void
 _npf_config_setsubmit(nl_config_t *ncf, const char *plist_file)
 {
-
 	ncf->ncf_plist = plist_file;
 }
 
@@ -487,6 +500,20 @@
 }
 
 int
+npf_rule_setinfo(nl_rule_t *rl, const void *info, size_t len)
+{
+	prop_dictionary_t rldict = rl->nrl_dict;
+	prop_data_t idata;
+
+	if ((idata = prop_data_create_data(info, len)) == NULL) {
+		return ENOMEM;
+	}
+	prop_dictionary_set(rldict, "info", idata);
+	prop_object_release(idata);
+	return 0;
+}
+
+int
 npf_rule_setprio(nl_rule_t *rl, pri_t pri)
 {
 	prop_dictionary_t rldict = rl->nrl_dict;
@@ -544,6 +571,97 @@
 	return 0;
 }
 
+static nl_rule_t *
+_npf_rule_iterate1(nl_config_t *ncf, prop_array_t rlist, unsigned *level)
+{
+	prop_dictionary_t rldict;
+	uint32_t skipto = 0;
+
+	if (!ncf->ncf_rule_iter) {
+		/* Initialise the iterator. */
+		ncf->ncf_rule_iter = prop_array_iterator(rlist);
+		ncf->ncf_nlevel = 0;
+		ncf->ncf_reduce[0] = 0;
+		ncf->ncf_counter = 0;
+	}
+
+	rldict = prop_object_iterator_next(ncf->ncf_rule_iter);
+	if ((ncf->ncf_cur_rule.nrl_dict = rldict) == NULL) {
+		prop_object_iterator_release(ncf->ncf_rule_iter);
+		ncf->ncf_rule_iter = NULL;
+		return NULL;
+	}
+	*level = ncf->ncf_nlevel;
+
+	prop_dictionary_get_uint32(rldict, "skip-to", &skipto);
+	if (skipto) {
+		ncf->ncf_nlevel++;
+		ncf->ncf_reduce[ncf->ncf_nlevel] = skipto;
+	}
+	if (ncf->ncf_reduce[ncf->ncf_nlevel] == ++ncf->ncf_counter) {
+		assert(ncf->ncf_nlevel > 0);
+		ncf->ncf_nlevel--;
+	}
+	return &ncf->ncf_cur_rule;
+}
+
+nl_rule_t *
+npf_rule_iterate(nl_config_t *ncf, unsigned *level)
+{
+	return _npf_rule_iterate1(ncf, ncf->ncf_rules_list, level);
+}
+
+const char *
+npf_rule_getname(nl_rule_t *rl)
+{
+	prop_dictionary_t rldict = rl->nrl_dict;
+	const char *rname = NULL;
+
+	prop_dictionary_get_cstring_nocopy(rldict, "name", &rname);
+	return rname;
+}
+
+uint32_t
+npf_rule_getattr(nl_rule_t *rl)
+{
+	prop_dictionary_t rldict = rl->nrl_dict;
+	uint32_t attr = 0;
+
+	prop_dictionary_get_uint32(rldict, "attributes", &attr);
+	return attr;
+}
+
+unsigned
+npf_rule_getinterface(nl_rule_t *rl)
+{
+	prop_dictionary_t rldict = rl->nrl_dict;
+	unsigned if_idx = 0;
+
+	prop_dictionary_get_uint32(rldict, "interface", &if_idx);
+	return if_idx;
+}
+
+const void *
+npf_rule_getinfo(nl_rule_t *rl, size_t *len)
+{
+	prop_dictionary_t rldict = rl->nrl_dict;
+	prop_object_t obj = prop_dictionary_get(rldict, "info");
+
+	*len = prop_data_size(obj);
+	return prop_data_data_nocopy(obj);
+}
+
+const char *
+npf_rule_getproc(nl_rule_t *rl)
+{
+	prop_dictionary_t rldict = rl->nrl_dict;
+	const char *rpname = NULL;
+
+	prop_dictionary_get_cstring_nocopy(rldict, "rproc", &rpname);
+	return rpname;
+}
+
+#if 1
 static int
 _npf_rule_foreach1(prop_array_t rules, nl_rule_callback_t func)
 {
@@ -588,6 +706,7 @@
 {
 	return _npf_rule_foreach1(ncf->ncf_rules_list, func);
 }
+#endif
 
 int
 _npf_ruleset_list(int fd, const char *rname, nl_config_t *ncf)
@@ -615,6 +734,7 @@
 	return error;
 }
 
+#if 1
 pri_t
 _npf_rule_getinfo(nl_rule_t *nrl, const char **rname, uint32_t *attr,
     u_int *if_idx)
@@ -637,16 +757,7 @@
 	*size = prop_data_size(obj);
 	return prop_data_data_nocopy(obj);
 }
-
-const char *
-_npf_rule_rproc(nl_rule_t *nrl)
-{
-	prop_dictionary_t rldict = nrl->nrl_dict;
-	const char *rpname = NULL;
-
-	prop_dictionary_get_cstring_nocopy(rldict, "rproc", &rpname);
-	return rpname;
-}
+#endif
 
 void
 npf_rule_destroy(nl_rule_t *rl)
@@ -710,7 +821,6 @@
 bool
 npf_rproc_exists_p(nl_config_t *ncf, const char *name)
 {
-
 	return _npf_prop_array_lookup(ncf->ncf_rproc_list, "name", name);
 }
 
@@ -730,6 +840,34 @@
 	return 0;
 }
 
+nl_rproc_t *
+npf_rproc_iterate(nl_config_t *ncf)
+{
+	prop_dictionary_t rpdict;
+
+	if (!ncf->ncf_rproc_iter) {
+		/* Initialise the iterator. */
+		ncf->ncf_rproc_iter = prop_array_iterator(ncf->ncf_rproc_list);
+	}
+	rpdict = prop_object_iterator_next(ncf->ncf_rproc_iter);
+	if ((ncf->ncf_cur_rproc.nrp_dict = rpdict) == NULL) {
+		prop_object_iterator_release(ncf->ncf_rproc_iter);
+		ncf->ncf_rproc_iter = NULL;
+		return NULL;
+	}
+	return &ncf->ncf_cur_rproc;
+}
+
+const char *
+npf_rproc_getname(nl_rproc_t *rp)
+{
+	prop_dictionary_t rpdict = rp->nrp_dict;
+	const char *rpname = NULL;
+
+	prop_dictionary_get_cstring_nocopy(rpdict, "name", &rpname);
+	return rpname;
+}
+
 /*
  * TRANSLATION INTERFACE.
  */
@@ -791,6 +929,37 @@
 	return 0;
 }
 
+nl_nat_t *
+npf_nat_iterate(nl_config_t *ncf)
+{
+	u_int level;
+	return _npf_rule_iterate1(ncf, ncf->ncf_nat_list, &level);
+}
+
+int
+npf_nat_gettype(nl_nat_t *nt)
+{
+	prop_dictionary_t rldict = nt->nrl_dict;
+	int type = 0;
+
+	prop_dictionary_get_int32(rldict, "type", &type);
+	return type;
+}
+
+void
+npf_nat_getmap(nl_nat_t *nt, npf_addr_t *addr, size_t *alen, in_port_t *port)
+{
+	prop_dictionary_t rldict = nt->nrl_dict;
+	prop_object_t obj = prop_dictionary_get(rldict, "translation-ip");
+
+	*alen = prop_data_size(obj);
+	memcpy(addr, prop_data_data_nocopy(obj), *alen);
+
+	*port = 0;
+	prop_dictionary_get_uint16(rldict, "translation-port", port);
+}
+
+#if 1
 int
 _npf_nat_foreach(nl_config_t *ncf, nl_rule_callback_t func)
 {
@@ -812,6 +981,7 @@
 
 	prop_dictionary_get_uint16(rldict, "translation-port", port);
 }
+#endif
 
 /*
  * TABLE INTERFACE.
@@ -891,10 +1061,10 @@
 {
 	prop_dictionary_t tldict;
 	prop_object_iterator_t it;
+	u_int i;
 
 	it = prop_array_iterator(ncf->ncf_table_list);
 	while ((tldict = prop_object_iterator_next(it)) != NULL) {
-		u_int i;
 		if (prop_dictionary_get_uint32(tldict, "id", &i) && tid == i)
 			break;
 	}
@@ -918,14 +1088,52 @@
 	return 0;
 }
 
+nl_table_t *
+npf_table_iterate(nl_config_t *ncf)
+{
+	prop_dictionary_t tldict;
+
+	if (!ncf->ncf_table_iter) {
+		/* Initialise the iterator. */
+		ncf->ncf_table_iter = prop_array_iterator(ncf->ncf_table_list);
+	}
+	tldict = prop_object_iterator_next(ncf->ncf_table_iter);
+	if ((ncf->ncf_cur_table.ntl_dict = tldict) == NULL) {
+		prop_object_iterator_release(ncf->ncf_table_iter);
+		ncf->ncf_table_iter = NULL;
+		return NULL;
+	}
+	return &ncf->ncf_cur_table;
+}
+
+unsigned
+npf_table_getid(nl_table_t *tl)
+{
+	prop_dictionary_t tldict = tl->ntl_dict;
+	u_int id = 0;
+
+	prop_dictionary_get_uint32(tldict, "id", &id);
+	return id;
+}
+
+int
+npf_table_gettype(nl_table_t *tl)
+{
+	prop_dictionary_t tldict = tl->ntl_dict;
+	int type = 0;
+
+	prop_dictionary_get_int32(tldict, "type", &type);
+	return type;
+}
+
 void
 npf_table_destroy(nl_table_t *tl)
 {
-
 	prop_object_release(tl->ntl_dict);
 	free(tl);
 }
 
+#if 1
 void
 _npf_table_foreach(nl_config_t *ncf, nl_table_callback_t func)
 {
@@ -943,6 +1151,7 @@
 	}
 	prop_object_iterator_release(it);
 }
+#endif
 
 /*
  * ALG INTERFACE.
@@ -966,7 +1175,6 @@
 int
 _npf_alg_unload(nl_config_t *ncf, const char *name)
 {
-
 	if (!_npf_prop_array_lookup(ncf->ncf_alg_list, "name", name))
 		return ENOENT;
 
--- a/lib/libnpf/npf.h	Thu Sep 19 00:58:11 2013 +0000
+++ b/lib/libnpf/npf.h	Thu Sep 19 01:04:45 2013 +0000
@@ -1,4 +1,4 @@
-/*	$NetBSD: npf.h,v 1.16 2013/03/20 00:29:46 christos Exp $	*/
+/*	$NetBSD: npf.h,v 1.17 2013/09/19 01:04:46 rmind Exp $	*/
 
 /*-
  * Copyright (c) 2011-2013 The NetBSD Foundation, Inc.
@@ -93,6 +93,7 @@
 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);
+int		npf_rule_setinfo(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 *);
 void *		npf_rule_export(nl_rule_t *, size_t *);
@@ -120,19 +121,37 @@
 int		npf_sessions_send(int, const char *);
 int		npf_sessions_recv(int, const char *);
 
+nl_rule_t *	npf_rule_iterate(nl_config_t *, unsigned *);
+const char *	npf_rule_getname(nl_rule_t *);
+uint32_t	npf_rule_getattr(nl_rule_t *);
+unsigned	npf_rule_getinterface(nl_rule_t *);
+const void *	npf_rule_getinfo(nl_rule_t *, size_t *);
+const char *	npf_rule_getproc(nl_rule_t *);
+
+nl_table_t *	npf_table_iterate(nl_config_t *);
+unsigned	npf_table_getid(nl_table_t *);
+int		npf_table_gettype(nl_table_t *);
+
+nl_nat_t *	npf_nat_iterate(nl_config_t *);
+int		npf_nat_gettype(nl_nat_t *);
+void		npf_nat_getmap(nl_nat_t *, npf_addr_t *, size_t *, in_port_t *);
+
+nl_rproc_t *	npf_rproc_iterate(nl_config_t *);
+const char *	npf_rproc_getname(nl_rproc_t *);
+
 void		_npf_config_error(nl_config_t *, nl_error_t *);
 void		_npf_config_setsubmit(nl_config_t *, const char *);
 int		_npf_ruleset_list(int, const char *, nl_config_t *);
+#if 1
 int		_npf_rule_foreach(nl_config_t *, nl_rule_callback_t);
 pri_t		_npf_rule_getinfo(nl_rule_t *, const char **, uint32_t *,
 		    u_int *);
 const void *	_npf_rule_ncode(nl_rule_t *, size_t *);
-const char *	_npf_rule_rproc(nl_rule_t *);
 int		_npf_nat_foreach(nl_config_t *, nl_rule_callback_t);
 void		_npf_nat_getinfo(nl_nat_t *, int *, u_int *, npf_addr_t *,
 		    size_t *, in_port_t *);
 void		_npf_table_foreach(nl_config_t *, nl_table_callback_t);
-
+#endif
 void		_npf_debug_addif(nl_config_t *, struct ifaddrs *, u_int);
 
 /* The ALG interface is experimental */
--- a/sys/modules/npf/Makefile	Thu Sep 19 00:58:11 2013 +0000
+++ b/sys/modules/npf/Makefile	Thu Sep 19 01:04:45 2013 +0000
@@ -1,4 +1,4 @@
-# $NetBSD: Makefile,v 1.13 2013/06/02 02:20:05 rmind Exp $
+# $NetBSD: Makefile,v 1.14 2013/09/19 01:04:46 rmind Exp $
 
 .include "../Makefile.inc"
 
@@ -7,7 +7,7 @@
 KMOD=		npf
 
 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_bpf.c 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
 SRCS+=		npf_tableset.c npf_tableset_ptree.c npf_worker.c
--- a/sys/net/npf/files.npf	Thu Sep 19 00:58:11 2013 +0000
+++ b/sys/net/npf/files.npf	Thu Sep 19 01:04:45 2013 +0000
@@ -1,4 +1,4 @@
-# $NetBSD: files.npf,v 1.13 2013/06/02 02:20:04 rmind Exp $
+# $NetBSD: files.npf,v 1.14 2013/09/19 01:04:46 rmind Exp $
 #
 # Public Domain.
 #
@@ -17,6 +17,7 @@
 file	net/npf/npf_instr.c			npf
 file	net/npf/npf_mbuf.c			npf
 file	net/npf/npf_processor.c			npf
+file	net/npf/npf_bpf.c			npf
 file	net/npf/npf_ruleset.c			npf
 file	net/npf/npf_rproc.c			npf
 file	net/npf/npf_tableset.c			npf
--- a/sys/net/npf/npf.c	Thu Sep 19 00:58:11 2013 +0000
+++ b/sys/net/npf/npf.c	Thu Sep 19 01:04:45 2013 +0000
@@ -1,4 +1,4 @@
-/*	$NetBSD: npf.c,v 1.16 2013/06/02 02:20:04 rmind Exp $	*/
+/*	$NetBSD: npf.c,v 1.17 2013/09/19 01:04:46 rmind Exp $	*/
 
 /*-
  * Copyright (c) 2009-2013 The NetBSD Foundation, Inc.
@@ -34,7 +34,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: npf.c,v 1.16 2013/06/02 02:20:04 rmind Exp $");
+__KERNEL_RCSID(0, "$NetBSD: npf.c,v 1.17 2013/09/19 01:04:46 rmind Exp $");
 
 #include <sys/param.h>
 #include <sys/types.h>
@@ -88,6 +88,7 @@
 	npf_stats_percpu = percpu_alloc(NPF_STATS_SIZE);
 	npf_sysctl = NULL;
 
+	npf_bpf_sysinit();
 	npf_worker_sysinit();
 	npf_tableset_sysinit();
 	npf_session_sysinit();
@@ -112,7 +113,6 @@
 static int
 npf_fini(void)
 {
-
 	/* At first, detach device and remove pfil hooks. */
 #ifdef _MODULE
 	devsw_detach(NULL, &npf_cdevsw);
@@ -129,6 +129,7 @@
 	npf_nat_sysfini();
 	npf_session_sysfini();
 	npf_tableset_sysfini();
+	npf_bpf_sysfini();
 
 	/* Note: worker is the last. */
 	npf_worker_sysfini();
--- a/sys/net/npf/npf.h	Thu Sep 19 00:58:11 2013 +0000
+++ b/sys/net/npf/npf.h	Thu Sep 19 01:04:45 2013 +0000
@@ -1,4 +1,4 @@
-/*	$NetBSD: npf.h,v 1.30 2013/03/11 17:20:02 christos Exp $	*/
+/*	$NetBSD: npf.h,v 1.31 2013/09/19 01:04:46 rmind Exp $	*/
 
 /*-
  * Copyright (c) 2009-2013 The NetBSD Foundation, Inc.
@@ -45,7 +45,7 @@
 #include <netinet/in_systm.h>
 #include <netinet/in.h>
 
-#define	NPF_VERSION		9
+#define	NPF_VERSION		10
 
 /*
  * Public declarations and definitions.
@@ -58,6 +58,16 @@
 #define	NPF_MAX_NETMASK		(128)
 #define	NPF_NO_NETMASK		((npf_netmask_t)~0)
 
+/* BPF coprocessor. */
+#if defined(NPF_BPFCOP)
+#define	NPF_COP_L3		0
+#define	NPF_COP_TABLE		1
+
+#define	BPF_MW_IPVER		0
+#define	BPF_MW_L4OFF		1
+#define	BPF_MW_L4PROTO		2
+#endif
+
 #if defined(_KERNEL)
 
 #define	NPF_DECISION_BLOCK	0
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sys/net/npf/npf_bpf.c	Thu Sep 19 01:04:45 2013 +0000
@@ -0,0 +1,155 @@
+/*	$NetBSD: npf_bpf.c,v 1.1 2013/09/19 01:04:46 rmind Exp $	*/
+
+/*-
+ * 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:
+ * 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 byte-code processing.
+ */
+
+#include <sys/cdefs.h>
+__KERNEL_RCSID(0, "$NetBSD: npf_bpf.c,v 1.1 2013/09/19 01:04:46 rmind Exp $");
+
+#include <sys/types.h>
+#include <sys/param.h>
+
+#include <sys/mbuf.h>
+
+#define NPF_BPFCOP
+#include "npf_impl.h"
+
+/*
+ * BPF context and the coprocessor.
+ */
+
+static bpf_ctx_t *npf_bpfctx __read_mostly;
+
+static uint32_t npf_cop_l3(const struct mbuf *, void *, uint32_t, uint32_t *);
+static uint32_t npf_cop_table(const struct mbuf *, void *, uint32_t, uint32_t *);
+
+static const bpf_copfunc_t npf_bpfcop[] = {
+	[NPF_COP_L3]	= npf_cop_l3,
+	[NPF_COP_TABLE]	= npf_cop_table,
+};
+
+void
+npf_bpf_sysinit(void)
+{
+	npf_bpfctx = bpf_create();
+	KASSERT(npf_bpfctx != NULL);
+	bpf_set_cop(npf_bpfctx, npf_bpfcop, __arraycount(npf_bpfcop));
+}
+
+void
+npf_bpf_sysfini(void)
+{
+	bpf_destroy(npf_bpfctx);
+}
+
+int
+npf_bpf_filter(npf_cache_t *npc, nbuf_t *nbuf,
+    const void *code, bpfjit_function_t jcode)
+{
+	const struct mbuf *m = nbuf_head_mbuf(nbuf);
+	const unsigned char *p = (const unsigned char *)m;
+	const size_t pktlen = m_length(m);
+
+	/* Execute JIT code. */
+	if (__predict_true(jcode)) {
+		return jcode(p, pktlen, 0);
+	}
+
+	/* Execute BPF byte-code. */
+	return bpf_filter_ext(npf_bpfctx, (void *)npc, code, p, pktlen, 0);
+}
+
+bool
+npf_bpf_validate(const void *code, size_t len)
+{
+	const size_t icount = len / sizeof(struct bpf_insn);
+	return bpf_validate_ext(npf_bpfctx, code, icount) != 0;
+}
+
+/*
+ * NPF_COP_L3: fetches layer 3 information.
+ *
+ * Output words in the memory store:
+ *	BPF_MW_IPVER	IP version (4 or 6).
+ *	BPF_MW_L4OFF	L4 header offset.
+ *	BPF_MW_L4PROTO	L4 protocol.
+ */
+static uint32_t
+npf_cop_l3(const struct mbuf *pkt, void *arg, uint32_t A, uint32_t *M)
+{
+	const npf_cache_t *npc = (const npf_cache_t *)arg;
+
+	KASSERT(npc != NULL);
+	memset(M, 0, sizeof(uint32_t) * BPF_MEMWORDS);
+
+	/*
+	 * Convert address length to IP version.  Just mask out
+	 * number 4 or set 6 if higher bits set, such that:
+	 *
+	 *	0	=>	0
+	 *	4	=>	4 (IPVERSION)
+	 *	16	=>	6 (IPV6_VERSION >> 4)
+	 */
+	const u_int alen = npc->npc_alen;
+	const uint32_t ver = (alen & 4) | ((alen >> 4) * 6);
+
+	M[BPF_MW_IPVER] = ver;
+	M[BPF_MW_L4OFF] = npc->npc_hlen;
+	M[BPF_MW_L4PROTO] = npc->npc_proto;
+
+	/* A <- IP version */
+	return ver;
+}
+
+#define	SRC_FLAG_BIT	(1U << 31)
+
+/*
+ * NPF_COP_TABLE: perform NPF table lookup.
+ *
+ *	A <- non-zero (true) if found and zero (false) otherwise
+ */
+static uint32_t
+npf_cop_table(const struct mbuf *pkt, void *arg, uint32_t A, uint32_t *M)
+{
+	const npf_cache_t *npc = (const npf_cache_t *)arg;
+	npf_tableset_t *tblset = npf_config_tableset();
+	const uint32_t tid = A & (SRC_FLAG_BIT - 1);
+	const npf_addr_t *addr;
+
+	KASSERT(npc != NULL);
+	KASSERT(npf_iscached(npc, NPC_IP46));
+	memset(M, 0, sizeof(uint32_t) * BPF_MEMWORDS);
+
+	addr = (A & SRC_FLAG_BIT) ? npc->npc_srcip : npc->npc_dstip;
+	return npf_table_lookup(tblset, tid, npc->npc_alen, addr) == 0;
+}
--- a/sys/net/npf/npf_ctl.c	Thu Sep 19 00:58:11 2013 +0000
+++ b/sys/net/npf/npf_ctl.c	Thu Sep 19 01:04:45 2013 +0000
@@ -1,4 +1,4 @@
-/*	$NetBSD: npf_ctl.c,v 1.27 2013/09/19 00:50:56 rmind Exp $	*/
+/*	$NetBSD: npf_ctl.c,v 1.28 2013/09/19 01:04:46 rmind Exp $	*/
 
 /*-
  * Copyright (c) 2009-2013 The NetBSD Foundation, Inc.
@@ -37,7 +37,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: npf_ctl.c,v 1.27 2013/09/19 00:50:56 rmind Exp $");
+__KERNEL_RCSID(0, "$NetBSD: npf_ctl.c,v 1.28 2013/09/19 01:04:46 rmind Exp $");
 
 #include <sys/param.h>
 #include <sys/conf.h>
@@ -273,7 +273,7 @@
 		}
 		break;
 	case NPF_CODE_BPF:
-		if (!bpf_validate(cptr, clen / sizeof(struct bpf_insn))) {
+		if (!npf_bpf_validate(cptr, clen)) {
 			return EINVAL;
 		}
 		break;
--- a/sys/net/npf/npf_impl.h	Thu Sep 19 00:58:11 2013 +0000
+++ b/sys/net/npf/npf_impl.h	Thu Sep 19 01:04:45 2013 +0000
@@ -1,4 +1,4 @@
-/*	$NetBSD: npf_impl.h,v 1.31 2013/06/02 02:20:04 rmind Exp $	*/
+/*	$NetBSD: npf_impl.h,v 1.32 2013/09/19 01:04:46 rmind Exp $	*/
 
 /*-
  * Copyright (c) 2009-2013 The NetBSD Foundation, Inc.
@@ -53,6 +53,9 @@
 #include <sys/rbtree.h>
 #include <sys/ptree.h>
 #include <sys/rwlock.h>
+
+#include <net/bpf.h>
+#include <net/bpfjit.h>
 #include <net/if.h>
 
 #include "npf.h"
@@ -193,6 +196,13 @@
 bool		npf_fetch_tcpopts(npf_cache_t *, nbuf_t *, uint16_t *, int *);
 bool		npf_return_block(npf_cache_t *, nbuf_t *, const int);
 
+/* BPF interface. */
+void		npf_bpf_sysinit(void);
+void		npf_bpf_sysfini(void);
+int		npf_bpf_filter(npf_cache_t *, nbuf_t *,
+		    const void *, bpfjit_function_t);
+bool		npf_bpf_validate(const void *, size_t);
+
 /* Complex instructions. */
 int		npf_match_ether(nbuf_t *, int, uint16_t, uint32_t *);
 int		npf_match_proto(const npf_cache_t *, uint32_t);
--- a/sys/net/npf/npf_ruleset.c	Thu Sep 19 00:58:11 2013 +0000
+++ b/sys/net/npf/npf_ruleset.c	Thu Sep 19 01:04:45 2013 +0000
@@ -1,4 +1,4 @@
-/*	$NetBSD: npf_ruleset.c,v 1.23 2013/09/18 23:34:55 rmind Exp $	*/
+/*	$NetBSD: npf_ruleset.c,v 1.24 2013/09/19 01:04:46 rmind Exp $	*/
 
 /*-
  * Copyright (c) 2009-2013 The NetBSD Foundation, Inc.
@@ -34,7 +34,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: npf_ruleset.c,v 1.23 2013/09/18 23:34:55 rmind Exp $");
+__KERNEL_RCSID(0, "$NetBSD: npf_ruleset.c,v 1.24 2013/09/19 01:04:46 rmind Exp $");
 
 #include <sys/param.h>
 #include <sys/types.h>
@@ -180,6 +180,9 @@
 	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);
+	} else {
+		KASSERTMSG(rl->r_parent == NULL, "cannot be dynamic rule");
+		rl->r_attr &= ~NPF_RULE_DYNAMIC;
 	}
 
 	rlset->rs_rules[n] = rl;
@@ -271,6 +274,8 @@
 		return ESRCH;
 	}
 	TAILQ_FOREACH(rl, &rg->r_subset, r_entry) {
+		KASSERT(rl->r_parent == rg);
+
 		/* Compare ID.  On match, remove and return. */
 		if (rl->r_id == id) {
 			npf_ruleset_unlink(rlset, rl);
@@ -295,6 +300,8 @@
 
 	/* Find the last in the list. */
 	TAILQ_FOREACH_REVERSE(rl, &rg->r_subset, npf_ruleq, r_entry) {
+		KASSERT(rl->r_parent == rg);
+
 		/* Compare the key.  On match, remove and return. */
 		if (memcmp(rl->r_key, key, len) == 0) {
 			npf_ruleset_unlink(rlset, rl);
@@ -324,6 +331,7 @@
 	}
 
 	TAILQ_FOREACH(rl, &rg->r_subset, r_entry) {
+		KASSERT(rl->r_parent == rg);
 		if (rl->r_dict && !prop_array_add(rules, rl->r_dict)) {
 			prop_object_release(rldict);
 			prop_object_release(rules);
@@ -348,6 +356,7 @@
 		return ESRCH;
 	}
 	while ((rl = TAILQ_FIRST(&rg->r_subset)) != NULL) {
+		KASSERT(rl->r_parent == rg);
 		npf_ruleset_unlink(rlset, rl);
 		LIST_INSERT_HEAD(&rlset->rs_gc, rl, r_aentry);
 	}
@@ -385,14 +394,16 @@
 		}
 
 		/*
-		 * Copy the list-head structure and move the rules from the
-		 * old ruleset to the new by reinserting to a new all-rules
-		 * list and resetting the parent rule.  Note that the rules
-		 * are still active and therefore accessible for inspection
-		 * via the old ruleset.
+		 * Copy the list-head structure.  This is necessary because
+		 * the rules are still active and therefore accessible for
+		 * inspection via the old ruleset.
 		 */
 		memcpy(&rg->r_subset, &arg->r_subset, sizeof(rg->r_subset));
 		TAILQ_FOREACH(rl, &rg->r_subset, r_entry) {
+			/*
+			 * We can safely migrate to the new all-rule list
+			 * and re-set the parent rule, though.
+			 */
 			LIST_REMOVE(rl, r_aentry);
 			LIST_INSERT_HEAD(&rlset->rs_all, rl, r_aentry);
 			rl->r_parent = rg;
@@ -544,15 +555,18 @@
 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;
+#if 0
 	/* Perform BPF JIT if possible. */
 	if (type == NPF_CODE_BPF && (membar_consumer(),
 	    bpfjit_module_ops.bj_generate_code != NULL)) {
 		KASSERT(rl->r_jcode == NULL);
 		rl->r_jcode = bpfjit_module_ops.bj_generate_code(code, size);
+		rl->r_code = NULL;
 	}
-	rl->r_type = type;
-	rl->r_code = code;
-	rl->r_clen = size;
+#endif
 }
 
 /*
@@ -662,27 +676,18 @@
 			return false;
 	}
 
-	/* Execute JIT code, if any. */
-	if (__predict_true(rl->r_jcode)) {
-		struct mbuf *m = nbuf_head_mbuf(nbuf);
-		size_t pktlen = m_length(m);
-
-		return rl->r_jcode((unsigned char *)m, pktlen, 0) != 0;
-	}
-
-	/* Execute the byte-code, if any. */
-	if ((code = rl->r_code) == NULL) {
+	/* Any code? */
+	if (rl->r_jcode == rl->r_code) {
+		KASSERT(rl->r_jcode == NULL);
+		KASSERT(rl->r_code == NULL);
 		return true;
 	}
 
 	switch (rl->r_type) {
+	case NPF_CODE_BPF:
+		return npf_bpf_filter(npc, nbuf, rl->r_code, rl->r_jcode) != 0;
 	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);
 	}
--- a/usr.sbin/npf/npfctl/Makefile	Thu Sep 19 00:58:11 2013 +0000
+++ b/usr.sbin/npf/npfctl/Makefile	Thu Sep 19 01:04:45 2013 +0000
@@ -1,17 +1,17 @@
-# $NetBSD: Makefile,v 1.10 2013/02/09 03:35:32 rmind Exp $
+# $NetBSD: Makefile,v 1.11 2013/09/19 01:04:45 rmind Exp $
 
-BINDIR=/sbin
 PROG=		npfctl
 MAN=		npfctl.8 npf.conf.5
+BINDIR=		/sbin
 
-SRCS=		npfctl.c npf_var.c npf_data.c npf_ncgen.c npf_build.c \
-		npf_extmod.c npf_disassemble.c
+SRCS=		npfctl.c npf_var.c npf_data.c npf_build.c npf_extmod.c
+SRCS+=		npf_bpf_comp.c npf_show.c
 
 CPPFLAGS+=	-I${.CURDIR}
 SRCS+=		npf_scan.l npf_parse.y
 YHEADER=	1
 
-LDADD+=		-lnpf -lprop -lcrypto -lutil -ly
+LDADD+=		-lnpf -lprop -lcrypto -lpcap -lutil -ly
 DPADD+=		${LIBNPF} ${LIBPROP} ${LIBUTIL}
 
 WARNS=		5
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr.sbin/npf/npfctl/npf_bpf_comp.c	Thu Sep 19 01:04:45 2013 +0000
@@ -0,0 +1,602 @@
+/*	$NetBSD: npf_bpf_comp.c,v 1.1 2013/09/19 01:04:45 rmind Exp $	*/
+
+/*-
+ * Copyright (c) 2010-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.
+ */
+
+/*
+ * BPF byte-code generation for NPF rules.
+ */
+
+#include <sys/cdefs.h>
+__RCSID("$NetBSD: npf_bpf_comp.c,v 1.1 2013/09/19 01:04:45 rmind Exp $");
+
+#include <stdlib.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <string.h>
+#include <inttypes.h>
+#include <err.h>
+#include <assert.h>
+
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/ip6.h>
+#include <netinet/udp.h>
+#include <netinet/tcp.h>
+#include <netinet/ip_icmp.h>
+#include <netinet/icmp6.h>
+
+#include <net/bpf.h>
+
+#include "npfctl.h"
+
+/*
+ * Note: clear X_EQ_L4OFF when register X is invalidated i.e. it stores
+ * something other than L4 header offset.  Generally, when BPF_LDX is used.
+ */
+#define	FETCHED_L3		0x01
+#define	X_EQ_L4OFF		0x02
+
+struct npf_bpf {
+	/*
+	 * BPF program code, the allocated length (in bytes), the number
+	 * of logical blocks and the flags.
+	 */
+	struct bpf_program	prog;
+	size_t			alen;
+	u_int			nblocks;
+	sa_family_t		af;
+	uint32_t		flags;
+
+	/* The current group offset and block number. */
+	bool			ingroup;
+	u_int			goff;
+	u_int			gblock;
+
+	/* BPF marks, allocated length and the real length. */
+	uint32_t *		marks;
+	size_t			malen;
+	size_t			mlen;
+};
+
+/*
+ * NPF success and failure values to be returned from BPF.
+ */
+#define	NPF_BPF_SUCCESS		((u_int)-1)
+#define	NPF_BPF_FAILURE		0
+
+/*
+ * Magic value to indicate the failure path, which is fixed up on completion.
+ * Note: this is the longest jump offset in BPF, since the offset is one byte.
+ */
+#define	JUMP_MAGIC		0xff
+
+/* Reduce re-allocations by expanding in 64 byte blocks. */
+#define	ALLOC_MASK		(64 - 1)
+#define	ALLOC_ROUND(x)		(((x) + ALLOC_MASK) & ~ALLOC_MASK)
+
+npf_bpf_t *
+npfctl_bpf_create(void)
+{
+	return ecalloc(1, sizeof(npf_bpf_t));
+}
+
+static void
+fixup_jumps(npf_bpf_t *ctx, u_int start, u_int end, bool swap)
+{
+	struct bpf_program *bp = &ctx->prog;
+
+	for (u_int i = start; i < end; i++) {
+		struct bpf_insn *insn = &bp->bf_insns[i];
+		const u_int fail_off = end - i;
+
+		if (fail_off >= JUMP_MAGIC) {
+			errx(EXIT_FAILURE, "BPF generation error: "
+			    "the number of instructions is over the limit");
+		}
+		if (BPF_CLASS(insn->code) != BPF_JMP) {
+			continue;
+		}
+		if (swap) {
+			uint8_t jt = insn->jt;
+			insn->jt = insn->jf;
+			insn->jf = jt;
+		}
+		if (insn->jt == JUMP_MAGIC)
+			insn->jt = fail_off;
+		if (insn->jf == JUMP_MAGIC)
+			insn->jf = fail_off;
+	}
+}
+
+static void
+add_insns(npf_bpf_t *ctx, struct bpf_insn *insns, size_t count)
+{
+	struct bpf_program *bp = &ctx->prog;
+	size_t offset, len, reqlen;
+
+	/* Note: bf_len is the count of instructions. */
+	offset = bp->bf_len * sizeof(struct bpf_insn);
+	len = count * sizeof(struct bpf_insn);
+
+	/* Ensure the memory buffer for the program. */
+	reqlen = ALLOC_ROUND(offset + len);
+	if (reqlen > ctx->alen) {
+		bp->bf_insns = erealloc(bp->bf_insns, reqlen);
+		ctx->alen = reqlen;
+	}
+
+	/* Add the code block. */
+	memcpy((uint8_t *)bp->bf_insns + offset, insns, len);
+	bp->bf_len += count;
+}
+
+static void
+done_raw_block(npf_bpf_t *ctx, const uint32_t *m, size_t len)
+{
+	size_t reqlen, nargs = m[1];
+
+	if ((len / sizeof(uint32_t) - 2) != nargs) {
+		errx(EXIT_FAILURE, "invalid BPF block description");
+	}
+	reqlen = ALLOC_ROUND(ctx->mlen + len);
+	if (reqlen > ctx->malen) {
+		ctx->marks = erealloc(ctx->marks, reqlen);
+		ctx->malen = reqlen;
+	}
+	memcpy((uint8_t *)ctx->marks + ctx->mlen, m, len);
+	ctx->mlen += len;
+}
+
+static void
+done_block(npf_bpf_t *ctx, const uint32_t *m, size_t len)
+{
+	done_raw_block(ctx, m, len);
+	ctx->nblocks++;
+}
+
+struct bpf_program *
+npfctl_bpf_complete(npf_bpf_t *ctx)
+{
+	struct bpf_program *bp = &ctx->prog;
+	const u_int retoff = bp->bf_len;
+
+	/* Add the return fragment (success and failure paths). */
+	struct bpf_insn insns_ret[] = {
+		BPF_STMT(BPF_RET+BPF_K, NPF_BPF_SUCCESS),
+		BPF_STMT(BPF_RET+BPF_K, NPF_BPF_FAILURE),
+	};
+	add_insns(ctx, insns_ret, __arraycount(insns_ret));
+
+	/* Fixup all jumps to the main failure path. */
+	fixup_jumps(ctx, 0, retoff, false);
+
+	return &ctx->prog;
+}
+
+const void *
+npfctl_bpf_bmarks(npf_bpf_t *ctx, size_t *len)
+{
+	*len = ctx->mlen;
+	return ctx->marks;
+}
+
+void
+npfctl_bpf_destroy(npf_bpf_t *ctx)
+{
+	free(ctx->prog.bf_insns);
+	free(ctx->marks);
+	free(ctx);
+}
+
+/*
+ * npfctl_bpf_group: begin a logical group.  It merely uses logical
+ * disjunction (OR) for compares within the group.
+ */
+void
+npfctl_bpf_group(npf_bpf_t *ctx)
+{
+	struct bpf_program *bp = &ctx->prog;
+
+	assert(ctx->goff == 0);
+	assert(ctx->gblock == 0);
+
+	ctx->goff = bp->bf_len;
+	ctx->gblock = ctx->nblocks;
+	ctx->ingroup = true;
+}
+
+void
+npfctl_bpf_endgroup(npf_bpf_t *ctx)
+{
+	struct bpf_program *bp = &ctx->prog;
+	const size_t curoff = bp->bf_len;
+
+	/* If there are no blocks or only one - nothing to do. */
+	if ((ctx->nblocks - ctx->gblock) <= 1) {
+		ctx->goff = ctx->gblock = 0;
+		return;
+	}
+
+	/*
+	 * Append a failure return as a fall-through i.e. if there is
+	 * no match within the group.
+	 */
+	struct bpf_insn insns_ret[] = {
+		BPF_STMT(BPF_RET+BPF_K, NPF_BPF_FAILURE),
+	};
+	add_insns(ctx, insns_ret, __arraycount(insns_ret));
+
+	/*
+	 * Adjust jump offsets: on match - jump outside the group i.e.
+	 * to the current offset.  Otherwise, jump to the next instruction
+	 * which would lead to the fall-through code above if none matches.
+	 */
+	fixup_jumps(ctx, ctx->goff, curoff, true);
+	ctx->goff = ctx->gblock = 0;
+}
+
+static void
+fetch_l3(npf_bpf_t *ctx, sa_family_t af, u_int flags)
+{
+	u_int ver;
+
+	switch (af) {
+	case AF_INET:
+		ver = IPVERSION;
+		break;
+	case AF_INET6:
+		ver = IPV6_VERSION >> 4;
+		break;
+	case AF_UNSPEC:
+		ver = 0;
+		break;
+	default:
+		abort();
+	}
+
+	/*
+	 * Fetch L3 information.  The coprocessor populates the following
+	 * words in the scratch memory store:
+	 * - BPF_MW_IPVER: IP version (4 or 6).
+	 * - BPF_MW_L4OFF: L4 header offset.
+	 * - BPF_MW_L4PROTO: L4 protocol.
+	 */
+	if ((ctx->flags & FETCHED_L3) == 0 || (af && ctx->af == 0)) {
+		const uint8_t jt = ver ? 0 : JUMP_MAGIC;
+		const uint8_t jf = ver ? JUMP_MAGIC : 0;
+		bool ingroup = ctx->ingroup;
+
+		/*
+		 * L3 block cannot be inserted in the middle of a group.
+		 * In fact, it never is.  Check and start the group after.
+		 */
+		if (ingroup) {
+			assert(ctx->nblocks == ctx->gblock);
+			npfctl_bpf_endgroup(ctx);
+		}
+
+		/*
+		 * A <- IP version; A == expected-version?
+		 * If no particular version specified, check for non-zero.
+		 */
+		struct bpf_insn insns_l3[] = {
+			BPF_STMT(BPF_MISC+BPF_COP, NPF_COP_L3),
+			BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, ver, jt, jf),
+		};
+		add_insns(ctx, insns_l3, __arraycount(insns_l3));
+		ctx->flags |= FETCHED_L3;
+		ctx->af = af;
+
+		if (af) {
+			uint32_t mwords[] = { BM_IPVER, 1, af };
+			done_raw_block(ctx, mwords, sizeof(mwords));
+		}
+		if (ingroup) {
+			npfctl_bpf_group(ctx);
+		}
+
+	} else if (af && af != ctx->af) {
+		errx(EXIT_FAILURE, "address family mismatch");
+	}
+
+	if ((flags & X_EQ_L4OFF) != 0 && (ctx->flags & X_EQ_L4OFF) == 0) {
+		/* X <- IP header length */
+		struct bpf_insn insns_hlen[] = {
+			BPF_STMT(BPF_LDX+BPF_MEM, BPF_MW_L4OFF),
+		};
+		add_insns(ctx, insns_hlen, __arraycount(insns_hlen));
+		ctx->flags |= X_EQ_L4OFF;
+	}
+}
+
+/*
+ * npfctl_bpf_proto: code block to match IP version and L4 protocol.
+ */
+void
+npfctl_bpf_proto(npf_bpf_t *ctx, sa_family_t af, int proto)
+{
+	assert(af != AF_UNSPEC || proto != -1);
+
+	/* Note: fails if IP version does not match. */
+	fetch_l3(ctx, af, 0);
+	if (proto == -1) {
+		return;
+	}
+
+	struct bpf_insn insns_proto[] = {
+		/* A <- L4 protocol; A == expected-protocol? */
+		BPF_STMT(BPF_LD+BPF_W+BPF_MEM, BPF_MW_L4PROTO),
+		BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, proto, 0, JUMP_MAGIC),
+	};
+	add_insns(ctx, insns_proto, __arraycount(insns_proto));
+
+	uint32_t mwords[] = { BM_PROTO, 1, proto };
+	done_block(ctx, mwords, sizeof(mwords));
+}
+
+/*
+ * npfctl_bpf_cidr: code block to match IPv4 or IPv6 CIDR.
+ *
+ * => IP address shall be in the network byte order.
+ */
+void
+npfctl_bpf_cidr(npf_bpf_t *ctx, u_int opts, sa_family_t af,
+    const npf_addr_t *addr, const npf_netmask_t mask)
+{
+	const uint32_t *awords = (const uint32_t *)addr;
+	u_int nwords, length, maxmask, off;
+
+	assert(((opts & MATCH_SRC) != 0) ^ ((opts & MATCH_DST) != 0));
+	assert((mask && mask <= NPF_MAX_NETMASK) || mask == NPF_NO_NETMASK);
+
+	switch (af) {
+	case AF_INET:
+		maxmask = 32;
+		off = (opts & MATCH_SRC) ?
+		    offsetof(struct ip, ip_src) :
+		    offsetof(struct ip, ip_dst);
+		nwords = sizeof(struct in_addr) / sizeof(uint32_t);
+		break;
+	case AF_INET6:
+		maxmask = 128;
+		off = (opts & MATCH_SRC) ?
+		    offsetof(struct ip6_hdr, ip6_src) :
+		    offsetof(struct ip6_hdr, ip6_dst);
+		nwords = sizeof(struct in6_addr) / sizeof(uint32_t);
+		break;
+	default:
+		abort();
+	}
+
+	/* Ensure address family. */
+	fetch_l3(ctx, af, 0);
+
+	length = (mask == NPF_NO_NETMASK) ? maxmask : mask;
+
+	/* CAUTION: BPF operates in host byte-order. */
+	for (u_int i = 0; i < nwords; i++) {
+		const u_int woff = i * sizeof(uint32_t);
+		uint32_t word = ntohl(awords[i]);
+		uint32_t wordmask;
+
+		if (length >= 32) {
+			/* The mask is a full word - do not apply it. */
+			wordmask = 0;
+			length -= 32;
+		} else if (length) {
+			wordmask = 0xffffffff << (maxmask - length);
+			length = 0;
+		} else {
+			/*
+			 * The mask is zero - just compare the word
+			 * against zero.
+			 */
+			wordmask = 0;
+			word = 0;
+		}
+
+		/* A <- IP address (or one word of it) */
+		struct bpf_insn insns_ip[] = {
+			BPF_STMT(BPF_LD+BPF_W+BPF_ABS, off + woff),
+		};
+		add_insns(ctx, insns_ip, __arraycount(insns_ip));
+
+		/* A <- (A & MASK) */
+		if (wordmask) {
+			struct bpf_insn insns_mask[] = {
+				BPF_STMT(BPF_ALU+BPF_AND+BPF_K, wordmask),
+			};
+			add_insns(ctx, insns_mask, __arraycount(insns_mask));
+		}
+
+		/* A == expected-IP-word ? */
+		struct bpf_insn insns_cmp[] = {
+			BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, word, 0, JUMP_MAGIC),
+		};
+		add_insns(ctx, insns_cmp, __arraycount(insns_cmp));
+	}
+
+	uint32_t mwords[] = {
+		(opts & MATCH_SRC) ? BM_SRC_CIDR: BM_DST_CIDR, 6,
+		af, mask, awords[0], awords[1], awords[2], awords[3],
+	};
+	done_block(ctx, mwords, sizeof(mwords));
+}
+
+/*
+ * npfctl_bpf_ports: code block to match TCP/UDP port range.
+ *
+ * => Port numbers shall be in the network byte order.
+ */
+void
+npfctl_bpf_ports(npf_bpf_t *ctx, u_int opts, in_port_t from, in_port_t to)
+{
+	const u_int sport_off = offsetof(struct udphdr, uh_sport);
+	const u_int dport_off = offsetof(struct udphdr, uh_dport);
+	u_int off;
+
+	/* TCP and UDP port offsets are the same. */
+	assert(sport_off == offsetof(struct tcphdr, th_sport));
+	assert(dport_off == offsetof(struct tcphdr, th_dport));
+
+	assert(((opts & MATCH_SRC) != 0) ^ ((opts & MATCH_DST) != 0));
+	off = (opts & MATCH_SRC) ? sport_off : dport_off;
+
+	/* X <- IP header length */
+	fetch_l3(ctx, 0, X_EQ_L4OFF);
+
+	struct bpf_insn insns_fetch[] = {
+		/* A <- port */
+		BPF_STMT(BPF_LD+BPF_H+BPF_IND, off),
+	};
+	add_insns(ctx, insns_fetch, __arraycount(insns_fetch));
+
+	/* CAUTION: BPF operates in host byte-order. */
+	from = ntohs(from);
+	to = ntohs(to);
+
+	if (from == to) {
+		/* Single port case. */
+		struct bpf_insn insns_port[] = {
+			BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, from, 0, JUMP_MAGIC),
+		};
+		add_insns(ctx, insns_port, __arraycount(insns_port));
+	} else {
+		/* Port range case. */
+		struct bpf_insn insns_range[] = {
+			BPF_JUMP(BPF_JMP+BPF_JGE+BPF_K, from, 0, JUMP_MAGIC),
+			BPF_JUMP(BPF_JMP+BPF_JGT+BPF_K, to, JUMP_MAGIC, 0),
+		};
+		add_insns(ctx, insns_range, __arraycount(insns_range));
+	}
+
+	uint32_t mwords[] = {
+		opts & MATCH_SRC ? BM_SRC_PORTS : BM_DST_PORTS, 2, from, to
+	};
+	done_block(ctx, mwords, sizeof(mwords));
+}
+
+/*
+ * npfctl_bpf_tcpfl: code block to match TCP flags.
+ */
+void
+npfctl_bpf_tcpfl(npf_bpf_t *ctx, uint8_t tf, uint8_t tf_mask)
+{
+	const u_int tcpfl_off = offsetof(struct tcphdr, th_flags);
+
+	/* X <- IP header length */
+	fetch_l3(ctx, 0, X_EQ_L4OFF);
+
+	struct bpf_insn insns_tf[] = {
+		/* A <- TCP flags */
+		BPF_STMT(BPF_LD+BPF_B+BPF_IND, tcpfl_off),
+	};
+	add_insns(ctx, insns_tf, __arraycount(insns_tf));
+
+	if (tf_mask != tf) {
+		/* A <- (A & mask) */
+		struct bpf_insn insns_mask[] = {
+			BPF_STMT(BPF_ALU+BPF_AND+BPF_K, tf_mask),
+		};
+		add_insns(ctx, insns_mask, __arraycount(insns_mask));
+	}
+
+	struct bpf_insn insns_cmp[] = {
+		/* A == expected-TCP-flags? */
+		BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, tf, 0, JUMP_MAGIC),
+	};
+	add_insns(ctx, insns_cmp, __arraycount(insns_cmp));
+
+	uint32_t mwords[] = { BM_TCPFL, 2, tf, tf_mask};
+	done_block(ctx, mwords, sizeof(mwords));
+}
+
+/*
+ * npfctl_bpf_icmp: code block to match ICMP type and/or code.
+ * Note: suitable both for the ICMPv4 and ICMPv6.
+ */
+void
+npfctl_bpf_icmp(npf_bpf_t *ctx, int type, int code)
+{
+	const u_int type_off = offsetof(struct icmp, icmp_type);
+	const u_int code_off = offsetof(struct icmp, icmp_code);
+
+	assert(offsetof(struct icmp6_hdr, icmp6_type) == type_off);
+	assert(offsetof(struct icmp6_hdr, icmp6_code) == code_off);
+	assert(type != -1 || code != -1);
+
+	/* X <- IP header length */
+	fetch_l3(ctx, 0, X_EQ_L4OFF);
+
+	if (type != -1) {
+		struct bpf_insn insns_type[] = {
+			BPF_STMT(BPF_LD+BPF_B+BPF_IND, type_off),
+			BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, type, 0, JUMP_MAGIC),
+		};
+		add_insns(ctx, insns_type, __arraycount(insns_type));
+
+		uint32_t mwords[] = { BM_ICMP_TYPE, 1, type };
+		done_block(ctx, mwords, sizeof(mwords));
+	}
+
+	if (code != -1) {
+		struct bpf_insn insns_code[] = {
+			BPF_STMT(BPF_LD+BPF_B+BPF_IND, code_off),
+			BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, code, 0, JUMP_MAGIC),
+		};
+		add_insns(ctx, insns_code, __arraycount(insns_code));
+
+		uint32_t mwords[] = { BM_ICMP_CODE, 1, code };
+		done_block(ctx, mwords, sizeof(mwords));
+	}
+}
+
+#define	SRC_FLAG_BIT	(1U << 31)
+
+/*
+ * npfctl_bpf_table: code block to match source/destination IP address
+ * against NPF table specified by ID.
+ */
+void
+npfctl_bpf_table(npf_bpf_t *ctx, u_int opts, u_int tid)
+{
+	const bool src = (opts & MATCH_SRC) != 0;
+
+	struct bpf_insn insns_table[] = {
+		BPF_STMT(BPF_LD+BPF_IMM, (src ? SRC_FLAG_BIT : 0) | tid),
+		BPF_STMT(BPF_MISC+BPF_COP, NPF_COP_TABLE),
+		BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, 0, JUMP_MAGIC, 0),
+	};
+	add_insns(ctx, insns_table, __arraycount(insns_table));
+
+	uint32_t mwords[] = { src ? BM_SRC_TABLE: BM_DST_TABLE, 1, tid };
+	done_block(ctx, mwords, sizeof(mwords));
+}
--- a/usr.sbin/npf/npfctl/npf_build.c	Thu Sep 19 00:58:11 2013 +0000
+++ b/usr.sbin/npf/npfctl/npf_build.c	Thu Sep 19 01:04:45 2013 +0000
@@ -1,4 +1,4 @@
-/*	$NetBSD: npf_build.c,v 1.24 2013/05/19 20:45:34 rmind Exp $	*/
+/*	$NetBSD: npf_build.c,v 1.25 2013/09/19 01:04:45 rmind Exp $	*/
 
 /*-
  * Copyright (c) 2011-2013 The NetBSD Foundation, Inc.
@@ -34,7 +34,7 @@
  */
 
 #include <sys/cdefs.h>
-__RCSID("$NetBSD: npf_build.c,v 1.24 2013/05/19 20:45:34 rmind Exp $");
+__RCSID("$NetBSD: npf_build.c,v 1.25 2013/09/19 01:04:45 rmind Exp $");
 
 #include <sys/types.h>
 #include <sys/ioctl.h>
@@ -45,6 +45,8 @@
 #include <errno.h>
 #include <err.h>
 
+#include <pcap/pcap.h>
+
 #include "npfctl.h"
 
 #define	MAX_RULE_NESTING	16
@@ -87,7 +89,9 @@
 		_npf_config_error(npf_conf, &ne);
 		npfctl_print_error(&ne);
 	}
-	npf_config_destroy(npf_conf);
+	if (fd) {
+		npf_config_destroy(npf_conf);
+	}
 	return error;
 }
 
@@ -154,7 +158,7 @@
 }
 
 static bool
-npfctl_build_fam(nc_ctx_t *nc, sa_family_t family,
+npfctl_build_fam(npf_bpf_t *ctx, sa_family_t family,
     fam_addr_mask_t *fam, int opts)
 {
 	/*
@@ -169,6 +173,7 @@
 		}
 		return false;
 	}
+	family = fam->fam_family;
 
 	/*
 	 * Optimise 0.0.0.0/0 case to be NOP.  Otherwise, address with
@@ -184,28 +189,20 @@
 		return false;
 	}
 
-	switch (fam->fam_family) {
-	case AF_INET:
-		npfctl_gennc_v4cidr(nc, opts,
-		    &fam->fam_addr, fam->fam_mask);
-		break;
-	case AF_INET6:
-		npfctl_gennc_v6cidr(nc, opts,
-		    &fam->fam_addr, fam->fam_mask);
-		break;
-	default:
-		yyerror("family %d is not supported", fam->fam_family);
+	if (family != AF_INET && family != AF_INET6) {
+		yyerror("family %d is not supported", family);
 	}
+	npfctl_bpf_cidr(ctx, opts, family, &fam->fam_addr, fam->fam_mask);
 	return true;
 }
 
 static void
-npfctl_build_vars(nc_ctx_t *nc, sa_family_t family, npfvar_t *vars, int opts)
+npfctl_build_vars(npf_bpf_t *ctx, sa_family_t family, npfvar_t *vars, int opts)
 {
 	const int type = npfvar_get_type(vars, 0);
 	size_t i;
 
-	npfctl_ncgen_group(nc);
+	npfctl_bpf_group(ctx);
 	for (i = 0; i < npfvar_get_count(vars); i++) {
 		void *data = npfvar_get_data(vars, type, i);
 		assert(data != NULL);
@@ -213,189 +210,140 @@
 		switch (type) {
 		case NPFVAR_FAM: {
 			fam_addr_mask_t *fam = data;
-			npfctl_build_fam(nc, family, fam, opts);
+			npfctl_build_fam(ctx, family, fam, opts);
 			break;
 		}
 		case NPFVAR_PORT_RANGE: {
 			port_range_t *pr = data;
-			if (opts & NC_MATCH_TCP) {
-				npfctl_gennc_ports(nc, opts & ~NC_MATCH_UDP,
-				    pr->pr_start, pr->pr_end);
-			}
-			if (opts & NC_MATCH_UDP) {
-				npfctl_gennc_ports(nc, opts & ~NC_MATCH_TCP,
-				    pr->pr_start, pr->pr_end);
-			}
+			npfctl_bpf_ports(ctx, opts, pr->pr_start, pr->pr_end);
 			break;
 		}
 		case NPFVAR_TABLE: {
 			u_int tid = atoi(data);
-			npfctl_gennc_tbl(nc, opts, tid);
+			npfctl_bpf_table(ctx, opts, tid);
 			break;
 		}
 		default:
 			assert(false);
 		}
 	}
-	npfctl_ncgen_endgroup(nc);
+	npfctl_bpf_endgroup(ctx);
 }
 
-static int
-npfctl_build_proto(nc_ctx_t *nc, sa_family_t family,
-    const opt_proto_t *op, bool noaddrs, bool noports)
+static void
+npfctl_build_proto(npf_bpf_t *ctx, sa_family_t family, const opt_proto_t *op)
 {
 	const npfvar_t *popts = op->op_opts;
 	const int proto = op->op_proto;
-	int pflag = 0;
+
+	/* IP version and/or L4 protocol matching. */
+	if (family != AF_UNSPEC || proto != -1) {
+		npfctl_bpf_proto(ctx, family, proto);
+	}
 
 	switch (proto) {
 	case IPPROTO_TCP:
-		pflag = NC_MATCH_TCP;
-		if (!popts) {
-			break;
-		}
-		assert(npfvar_get_count(popts) == 2);
+		/* Build TCP flags matching (optional). */
+		if (popts) {
+			uint8_t *tf, *tf_mask;
 
-		/* Build TCP flags block (optional). */
-		uint8_t *tf, *tf_mask;
-
-		tf = npfvar_get_data(popts, NPFVAR_TCPFLAG, 0);
-		tf_mask = npfvar_get_data(popts, NPFVAR_TCPFLAG, 1);
-		npfctl_gennc_tcpfl(nc, *tf, *tf_mask);
-		noports = false;
-		break;
-	case IPPROTO_UDP:
-		pflag = NC_MATCH_UDP;
+			assert(npfvar_get_count(popts) == 2);
+			tf = npfvar_get_data(popts, NPFVAR_TCPFLAG, 0);
+			tf_mask = npfvar_get_data(popts, NPFVAR_TCPFLAG, 1);
+			npfctl_bpf_tcpfl(ctx, *tf, *tf_mask);
+		}
 		break;
 	case IPPROTO_ICMP:
-		/*
-		 * Build ICMP block.
-		 */
-		if (!noports) {
-			goto invop;
-		}
-		assert(npfvar_get_count(popts) == 2);
+	case IPPROTO_ICMPV6:
+		/* Build ICMP/ICMPv6 type and/or code matching. */
+		if (popts) {
+			int *icmp_type, *icmp_code;
 
-		int *icmp_type, *icmp_code;
-		icmp_type = npfvar_get_data(popts, NPFVAR_ICMP, 0);
-		icmp_code = npfvar_get_data(popts, NPFVAR_ICMP, 1);
-		npfctl_gennc_icmp(nc, *icmp_type, *icmp_code);
-		noports = false;
-		break;
-	case IPPROTO_ICMPV6:
-		/*
-		 * Build ICMP block.
-		 */
-		if (!noports) {
-			goto invop;
+			assert(npfvar_get_count(popts) == 2);
+			icmp_type = npfvar_get_data(popts, NPFVAR_ICMP, 0);
+			icmp_code = npfvar_get_data(popts, NPFVAR_ICMP, 1);
+			npfctl_bpf_icmp(ctx, *icmp_type, *icmp_code);
 		}
-		assert(npfvar_get_count(popts) == 2);
-
-		int *icmp6_type, *icmp6_code;
-		icmp6_type = npfvar_get_data(popts, NPFVAR_ICMP6, 0);
-		icmp6_code = npfvar_get_data(popts, NPFVAR_ICMP6, 1);
-		npfctl_gennc_icmp6(nc, *icmp6_type, *icmp6_code);
-		noports = false;
-		break;
-	case -1:
-		pflag = NC_MATCH_TCP | NC_MATCH_UDP;
-		noports = false;
 		break;
 	default:
-		/*
-		 * No filter options are supported for other protocols,
-		 * only the IP addresses are allowed.
-		 */
-		if (noports) {
-			break;
-		}
-invop:
-		yyerror("invalid filter options for protocol %d", proto);
+		/* No options for other protocols. */
+		break;
 	}
-
-	/*
-	 * Build the protocol block, unless other blocks will implicitly
-	 * perform the family/protocol checks for us.
-	 */
-	if ((family != AF_UNSPEC && noaddrs) || (proto != -1 && noports)) {
-		uint8_t addrlen;
-
-		switch (family) {
-		case AF_INET:
-			addrlen = sizeof(struct in_addr);
-			break;
-		case AF_INET6:
-			addrlen = sizeof(struct in6_addr);
-			break;
-		default:
-			addrlen = 0;
-		}
-		npfctl_gennc_proto(nc,
-		    noaddrs ? addrlen : 0,
-		    noports ? proto : 0xff);
-	}
-	return pflag;
 }
 
 static bool
-npfctl_build_ncode(nl_rule_t *rl, sa_family_t family, const opt_proto_t *op,
+npfctl_build_code(nl_rule_t *rl, sa_family_t family, const opt_proto_t *op,
     const filt_opts_t *fopts, bool invert)
 {
 	const addr_port_t *apfrom = &fopts->fo_from;
 	const addr_port_t *apto = &fopts->fo_to;
 	const int proto = op->op_proto;
-	bool noaddrs, noports;
-	nc_ctx_t *nc;
-	void *code;
+	bool noproto, noaddrs, noports;
+	npf_bpf_t *bc;
 	size_t len;
 
-	/*
-	 * If none specified, no n-code.
-	 */
+	/* If none specified, then no byte-code. */
+	noproto = family == AF_UNSPEC && proto == -1 && !op->op_opts;
 	noaddrs = !apfrom->ap_netaddr && !apto->ap_netaddr;
 	noports = !apfrom->ap_portrange && !apto->ap_portrange;
-	if (family == AF_UNSPEC && proto == -1 && !op->op_opts &&
-	    noaddrs && noports)
+	if (noproto && noaddrs && noports) {
 		return false;
-
-	int srcflag = NC_MATCH_SRC;
-	int dstflag = NC_MATCH_DST;
-
-	if (invert) {
-		srcflag = NC_MATCH_DST;
-		dstflag = NC_MATCH_SRC;
 	}
 
-	nc = npfctl_ncgen_create();
+	/*
+	 * Sanity check: ports can only be used with TCP or UDP protocol.
+	 * No filter options are supported for other protocols, only the
+	 * IP addresses are allowed.
+	 */
+	if (!noports) {
+		switch (proto) {
+		case IPPROTO_TCP:
+		case IPPROTO_UDP:
+		case -1:
+			break;
+		default:
+			yyerror("invalid filter options for protocol %d", proto);
+		}
+	}
+
+	const int srcflag = invert ? MATCH_DST : MATCH_SRC;
+	const int dstflag = invert ? MATCH_SRC : MATCH_DST;
+
+	bc = npfctl_bpf_create();
 
 	/* Build layer 4 protocol blocks. */
-	int pflag = npfctl_build_proto(nc, family, op, noaddrs, noports);
+	npfctl_build_proto(bc, family, op);
 
 	/* Build IP address blocks. */
-	npfctl_build_vars(nc, family, apfrom->ap_netaddr, srcflag);
-	npfctl_build_vars(nc, family, apto->ap_netaddr, dstflag);
+	npfctl_build_vars(bc, family, apfrom->ap_netaddr, srcflag);
+	npfctl_build_vars(bc, family, apto->ap_netaddr, dstflag);
 
 	/* Build port-range blocks. */
-	npfctl_build_vars(nc, family, apfrom->ap_portrange, srcflag | pflag);
-	npfctl_build_vars(nc, family, apto->ap_portrange, dstflag | pflag);
+	npfctl_build_vars(bc, family, apfrom->ap_portrange, srcflag);
+	npfctl_build_vars(bc, family, apto->ap_portrange, dstflag);
 
-	/*
-	 * Complete n-code (destroys the context) and pass to the rule.
-	 */
-	code = npfctl_ncgen_complete(nc, &len);
+	/* Set the byte-code marks, if any. */
+	const void *bmarks = npfctl_bpf_bmarks(bc, &len);
+	if (npf_rule_setinfo(rl, bmarks, len) == -1) {
+		errx(EXIT_FAILURE, "npf_rule_setinfo failed");
+	}
+
+	/* Complete BPF byte-code and pass to the rule. */
+	struct bpf_program *bf = npfctl_bpf_complete(bc);
 	if (npf_debug) {
 		extern char *yytext;
 		extern int yylineno;
 
-		printf("RULE AT LINE %d\n", yylineno - (int)(*yytext == '\n'));
-		npfctl_ncgen_print(code, len);
+		printf("\nRULE AT LINE %d\n", yylineno - (int)(*yytext == '\n'));
+		bpf_dump(bf, 0);
 	}
-	assert(code && len > 0);
+	len = bf->bf_len * sizeof(struct bpf_insn);
 
-	if (npf_rule_setcode(rl, NPF_CODE_NC, code, len) == -1) {
+	if (npf_rule_setcode(rl, NPF_CODE_BPF, bf->bf_insns, len) == -1) {
 		errx(EXIT_FAILURE, "npf_rule_setcode failed");
 	}
-	free(code);
+	npfctl_bpf_destroy(bc);
+
 	return true;
 }
 
@@ -527,7 +475,7 @@
 	attr |= (npf_conf ? 0 : NPF_RULE_DYNAMIC);
 
 	rl = npf_rule_create(NULL, attr, if_idx);
-	npfctl_build_ncode(rl, family, op, fopts, false);
+	npfctl_build_code(rl, family, op, fopts, false);
 	if (rproc) {
 		npf_rule_setproc(rl, rproc);
 	}
@@ -599,7 +547,7 @@
 		assert(false);
 	}
 
-	npfctl_build_ncode(nat, family, &op, fopts, false);
+	npfctl_build_code(nat, family, &op, fopts, false);
 	npf_nat_insert(npf_conf, nat, NPF_PRI_LAST);
 }
 
@@ -714,7 +662,7 @@
 }
 
 /*
- * npfctl_build_alg: create an NPF application level gatewayl and add it
+ * npfctl_build_alg: create an NPF application level gateway and add it
  * to the configuration.
  */
 void
--- a/usr.sbin/npf/npfctl/npf_data.c	Thu Sep 19 00:58:11 2013 +0000
+++ b/usr.sbin/npf/npfctl/npf_data.c	Thu Sep 19 01:04:45 2013 +0000
@@ -1,4 +1,4 @@
-/*	$NetBSD: npf_data.c,v 1.19 2012/11/26 20:34:28 rmind Exp $	*/
+/*	$NetBSD: npf_data.c,v 1.20 2013/09/19 01:04:45 rmind Exp $	*/
 
 /*-
  * Copyright (c) 2009-2012 The NetBSD Foundation, Inc.
@@ -31,7 +31,7 @@
  */
 
 #include <sys/cdefs.h>
-__RCSID("$NetBSD: npf_data.c,v 1.19 2012/11/26 20:34:28 rmind Exp $");
+__RCSID("$NetBSD: npf_data.c,v 1.20 2013/09/19 01:04:45 rmind Exp $");
 
 #include <sys/types.h>
 #include <sys/null.h>
@@ -567,23 +567,11 @@
 npfctl_parse_icmp(int proto, int type, int code)
 {
 	npfvar_t *vp = npfvar_create(".icmp");
-	int varnum;
 
-	switch (proto) {
-	case IPPROTO_ICMP:
-		varnum = NPFVAR_ICMP;
-		break;
-	case IPPROTO_ICMPV6:
-		varnum = NPFVAR_ICMP6;
-		break;
-	default:
-		assert(false);
-	}
-
-	if (!npfvar_add_element(vp, varnum, &type, sizeof(type)))
+	if (!npfvar_add_element(vp, NPFVAR_ICMP, &type, sizeof(type)))
 		goto out;
 
-	if (!npfvar_add_element(vp, varnum, &code, sizeof(code)))
+	if (!npfvar_add_element(vp, NPFVAR_ICMP, &code, sizeof(code)))
 		goto out;
 
 	return vp;
--- a/usr.sbin/npf/npfctl/npf_disassemble.c	Thu Sep 19 00:58:11 2013 +0000
+++ b/usr.sbin/npf/npfctl/npf_disassemble.c	Thu Sep 19 01:04:45 2013 +0000
@@ -1,4 +1,4 @@
-/*	$NetBSD: npf_disassemble.c,v 1.17 2013/02/16 21:11:14 rmind Exp $	*/
+/*	$NetBSD: npf_disassemble.c,v 1.18 2013/09/19 01:04:45 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.17 2013/02/16 21:11:14 rmind Exp $");
+__RCSID("$NetBSD: npf_disassemble.c,v 1.18 2013/09/19 01:04:45 rmind Exp $");
 
 #include <stdio.h>
 #include <stdlib.h>
@@ -640,7 +640,7 @@
 		printf("all ");
 	}
 
-	if ((rproc = _npf_rule_rproc(nrl)) != NULL) {
+	if ((rproc = npf_rule_getproc(nrl)) != NULL) {
 		printf("apply \"%s\"", rproc);
 	}
 	puts("");
--- a/usr.sbin/npf/npfctl/npf_parse.y	Thu Sep 19 00:58:11 2013 +0000
+++ b/usr.sbin/npf/npfctl/npf_parse.y	Thu Sep 19 01:04:45 2013 +0000
@@ -1,4 +1,4 @@
-/*	$NetBSD: npf_parse.y,v 1.24 2013/05/19 20:45:34 rmind Exp $	*/
+/*	$NetBSD: npf_parse.y,v 1.25 2013/09/19 01:04:45 rmind Exp $	*/
 
 /*-
  * Copyright (c) 2011-2012 The NetBSD Foundation, Inc.
@@ -720,10 +720,7 @@
 		$$ = npfctl_parse_icmp($<num>0, $2,
 		    npfctl_icmpcode($<num>0, $2, s));
 	}
-	|
-	{
-		$$ = npfctl_parse_icmp($<num>0, -1, -1);
-	}
+	|		{ $$ = NULL; }
 	;
 
 tcp_flags_and_mask
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr.sbin/npf/npfctl/npf_show.c	Thu Sep 19 01:04:45 2013 +0000
@@ -0,0 +1,490 @@
+/*	$NetBSD: npf_show.c,v 1.1 2013/09/19 01:04:45 rmind Exp $	*/
+
+/*-
+ * Copyright (c) 2013 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by 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 configuration printing.
+ *
+ * Each rule having BPF byte-code has a binary description.
+ */
+
+#include <sys/cdefs.h>
+__RCSID("$NetBSD: npf_show.c,v 1.1 2013/09/19 01:04:45 rmind Exp $");
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <net/if.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdbool.h>
+#include <inttypes.h>
+#include <errno.h>
+#include <err.h>
+
+#include "npfctl.h"
+
+typedef struct {
+	FILE *		fp;
+	long		fpos;
+} npf_conf_info_t;
+
+static npf_conf_info_t	stdout_ctx = { .fp = stdout, .fpos = 0 };
+
+static void	print_indent(npf_conf_info_t *, u_int);
+static void	print_linesep(npf_conf_info_t *);
+
+/*
+ * Helper routines to print various pieces of information.
+ */
+
+static void
+print_indent(npf_conf_info_t *ctx, u_int level)
+{
+	if (level == 0) { /* XXX */
+		print_linesep(ctx);
+	}
+	while (level--)
+		fprintf(ctx->fp, "\t");
+}
+
+static void
+print_linesep(npf_conf_info_t *ctx)
+{
+	if (ftell(ctx->fp) != ctx->fpos) {
+		fputs("\n", ctx->fp);
+		ctx->fpos = ftell(ctx->fp);
+	}
+}
+
+static size_t
+tcpflags2string(char *buf, u_int tfl)
+{
+	u_int i = 0;
+
+	if (tfl & TH_FIN)	buf[i++] = 'F';
+	if (tfl & TH_SYN)	buf[i++] = 'S';
+	if (tfl & TH_RST)	buf[i++] = 'R';
+	if (tfl & TH_PUSH)	buf[i++] = 'P';
+	if (tfl & TH_ACK)	buf[i++] = 'A';
+	if (tfl & TH_URG)	buf[i++] = 'U';
+	if (tfl & TH_ECE)	buf[i++] = 'E';
+	if (tfl & TH_CWR)	buf[i++] = 'C';
+	buf[i] = '\0';
+	return i;
+}
+
+static char *
+print_family(const uint32_t *words)
+{
+	const int af = words[0];
+
+	switch (af) {
+	case AF_INET:
+		return estrdup("inet");
+	case AF_INET6:
+		return estrdup("inet6");
+	default:
+		errx(EXIT_FAILURE, "invalid byte-code mark (family)");
+	}
+	return NULL;
+}
+
+static char *
+print_address(const uint32_t *words)
+{
+	const int af = *words++;
+	const u_int mask = *words++;
+	const npf_addr_t *addr;
+	int alen = 0;
+
+	switch (af) {
+	case AF_INET:
+		alen = 4;
+		break;
+	case AF_INET6:
+		alen = 16;
+		break;
+	default:
+		errx(EXIT_FAILURE, "invalid byte-code mark (address)");
+	}
+	addr = (const npf_addr_t *)words;
+	return npfctl_print_addrmask(alen, addr, mask);
+}
+
+static char *
+print_number(const uint32_t *words)
+{
+	char *p;
+	easprintf(&p, "%u", words[0]);
+	return p;
+}
+
+static char *
+print_proto(const uint32_t *words)
+{
+	switch (words[0]) {
+	case IPPROTO_TCP:
+		return estrdup("tcp");
+	case IPPROTO_UDP:
+		return estrdup("udp");
+	case IPPROTO_ICMP:
+		return estrdup("icmp");
+	case IPPROTO_ICMPV6:
+		return estrdup("ipv6-icmp");
+	}
+	return print_number(words);
+}
+
+static char *
+print_tcpflags(const uint32_t *words)
+{
+	const u_int tf = words[0], tf_mask = words[1];
+	char buf[16];
+
+	size_t n = tcpflags2string(buf, tf);
+	if (tf != tf_mask) {
+		buf[n++] = '/';
+		tcpflags2string(buf + n, tf_mask);
+	}
+	return estrdup(buf);
+}
+
+static char *
+print_portrange(const uint32_t *words)
+{
+	u_int fport = words[0], tport = words[1];
+	char *p;
+
+	if (fport != tport) {
+		easprintf(&p, "%u:%u", fport, tport);
+	} else {
+		easprintf(&p, "%u", fport);
+	}
+	return p;
+}
+
+/*
+ * The main keyword mapping tables defining the syntax:
+ * - Mapping of rule attributes (flags) to the keywords.
+ * - Mapping of the byte-code marks to the keywords.
+ */
+
+#define	F(name)		__CONCAT(NPF_RULE_, name)
+#define	NAME_AT		2
+
+static const struct attr_keyword_mapent {
+	uint32_t	mask;
+	uint32_t	flags;
+	const char *	val;
+} attr_keyword_map[] = {
+	{ F(GROUP)|F(DYNAMIC),	F(GROUP),		"group"		},
+	{ F(DYNAMIC),		F(DYNAMIC),		"ruleset"	},
+	{ F(GROUP)|F(PASS),	0,			"block"		},
+	{ F(GROUP)|F(PASS),	F(PASS),		"pass"		},
+	{ F(RETRST)|F(RETICMP),	F(RETRST)|F(RETICMP),	"return"	},
+	{ F(RETRST)|F(RETICMP),	F(RETRST),		"return-rst"	},
+	{ F(RETRST)|F(RETICMP),	F(RETICMP),		"return-icmp"	},
+	{ F(STATEFUL),		F(STATEFUL),		"stateful"	},
+	{ F(DIMASK),		F(IN),			"in"		},
+	{ F(DIMASK),		F(OUT),			"out"		},
+	{ F(FINAL),		F(FINAL),		"final"		},
+};
+
+static const struct mark_keyword_mapent {
+	u_int		mark;
+	const char *	token;
+	const char *	sep;
+	char *		(*printfn)(const uint32_t *);
+	u_int		fwords;
+} mark_keyword_map[] = {
+	{ BM_IPVER,	"family %s",	NULL,		print_family,	1 },
+	{ BM_PROTO,	"proto %s",	NULL,		print_proto,	1 },
+	{ BM_TCPFL,	"flags %s",	NULL,		print_tcpflags,	2 },
+	{ BM_ICMP_TYPE,	"icmp-type %s",	NULL,		print_number,	1 },
+	{ BM_ICMP_CODE,	"code %s",	NULL,		print_number,	1 },
+
+	{ BM_SRC_CIDR,	"from %s",	", ",		print_address,	6 },
+	{ BM_SRC_TABLE,	"from <%s>",	NULL,		print_number,	1 },
+	{ BM_SRC_PORTS,	"port %s",	", ",		print_portrange,2 },
+
+	{ BM_DST_CIDR,	"to %s",	", ",		print_address,	6 },
+	{ BM_DST_TABLE,	"to <%s>",	NULL,		print_number,	1 },
+	{ BM_DST_PORTS,	"port %s",	", ",		print_portrange,2 },
+};
+
+static const char * __attribute__((format_arg(2)))
+verified_fmt(const char *fmt, const char *t __unused)
+{
+	return fmt;
+}
+
+static char *
+scan_marks(npf_conf_info_t *ctx, const struct mark_keyword_mapent *mk,
+    const uint32_t *marks, size_t mlen)
+{
+	char buf[2048], *vals[256], *p;
+	size_t nvals = 0;
+
+	/* Scan for the marks and extract the values. */
+	mlen /= sizeof(uint32_t);
+	while (mlen > 2) {
+		const uint32_t m = *marks++;
+		const u_int nwords = *marks++;
+
+		if ((mlen -= 2) < nwords) {
+			errx(EXIT_FAILURE, "byte-code marking inconsistency");
+		}
+		if (m == mk->mark) {
+			/* Value is processed by the print function. */
+			assert(mk->fwords == nwords);
+			vals[nvals++] = mk->printfn(marks);
+		}
+		marks += nwords;
+		mlen -= nwords;
+	}
+	if (nvals == 0) {
+		return NULL;
+	}
+	assert(nvals == 1 || mk->sep != NULL);
+
+	/*
+	 * Join all the values and print.  Add curly brackets if there
+	 * is more than value and it can be a set.
+	 */
+	if (!join(buf, sizeof(buf), nvals, vals, mk->sep ? mk->sep : "")) {
+		errx(EXIT_FAILURE, "out of memory while parsing the rule");
+	}
+	easprintf(&p, nvals > 1 ? "{ %s }" : "%s", buf);
+
+	for (u_int i = 0; i < nvals; i++) {
+		free(vals[i]);
+	}
+	return p;
+}
+
+static void
+npfctl_print_filter(npf_conf_info_t *ctx, nl_rule_t *rl)
+{
+	const void *marks;
+	size_t mlen;
+
+	/* BPF filter criteria described by the byte-code marks. */
+	marks = npf_rule_getinfo(rl, &mlen);
+	for (u_int i = 0; i < __arraycount(mark_keyword_map); i++) {
+		const struct mark_keyword_mapent *mk = &mark_keyword_map[i];
+		char *val;
+
+		if ((val = scan_marks(ctx, mk, marks, mlen)) != NULL) {
+			fprintf(ctx->fp, verified_fmt(mk->token, "%s"), val);
+			fputs(" ", ctx->fp);
+			free(val);
+		}
+	}
+	if (!mlen) {
+		fputs("all ", ctx->fp);
+	}
+}
+
+static void
+npfctl_print_rule(npf_conf_info_t *ctx, nl_rule_t *rl)
+{
+	const uint32_t attr = npf_rule_getattr(rl);
+	const char *rproc, *name;
+	u_int if_idx;
+
+	/* Rule attributes/flags. */
+	for (u_int i = 0; i < __arraycount(attr_keyword_map); i++) {
+		const struct attr_keyword_mapent *ak = &attr_keyword_map[i];
+
+		if (i == NAME_AT && (name = npf_rule_getname(rl)) != NULL) {
+			fprintf(ctx->fp, "\"%s\" ", name);
+		}
+		if ((attr & ak->mask) == ak->flags) {
+			fprintf(ctx->fp, "%s ", ak->val);
+		}
+	}
+	if ((if_idx = npf_rule_getinterface(rl)) != 0) {
+		char ifnamebuf[IFNAMSIZ], *ifname;
+		ifname = if_indextoname(if_idx, ifnamebuf);
+		fprintf(ctx->fp, "on %s ", ifname);
+	}
+
+	if ((attr & (NPF_RULE_GROUP | NPF_RULE_DYNAMIC)) == NPF_RULE_GROUP) {
+		/* Group; done. */
+		fputs("\n", ctx->fp);
+		return;
+	}
+
+	/* Print filter criteria. */
+	npfctl_print_filter(ctx, rl);
+
+	/* Rule procedure. */
+	if ((rproc = npf_rule_getproc(rl)) != NULL) {
+		fprintf(ctx->fp, "apply \"%s\"", rproc);
+	}
+	fputs("\n", ctx->fp);
+}
+
+static void
+npfctl_print_nat(npf_conf_info_t *ctx, nl_nat_t *nt)
+{
+	nl_rule_t *rl = (nl_nat_t *)nt;
+	const char *ifname, *seg1, *seg2, *arrow;
+	char *seg, ifnamebuf[IFNAMSIZ];
+	size_t if_idx, alen;
+	npf_addr_t addr;
+	in_port_t port;
+
+	/* Get the interface. */
+	if_idx = npf_rule_getinterface(rl);
+	ifname = if_indextoname(if_idx, ifnamebuf);
+
+	/* Get the translation address (and port, if used). */
+	npf_nat_getmap(nt, &addr, &alen, &port);
+	seg = npfctl_print_addrmask(alen, &addr, NPF_NO_NETMASK);
+	if (port) {
+		char *p;
+		easprintf(&p, "%s port %u", seg, port);
+		free(seg), seg = p;
+	}
+	seg1 = seg2 = "any";
+
+	/* Get the NAT type and determine the translation segment. */
+	switch (npf_nat_gettype(nt)) {
+	case NPF_NATIN:
+		arrow = "<-";
+		seg1 = seg;
+		break;
+	case NPF_NATOUT:
+		arrow = "->";
+		seg2 = seg;
+		break;
+	default:
+		assert(false);
+	}
+
+	/* Print out the NAT policy with the filter criteria. */
+	fprintf(ctx->fp, "map %s dynamic %s %s %s pass ",
+	    ifname, seg1, arrow, seg2);
+	npfctl_print_filter(ctx, rl);
+	fputs("\n", ctx->fp);
+	free(seg);
+}
+
+static void
+npfctl_print_table(npf_conf_info_t *ctx, nl_table_t *tl)
+{
+	const u_int id = npf_table_getid(tl);
+	const int type = npf_table_gettype(tl);
+
+	fprintf(ctx->fp, "table <%u> type %s\n", id,
+	    (type == NPF_TABLE_HASH) ? "hash" :
+	    (type == NPF_TABLE_TREE) ? "tree" :
+	    "unknown");
+}
+
+int
+npfctl_config_show(int fd)
+{
+	npf_conf_info_t *ctx = &stdout_ctx;
+	nl_config_t *ncf;
+	bool active, loaded;
+
+	if (fd) {
+		ncf = npf_config_retrieve(fd, &active, &loaded);
+		if (ncf == NULL) {
+			return errno;
+		}
+		fprintf(ctx->fp, "Filtering:\t%s\nConfiguration:\t%s\n",
+		    active ? "active" : "inactive",
+		    loaded ? "loaded" : "empty");
+		print_linesep(ctx);
+	} else {
+		npfctl_config_send(0, NULL);
+		ncf = npfctl_config_ref();
+		loaded = true;
+	}
+
+	if (loaded) {
+		nl_rule_t *rl;
+		nl_rproc_t *rp;
+		nl_nat_t *nt;
+		nl_table_t *tl;
+		u_int level;
+
+		while ((tl = npf_table_iterate(ncf)) != NULL) {
+			npfctl_print_table(ctx, tl);
+		}
+		print_linesep(ctx);
+
+		while ((rp = npf_rproc_iterate(ncf)) != NULL) {
+			const char *rpname = npf_rproc_getname(rp);
+			fprintf(ctx->fp, "procedure \"%s\"\n", rpname);
+		}
+		print_linesep(ctx);
+
+		while ((nt = npf_nat_iterate(ncf)) != NULL) {
+			npfctl_print_nat(ctx, nt);
+		}
+		print_linesep(ctx);
+
+		while ((rl = npf_rule_iterate(ncf, &level)) != NULL) {
+			print_indent(ctx, level);
+			npfctl_print_rule(ctx, rl);
+		}
+		print_linesep(ctx);
+	}
+	npf_config_destroy(ncf);
+	return 0;
+}
+
+int
+npfctl_ruleset_show(int fd, const char *ruleset_name)
+{
+	npf_conf_info_t *ctx = &stdout_ctx;
+	nl_config_t *ncf;
+	nl_rule_t *rl;
+	u_int level;
+	int error;
+
+	ncf = npf_config_create();
+	if ((error = _npf_ruleset_list(fd, ruleset_name, ncf)) != 0) {
+		return error;
+	}
+	while ((rl = npf_rule_iterate(ncf, &level)) != NULL) {
+		print_indent(ctx, level);
+		npfctl_print_rule(ctx, rl);
+	}
+	npf_config_destroy(ncf);
+	return error;
+}
--- a/usr.sbin/npf/npfctl/npf_var.h	Thu Sep 19 00:58:11 2013 +0000
+++ b/usr.sbin/npf/npfctl/npf_var.h	Thu Sep 19 01:04:45 2013 +0000
@@ -1,4 +1,4 @@
-/*	$NetBSD: npf_var.h,v 1.6 2012/11/26 20:34:28 rmind Exp $	*/
+/*	$NetBSD: npf_var.h,v 1.7 2013/09/19 01:04:45 rmind Exp $	*/
 
 /*-
  * Copyright (c) 2011-2012 The NetBSD Foundation, Inc.
@@ -48,8 +48,7 @@
 #define	NPFVAR_PROC_PARAM	8
 #define	NPFVAR_TCPFLAG		9
 #define	NPFVAR_ICMP		10
-#define	NPFVAR_ICMP6		11
-#define	NPFVAR_INTERFACE	12
+#define	NPFVAR_INTERFACE	11
 
 #ifdef _NPFVAR_PRIVATE
 static const char *npfvar_types[ ] = {
@@ -64,7 +63,6 @@
 	[NPFVAR_PROC_PARAM]	= "procedure-parameter",
 	[NPFVAR_TCPFLAG]	= "tcp-flag",
 	[NPFVAR_ICMP]		= "icmp",
-	[NPFVAR_ICMP6]		= "icmp6",
 	[NPFVAR_INTERFACE]	= "interface"
 };
 #endif
--- a/usr.sbin/npf/npfctl/npfctl.c	Thu Sep 19 00:58:11 2013 +0000
+++ b/usr.sbin/npf/npfctl/npfctl.c	Thu Sep 19 01:04:45 2013 +0000
@@ -1,4 +1,4 @@
-/*	$NetBSD: npfctl.c,v 1.37 2013/05/19 20:45:34 rmind Exp $	*/
+/*	$NetBSD: npfctl.c,v 1.38 2013/09/19 01:04:45 rmind Exp $	*/
 
 /*-
  * Copyright (c) 2009-2013 The NetBSD Foundation, Inc.
@@ -30,7 +30,7 @@
  */
 
 #include <sys/cdefs.h>
-__RCSID("$NetBSD: npfctl.c,v 1.37 2013/05/19 20:45:34 rmind Exp $");
+__RCSID("$NetBSD: npfctl.c,v 1.38 2013/09/19 01:04:45 rmind Exp $");
 
 #include <sys/ioctl.h>
 #include <sys/stat.h>
@@ -88,22 +88,23 @@
 	{	NULL,			0			}
 };
 
-static bool
-join(char *buf, size_t buflen, int count, char **args)
+bool
+join(char *buf, size_t buflen, int count, char **args, const char *sep)
 {
+	const u_int seplen = strlen(sep);
 	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;
+		len = p - s + seplen;
 		if (len >= buflen) {
 			return false;
 		}
 		buflen -= len;
-		*p = ' ';
-		s = p + 1;
+		strcpy(p, sep);
+		s = p + seplen;
 	}
 	*p = '\0';
 	return true;
@@ -233,7 +234,7 @@
 }
 
 char *
-npfctl_print_addrmask(int alen, npf_addr_t *addr, npf_netmask_t mask)
+npfctl_print_addrmask(int alen, const npf_addr_t *addr, npf_netmask_t mask)
 {
 	struct sockaddr_storage ss;
 	char *buf = ecalloc(1, 64);
@@ -261,7 +262,7 @@
 		assert(false);
 	}
 	len = sockaddr_snprintf(buf, 64, "%a", (struct sockaddr *)&ss);
-	if (mask) {
+	if (mask && mask != NPF_NO_NETMASK) {
 		snprintf(&buf[len], 64 - len, "/%u", mask);
 	}
 	return buf;
@@ -384,7 +385,7 @@
 	nl_rule_t *rl;
 
 	/* Get the rule string and parse it. */
-	if (!join(rule_string, sizeof(rule_string), argc, argv)) {
+	if (!join(rule_string, sizeof(rule_string), argc, argv, " ")) {
 		errx(EXIT_FAILURE, "command too long");
 	}
 	npfctl_parse_string(rule_string);
--- a/usr.sbin/npf/npfctl/npfctl.h	Thu Sep 19 00:58:11 2013 +0000
+++ b/usr.sbin/npf/npfctl/npfctl.h	Thu Sep 19 01:04:45 2013 +0000
@@ -1,4 +1,4 @@
-/*	$NetBSD: npfctl.h,v 1.29 2013/03/20 00:29:47 christos Exp $	*/
+/*	$NetBSD: npfctl.h,v 1.30 2013/09/19 01:04:45 rmind Exp $	*/
 
 /*-
  * Copyright (c) 2009-2013 The NetBSD Foundation, Inc.
@@ -38,6 +38,7 @@
 #include <assert.h>
 #include <util.h>
 
+#define	NPF_BPFCOP
 #include <net/npf_ncode.h>
 #include <net/npf.h>
 
@@ -103,12 +104,13 @@
 
 enum { NPFCTL_PARSE_FILE, NPFCTL_PARSE_STRING };
 
+bool		join(char *, size_t, int, char **, const char *);
 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);
+char *		npfctl_print_addrmask(int, const npf_addr_t *, npf_netmask_t);
 bool		npfctl_table_exists_p(const char *);
 int		npfctl_protono(const char *);
 in_port_t	npfctl_portno(const char *);
@@ -136,6 +138,47 @@
 		    const char *, const char *);
 
 /*
+ * BFF byte-code generation interface.
+ */
+
+#define	NPFCTL_USE_BPF	1
+
+typedef struct npf_bpf npf_bpf_t;
+
+#define	MATCH_DST	0x01
+#define	MATCH_SRC	0x02
+
+enum {
+	BM_IPVER,
+	BM_PROTO,
+	BM_SRC_CIDR,
+	BM_SRC_TABLE,
+	BM_DST_CIDR,
+	BM_DST_TABLE,
+	BM_SRC_PORTS,
+	BM_DST_PORTS,
+	BM_TCPFL,
+	BM_ICMP_TYPE,
+	BM_ICMP_CODE,
+};
+
+npf_bpf_t *npfctl_bpf_create(void);
+struct bpf_program *npfctl_bpf_complete(npf_bpf_t *);
+const void *npfctl_bpf_bmarks(npf_bpf_t *, size_t *);
+void	npfctl_bpf_destroy(npf_bpf_t *);
+
+void	npfctl_bpf_group(npf_bpf_t *);
+void	npfctl_bpf_endgroup(npf_bpf_t *);
+
+void	npfctl_bpf_proto(npf_bpf_t *, sa_family_t, int);
+void	npfctl_bpf_cidr(npf_bpf_t *, u_int, sa_family_t,
+	    const npf_addr_t *, const npf_netmask_t);
+void	npfctl_bpf_ports(npf_bpf_t *, u_int, in_port_t, in_port_t);
+void	npfctl_bpf_tcpfl(npf_bpf_t *, uint8_t, uint8_t);
+void	npfctl_bpf_icmp(npf_bpf_t *, int, int);
+void	npfctl_bpf_table(npf_bpf_t *, u_int, u_int);
+
+/*
  * N-code generation interface.
  */
 
--- a/usr.sbin/npf/npftest/libnpftest/Makefile	Thu Sep 19 00:58:11 2013 +0000
+++ b/usr.sbin/npf/npftest/libnpftest/Makefile	Thu Sep 19 01:04:45 2013 +0000
@@ -13,6 +13,7 @@
 
 SRCS+=		npf_nbuf_test.c
 SRCS+=		npf_processor_test.c
+SRCS+=		npf_bpf_test.c
 SRCS+=		npf_table_test.c
 SRCS+=		npf_state_test.c
 SRCS+=		npf_rule_test.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr.sbin/npf/npftest/libnpftest/npf_bpf_test.c	Thu Sep 19 01:04:45 2013 +0000
@@ -0,0 +1,122 @@
+/*	$NetBSD: npf_bpf_test.c,v 1.1 2013/09/19 01:04:45 rmind Exp $	*/
+
+/*-
+ * Copyright (c) 2013 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by 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 test of BPF coprocessor.
+ */
+
+#include <sys/types.h>
+#include <sys/endian.h>
+
+#define	NPF_BPFCOP
+#include "npf_impl.h"
+#include "npf_test.h"
+
+static struct mbuf *
+fill_packet(int proto)
+{
+	struct mbuf *m;
+	struct ip *ip;
+	struct tcphdr *th;
+
+	m = mbuf_construct(IPPROTO_TCP);
+	th = mbuf_return_hdrs(m, false, &ip);
+	ip->ip_src.s_addr = inet_addr("192.168.2.100");
+	ip->ip_dst.s_addr = inet_addr("10.0.0.1");
+	th->th_sport = htons(15000);
+	th->th_dport = htons(80);
+	return m;
+}
+
+static int
+test_bpf_code(const void *code)
+{
+	npf_cache_t npc = { .npc_info = 0 };
+	const void *dummy_ifp = (void *)0xdeadbeef;
+	struct mbuf *m;
+	nbuf_t nbuf;
+	int ret;
+
+	/* Layer 3 (IP + TCP). */
+	m = fill_packet(IPPROTO_TCP);
+	nbuf_init(&nbuf, m, dummy_ifp);
+	npf_cache_all(&npc, &nbuf);
+
+	ret = npf_bpf_filter(&npc, &nbuf, code, NULL);
+	m_freem(m);
+
+	return ret;
+}
+
+static uint32_t
+npf_bpfcop_run(u_int reg)
+{
+	struct bpf_insn insns_npf_bpfcop[] = {
+		BPF_STMT(BPF_MISC+BPF_COP, NPF_COP_L3),
+		BPF_STMT(BPF_LD+BPF_W+BPF_MEM, reg),
+		BPF_STMT(BPF_RET+BPF_A, 0),
+	};
+	return test_bpf_code(&insns_npf_bpfcop);
+}
+
+static bool
+npf_bpfcop_test(void)
+{
+	bool fail = false;
+
+	/* A <- IP version (4 or 6) */
+	struct bpf_insn insns_ipver[] = {
+		BPF_STMT(BPF_MISC+BPF_COP, NPF_COP_L3),
+		BPF_STMT(BPF_RET+BPF_A, 0),
+	};
+	fail |= (test_bpf_code(&insns_ipver) != IPVERSION);
+
+	/* BPF_MW_IPVERI <- IP version */
+	fail |= (npf_bpfcop_run(BPF_MW_IPVER) != IPVERSION);
+
+	/* BPF_MW_L4OFF <- L4 header offset */
+	fail |= (npf_bpfcop_run(BPF_MW_L4OFF) != sizeof(struct ip));
+
+	/* BPF_MW_L4PROTO <- L4 protocol */
+	fail |= (npf_bpfcop_run(BPF_MW_L4PROTO) != IPPROTO_TCP);
+
+	return fail;
+}
+
+bool
+npf_bpf_test(bool verbose)
+{
+	bool fail = false;
+
+	fail |= npf_bpfcop_test();
+
+	return !fail;
+}
--- a/usr.sbin/npf/npftest/libnpftest/npf_nat_test.c	Thu Sep 19 00:58:11 2013 +0000
+++ b/usr.sbin/npf/npftest/libnpftest/npf_nat_test.c	Thu Sep 19 01:04:45 2013 +0000
@@ -1,4 +1,4 @@
-/*	$NetBSD: npf_nat_test.c,v 1.2 2012/12/24 19:05:47 rmind Exp $	*/
+/*	$NetBSD: npf_nat_test.c,v 1.3 2013/09/19 01:04:46 rmind Exp $	*/
 
 /*
  * NPF NAT test.
@@ -154,6 +154,9 @@
 		printf(" dst %s (%d)\n",
 		    inet_ntoa(ip->ip_dst), ntohs(uh->uh_dport));
 	}
+	if (error != t->ret) {
+		return false;
+	}
 
 	const bool forw = t->di == PFIL_OUT;
 	const char *saddr = forw ? t->taddr : t->src;
@@ -166,8 +169,7 @@
 	defect |= sport != ntohs(uh->uh_sport);
 	defect |= nmatch_addr(daddr, &ip->ip_dst);
 	defect |= dport != ntohs(uh->uh_dport);
-
-	return !defect && error == t->ret;
+	return !defect;
 }
 
 static struct mbuf *
--- a/usr.sbin/npf/npftest/libnpftest/npf_rule_test.c	Thu Sep 19 00:58:11 2013 +0000
+++ b/usr.sbin/npf/npftest/libnpftest/npf_rule_test.c	Thu Sep 19 01:04:45 2013 +0000
@@ -1,4 +1,4 @@
-/*	$NetBSD: npf_rule_test.c,v 1.7 2013/02/18 23:09:20 rmind Exp $	*/
+/*	$NetBSD: npf_rule_test.c,v 1.8 2013/09/19 01:04:46 rmind Exp $	*/
 
 /*
  * NPF ruleset test.
@@ -101,9 +101,9 @@
 }
 
 static int
-npf_test_first(bool verbose)
+npf_test_case(u_int i, bool verbose)
 {
-	const struct test_case *t = &test_cases[0];
+	const struct test_case *t = &test_cases[i];
 	ifnet_t *ifp = ifunit(t->ifname);
 	int error;
 
@@ -159,7 +159,11 @@
 		fail |= (serror != t->stateful_ret || error != t->ret);
 	}
 
-	error = npf_test_first(verbose);
+	/*
+	 * Test dynamic NPF rules.
+	 */
+
+	error = npf_test_case(0, verbose);
 	assert(error == RESULT_PASS);
 
 	npf_config_enter();
@@ -169,7 +173,7 @@
 	error = npf_ruleset_add(rlset, "test-rules", rl);
 	fail |= error != 0;
 
-	error = npf_test_first(verbose);
+	error = npf_test_case(0, verbose);
 	fail |= (error != RESULT_BLOCK);
 
 	id = npf_rule_getid(rl);
@@ -178,7 +182,7 @@
 
 	npf_config_exit();
 
-	error = npf_test_first(verbose);
+	error = npf_test_case(0, verbose);
 	fail |= (error != RESULT_PASS);
 
 	return !fail;
--- a/usr.sbin/npf/npftest/libnpftest/npf_test.h	Thu Sep 19 00:58:11 2013 +0000
+++ b/usr.sbin/npf/npftest/libnpftest/npf_test.h	Thu Sep 19 01:04:45 2013 +0000
@@ -40,6 +40,7 @@
 
 bool		npf_nbuf_test(bool);
 bool		npf_processor_test(bool);
+bool		npf_bpf_test(bool);
 bool		npf_table_test(bool);
 bool		npf_state_test(bool);
 
--- a/usr.sbin/npf/npftest/npftest.c	Thu Sep 19 00:58:11 2013 +0000
+++ b/usr.sbin/npf/npftest/npftest.c	Thu Sep 19 01:04:45 2013 +0000
@@ -1,4 +1,4 @@
-/*	$NetBSD: npftest.c,v 1.8 2012/09/14 15:37:03 joerg Exp $	*/
+/*	$NetBSD: npftest.c,v 1.9 2013/09/19 01:04:45 rmind Exp $	*/
 
 /*
  * NPF testing framework.
@@ -221,6 +221,12 @@
 			tname_matched = true;
 		}
 
+		if (!testname || strcmp("bpf", testname) == 0) {
+			ok = rumpns_npf_bpf_test(verbose);
+			fail |= result("bpf", ok);
+			tname_matched = true;
+		}
+
 		if (!testname || strcmp("processor", testname) == 0) {
 			ok = rumpns_npf_processor_test(verbose);
 			fail |= result("processor", ok);
--- a/usr.sbin/npf/npftest/npftest.h	Thu Sep 19 00:58:11 2013 +0000
+++ b/usr.sbin/npf/npftest/npftest.h	Thu Sep 19 01:04:45 2013 +0000
@@ -19,6 +19,7 @@
 
 bool		rumpns_npf_nbuf_test(bool);
 bool		rumpns_npf_processor_test(bool);
+bool		rumpns_npf_bpf_test(bool);
 bool		rumpns_npf_table_test(bool);
 bool		rumpns_npf_state_test(bool);