Full rewrite of npfctl(8) parser and rework of n-code generation part. trunk
authorrmind <rmind@NetBSD.org>
Sun, 08 Jan 2012 21:34:21 +0000
branchtrunk
changeset 208007 43b56e84fe94
parent 208006 6c624845a1d9
child 208008 0f192ecd6511
Full rewrite of npfctl(8) parser and rework of n-code generation part. Fixes most of the known bugs and issues with the utility. Note: rule procedures are not yet (as we want to make them fully modular). Huge thanks to Martin Husemann who wrote the parser and Christos Zoulas who wrote intermediate structures and helped to complete the work.
usr.sbin/npf/npfctl/Makefile
usr.sbin/npf/npfctl/npf_build.c
usr.sbin/npf/npfctl/npf_data.c
usr.sbin/npf/npfctl/npf_ncgen.c
usr.sbin/npf/npfctl/npf_parse.y
usr.sbin/npf/npfctl/npf_parser.c
usr.sbin/npf/npfctl/npf_scan.l
usr.sbin/npf/npfctl/npf_var.c
usr.sbin/npf/npfctl/npf_var.h
usr.sbin/npf/npfctl/npfctl.c
usr.sbin/npf/npfctl/npfctl.h
--- a/usr.sbin/npf/npfctl/Makefile	Sun Jan 08 21:20:40 2012 +0000
+++ b/usr.sbin/npf/npfctl/Makefile	Sun Jan 08 21:34:21 2012 +0000
@@ -1,14 +1,18 @@
-# $NetBSD: Makefile,v 1.4 2011/02/04 00:19:51 rmind Exp $
+# $NetBSD: Makefile,v 1.5 2012/01/08 21:34:21 rmind Exp $
 
 PROG=		npfctl
 MAN=		npfctl.8 npf.conf.5
 
-SRCS=		npfctl.c npf_parser.c npf_data.c npf_ncgen.c
+SRCS=		npfctl.c npf_var.c npf_data.c npf_ncgen.c npf_build.c
 
-LDADD+=		-lnpf -lprop
+CPPFLAGS+=	-I${.CURDIR}
+SRCS+=		npf_scan.l npf_parse.y
+YHEADER=	1
+
+LDADD+=		-lnpf -lprop -ly
 DPADD+=		${LIBNPF} ${LIBPROP}
 
 WARNS?=		4
-NOLINT=		# defined (note: deliberately)
+NOLINT=		# disabled (note: deliberately)
 
 .include <bsd.prog.mk>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr.sbin/npf/npfctl/npf_build.c	Sun Jan 08 21:34:21 2012 +0000
@@ -0,0 +1,495 @@
+/*	$NetBSD: npf_build.c,v 1.1 2012/01/08 21:34:21 rmind Exp $	*/
+
+/*-
+ * Copyright (c) 2011-2012 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.
+ */
+
+/*
+ * npfctl(8) building of the configuration.
+ */
+
+#include <sys/cdefs.h>
+__RCSID("$NetBSD: npf_build.c,v 1.1 2012/01/08 21:34:21 rmind Exp $");
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+
+#include <stdlib.h>
+#include <inttypes.h>
+#include <string.h>
+#include <assert.h>
+#include <err.h>
+
+#include "npfctl.h"
+
+static nl_config_t *		npf_conf = NULL;
+static nl_rule_t *		current_group = NULL;
+static bool			npf_debug = false;
+static bool			defgroup_set = false;
+
+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;
+}
+
+int
+npfctl_config_send(int fd)
+{
+	int error;
+
+	if (!fd) {
+		_npf_config_setsubmit(npf_conf, "./npf.plist");
+	}
+	if (!defgroup_set) {
+		errx(EXIT_FAILURE, "default group was not defined");
+	}
+	error = npf_config_submit(npf_conf, fd);
+	npf_config_destroy(npf_conf);
+	return error;
+}
+
+int
+npfctl_config_flush(int fd)
+{
+	int ret;
+
+	/* Pass empty configuration to flush. */
+	npfctl_config_init(false);
+	defgroup_set = true;
+	ret = npfctl_config_send(fd);
+	if (ret) {
+		return ret;
+	}
+	return npf_sessions_send(fd, NULL);
+}
+
+bool
+npfctl_table_exists_p(const char *id)
+{
+	return npf_table_exists_p(npf_conf, atoi(id));
+}
+
+static in_port_t *
+npfctl_get_singleport(const npfvar_t *vp)
+{
+	port_range_t *pr;
+
+	if (npfvar_get_count(vp) > 1) {
+		yyerror("multiple ports are not valid");
+	}
+	pr = npfvar_get_data(vp, NPFVAR_PORT_RANGE, 0);
+	if (pr->pr_start != pr->pr_end) {
+		yyerror("port range is not valid");
+	}
+	return &pr->pr_start;
+}
+
+static fam_addr_mask_t *
+npfctl_get_singlefam(const npfvar_t *vp)
+{
+	if (npfvar_get_count(vp) > 1) {
+		yyerror("multiple addresses are not valid");
+	}
+	return npfvar_get_data(vp, NPFVAR_FAM, 0);
+}
+
+static void
+npfctl_build_fam(nc_ctx_t *nc, sa_family_t family,
+    fam_addr_mask_t *fam, int opts)
+{
+	/*
+	 * If family is specified, address does not match it and the
+	 * address is extracted from the interface, then simply ignore.
+	 * Otherwise, address of invalid family was passed manually.
+	 */
+	if (family != AF_UNSPEC && family != fam->fam_family) {
+		if (!fam->fam_interface) {
+			yyerror("specified address is not of the required "
+			    "family %d", family);
+		}
+		return;
+	}
+
+	/*
+	 * Optimise 0.0.0.0/0 case to be NOP.  Otherwise, address with
+	 * zero mask would never match and therefore is not valid.
+	 */
+	if (fam->fam_mask == 0) {
+		npf_addr_t zero;
+		memset(&zero, 0, sizeof(npf_addr_t));
+		if (memcmp(&fam->fam_addr, &zero, sizeof(npf_addr_t))) {
+			yyerror("filter criterion would never match");
+		}
+		return;
+	}
+
+	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);
+	}
+}
+
+static void
+npfctl_build_vars(nc_ctx_t *nc, sa_family_t family, npfvar_t *vars, int opts)
+{
+	const int type = npfvar_get_type(vars);
+	size_t i;
+
+	npfctl_ncgen_group(nc);
+	for (i = 0; i < npfvar_get_count(vars); i++) {
+		void *data = npfvar_get_data(vars, type, i);
+		assert(data != NULL);
+
+		switch (type) {
+		case NPFVAR_FAM: {
+			fam_addr_mask_t *fam = data;
+			npfctl_build_fam(nc, 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);
+			}
+			break;
+		}
+		case NPFVAR_TABLE: {
+			u_int tid = atoi(data);
+			npfctl_gennc_tbl(nc, opts, tid);
+			break;
+		}
+		default:
+			assert(false);
+		}
+	}
+	npfctl_ncgen_endgroup(nc);
+}
+
+static int
+npfctl_build_proto(nc_ctx_t *nc, const opt_proto_t *op)
+{
+	const npfvar_t *popts = op->op_opts;
+	int pflag = 0;
+
+	switch (op->op_proto) {
+	case IPPROTO_TCP:
+		pflag = NC_MATCH_TCP;
+		if (!popts) {
+			break;
+		}
+		assert(npfvar_get_count(popts) == 2);
+
+		/* 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);
+		break;
+	case IPPROTO_UDP:
+		pflag = NC_MATCH_UDP;
+		break;
+	case IPPROTO_ICMP:
+		/*
+		 * Build ICMP block.
+		 */
+		assert(npfvar_get_count(popts) == 2);
+
+		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);
+		break;
+	case -1:
+		pflag = NC_MATCH_TCP | NC_MATCH_UDP;
+		break;
+	default:
+		yyerror("protocol %d is not supported", op->op_proto);
+	}
+	return pflag;
+}
+
+static bool
+npfctl_build_ncode(nl_rule_t *rl, sa_family_t family, const opt_proto_t *op,
+    const filt_opts_t *fopts, bool invert)
+{
+	nc_ctx_t *nc;
+	void *code;
+	size_t len;
+
+	if (family == AF_UNSPEC && op->op_proto == -1 &&
+	    op->op_opts == NULL && !fopts->fo_from && !fopts->fo_to &&
+	    !fopts->fo_from_port_range && !fopts->fo_to_port_range)
+		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();
+
+	/* Build IP address blocks. */
+	npfctl_build_vars(nc, family, fopts->fo_from, srcflag);
+	npfctl_build_vars(nc, family, fopts->fo_to, dstflag);
+
+	/* Build layer 4 protocol blocks. */
+	int pflag = npfctl_build_proto(nc, op);
+
+	/* Build port-range blocks. */
+	if (fopts->fo_from_port_range) {
+		npfctl_build_vars(nc, family, fopts->fo_from_port_range,
+		    srcflag | pflag);
+	}
+	if (fopts->fo_to_port_range) {
+		npfctl_build_vars(nc, family, fopts->fo_to_port_range,
+		    dstflag | pflag);
+	}
+
+	/*
+	 * Complete n-code (destroys the context) and pass to the rule.
+	 */
+	code = npfctl_ncgen_complete(nc, &len);
+	if (npf_debug) {
+		extern int yylineno;
+		printf("RULE AT LINE %d\n", yylineno - 1);
+		npfctl_ncgen_print(code, len);
+	}
+	if (npf_rule_setcode(rl, NPF_CODE_NCODE, code, len) == -1) {
+		errx(EXIT_FAILURE, "npf_rule_setcode failed");
+	}
+	free(code);
+	return true;
+}
+
+/*
+ * npfctl_build_rproc: create and insert a rule procedure.
+ */
+void
+npfctl_build_rproc(const char *name, npfvar_t *var)
+{
+	nl_rproc_t *rp;
+
+	rp = npf_rproc_create(name);
+	if (rp == NULL) {
+		errx(EXIT_FAILURE, "npf_rproc_create failed");
+	}
+	npf_rproc_insert(npf_conf, rp);
+}
+
+/*
+ * npfctl_build_group: create a group, insert into the global ruleset
+ * and update the current group pointer.
+ */
+void
+npfctl_build_group(const char *name, int attr, u_int if_idx)
+{
+	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) {
+		attr |= attr_di;
+	}
+	attr |= (NPF_RULE_PASS | NPF_RULE_FINAL);
+
+	rl = npf_rule_create(name, attr, if_idx);
+	npf_rule_insert(npf_conf, NULL, rl, NPF_PRI_NEXT);
+	current_group = rl;
+}
+
+/*
+ * npfctl_build_rule: create a rule, build n-code from filter options,
+ * if any, and insert into the ruleset of current group.
+ */
+void
+npfctl_build_rule(int attr, u_int if_idx, sa_family_t family,
+    const opt_proto_t *op, const filt_opts_t *fopts, const char *rproc)
+{
+	nl_rule_t *rl;
+
+	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);
+	}
+	assert(current_group != NULL);
+	npf_rule_insert(npf_conf, current_group, rl, NPF_PRI_NEXT);
+}
+
+/*
+ * npfctl_build_nat: create a NAT policy of a specified type with a
+ * given filter options.
+ */
+void
+npfctl_build_nat(int type, u_int if_idx, const filt_opts_t *fopts,
+    npfvar_t *var1, npfvar_t *var2)
+{
+	opt_proto_t op = { .op_proto = -1, .op_opts = NULL };
+	nl_nat_t *nat;
+	fam_addr_mask_t *ai;
+
+	assert(type != 0 && if_idx != 0);
+	assert(fopts != NULL && var1 != NULL);
+
+	ai = npfctl_get_singlefam(var1);
+	assert(ai != NULL);
+	if (ai->fam_family != AF_INET) {
+		yyerror("IPv6 NAT is not supported");
+	}
+
+	switch (type) {
+	case NPFCTL_RDR: {
+		/*
+		 * Redirection: an inbound NAT with a specific port.
+		 */
+		in_port_t *port = npfctl_get_singleport(var2);
+		nat = npf_nat_create(NPF_NATIN, NPF_NAT_PORTS,
+		    if_idx, &ai->fam_addr, ai->fam_family, *port);
+		break;
+	}
+	case NPFCTL_BINAT: {
+		/*
+		 * Bi-directional NAT: a combination of inbound NAT and
+		 * outbound NAT policies.  Note that the translation address
+		 * is local IP and filter criteria is inverted accordingly.
+		 */
+		fam_addr_mask_t *tai = npfctl_get_singlefam(var2);
+		assert(tai != NULL);
+		if (ai->fam_family != AF_INET) {
+			yyerror("IPv6 NAT is not supported");
+		}
+		nat = npf_nat_create(NPF_NATIN, 0, if_idx,
+		    &tai->fam_addr, tai->fam_family, 0);
+		npfctl_build_ncode(nat, AF_INET, &op, fopts, true);
+		npf_nat_insert(npf_conf, nat, NPF_PRI_NEXT);
+		/* FALLTHROUGH */
+	}
+	case NPFCTL_NAT: {
+		/*
+		 * Traditional NAPT: an outbound NAT policy with port.
+		 * If this is another hald for bi-directional NAT, then
+		 * no port translation with mapping.
+		 */
+		nat = npf_nat_create(NPF_NATOUT, type == NPFCTL_NAT ?
+		    (NPF_NAT_PORTS | NPF_NAT_PORTMAP) : 0,
+		    if_idx, &ai->fam_addr, ai->fam_family, 0);
+		break;
+	}
+	default:
+		assert(false);
+	}
+	npfctl_build_ncode(nat, AF_INET, &op, fopts, false);
+	npf_nat_insert(npf_conf, nat, NPF_PRI_NEXT);
+}
+
+/*
+ * npfctl_fill_table: fill NPF table with entries from a specified file.
+ */
+static void
+npfctl_fill_table(nl_table_t *tl, const char *fname)
+{
+	char *buf = NULL;
+	int l = 0;
+	FILE *fp;
+	size_t n;
+
+	fp = fopen(fname, "r");
+	if (fp == NULL) {
+		err(EXIT_FAILURE, "open '%s'", fname);
+	}
+	while (l++, getline(&buf, &n, fp) != -1) {
+		fam_addr_mask_t *fam;
+
+		if (*buf == '\n' || *buf == '#') {
+			continue;
+		}
+		fam = npfctl_parse_cidr(buf);
+		if (fam == NULL) {
+			errx(EXIT_FAILURE, "%s:%d: invalid table entry",
+			    fname, l);
+		}
+
+		/* Create and add a table entry. */
+		npf_table_add_entry(tl, &fam->fam_addr, fam->fam_mask);
+	}
+	if (buf != NULL) {
+		free(buf);
+	}
+}
+
+/*
+ * npfctl_build_table: create an NPF table, add to the configuration and,
+ * if required, fill with contents from a file.
+ */
+void
+npfctl_build_table(const char *tid, u_int type, const char *fname)
+{
+	nl_table_t *tl;
+	u_int id;
+
+	id = atoi(tid);
+	tl = npf_table_create(id, type);
+	assert(tl != NULL);
+
+	if (npf_table_insert(npf_conf, tl)) {
+		errx(EXIT_FAILURE, "table '%d' is already defined\n", id);
+	}
+
+	if (fname) {
+		npfctl_fill_table(tl, fname);
+	}
+}
--- a/usr.sbin/npf/npfctl/npf_data.c	Sun Jan 08 21:20:40 2012 +0000
+++ b/usr.sbin/npf/npfctl/npf_data.c	Sun Jan 08 21:34:21 2012 +0000
@@ -1,7 +1,7 @@
-/*	$NetBSD: npf_data.c,v 1.9 2011/11/05 19:19:29 jakllsch Exp $	*/
+/*	$NetBSD: npf_data.c,v 1.10 2012/01/08 21:34:21 rmind Exp $	*/
 
 /*-
- * Copyright (c) 2009-2011 The NetBSD Foundation, Inc.
+ * Copyright (c) 2009-2012 The NetBSD Foundation, Inc.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -27,484 +27,427 @@
  */
 
 /*
- * npfctl(8) helper routines.
+ * npfctl(8) data manipulation and helper routines.
  */
 
 #include <sys/cdefs.h>
-__RCSID("$NetBSD: npf_data.c,v 1.9 2011/11/05 19:19:29 jakllsch Exp $");
+__RCSID("$NetBSD: npf_data.c,v 1.10 2012/01/08 21:34:21 rmind Exp $");
 
 #include <sys/types.h>
-#include <sys/socket.h>
-#include <sys/ioctl.h>
+#include <sys/null.h>
+
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#define ICMP_STRINGS
+#include <netinet/ip_icmp.h>
+#include <netinet/tcp.h>
 #include <net/if.h>
-#include <netinet/tcp.h>
-
-#include <arpa/inet.h>
 
 #include <stdlib.h>
 #include <string.h>
-#include <unistd.h>
-#include <ctype.h>
 #include <err.h>
+#include <errno.h>
 #include <ifaddrs.h>
 #include <netdb.h>
-#include <assert.h>
 
 #include "npfctl.h"
 
 static struct ifaddrs *		ifs_list = NULL;
-nl_config_t *			npf_conf = NULL;
 
-void
-npfctl_init_data(void)
+unsigned long
+npfctl_find_ifindex(const char *ifname)
 {
+	return if_nametoindex(ifname);
+}
 
-	npf_conf = npf_config_create();
-	if (npf_conf == NULL) {
-		errx(EXIT_FAILURE, "npf_config_create");
+static bool
+npfctl_copy_address(sa_family_t fam, npf_addr_t *addr, const void *ptr)
+{
+	switch (fam) {
+	case AF_INET: {
+		const struct sockaddr_in *sin = ptr;
+		memcpy(addr, &sin->sin_addr, sizeof(sin->sin_addr));
+		return true;
 	}
-	if (getifaddrs(&ifs_list) == -1) {
-		err(EXIT_FAILURE, "getifaddrs");
+	case AF_INET6: {
+		const struct sockaddr_in6 *sin6 = ptr;
+		memcpy(addr, &sin6->sin6_addr, sizeof(sin6->sin6_addr));
+		return true;
+	}
+	default:
+		yyerror("unknown address family %u", fam);
+		return false;
 	}
 }
 
-int
-npfctl_ioctl_send(int fd)
+static bool
+npfctl_parse_fam_addr(const char *name, sa_family_t *fam, npf_addr_t *addr)
 {
-	int error = npf_config_submit(npf_conf, fd);
-	npf_config_destroy(npf_conf);
-	return error;
-}
+	static const struct addrinfo hint = {
+		.ai_family = AF_UNSPEC,
+		.ai_flags = AI_NUMERICHOST
+	};
+	struct addrinfo *ai;
+	int ret;
 
-/*
- * Helper routines:
- *
- *	npfctl_getif() - get interface addresses and index number from name.
- *	npfctl_parse_v4mask() - parse address/mask integers from CIDR block.
- *	npfctl_parse_port() - parse port number (which may be a service name).
- *	npfctl_parse_tcpfl() - parse TCP flags.
- */
-
-struct ifaddrs *
-npfctl_getif(char *ifname, unsigned int *if_idx, bool reqaddr, sa_family_t addrtype)
-{
-	struct ifaddrs *ifent;
-	struct sockaddr_in *sin;
-
-	for (ifent = ifs_list; ifent != NULL; ifent = ifent->ifa_next) {
-		sin = (struct sockaddr_in *)ifent->ifa_addr;
-		if (sin->sin_family != addrtype && reqaddr)
-			continue;
-		if (strcmp(ifent->ifa_name, ifname) == 0)
-			break;
+	ret = getaddrinfo(name, NULL, &hint, &ai);
+	if (ret) {
+		yyerror("cannot parse '%s' (%s)", name, gai_strerror(ret));
+		return false;
 	}
-	if (ifent) {
-		*if_idx = if_nametoindex(ifname);
+	if (fam) {
+		*fam = ai->ai_family;
 	}
-	return ifent;
-}
-
-bool
-npfctl_parse_port(char *ostr, bool *range, in_port_t *fport, in_port_t *tport)
-{
-	char *str = xstrdup(ostr), *sep;
-
-	*range = false;
-	if ((sep = strchr(str, ':')) != NULL) {
-		/* Port range (only numeric). */
-		*range = true;
-		*sep = '\0';
-
-	} else if (isalpha((unsigned char)*str)) {
-		struct servent *se;
-
-		se = getservbyname(str, NULL);
-		if (se == NULL) {
-			free(str);
-			return false;
-		}
-		*fport = se->s_port;
-	} else {
-		*fport = htons(atoi(str));
+	if (!npfctl_copy_address(*fam, addr, ai->ai_addr)) {
+		return false;
 	}
-	*tport = sep ? htons(atoi(sep + 1)) : *fport;
-	free(str);
+	freeaddrinfo(ai);
 	return true;
 }
 
-void
-npfctl_create_mask(sa_family_t family, u_int length, npf_addr_t *omask)
+static bool
+npfctl_parse_mask(const char *s, sa_family_t fam, npf_netmask_t *mask)
 {
-	uint32_t part;
-	uint32_t *mask = (uint32_t*)omask;
-
-	memset(omask, 0, sizeof(npf_addr_t));
-	if (family == AF_INET) {
-		part = htonl(0xffffffff << (32 - length));
-		memcpy(mask, &part, 4);
-	} else if (family == AF_INET6) {
-		while (length > 32) {
-			part = htonl(0xffffffff);
-			memcpy(mask, &part, 4);
-			mask += 1;
-			length -= 32;
-		}
-		part = htonl(0xffffffff << (32 - length));
-		memcpy(mask, &part, 4);
-	}
-}
-
-sa_family_t
-npfctl_get_addrfamily(const char *ostr)
-{
-	struct addrinfo hint, *res = NULL;
-	int ret; 
-	char *str = xstrdup(ostr);
-	char *p = strchr(str, '/');
-	sa_family_t family;
+	char *ep = NULL;
+	npf_addr_t addr;
+	uint8_t *ap;
 
-	if (p)
-	    *p = '\0';
-	memset(&hint, '\0', sizeof(hint));
-	hint.ai_family = PF_UNSPEC;
-	hint.ai_flags = AI_NUMERICHOST;
-	ret = getaddrinfo(str, NULL, &hint, &res);
-	if (ret) {
-		family = AF_UNSPEC;
-	} else {
-		family = res->ai_family;
-		freeaddrinfo(res);
-	}
-	free(str);
-	return family;
-}
-
-sa_family_t
-npfctl_parse_cidr(char *str, sa_family_t addrfamily, npf_addr_t *addr, npf_netmask_t *mask)
-{
-
-	if (strcmp(str, "any") == 0) {
-		memset(addr, 0, sizeof(npf_addr_t));
-		memset(mask, 0, sizeof(npf_netmask_t));
-	} else if (isalpha((unsigned char)*str)) {
-		/* TODO: handle multiple addresses per interface */
-		struct ifaddrs *ifa;
-		struct sockaddr_in *sin;
-		u_int idx;
-		if ((ifa = npfctl_getif(str, &idx, true, AF_INET)) == NULL) {
-			errx(EXIT_FAILURE, "invalid interface '%s'", str);
-		}
-		/* Interface address. */
-		sin = (struct sockaddr_in *)ifa->ifa_addr;
-		memcpy(addr, &(sin->sin_addr.s_addr), sizeof(struct in_addr));
-		//v4mask = 0xffffffff; - TODO!
-	} else {
-		char *p = strchr(str, '/');
-		if (p != NULL) {
-			*p++ = '\0';
-			*mask = atoi(p);
-		} else {
-			if (addrfamily == AF_INET)
-				*mask = 32;
-			else
-				*mask = 128;
-		}
-		memset(addr, 0, sizeof(npf_addr_t));
-		int ret = inet_pton(addrfamily, str, addr);
-		if (ret != 1) {
-			printf("TODO: error");
-		}
+	if (s) {
+		errno = 0;
+		*mask = (npf_netmask_t)strtol(s, &ep, 0);
+		if (*ep == '\0' && s != ep && errno != ERANGE)
+			return true;
+		if (!npfctl_parse_fam_addr(s, &fam, &addr))
+			return false;
 	}
 
-	return addrfamily;
-}
-
-static bool
-npfctl_parse_tcpfl(char *s, uint8_t *tfl, uint8_t *tfl_mask)
-{
-	uint8_t tcpfl = 0;
-	bool mask = false;
+	switch (fam) {
+	case AF_INET:
+		*mask = 32;
+		break;
+	case AF_INET6:
+		*mask = 128;
+		break;
+	default:
+		yyerror("unknown address family %u", fam);
+		return false;
+	}
 
-	while (*s) {
-		switch (*s) {
-		case 'F': tcpfl |= TH_FIN; break;
-		case 'S': tcpfl |= TH_SYN; break;
-		case 'R': tcpfl |= TH_RST; break;
-		case 'P': tcpfl |= TH_PUSH; break;
-		case 'A': tcpfl |= TH_ACK; break;
-		case 'U': tcpfl |= TH_URG; break;
-		case 'E': tcpfl |= TH_ECE; break;
-		case 'W': tcpfl |= TH_CWR; break;
-		case '/':
-			*s = '\0';
-			*tfl = tcpfl;
-			tcpfl = 0;
-			mask = true;
-			break;
-		default:
-			return false;
+	if (ep == NULL) {
+		return true;
+	}
+	ap = addr.s6_addr + (*mask / 8) - 1;
+	while (ap >= addr.s6_addr) {
+		for (int j = 8; j > 0; j--) {
+			if (*ap & 1)
+				return true;
+			*ap >>= 1;
+			(*mask)--;
+			if (*mask == 0)
+				return true;
 		}
-		s++;
+		ap--;
 	}
-	if (!mask) {
-		*tfl = tcpfl;
-	}
-	*tfl_mask = tcpfl;
 	return true;
 }
 
-void
-npfctl_fill_table(nl_table_t *tl, char *fname)
+/*
+ * npfctl_parse_fam_addr_mask: return address family, address and mask.
+ *
+ * => Mask is optional and can be NULL.
+ * => Returns true on success or false if unable to parse.
+ */
+npfvar_t *
+npfctl_parse_fam_addr_mask(const char *addr, const char *mask,
+    unsigned long *nummask)
 {
-	char *buf;
-	FILE *fp;
-	size_t n;
-	int l;
+	npfvar_t *vp = npfvar_create(".addr");
+	fam_addr_mask_t fam;
+
+	memset(&fam, 0, sizeof(fam));
+
+	if (!npfctl_parse_fam_addr(addr, &fam.fam_family, &fam.fam_addr))
+		goto out;
 
-	fp = fopen(fname, "r");
-	if (fp == NULL) {
-		err(EXIT_FAILURE, "open '%s'", fname);
+	/*
+	 * Note: both mask and nummask may be NULL.  In such case,
+	 * npfctl_parse_mask() will handle and will set full mask.
+	 */
+	if (nummask) {
+		fam.fam_mask = *nummask;
+	} else if (!npfctl_parse_mask(mask, fam.fam_family, &fam.fam_mask)) {
+		goto out;
 	}
-	l = 1;
-	buf = NULL;
-	while (getline(&buf, &n, fp) != -1) {
-		npf_addr_t addr;
-		npf_netmask_t mask;
+
+	if (!npfvar_add_element(vp, NPFVAR_FAM, &fam, sizeof(fam)))
+		goto out;
 
-		if (*buf == '\n' || *buf == '#')
-			continue;
+	return vp;
+out:
+	npfvar_destroy(vp);
+	return NULL;
+}
 
-		if (!npfctl_parse_cidr(buf, npfctl_get_addrfamily(buf), &addr, &mask)) {
-			errx(EXIT_FAILURE, "invalid table entry at line %d", l);
-		}
+npfvar_t *
+npfctl_parse_table_id(const char *id)
+{
+	npfvar_t *vp;
 
-		/* Create and add table entry. */
-		npf_table_add_entry(tl, &addr, mask);
-		l++;
+	if (!npfctl_table_exists_p(id)) {
+		yyerror("table '%s' is not defined", id);
+		return NULL;
 	}
-	if (buf != NULL) {
-		free(buf);
-	}
+	vp = npfvar_create(".table");
+
+	if (!npfvar_add_element(vp, NPFVAR_TABLE, id, strlen(id) + 1))
+		goto out;
+
+	return vp;
+out:
+	npfvar_destroy(vp);
+	return NULL;
 }
 
 /*
- * N-code generation helpers.
+ * npfctl_parse_port_range: create a port-range variable.  Note that the
+ * passed port numbers are in network byte order.
  */
-
-static void
-npfctl_rulenc_cidr(void **nc, int nblocks[], var_t *dat, bool sd, sa_family_t addrfamily)
+npfvar_t *
+npfctl_parse_port_range(in_port_t s, in_port_t e)
 {
-	element_t *el = dat->v_elements;
-	int foff;
-
-	/* If table, generate a single table matching block. */
-	if (dat->v_type == VAR_TABLE) {
-		u_int tid = atoi(el->e_data);
+	npfvar_t *vp = npfvar_create(".port_range");
+	port_range_t pr;
 
-		nblocks[0]--;
-		foff = npfctl_failure_offset(nblocks);
-		npfctl_gennc_tbl(nc, foff, tid, sd);
-		return;
-	}
-
-	/* Generate v4/v6 CIDR matching blocks. */
-	for (el = dat->v_elements; el != NULL; el = el->e_next) {
-		npf_addr_t addr;
-		npf_netmask_t mask;
+	pr.pr_start = s;
+	pr.pr_end = e;
 
-		npfctl_parse_cidr(el->e_data, addrfamily, &addr, &mask);
-		if (addrfamily == AF_INET)
-		    nblocks[1]--;
-		else if (addrfamily == AF_INET6)
-		    nblocks[3]--;
-		foff = npfctl_failure_offset(nblocks);
-		if (addrfamily == AF_INET) 
-			npfctl_gennc_v4cidr(nc, foff, &addr, mask, sd);
-		else if (addrfamily == AF_INET6)
-			npfctl_gennc_v6cidr(nc, foff, &addr, mask, sd);
-	}
+	if (!npfvar_add_element(vp, NPFVAR_PORT_RANGE, &pr, sizeof(pr)))
+		goto out;
+
+	return vp;
+out:
+	npfvar_destroy(vp);
+	return NULL;
 }
 
-static void
-npfctl_rulenc_ports(void **nc, int nblocks[], var_t *dat, bool tcpudp,
-    bool both, bool sd)
+npfvar_t *
+npfctl_parse_iface(const char *ifname)
 {
-	element_t *el = dat->v_elements;
-	int foff;
+	npfvar_t *vp = npfvar_create(".iface");
+	struct ifaddrs *ifa;
+	fam_addr_mask_t fam;
+	bool gotif = false;
+
+	if (ifs_list == NULL && getifaddrs(&ifs_list) == -1) {
+		err(EXIT_FAILURE, "getifaddrs");
+	}
+	memset(&fam, 0, sizeof(fam));
 
-	assert(dat->v_type != VAR_TABLE);
+	npfvar_t *ip = npfvar_create(".ifname");
+	if (!npfvar_add_element(ip, NPFVAR_STRING, ifname, strlen(ifname) + 1))
+		goto out;
+
+	for (ifa = ifs_list; ifa != NULL; ifa = ifa->ifa_next) {
+		struct sockaddr *sa;
+		sa_family_t family;
+
+		if (strcmp(ifa->ifa_name, ifname) != 0)
+			continue;
+
+		gotif = true;
+		if ((ifa->ifa_flags & IFF_UP) == 0)
+			warnx("interface '%s' is down", ifname);
 
-	/* Generate TCP/UDP port matching blocks. */
-	for (el = dat->v_elements; el != NULL; el = el->e_next) {
-		in_port_t fport, tport;
-		bool range;
+		sa = ifa->ifa_addr;
+		family = sa->sa_family;
+		if (family != AF_INET && family != AF_INET6)
+			continue;
+
+		fam.fam_family = family;
+		fam.fam_interface = ip;
+
+		if (!npfctl_copy_address(family, &fam.fam_addr, sa))
+			goto out;
+
+		if (!npfctl_parse_mask(NULL, fam.fam_family, &fam.fam_mask))
+			goto out;
 
-		if (!npfctl_parse_port(el->e_data, &range, &fport, &tport)) {
-			errx(EXIT_FAILURE, "invalid service '%s'", el->e_data);
-		}
-		nblocks[0]--;
-		foff = both ? 0 : npfctl_failure_offset(nblocks);
-		npfctl_gennc_ports(nc, foff, fport, tport, tcpudp, sd);
+		if (!npfvar_add_element(vp, NPFVAR_FAM, &fam, sizeof(fam)))
+			goto out;
+	}
+	if (!gotif) {
+		yyerror("interface '%s' not found", ifname);
+		goto out;
 	}
+	if (npfvar_get_count(vp) == 0) {
+		yyerror("no addresses matched for interface '%s'", ifname);
+		goto out;
+	}
+	return vp;
+out:
+	npfvar_destroy(vp);
+	npfvar_destroy(ip);
+	return NULL;
 }
 
-static void
-npfctl_rulenc_block(void **nc, int nblocks[], var_t *cidr, var_t *ports,
-    bool both, bool tcpudp, bool sd, sa_family_t addrfamily)
+fam_addr_mask_t *
+npfctl_parse_cidr(char *cidr)
 {
+	npfvar_t *vp;
+	char *p;
 
-	npfctl_rulenc_cidr(nc, nblocks, cidr, sd, addrfamily);
-	if (ports == NULL) {
-		return;
+	p = strchr(cidr, '/');
+	if (p) {
+		*p++ = '\0';
 	}
-	npfctl_rulenc_ports(nc, nblocks, ports, tcpudp, both, sd);
-	if (!both) {
-		return;
+	vp = npfctl_parse_fam_addr_mask(cidr, p, NULL);
+	if (vp == NULL) {
+		return NULL;
 	}
-	npfctl_rulenc_ports(nc, nblocks, ports, !tcpudp, false, sd);
+	return npfvar_get_data(vp, NPFVAR_FAM, 0);
 }
 
-void
-npfctl_rule_ncode(nl_rule_t *rl, char *proto, char *tcpfl, int icmp_type,
-    int icmp_code, var_t *from, sa_family_t addrfamily, var_t *fports, var_t *to, var_t *tports)
+/*
+ * npfctl_portno: convert port identifier (string) to a number.
+ *
+ * => Returns port number in network byte order.
+ */
+in_port_t
+npfctl_portno(const char *port)
 {
-	int nblocks[4] = { 0, 0, 0, 0 };
-	bool icmp, tcpudp, both;
-	void *ncptr, *nc;
-	size_t sz, foff;
+	struct addrinfo *ai, *rai;
+	in_port_t p = 0;
+	int e;
 
-	/*
-	 * Default: both TCP and UDP.
-	 */
-	icmp = false;
-	tcpudp = true;
-	if (proto == NULL) {
-		both = true;
-		goto skip_proto;
+	e = getaddrinfo(NULL, port, NULL, &rai);
+	if (e != 0) {
+		yyerror("invalid port name: '%s' (%s)", port, gai_strerror(e));
+		return 0;
 	}
-	both = false;
 
-	if (strcmp(proto, "icmp") == 0) {
-		/* ICMP case. */
-		fports = NULL;
-		tports = NULL;
-		icmp = true;
-
-	} else if (strcmp(proto, "tcp") == 0) {
-		/* Just TCP. */
-		tcpudp = true;
+	for (ai = rai; ai; ai = ai->ai_next) {
+		switch (ai->ai_family) {
+		case AF_INET: {
+			struct sockaddr_in *sin = (void *)ai->ai_addr;
+			p = sin->sin_port;
+			goto out;
+		}
+		case AF_INET6: {
+			struct sockaddr_in6 *sin6 = (void *)ai->ai_addr;
+			p = sin6->sin6_port;
+			goto out;
+		}
+		default:
+			break;
+		}
+	}
+out:
+	freeaddrinfo(rai);
+	return p;
+}
 
-	} else if (strcmp(proto, "udp") == 0) {
-		/* Just UDP. */
-		tcpudp = false;
+npfvar_t *
+npfctl_parse_tcpflag(const char *s)
+{
+	uint8_t tfl = 0;
 
-	} else {
-		/* Default. */
+	while (*s) {
+		switch (*s) {
+		case 'F': tfl |= TH_FIN; break;
+		case 'S': tfl |= TH_SYN; break;
+		case 'R': tfl |= TH_RST; break;
+		case 'P': tfl |= TH_PUSH; break;
+		case 'A': tfl |= TH_ACK; break;
+		case 'U': tfl |= TH_URG; break;
+		case 'E': tfl |= TH_ECE; break;
+		case 'W': tfl |= TH_CWR; break;
+		default:
+			yyerror("invalid flag '%c'", *s);
+			return NULL;
+		}
+		s++;
 	}
-skip_proto:
-	if (icmp || icmp_type != -1) {
-		assert(tcpfl == NULL);
-		icmp = true;
-		nblocks[2] += 1;
-	}
-	if (tcpudp && tcpfl) {
-		assert(icmp_type == -1 && icmp_code == -1);
-		nblocks[2] += 1;
+
+	npfvar_t *vp = npfvar_create(".tcp_flag");
+	if (!npfvar_add_element(vp, NPFVAR_TCPFLAG, &tfl, sizeof(tfl))) {
+		npfvar_destroy(vp);
+		return NULL;
 	}
 
-	/* Calculate how blocks to determince n-code. */
-	if (from && from->v_count) {
-		if (from->v_type == VAR_TABLE)
-			nblocks[0] += 1;
-		else {
-			if (addrfamily == AF_INET)
-				nblocks[1] += from->v_count;
-			else
-				nblocks[3] += from->v_count;
-		}
-		if (fports && fports->v_count)
-			nblocks[0] += fports->v_count * (both ? 2 : 1);
-	}
-	if (to && to->v_count) {
-		if (to->v_type == VAR_TABLE)
-			nblocks[0] += 1;
-		else {
-			if (addrfamily == AF_INET)
-				nblocks[1] += to->v_count;
-			else
-				nblocks[3] += to->v_count;
-		}
-		if (tports && tports->v_count)
-			nblocks[0] += tports->v_count * (both ? 2 : 1);
-	}
+	return vp;
+}
+
+uint8_t
+npfctl_icmptype(const char *type)
+{
+	for (uint8_t ul = 0; icmp_type[ul]; ul++)
+		if (strcmp(icmp_type[ul], type) == 0)
+			return ul;
+	return ~0;
+}
+
+uint8_t
+npfctl_icmpcode(uint8_t type, const char *code)
+{
+	const char **arr;
 
-	/* Any n-code to generate? */
-	if (!icmp && (nblocks[0] + nblocks[1] + nblocks[2] + nblocks[3]) == 0) {
-		/* Done, if none. */
-		return;
+	switch (type) {
+	case ICMP_ECHOREPLY:
+	case ICMP_SOURCEQUENCH:
+	case ICMP_ALTHOSTADDR:
+	case ICMP_ECHO:
+	case ICMP_ROUTERSOLICIT:
+	case ICMP_TSTAMP:
+	case ICMP_TSTAMPREPLY:
+	case ICMP_IREQ:
+	case ICMP_IREQREPLY:
+	case ICMP_MASKREQ:
+	case ICMP_MASKREPLY:
+		arr = icmp_code_none;
+		break;
+	case ICMP_ROUTERADVERT:
+		arr = icmp_code_routeradvert;
+		break;
+	case ICMP_UNREACH:
+		arr = icmp_code_unreach;
+		break;
+	case ICMP_REDIRECT:
+		arr = icmp_code_redirect;
+		break;
+	case ICMP_TIMXCEED:
+		arr = icmp_code_timxceed;
+		break;
+	case ICMP_PARAMPROB:
+		arr = icmp_code_paramprob;
+		break;
+	case ICMP_PHOTURIS:
+		arr = icmp_code_photuris;
+		break;
+	default:
+		return ~0;
 	}
 
-	/* Allocate memory for the n-code. */
-	sz = npfctl_calc_ncsize(nblocks);
-	ncptr = malloc(sz);
-	if (ncptr == NULL) {
-		err(EXIT_FAILURE, "malloc");
+	for (uint8_t ul = 0; arr[ul]; ul++) {
+		if (strcmp(arr[ul], code) == 0)
+			return ul;
 	}
-	nc = ncptr;
+	return ~0;
+}
 
-	/*
-	 * Generate v4/v6 CIDR matching blocks and TCP/UDP port matching.
-	 */
-	if (from) {
-		npfctl_rulenc_block(&nc, nblocks, from, fports,
-		    both, tcpudp, true, addrfamily);
-	}
-	if (to) {
-		npfctl_rulenc_block(&nc, nblocks, to, tports,
-		    both, tcpudp, false, addrfamily);
-	}
-
-	if (icmp) {
-		/*
-		 * ICMP case.
-		 */
-		nblocks[2]--;
-		foff = npfctl_failure_offset(nblocks);
-		npfctl_gennc_icmp(&nc, foff, icmp_type, icmp_code);
+npfvar_t *
+npfctl_parse_icmp(uint8_t type, uint8_t code)
+{
+	npfvar_t *vp = npfvar_create(".icmp");
 
-	} else if (tcpudp && tcpfl) {
-		/*
-		 * TCP case, flags.
-		 */
-		uint8_t tfl = 0, tfl_mask;
+	if (!npfvar_add_element(vp, NPFVAR_ICMP, &type, sizeof(type)))
+		goto out;
 
-		nblocks[2]--;
-		foff = npfctl_failure_offset(nblocks);
-		if (!npfctl_parse_tcpfl(tcpfl, &tfl, &tfl_mask)) {
-			errx(EXIT_FAILURE, "invalid TCP flags '%s'", tcpfl);
-		}
-		npfctl_gennc_tcpfl(&nc, foff, tfl, tfl_mask);
-	}
-	npfctl_gennc_complete(&nc);
+	if (!npfvar_add_element(vp, NPFVAR_ICMP, &code, sizeof(code)))
+		goto out;
 
-	if ((uintptr_t)nc - (uintptr_t)ncptr != sz) {
-		errx(EXIT_FAILURE, "n-code size got wrong (%tu != %zu)",
-		    (uintptr_t)nc - (uintptr_t)ncptr, sz);
-	}
-
-#ifdef DEBUG
-	uint32_t *op = ncptr;
-	size_t n = sz;
-	do {
-		DPRINTF(("\t> |0x%02x|\n", (u_int)*op));
-		op++;
-		n -= sizeof(*op);
-	} while (n);
-#endif
-
-	/* Create a final memory block of data, ready to send. */
-	if (npf_rule_setcode(rl, NPF_CODE_NCODE, ncptr, sz) == -1) {
-		errx(EXIT_FAILURE, "npf_rule_setcode");
-	}
-	free(ncptr);
+	return vp;
+out:
+	npfvar_destroy(vp);
+	return NULL;
 }
--- a/usr.sbin/npf/npfctl/npf_ncgen.c	Sun Jan 08 21:20:40 2012 +0000
+++ b/usr.sbin/npf/npfctl/npf_ncgen.c	Sun Jan 08 21:34:21 2012 +0000
@@ -1,7 +1,7 @@
-/*	$NetBSD: npf_ncgen.c,v 1.5 2011/11/04 01:00:28 zoltan Exp $	*/
+/*	$NetBSD: npf_ncgen.c,v 1.6 2012/01/08 21:34:21 rmind Exp $	*/
 
 /*-
- * Copyright (c) 2009-2010 The NetBSD Foundation, Inc.
+ * Copyright (c) 2009-2012 The NetBSD Foundation, Inc.
  * All rights reserved.
  *
  * This material is based upon work partially supported by The
@@ -30,194 +30,317 @@
  */
 
 /*
- * N-code generation.
- *
- * WARNING: Update npfctl_calc_ncsize() and npfctl_failure_offset()
- * calculations, when changing generation routines.
+ * N-code generation interface.
  */
 
 #include <sys/cdefs.h>
-__RCSID("$NetBSD: npf_ncgen.c,v 1.5 2011/11/04 01:00:28 zoltan Exp $");
+__RCSID("$NetBSD: npf_ncgen.c,v 1.6 2012/01/08 21:34:21 rmind Exp $");
 
-#include <sys/types.h>
-#include <string.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <inttypes.h>
+#include <assert.h>
+#include <err.h>
 
 #include "npfctl.h"
 
+/* Reduce re-allocations by expanding in 64 byte blocks. */
+#define	NC_ALLOC_MASK		(64 - 1)
+#define	NC_ALLOC_ROUND(x)	(((x) + NC_ALLOC_MASK) & ~NC_ALLOC_MASK)
+
+struct nc_ctx {
+	/*
+	 * Original buffer address, size of the buffer and instruction
+	 * pointer for appending n-code fragments.
+	 */
+	void *			nc_buf;
+	void *			nc_iptr;
+	size_t			nc_len;
+	/* Expected number of words for diagnostic check. */
+	size_t			nc_expected;
+	/* List of jump values, length of the memory and iterator. */
+	ptrdiff_t *		nc_jmp_list;
+	size_t			nc_jmp_len;
+	size_t			nc_jmp_it;
+	/* Current logical operation for a group and saved iterator. */
+	size_t			nc_saved_it;
+};
+
 /*
- * npfctl_calc_ncsize: calculate size required for the n-code.
+ * npfctl_ncgen_getptr: return the instruction pointer and make sure that
+ * buffer is large enough to add a new fragment of a given size.
  */
-size_t
-npfctl_calc_ncsize(int nblocks[])
+static uint32_t *
+npfctl_ncgen_getptr(nc_ctx_t *ctx, size_t nwords)
 {
+	size_t offset, reqlen;
+
+	/* Save the number of expected words for diagnostic check. */
+	assert(ctx->nc_expected == 0);
+	ctx->nc_expected = (sizeof(uint32_t) * nwords);
+
 	/*
-	 * Blocks:
-	 * - 5 words each by npfctl_gennc_ports/tbl(), stored in nblocks[0].
-	 * - 6 words each by npfctl_gennc_v4cidr(), stored in nblocks[1].
-	 * - 4 words by npfctl_gennc_{icmp,tcpfl}(), stored in nblocks[2].
-	 * - 9 words each by npfctl_gennc_v6cidr(), stored in nblocks[3].
-	 * - 4 words by npfctl_gennc_complete(), single last fragment.
+	 * Calculate the required length.  If buffer size is large enough,
+	 * just return the pointer.
 	 */
-	return nblocks[0] * 5 * sizeof(uint32_t) +
-	    nblocks[1] * 6 * sizeof(uint32_t) +
-	    nblocks[2] * 4 * sizeof(uint32_t) +
-	    nblocks[3] * 9 * sizeof(uint32_t) +
-	    4 * sizeof(uint32_t);
+	offset = (uintptr_t)ctx->nc_iptr - (uintptr_t)ctx->nc_buf;
+	assert(offset <= ctx->nc_len);
+	reqlen = offset + ctx->nc_expected;
+	if (reqlen < ctx->nc_len) {
+		return ctx->nc_iptr;
+	}
+
+	/* Otherwise, re-allocate the buffer and update the pointers. */
+	ctx->nc_len = NC_ALLOC_ROUND(reqlen);
+	ctx->nc_buf = xrealloc(ctx->nc_buf, ctx->nc_len);
+	ctx->nc_iptr = (uint8_t *)ctx->nc_buf + offset;
+	return ctx->nc_iptr;
+}
+
+/*
+ * npfctl_ncgen_putptr: perform a diagnostic check whether expected words
+ * were appended and save the instruction pointer.
+ */
+static void
+npfctl_ncgen_putptr(nc_ctx_t *ctx, void *nc)
+{
+	ptrdiff_t diff = (uintptr_t)nc - (uintptr_t)ctx->nc_iptr;
+
+	if ((ptrdiff_t)ctx->nc_expected != diff) {
+		errx(EXIT_FAILURE, "unexpected n-code fragment size "
+		    "(expected words %lu, diff %td)", ctx->nc_expected, diff);
+	}
+	ctx->nc_expected = 0;
+	ctx->nc_iptr = nc;
+}
+
+/*
+ * npfctl_ncgen_addjmp: add the compare/jump opcode, dummy value and
+ * its pointer into the list.
+ */
+static void
+npfctl_ncgen_addjmp(nc_ctx_t *ctx, uint32_t **nc_ptr)
+{
+	size_t reqlen, i = ctx->nc_jmp_it++;
+	uint32_t *nc = *nc_ptr;
+
+	reqlen = NC_ALLOC_ROUND(ctx->nc_jmp_it * sizeof(ptrdiff_t));
+
+	if (reqlen > NC_ALLOC_ROUND(ctx->nc_jmp_len)) {
+		ctx->nc_jmp_list = xrealloc(ctx->nc_jmp_list, reqlen);
+		ctx->nc_jmp_len = reqlen;
+	}
+
+	/* Save the offset (note: we cannot save the pointer). */
+	ctx->nc_jmp_list[i] = (uintptr_t)nc - (uintptr_t)ctx->nc_buf;
+
+	/* Note: if OR grouping case, BNE will be replaced with BEQ. */
+	*nc++ = NPF_OPCODE_BNE;
+	*nc++ = 0xdeadbeef;
+	*nc_ptr = nc;
 }
 
 /*
- * npfctl_failure_offset: calculate offset value to the failure block.
+ * npfctl_ncgen_create: new n-code generation context.
  */
-size_t
-npfctl_failure_offset(int nblocks[])
+nc_ctx_t *
+npfctl_ncgen_create(void)
 {
-	size_t tblport_blocks, v4cidr_blocks, v6cidr_blocks, icmp_tcpfl;
-	/*
-	 * Take into account all blocks (plus 2 words for comparison each),
-	 * and additional 4 words to skip the last comparison and success path.
-	 */
-	tblport_blocks = (3 + 2) * nblocks[0];
-	v4cidr_blocks = (4 + 2) * nblocks[1];
-	icmp_tcpfl = (2 + 2) * nblocks[2];
-	v6cidr_blocks = (7 + 2) * nblocks[3];
-	return tblport_blocks + v4cidr_blocks + v6cidr_blocks + icmp_tcpfl + 4;
+	return zalloc(sizeof(nc_ctx_t));
 }
 
-#if 0
 /*
- * npfctl_gennc_ether: initial n-code fragment to check Ethernet frame.
+ * npfctl_ncgen_complete: complete generation, destroy the context and
+ * return a pointer to the final buffer containing n-code.
+ */
+void *
+npfctl_ncgen_complete(nc_ctx_t *ctx, size_t *sz)
+{
+	uint32_t *nc = npfctl_ncgen_getptr(ctx, 4 /* words */);
+	ptrdiff_t foff;
+	size_t i;
+
+	assert(ctx->nc_saved_it == 0);
+
+	/* Success path (return 0x0). */
+	*nc++ = NPF_OPCODE_RET;
+	*nc++ = 0x0;
+
+	/* Failure path (return 0xff). */
+	foff = ((uintptr_t)nc - (uintptr_t)ctx->nc_buf) / sizeof(uint32_t);
+	*nc++ = NPF_OPCODE_RET;
+	*nc++ = 0xff;
+
+	/* + 4 words. */
+	npfctl_ncgen_putptr(ctx, nc);
+
+	/* Change the jump values. */
+	for (i = 0; i < ctx->nc_jmp_it; i++) {
+		ptrdiff_t off = ctx->nc_jmp_list[i] / sizeof(uint32_t);
+		uint32_t *jmpop = (uint32_t *)ctx->nc_buf + off;
+		uint32_t *jmpval = jmpop + 1;
+
+		assert(foff > off);
+		assert(*jmpop == NPF_OPCODE_BNE);
+		assert(*jmpval == 0xdeadbeef);
+		*jmpval = foff - off;
+	}
+
+	/* Return the buffer, destroy the context. */
+	void *buf = ctx->nc_buf;
+	*sz = (uintptr_t)ctx->nc_iptr - (uintptr_t)ctx->nc_buf;
+	free(ctx->nc_jmp_list);
+	free(ctx);
+	return buf;
+}
+
+/*
+ * npfctl_ncgen_group: begin a logical group.
  */
 void
-npfctl_gennc_ether(void **ncptr, int foff, uint16_t ethertype)
+npfctl_ncgen_group(nc_ctx_t *ctx)
 {
-	uint32_t *nc = *ncptr;
+	assert(ctx->nc_expected == 0);
+	assert(ctx->nc_saved_it == 0);
+	ctx->nc_saved_it = ctx->nc_jmp_it;
+}
 
-	/* NPF handler will set REG_0 to either NPF_LAYER_2 or NPF_LAYER_3. */
-	*nc++ = NPF_OPCODE_CMP;
-	*nc++ = NPF_LAYER_3;
-	*nc++ = 0;
+/*
+ * npfctl_ncgen_endgroup: end a logical group, fix up the code accordingly.
+ */
+void
+npfctl_ncgen_endgroup(nc_ctx_t *ctx)
+{
+	uint32_t *nc;
 
-	/* Skip all further code, if layer 3. */
-	*nc++ = NPF_OPCODE_BEQ;
-	*nc++ = 0x0a;
+	/* If there are no fragments or only one - nothing to do. */
+	if ((ctx->nc_jmp_it - ctx->nc_saved_it) <= 1) {
+		ctx->nc_saved_it = 0;
+		return;
+	}
+
+	/* Append failure return for OR grouping. */
+	nc = npfctl_ncgen_getptr(ctx, 2 /* words */);
+	*nc++ = NPF_OPCODE_RET;
+	*nc++ = 0xff;
+	npfctl_ncgen_putptr(ctx, nc);
 
-	/* Otherwise, assume layer 2 and perform NPF_OPCODE_ETHER. */
-	*nc++ = NPF_OPCODE_ETHER;
-	*nc++ = 0x00;		/* reserved */
-	*nc++ = 0x00;		/* reserved */
-	*nc++ = ethertype;
+	/* Update any group jumps values on success to the current point. */
+	for (size_t i = ctx->nc_saved_it; i < ctx->nc_jmp_it; i++) {
+		ptrdiff_t off = ctx->nc_jmp_list[i] / sizeof(uint32_t);
+		uint32_t *jmpop = (uint32_t *)ctx->nc_buf + off;
+		uint32_t *jmpval = jmpop + 1;
+
+		assert(*jmpop == NPF_OPCODE_BNE);
+		assert(*jmpval == 0xdeadbeef);
+
+		*jmpop = NPF_OPCODE_BEQ;
+		*jmpval = nc - jmpop;
+		ctx->nc_jmp_list[i] = 0;
+	}
 
-	/* Fail (+ 2 words of ADVR) or advance to layer 3 (IPv4) header. */
-	*nc++ = NPF_OPCODE_BNE;
-	*nc++ = foff + 2;
-	/* Offset to the header is returned by NPF_OPCODE_ETHER in REG_3. */
-	*nc++ = NPF_OPCODE_ADVR;
-	*nc++ = 3;
+	/* Reset the iterator. */
+	ctx->nc_jmp_it = ctx->nc_saved_it;
+	ctx->nc_saved_it = 0;
+}
 
-	/* + 13 words. */
-	*ncptr = (void *)nc;
-}
-#endif
-
+/*
+ * npfctl_gennc_v6cidr: fragment to match IPv6 CIDR.
+ */
 void
-npfctl_gennc_v6cidr(void **ncptr, int foff,
-    const npf_addr_t *netaddr, const npf_netmask_t mask, bool sd)
+npfctl_gennc_v6cidr(nc_ctx_t *ctx, int opts, const npf_addr_t *netaddr,
+    const npf_netmask_t mask)
 {
-	uint32_t *nc = *ncptr;
+	uint32_t *nc = npfctl_ncgen_getptr(ctx, 9 /* words */);
 	const uint32_t *addr = (const uint32_t *)netaddr;
 
-	/* OP, direction, netaddr/subnet (10 words) */
+	assert(((opts & NC_MATCH_SRC) != 0) ^ ((opts & NC_MATCH_DST) != 0));
+	assert((mask && mask <= NPF_MAX_NETMASK) || mask == NPF_NO_NETMASK);
+
+	/* OP, direction, netaddr/subnet (7 words) */
 	*nc++ = NPF_OPCODE_IP6MASK;
-	*nc++ = (sd ? 0x01 : 0x00);
+	*nc++ = (opts & (NC_MATCH_DST | NC_MATCH_SRC)) >> 1;
 	*nc++ = addr[0];
 	*nc++ = addr[1];
 	*nc++ = addr[2];
 	*nc++ = addr[3];
 	*nc++ = mask;
 
-	/* If not equal, jump to failure block, continue otherwise (2 words). */
-	*nc++ = NPF_OPCODE_BNE;
-	*nc++ = foff;
+	/* Comparison block (2 words). */
+	npfctl_ncgen_addjmp(ctx, &nc);
 
 	/* + 9 words. */
-	*ncptr = (void *)nc;
+	npfctl_ncgen_putptr(ctx, nc);
 }
 
-
 /*
  * npfctl_gennc_v4cidr: fragment to match IPv4 CIDR.
  */
 void
-npfctl_gennc_v4cidr(void **ncptr, int foff,
-    const npf_addr_t *netaddr, const npf_netmask_t mask, bool sd)
+npfctl_gennc_v4cidr(nc_ctx_t *ctx, int opts, const npf_addr_t *netaddr,
+    const npf_netmask_t mask)
 {
-	uint32_t *nc = *ncptr;
+	uint32_t *nc = npfctl_ncgen_getptr(ctx, 6 /* words */);
 	const uint32_t *addr = (const uint32_t *)netaddr;
 
+	assert(((opts & NC_MATCH_SRC) != 0) ^ ((opts & NC_MATCH_DST) != 0));
+	assert((mask && mask <= NPF_MAX_NETMASK) || mask == NPF_NO_NETMASK);
+
 	/* OP, direction, netaddr/subnet (4 words) */
 	*nc++ = NPF_OPCODE_IP4MASK;
-	*nc++ = (sd ? 0x01 : 0x00);
+	*nc++ = (opts & (NC_MATCH_DST | NC_MATCH_SRC)) >> 1;
 	*nc++ = addr[0];
 	*nc++ = mask;
 
-	/* If not equal, jump to failure block, continue otherwise (2 words). */
-	*nc++ = NPF_OPCODE_BNE;
-	*nc++ = foff;
+	/* Comparison block (2 words). */
+	npfctl_ncgen_addjmp(ctx, &nc);
 
 	/* + 6 words. */
-	*ncptr = (void *)nc;
+	npfctl_ncgen_putptr(ctx, nc);
 }
 
 /*
  * npfctl_gennc_ports: fragment to match TCP or UDP ports.
  */
 void
-npfctl_gennc_ports(void **ncptr, int foff,
-    in_port_t pfrom, in_port_t pto, bool tcpudp, bool sd)
+npfctl_gennc_ports(nc_ctx_t *ctx, int opts, in_port_t from, in_port_t to)
 {
-	uint32_t *nc = *ncptr;
+	uint32_t *nc = npfctl_ncgen_getptr(ctx, 5 /* words */);
+
+	assert(((opts & NC_MATCH_SRC) != 0) ^ ((opts & NC_MATCH_DST) != 0));
+	assert(((opts & NC_MATCH_TCP) != 0) ^ ((opts & NC_MATCH_UDP) != 0));
 
 	/* OP, direction, port range (3 words). */
-	*nc++ = (tcpudp ? NPF_OPCODE_TCP_PORTS : NPF_OPCODE_UDP_PORTS);
-	*nc++ = (sd ? 0x01 : 0x00);
-	*nc++ = ((uint32_t)pfrom << 16) | pto;
+	*nc++ = (opts & NC_MATCH_TCP) ?
+	    NPF_OPCODE_TCP_PORTS : NPF_OPCODE_UDP_PORTS;
+	*nc++ = (opts & (NC_MATCH_DST | NC_MATCH_SRC)) >> 1;
+	*nc++ = ((uint32_t)from << 16) | to;
 
-	/*
-	 * If not equal, jump to failure block, continue otherwise (2 words).
-	 * Specific case (foff == 0): when matching both TCP and UDP ports,
-	 * skip next port-matching fragment on success (5 + 2 words).
-	 */
-	if (foff) {
-		*nc++ = NPF_OPCODE_BNE;
-		*nc++ = foff;
-	} else {
-		*nc++ = NPF_OPCODE_BEQ;
-		*nc++ = 5 + 2;
-	}
+	/* Comparison block (2 words). */
+	npfctl_ncgen_addjmp(ctx, &nc);
 
 	/* + 5 words. */
-	*ncptr = (void *)nc;
+	npfctl_ncgen_putptr(ctx, nc);
 }
 
 /*
  * npfctl_gennc_icmp: fragment to match ICMP type and code.
  */
 void
-npfctl_gennc_icmp(void **ncptr, int foff, int type, int code)
+npfctl_gennc_icmp(nc_ctx_t *ctx, int type, int code)
 {
-	uint32_t *nc = *ncptr;
+	uint32_t *nc = npfctl_ncgen_getptr(ctx, 4 /* words */);
 
 	/* OP, code, type (2 words) */
 	*nc++ = NPF_OPCODE_ICMP4;
 	*nc++ = (type == -1 ? 0 : (1 << 31) & (type & 0xff << 8)) |
 		(code == -1 ? 0 : (1 << 31) & (code & 0xff));
 
-	/* If not equal, jump to failure block, continue otherwise (2 words). */
-	*nc++ = NPF_OPCODE_BNE;
-	*nc++ = foff;
+	/* Comparison block (2 words). */
+	npfctl_ncgen_addjmp(ctx, &nc);
 
 	/* + 4 words. */
-	*ncptr = (void *)nc;
+	npfctl_ncgen_putptr(ctx, nc);
 }
 
 /*
@@ -225,59 +348,50 @@
  * the packet against table specified by ID.
  */
 void
-npfctl_gennc_tbl(void **ncptr, int foff, u_int tid, bool sd)
+npfctl_gennc_tbl(nc_ctx_t *ctx, int opts, u_int tableid)
 {
-	uint32_t *nc = *ncptr;
+	uint32_t *nc = npfctl_ncgen_getptr(ctx, 5 /* words */);
+
+	assert(((opts & NC_MATCH_SRC) != 0) ^ ((opts & NC_MATCH_DST) != 0));
 
 	/* OP, direction, table ID (3 words). */
 	*nc++ = NPF_OPCODE_TABLE;
-	*nc++ = (sd ? 0x01 : 0x00);
-	*nc++ = tid;
+	*nc++ = (opts & (NC_MATCH_DST | NC_MATCH_SRC)) >> 1;
+	*nc++ = tableid;
 
-	/* If not equal, jump to failure block, continue otherwise (2 words). */
-	*nc++ = NPF_OPCODE_BNE;
-	*nc++ = foff;
+	/* Comparison block (2 words). */
+	npfctl_ncgen_addjmp(ctx, &nc);
 
 	/* + 5 words. */
-	*ncptr = (void *)nc;
+	npfctl_ncgen_putptr(ctx, nc);
 }
 
 /*
  * npfctl_gennc_tcpfl: fragment to match TCP flags/mask.
  */
 void
-npfctl_gennc_tcpfl(void **ncptr, int foff, uint8_t tf, uint8_t tf_mask)
+npfctl_gennc_tcpfl(nc_ctx_t *ctx, uint8_t tf, uint8_t tf_mask)
 {
-	uint32_t *nc = *ncptr;
+	uint32_t *nc = npfctl_ncgen_getptr(ctx, 4 /* words */);
 
 	/* OP, code, type (2 words) */
 	*nc++ = NPF_OPCODE_TCP_FLAGS;
 	*nc++ = (tf << 8) | tf_mask;
 
-	/* If not equal, jump to failure block, continue otherwise (2 words). */
-	*nc++ = NPF_OPCODE_BNE;
-	*nc++ = foff;
+	/* Comparison block (2 words). */
+	npfctl_ncgen_addjmp(ctx, &nc);
 
 	/* + 4 words. */
-	*ncptr = (void *)nc;
+	npfctl_ncgen_putptr(ctx, nc);
 }
 
-/*
- * npfctl_gennc_complete: append success and failure fragments.
- */
 void
-npfctl_gennc_complete(void **ncptr)
+npfctl_ncgen_print(const void *code, size_t len)
 {
-	uint32_t *nc = *ncptr;
+	const uint32_t *op = code;
 
-	/* Success path (return 0x0). */
-	*nc++ = NPF_OPCODE_RET;
-	*nc++ = 0x0;
-
-	/* Failure path (return 0xff). */
-	*nc++ = NPF_OPCODE_RET;
-	*nc++ = 0xff;
-
-	/* + 4 words. */
-	*ncptr = (void *)nc;
+	while (len) {
+		printf("\t> |0x%02x|\n", (u_int)*op++);
+		len -= sizeof(*op);
+	}
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr.sbin/npf/npfctl/npf_parse.y	Sun Jan 08 21:34:21 2012 +0000
@@ -0,0 +1,713 @@
+/*	$NetBSD: npf_parse.y,v 1.1 2012/01/08 21:34:21 rmind Exp $	*/
+
+/*-
+ * Copyright (c) 2011-2012 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Martin Husemann and Christos Zoulas.
+ *
+ * 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.
+ */
+
+%{
+
+#include <stdio.h>
+#include <err.h>
+#include <vis.h>
+#include <netdb.h>
+#include <assert.h>
+
+#include "npfctl.h"
+
+const char *		yyfilename;
+
+extern int		yylineno, yycolumn;
+extern int		yylex(void);
+
+/* Variable under construction (bottom up). */
+static npfvar_t *	cvar;
+
+void
+yyerror(const char *fmt, ...)
+{
+	extern int yyleng;
+	extern char *yytext;
+
+	char *msg, *context = xstrndup(yytext, yyleng);
+	size_t len = strlen(context);
+	char *dst = zalloc(len * 4 + 1);
+	va_list ap;
+
+	va_start(ap, fmt);
+	vasprintf(&msg, fmt, ap);
+	va_end(ap);
+
+	strvisx(dst, context, len, VIS_WHITE|VIS_CSTYLE);
+	fprintf(stderr, "%s:%d:%d: %s near '%s'\n", yyfilename, yylineno,
+	    yycolumn, msg, dst);
+	exit(EXIT_FAILURE);
+}
+
+%}
+
+%token			ALL
+%token			ANY
+%token			APPLY
+%token			ARROW
+%token			BINAT
+%token			BLOCK
+%token			CURLY_CLOSE
+%token			CURLY_OPEN
+%token			CODE
+%token			COLON
+%token			COMMA
+%token			DEFAULT
+%token			TDYNAMIC
+%token			EQ
+%token			TFILE
+%token			FLAGS
+%token			FROM
+%token			GROUP
+%token			HASH
+%token			ICMPTYPE
+%token			ID
+%token			IN
+%token			INET
+%token			INET6
+%token			INTERFACE
+%token			KEEP
+%token			MINUS
+%token			NAT
+%token			NAME
+%token			ON
+%token			OUT
+%token			PAR_CLOSE
+%token			PAR_OPEN
+%token			PASS
+%token			PORT
+%token			PROCEDURE
+%token			PROTO
+%token			FAMILY
+%token			QUICK
+%token			RDR
+%token			RETURN
+%token			RETURNICMP
+%token			RETURNRST
+%token			SEPLINE
+%token			SLASH
+%token			STATE
+%token			TABLE
+%token			TCP
+%token			TO
+%token			TREE
+%token			TYPE
+%token			UDP
+%token			ICMP
+
+%token	<num>		HEX
+%token	<str>		IDENTIFIER
+%token	<str>		IPV4ADDR
+%token	<str>		IPV6ADDR
+%token	<num>		NUM
+%token	<str>		STRING
+%token	<str>		TABLE_ID
+%token	<str>		VAR_ID
+
+%type	<str>		addr, iface_name, moduleargname, list_elem, table_store
+%type	<str>		opt_apply
+%type	<num>		ifindex, port, opt_quick, on_iface
+%type	<num>		block_or_pass, rule_dir, block_opts, family, opt_family
+%type	<num>		opt_keep_state, icmp_type, table_type
+%type	<var>		addr_or_iface, port_range, iface, icmp_type_and_code
+%type	<var>		filt_addr, addr_and_mask, tcp_flags, tcp_flags_and_mask
+%type	<var>		modulearg_opts, procs, proc_op, modulearg, moduleargs
+%type	<filtopts>	filt_opts, all_or_filt_opts
+%type	<optproto>	opt_proto
+%type	<rulegroup>	group_attr, group_opt
+
+%union {
+	char *		str;
+	unsigned long	num;
+	filt_opts_t	filtopts;
+	npfvar_t *	var;
+	opt_proto_t	optproto;
+	rule_group_t	rulegroup;
+}
+
+%%
+
+input
+	: lines
+	;
+
+lines
+	: line SEPLINE lines
+	| line
+	;
+
+line
+	: def
+	| table
+	| nat
+	| group
+	| rproc
+	|
+	;
+
+def
+	: VAR_ID
+	{
+		cvar = npfvar_create($1);
+		npfvar_add(cvar);
+	}
+	  EQ definition
+	{
+		cvar = NULL;
+	}
+	;
+
+definition
+	: list_elem
+	| listdef
+	;
+
+listdef
+	: CURLY_OPEN list_elems CURLY_CLOSE
+	;
+
+list_elems
+	: list_elem COMMA list_elems
+	| list_elem
+	;
+
+list_elem
+	: IDENTIFIER
+	{
+		npfvar_t *vp = npfvar_create(".identifier");
+		npfvar_add_element(vp, NPFVAR_IDENTIFIER, $1, strlen($1) + 1);
+		npfvar_add_elements(cvar, vp);
+	}
+	| STRING
+	{
+		npfvar_t *vp = npfvar_create(".string");
+		npfvar_add_element(vp, NPFVAR_STRING, $1, strlen($1) + 1);
+		npfvar_add_elements(cvar, vp);
+	}
+	| NUM
+	{
+		npfvar_t *vp = npfvar_create(".num");
+		npfvar_add_element(vp, NPFVAR_NUM, &$1, sizeof($1));
+		npfvar_add_elements(cvar, vp);
+	}
+	| VAR_ID
+	{
+		npfvar_t *vp = npfvar_create(".var_id");
+		npfvar_add_element(vp, NPFVAR_VAR_ID, $1, strlen($1) + 1);
+		npfvar_add_elements(cvar, vp);
+	}
+	| addr_and_mask
+	{
+		npfvar_add_elements(cvar, $1);
+	}
+	;
+
+table
+	: TABLE TABLE_ID TYPE table_type table_store
+	{
+		npfctl_build_table($2, $4, $5);
+	}
+	;
+
+table_type
+	: HASH		{ $$ = NPF_TABLE_HASH; }
+	| TREE		{ $$ = NPF_TABLE_RBTREE; }
+	;
+
+table_store
+	: TDYNAMIC	{ $$ = NULL; }
+	| TFILE STRING	{ $$ = $2; }
+	;
+
+nat
+	: natdef
+	| binatdef
+	| rdrdef
+	;
+
+natdef
+	: NAT ifindex filt_opts ARROW addr_or_iface
+	{
+		npfctl_build_nat(NPFCTL_NAT, $2, &$3, $5, NULL);
+	}
+	;
+
+binatdef
+	: BINAT ifindex filt_opts ARROW addr_or_iface
+	{
+		npfctl_build_nat(NPFCTL_BINAT, $2, &$3, $5, $3.fo_from);
+	}
+	;
+
+rdrdef
+	: RDR ifindex filt_opts ARROW addr_or_iface port_range
+	{
+		npfctl_build_nat(NPFCTL_RDR, $2, &$3, $5, $6);
+	}
+	;
+
+rproc
+	: PROCEDURE STRING CURLY_OPEN procs CURLY_CLOSE
+	{
+		npfctl_build_rproc($2, $4);
+	}
+	;
+
+procs
+	: proc_op SEPLINE procs	{ $$ = npfvar_add_elements($1, $3); }
+	| proc_op		{ $$ = $1; }
+	;
+
+proc_op
+	: IDENTIFIER COLON moduleargs
+	{
+		proc_op_t po;
+
+		po.po_name = xstrdup($1);
+		po.po_opts = $3;
+		$$ = npfvar_create(".proc_ops");
+		npfvar_add_element($$, NPFVAR_PROC_OP, &po, sizeof(po));
+	}
+	|	{ $$ = NULL; }
+	;
+
+moduleargs
+	: modulearg COMMA moduleargs
+	{
+		$$ = npfvar_add_elements($1, $3);
+	}
+	| modulearg	{ $$ = $1; }
+	|		{ $$ = NULL; }
+	;
+
+modulearg
+	: moduleargname modulearg_opts
+	{
+		module_arg_t ma;
+
+		ma.ma_name = xstrdup($1);
+		ma.ma_opts = $2;
+		$$ = npfvar_create(".module_arg");
+		npfvar_add_element($$, NPFVAR_MODULE_ARG, &ma, sizeof(ma));
+	}
+	;
+
+moduleargname
+	: STRING	{ $$ = $1; }
+	| IDENTIFIER	{ $$ = $1; }
+	;
+
+modulearg_opts
+	: STRING modulearg_opts
+	{
+		npfvar_t *vp = npfvar_create(".modstring");
+		npfvar_add_element(vp, NPFVAR_STRING, $1, strlen($1) + 1);
+		$$ = $2 ? npfvar_add_elements($2, vp) : vp;
+	}
+	| IDENTIFIER modulearg_opts
+	{
+		npfvar_t *vp = npfvar_create(".modident");
+		npfvar_add_element(vp, NPFVAR_IDENTIFIER, $1, strlen($1) + 1);
+		$$ = $2 ? npfvar_add_elements($2, vp) : vp;
+	}
+	| NUM modulearg_opts
+	{
+		npfvar_t *vp = npfvar_create(".modnum");
+		npfvar_add_element(vp, NPFVAR_NUM, &$1, sizeof($1));
+		$$ = $2 ? npfvar_add_elements($2, vp) : vp;
+	}
+	|	{ $$ = NULL; }
+	;
+
+group
+	: GROUP PAR_OPEN group_attr PAR_CLOSE
+	{
+		npfctl_build_group($3.rg_name, $3.rg_attr, $3.rg_ifnum);
+	}
+	  ruleset
+	;
+
+group_attr
+	: group_opt COMMA group_attr
+	{
+		$$ = $3;
+
+		if (($1.rg_name && $$.rg_name) ||
+		    ($1.rg_ifnum && $$.rg_ifnum) ||
+		    ($1.rg_attr & $$.rg_attr) != 0)
+			yyerror("duplicate group option");
+
+		if ($1.rg_name) {
+			$$.rg_name = $1.rg_name;
+		}
+		if ($1.rg_attr) {
+			$$.rg_attr |= $1.rg_attr;
+		}
+		if ($1.rg_ifnum) {
+			$$.rg_ifnum = $1.rg_ifnum;
+		}
+	}
+	| group_opt		{ $$ = $1; }
+	;
+
+group_opt
+	: DEFAULT
+	{
+		$$.rg_name = NULL;
+		$$.rg_ifnum = 0;
+		$$.rg_attr = NPF_RULE_DEFAULT;
+	}
+	| NAME STRING
+	{
+		$$.rg_name = $2;
+		$$.rg_ifnum = 0;
+		$$.rg_attr = 0;
+	}
+	| INTERFACE ifindex
+	{
+		$$.rg_name = NULL;
+		$$.rg_ifnum = $2;
+		$$.rg_attr = 0;
+	}
+	| rule_dir
+	{
+		$$.rg_name = NULL;
+		$$.rg_ifnum = 0;
+		$$.rg_attr = $1;
+	}
+	;
+
+ruleset
+	: CURLY_OPEN rules CURLY_CLOSE
+	;
+
+rules
+	: rule SEPLINE rules
+	| rule
+	;
+
+rule
+	: block_or_pass rule_dir opt_quick on_iface opt_family
+	  opt_proto all_or_filt_opts opt_keep_state opt_apply
+	{
+		/*
+		 * Arguments: attributes, interface index, address
+		 * family, protocol options, filter options.
+		 */
+		npfctl_build_rule($1 | $2 | $3 | $8, $4,
+		    $5, &$6, &$7, $9);
+	}
+	|
+	;
+
+block_or_pass
+	: BLOCK block_opts	{ $$ = $2; }
+	| PASS			{ $$ = NPF_RULE_PASS; }
+	;
+
+rule_dir
+	: IN			{ $$ = NPF_RULE_IN; }
+	| OUT			{ $$ = NPF_RULE_OUT; }
+	|			{ $$ = NPF_RULE_IN | NPF_RULE_OUT; }
+	;
+
+opt_quick
+	: QUICK			{ $$ = NPF_RULE_FINAL; }
+	|			{ $$ = 0; }
+	;
+
+on_iface
+	: ON ifindex		{ $$ = $2; }
+	|			{ $$ = 0; }
+	;
+
+family
+	: INET			{ $$ = AF_INET; }
+	| INET6			{ $$ = AF_INET6; }
+	;
+
+opt_proto
+	: PROTO TCP tcp_flags_and_mask
+	{
+		$$.op_proto = IPPROTO_TCP;
+		$$.op_opts = $3;
+	}
+	| PROTO ICMP icmp_type_and_code
+	{
+		$$.op_proto = IPPROTO_ICMP;
+		$$.op_opts = $3;
+	}
+	| PROTO UDP
+	{
+		$$.op_proto = IPPROTO_UDP;
+		$$.op_opts = NULL;
+	}
+	|
+	{
+		$$.op_proto = -1;
+		$$.op_opts = NULL;
+	}
+	;
+
+opt_family
+	: FAMILY family		{ $$ = $2; }
+	|			{ $$ = AF_UNSPEC; }
+	;
+
+all_or_filt_opts
+	: ALL
+	{
+		$$.fo_from = NULL;
+		$$.fo_from_port_range = NULL;
+		$$.fo_to = NULL;
+		$$.fo_to_port_range = NULL;
+	}
+	| filt_opts	{ $$ = $1; }
+	;
+
+opt_keep_state
+	: KEEP STATE	{ $$ = NPF_RULE_KEEPSTATE; }
+	|		{ $$ = 0; }
+	;
+
+opt_apply
+	: APPLY STRING	{ $$ = $2; }
+	|		{ $$ = NULL; }
+	;
+
+block_opts
+	: RETURNRST	{ $$ = NPF_RULE_RETRST; }
+	| RETURNICMP	{ $$ = NPF_RULE_RETICMP; }
+	| RETURN	{ $$ = NPF_RULE_RETRST | NPF_RULE_RETICMP; }
+	|		{ $$ = 0; }
+	;
+
+filt_opts
+	: FROM filt_addr port_range TO filt_addr port_range
+	{
+		$$.fo_from = $2;
+		$$.fo_from_port_range = $3;
+		$$.fo_to = $5;
+		$$.fo_to_port_range = $6;
+	}
+	| FROM filt_addr port_range
+	{
+		$$.fo_from = $2;
+		$$.fo_from_port_range = $3;
+		$$.fo_to = NULL;
+		$$.fo_to_port_range = NULL;
+	}
+	| TO filt_addr port_range
+	{
+		$$.fo_from = NULL;
+		$$.fo_from_port_range = NULL;
+		$$.fo_to = $2;
+		$$.fo_to_port_range = $3;
+	}
+	;
+
+filt_addr
+	: iface		{ $$ = $1; }
+	| addr_and_mask	{ $$ = $1; }
+	| TABLE_ID 	{ $$ = npfctl_parse_table_id($1); }
+	| ANY		{ $$ = NULL; }
+	;
+
+addr_and_mask
+	: addr SLASH NUM
+	{
+		$$ = npfctl_parse_fam_addr_mask($1, NULL, &$3);
+	}
+	| addr SLASH HEX
+	{
+		$$ = npfctl_parse_fam_addr_mask($1, NULL, &$3);
+	}
+	| addr SLASH addr
+	{
+		$$ = npfctl_parse_fam_addr_mask($1, $3, NULL);
+	}
+	| addr
+	{
+		$$ = npfctl_parse_fam_addr_mask($1, NULL, NULL);
+	}
+	;
+
+addr_or_iface
+	: addr_and_mask	{ assert($1 != NULL); $$ = $1; }
+	| iface		{ assert($1 != NULL); $$ = $1; }
+	;
+
+addr
+	: IPV4ADDR	{ $$ = $1; }
+	| IPV6ADDR	{ $$ = $1; }
+	;
+
+
+port_range
+	: PORT port		/* just port */
+	{
+		$$ = npfctl_parse_port_range($2, $2);
+	}
+	| PORT port MINUS port	/* port from:to */
+	{
+		$$ = npfctl_parse_port_range($2, $4);
+	}
+	|
+	{
+		$$ = NULL;
+	}
+	;
+
+port
+	: NUM		{ $$ = htons($1); }
+	| IDENTIFIER	{ $$ = npfctl_portno($1); }
+	| VAR_ID
+	{
+		char *s = npfvar_expand_string(npfvar_lookup($1));
+		$$ = npfctl_portno(s);
+	}
+	;
+
+icmp_type_and_code
+	: ICMPTYPE icmp_type
+	{
+		$$ = npfctl_parse_icmp($2, -1);
+	}
+	| ICMPTYPE icmp_type CODE NUM
+	{
+		$$ = npfctl_parse_icmp($2, $4);
+	}
+	| ICMPTYPE icmp_type CODE IDENTIFIER
+	{
+		$$ = npfctl_parse_icmp($2, npfctl_icmpcode($2, $4));
+	}
+	| ICMPTYPE icmp_type CODE VAR_ID
+	{
+		char *s = npfvar_expand_string(npfvar_lookup($4));
+		$$ = npfctl_parse_icmp($2, npfctl_icmpcode($2, s));
+	}
+	|
+	{
+		$$ = npfctl_parse_icmp(-1, -1);
+	}
+	;
+
+tcp_flags_and_mask
+	: FLAGS tcp_flags SLASH tcp_flags
+	{
+		npfvar_add_elements($2, $4);
+		$$ = $2;
+	}
+	| FLAGS tcp_flags
+	{
+		char *s = npfvar_get_data($2, NPFVAR_TCPFLAG, 0);
+		npfvar_add_elements($2, npfctl_parse_tcpflag(s));
+		$$ = $2;
+	}
+	|		{ $$ = NULL; }
+	;
+
+tcp_flags
+	: IDENTIFIER	{ $$ = npfctl_parse_tcpflag($1); }
+	;
+
+icmp_type
+	: NUM		{ $$ = $1; }
+	| IDENTIFIER	{ $$ = npfctl_icmptype($1); }
+	| VAR_ID
+	{
+		char *s = npfvar_expand_string(npfvar_lookup($1));
+		$$ = npfctl_icmptype(s);
+	}
+	;
+
+iface
+	: iface_name
+	{
+		$$ = npfctl_parse_iface($1);
+	}
+	| VAR_ID
+	{
+		npfvar_t *vp = npfvar_lookup($1);
+		const int type = npfvar_get_type(vp);
+
+		switch (type) {
+		case NPFVAR_STRING:
+			$$ = npfctl_parse_iface(npfvar_expand_string(vp));
+			break;
+		case NPFVAR_FAM:
+			$$ = vp;
+			break;
+		case -1:
+			yyerror("undefined variable '%s' for interface", $1);
+			break;
+		default:
+			yyerror("wrong variable '%s' type '%s' or interface",
+			    $1, npfvar_type(type));
+			$$ = NULL;
+			break;
+		}
+	}
+	;
+
+ifindex
+	: iface_name
+	{
+		$$ = npfctl_find_ifindex($1);
+	}
+	| VAR_ID
+	{
+		npfvar_t *vp = npfvar_lookup($1);
+		const int type = npfvar_get_type(vp);
+
+		switch (type) {
+		case NPFVAR_STRING:
+			$$ = npfctl_find_ifindex(npfvar_expand_string(vp));
+			break;
+		case -1:
+			yyerror("undefined variable '%s' for interface", $1);
+			break;
+		default:
+			yyerror("wrong variable '%s' type '%s' for interface",
+			    $1, npfvar_type(type));
+			$$ = -1;
+			break;
+		}
+	}
+	;
+
+iface_name
+	: IDENTIFIER	{ $$ = $1; }
+	| STRING	{ $$ = $1; }
+	;
+
+%%
--- a/usr.sbin/npf/npfctl/npf_parser.c	Sun Jan 08 21:20:40 2012 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,875 +0,0 @@
-/*	$NetBSD: npf_parser.c,v 1.7 2011/11/04 01:00:28 zoltan Exp $	*/
-
-/*-
- * Copyright (c) 2009-2011 The NetBSD Foundation, Inc.
- * All rights reserved.
- *
- * 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.
- */
-
-/*
- * XXX: This needs clean-up!
- */
-
-#include <sys/cdefs.h>
-__RCSID("$NetBSD: npf_parser.c,v 1.7 2011/11/04 01:00:28 zoltan Exp $");
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <ctype.h>
-#include <err.h>
-
-#include "npfctl.h"
-
-/*
- * Few ugly helpers.
- */
-
-#define	PARSE_ERR()			(-1)
-
-#define	PARSE_TOKEN(_arg_)					\
-	if ((p = strtok_r(_arg_, " \t", &sptr)) == NULL)	\
-		return PARSE_ERR();
-
-#define	PARSE_FIRST_TOKEN()		PARSE_TOKEN(p)
-#define	PARSE_NEXT_TOKEN()		PARSE_TOKEN(NULL)
-#define	PARSE_NEXT_TOKEN_NOCHECK()	p = strtok_r(NULL, " \t", &sptr)
-
-/*
- * Global variable list.
- *
- * npfctl_lookup_varlist(): lookups the list by key.
- */
-
-static var_t *			var_list = NULL;
-
-static var_t *
-npfctl_lookup_varlist(char *key)
-{
-	var_t *it;
-
-	for (it = var_list; it != NULL; it = it->v_next)
-		if (strcmp(it->v_key, key) == 0)
-			break;
-	return it;
-}
-
-/*
- * npfctl_parsevalue: helper function to parse a value.
- *
- * => Value could be a single element (no quotes),
- * => or an array of elements between { }.
- */
-static var_t *
-npfctl_parsevalue(char *buf)
-{
-	var_t *vr = NULL;
-	element_t *el = NULL, *it = NULL;
-	char *p = buf, *tend, *sptr;
-
-	switch (*p) {
-	case '$':
-		/* Definition - lookup. */
-		vr = npfctl_lookup_varlist(++p);
-		if (vr == NULL) {
-			errx(EXIT_FAILURE, "variable '%s' is not defined", p);
-		}
-		break;
-	case '{':
-		/* Array. */
-		vr = zalloc(sizeof(var_t));
-		p = strtok_r(buf, ", \t", &sptr);
-		while (p) {
-			if (*p == '}')
-				break;
-			el = zalloc(sizeof(element_t));
-			el->e_data = xstrdup(p);
-			el->e_next = it;
-			vr->v_count++;
-			it = el;
-			p = strtok_r(NULL, ", \t", &sptr);
-		}
-		if (el) {
-			vr->v_type = VAR_ARRAY;
-			vr->v_elements = el;
-		} else {
-			free(vr);
-			vr = NULL;
-		}
-		break;
-	case '<':
-		/* Table. */
-		if ((tend = strchr(++p, '>')) == NULL) {
-			return NULL;
-		}
-		*tend = '\0';
-		if (!npf_table_exists_p(npf_conf, (u_int)atoi(p))) {
-			errx(EXIT_FAILURE, "table '%s' is not defined", p);
-		}
-		vr = zalloc(sizeof(var_t));
-		vr->v_type = VAR_TABLE;
-		/* FALLTHROUGH */
-	default:
-		/* Data. */
-		el = zalloc(sizeof(element_t));
-		el->e_data = xstrdup(p);
-		if (vr == NULL) {
-			vr = zalloc(sizeof(var_t));
-			vr->v_type = VAR_SINGLE;
-		}
-		vr->v_elements = el;
-		vr->v_count = 1;
-	}
-	return vr;
-}
-
-static char *
-npfctl_val_single(var_t *v, char *p)
-{
-	element_t *el;
-
-	if (v->v_type != VAR_SINGLE) {
-		errx(EXIT_FAILURE, "multiple elements in variable '%s'", p);
-	}
-	el = v->v_elements;
-	return el->e_data;
-}
-
-static u_int
-npfctl_val_interface(var_t *v, char *p, bool reqaddr)
-{
-	char *iface = npfctl_val_single(v, p);
-	u_int if_idx;
-
-	if (iface == NULL ||
-		((npfctl_getif(iface, &if_idx, reqaddr, AF_INET) == NULL) &&
-		 (npfctl_getif(iface, &if_idx, reqaddr, AF_INET6) == NULL))) {
-		errx(EXIT_FAILURE, "invalid interface '%s'", iface);
-	}
-	return if_idx;
-}
-
-static int
-npfctl_parsenorm(char *buf, nl_rproc_t *rp)
-{
-	char *p = buf, *sptr;
-	int minttl = 0, maxmss = 0;
-	bool rnd = false, no_df = false;
-
-	p = strtok_r(buf, ", \t", &sptr);
-	if (p == NULL) {
-		return -1;
-	}
-	do {
-		if (strcmp(p, "random-id") == 0) {
-			rnd = true;
-		} else if (strcmp(p, "min-ttl") == 0) {
-			p = strtok_r(NULL, ", \t", &sptr);
-			minttl = atoi(p);
-		} else if (strcmp(p, "max-mss") == 0) {
-			p = strtok_r(NULL, ", \t", &sptr);
-			maxmss = atoi(p);
-		} else if (strcmp(p, "no-df") == 0) {
-			no_df = true;
-		} else {
-			return -1;
-		}
-	} while ((p = strtok_r(NULL, ", \t", &sptr)) != 0);
-
-	return _npf_rproc_setnorm(rp, rnd, no_df, minttl, maxmss);
-}
-
-static int
-npfctl_parserproc(char *buf, nl_rproc_t **rp)
-{
-	char *p = buf, *end;
-
-	DPRINTF(("rproc\t|%s|\n", buf));
-
-	if ((p = strchr(p, '"')) == NULL)
-		return -1;
-	if ((end = strchr(++p, '"')) == NULL)
-		return -1;
-	*end = '\0';
-
-	*rp = npf_rproc_create(p);
-	if (*rp == NULL) {
-		return -1;
-	}
-	return npf_rproc_insert(npf_conf, *rp) ? -1 : 0;
-}
-
-static int
-npfctl_parserproc_lines(char *buf, nl_rproc_t *rp)
-{
-	char *p = buf, *sptr;
-
-	DPRINTF(("rproc\t|%s|\n", p));
-	PARSE_FIRST_TOKEN();
-
-	/* log <interface> */
-	if (strcmp(p, "log") == 0) {
-		var_t *ifvar;
-		u_int if_idx;
-
-		PARSE_NEXT_TOKEN();
-		if ((ifvar = npfctl_parsevalue(p)) == NULL)
-			return PARSE_ERR();
-		if_idx = npfctl_val_interface(ifvar, p, false);
-		(void)_npf_rproc_setlog(rp, if_idx);
-
-	} else if (strcmp(p, "normalize") == 0) {
-		/* normalize ( .. ) */
-		p = strtok_r(NULL, "()", &sptr);
-		if (p == NULL) {
-			return PARSE_ERR();
-		}
-		if (npfctl_parsenorm(p, rp)) {
-			return PARSE_ERR();
-		}
-		PARSE_NEXT_TOKEN_NOCHECK();
-	}
-	return 0;
-}
-
-/*
- * npfctl_parserule: main routine to parse a rule.  Syntax:
- *
- *	{ pass | block } [ in | out ] [ quick ]
- *	    [on <if>] [inet | inet6 ] proto <array>
- *	    from <addr/mask> port <port(s)|range>
- *	    to <addr/mask> port <port(s)|range>
- *	    [ keep state ] [ apply "<rproc>" ]
- */
-static int
-npfctl_parserule(char *buf, nl_rule_t *parent)
-{
-	var_t *from_v = NULL, *fports = NULL, *to_v = NULL, *tports = NULL;
-	char *p, *sptr, *proto = NULL, *tcp_flags = NULL, *rproc = NULL;
-	int icmp_type = -1, icmp_code = -1;
-	bool icmp = false, tcp = false;
-	u_int if_idx = 0;
-	int ret, attr = 0;
-	nl_rule_t *rl;
-	sa_family_t addrfamily = AF_UNSPEC;
-
-	DPRINTF(("rule\t|%s|\n", buf));
-
-	p = buf;
-	PARSE_FIRST_TOKEN();
-
-	/* pass or block (mandatory) */
-	if (strcmp(p, "block") == 0) {
-		PARSE_NEXT_TOKEN();
-		/* return-rst or return-icmp */
-		if (strcmp(p, "return-rst") == 0) {
-			attr |= NPF_RULE_RETRST;
-			PARSE_NEXT_TOKEN();
-		} else if (strcmp(p, "return-icmp") == 0) {
-			attr |= NPF_RULE_RETICMP;
-			PARSE_NEXT_TOKEN();
-		} else if (strcmp(p, "return") == 0) {
-			attr |= NPF_RULE_RETRST | NPF_RULE_RETICMP;
-			PARSE_NEXT_TOKEN();
-		}
-	} else if (strcmp(p, "pass") == 0) {
-		attr |= NPF_RULE_PASS;
-		PARSE_NEXT_TOKEN();
-	} else {
-		return PARSE_ERR();
-	}
-
-	/* in or out */
-	if (strcmp(p, "in") == 0) {
-		attr |= NPF_RULE_IN;
-		PARSE_NEXT_TOKEN();
-	} else if (strcmp(p, "out") == 0) {
-		attr |= NPF_RULE_OUT;
-		PARSE_NEXT_TOKEN();
-	} else {
-		attr |= (NPF_RULE_IN | NPF_RULE_OUT);
-	}
-
-	/* quick */
-	if (strcmp(p, "quick") == 0) {
-		attr |= NPF_RULE_FINAL;
-		PARSE_NEXT_TOKEN();
-	}
-
-	/* on <interface> */
-	if (strcmp(p, "on") == 0) {
-		var_t *ifvar;
-
-		PARSE_NEXT_TOKEN();
-		if ((ifvar = npfctl_parsevalue(p)) == NULL)
-			return PARSE_ERR();
-		if_idx = npfctl_val_interface(ifvar, p, true);
-		PARSE_NEXT_TOKEN();
-	}
-
-	if (strcmp(p, "inet") == 0) {
-		addrfamily = AF_INET;
-		PARSE_NEXT_TOKEN();
-	} else if (strcmp(p, "inet6") == 0) {
-		addrfamily = AF_INET6;
-		PARSE_NEXT_TOKEN();
-	}
-
-	/* proto <proto> */
-	if (strcmp(p, "proto") == 0) {
-		PARSE_NEXT_TOKEN();
-		var_t *pvar = npfctl_parsevalue(p);
-		PARSE_NEXT_TOKEN();
-		if (pvar->v_type != VAR_SINGLE) {
-			errx(EXIT_FAILURE, "only one protocol can be specified");
-		}
-		element_t *el = pvar->v_elements;
-		proto = el->e_data;
-		/* Determine TCP, ICMP. */
-		tcp = (strcmp(proto, "tcp") == 0);
-		icmp = (strcmp(proto, "icmp") == 0);
-	}
-
-	/*
-	 * Can be: "all", "from", "to" or "from + to".
-	 */
-
-	if (strcmp(p, "all") == 0) {
-		/* Should be no "from"/"to" after it. */
-		PARSE_NEXT_TOKEN_NOCHECK();
-		goto last;
-	}
-
-	ret = PARSE_ERR();
-
-	/* from <addr> port <port | range> */
-	if (strcmp(p, "from") == 0) {
-		PARSE_NEXT_TOKEN();
-		if (addrfamily == AF_UNSPEC)
-			addrfamily = npfctl_get_addrfamily(p);
-		from_v = npfctl_parsevalue(p);
-
-		PARSE_NEXT_TOKEN_NOCHECK();
-		if (p && strcmp(p, "port") == 0) {
-			PARSE_NEXT_TOKEN();
-			fports = npfctl_parsevalue(p);
-			PARSE_NEXT_TOKEN_NOCHECK();
-		}
-		ret = 0;
-	}
-
-	/* to <addr> port <port | range> */
-	if (p && strcmp(p, "to") == 0) {
-		PARSE_NEXT_TOKEN();
-		if (addrfamily == AF_UNSPEC)
-			addrfamily = npfctl_get_addrfamily(p);
-		to_v = npfctl_parsevalue(p);
-
-		PARSE_NEXT_TOKEN_NOCHECK();
-		if (p && strcmp(p, "port") == 0) {
-			PARSE_NEXT_TOKEN();
-			tports = npfctl_parsevalue(p);
-			PARSE_NEXT_TOKEN_NOCHECK();
-		}
-		ret = 0;
-	}
-
-	if (ret) {
-		return ret;
-	}
-
-	/* flags <fl/mask> */
-	if (p && strcmp(p, "flags") == 0) {
-		if (icmp) {
-			errx(EXIT_FAILURE,
-			    "TCP flags used with ICMP protocol");
-		}
-		PARSE_NEXT_TOKEN();
-		var_t *tfvar = npfctl_parsevalue(p);
-		tcp_flags = npfctl_val_single(tfvar, p);
-		PARSE_NEXT_TOKEN_NOCHECK();
-	}
-
-	/* icmp-type <t> code <c> */
-	if (p && strcmp(p, "icmp-type") == 0) {
-		if (tcp) {
-			errx(EXIT_FAILURE,
-			    "ICMP options used with TCP protocol");
-		}
-		PARSE_NEXT_TOKEN();
-		icmp_type = atoi(p);
-		PARSE_NEXT_TOKEN_NOCHECK();
-		if (p && strcmp(p, "code") == 0) {
-			PARSE_NEXT_TOKEN();
-			icmp_code = atoi(p);
-			PARSE_NEXT_TOKEN_NOCHECK();
-		}
-	}
-last:
-	/* keep state */
-	if (p && strcmp(p, "keep") == 0) {
-		attr |= NPF_RULE_KEEPSTATE;
-		PARSE_NEXT_TOKEN();
-		if (p == NULL || strcmp(p, "state") != 0) {
-			return PARSE_ERR();
-		}
-		PARSE_NEXT_TOKEN_NOCHECK();
-	}
-
-	/* apply "<rproc>" */
-	if (p && strcmp(p, "apply") == 0) {
-		char *end;
-		PARSE_NEXT_TOKEN();
-		if ((p = strchr(p, '"')) == NULL)
-			return PARSE_ERR();
-		if ((end = strchr(++p, '"')) == NULL)
-			return PARSE_ERR();
-		*end = '\0';
-		if (!npf_rproc_exists_p(npf_conf, p)) {
-			errx(EXIT_FAILURE, "procedure '%s' is not defined", p);
-		}
-		rproc = p;
-		PARSE_NEXT_TOKEN_NOCHECK();
-	}
-
-	/* Should have nothing more. */
-	if (p != NULL) {
-		return PARSE_ERR();
-	}
-
-	/*
-	 * Set the rule attributes and interface.  Generate all protocol data.
-	 */
-	rl = npf_rule_create(NULL, attr, if_idx);
-	npfctl_rule_ncode(rl, proto, tcp_flags, icmp_type, icmp_code,
-	    from_v, addrfamily, fports, to_v, tports);
-	if (rproc && npf_rule_setproc(npf_conf, rl, rproc) != 0) {
-		errx(EXIT_FAILURE, "procedure '%s' is not defined", rproc);
-	}
-	npf_rule_insert(npf_conf, parent, rl, NPF_PRI_NEXT);
-	return 0;
-}
-
-/*
- * npfctl_parsegroup: parse group definition.  Syntax:
- *
- *	group (name <name>, interface <if>, [ in | out ]) { <rules> }
- *	group (default) { <rules> }
- */
-
-#define	GROUP_ATTRS	(NPF_RULE_PASS | NPF_RULE_FINAL)
-
-static int
-npfctl_parsegroup(char *buf, nl_rule_t **rl)
-{
-	char *p = buf, *end, *sptr, *rname = NULL;
-	u_int if_idx = 0;
-	int attr_dir;
-
-	DPRINTF(("group\t|%s|\n", buf));
-
-	p = strchr(p, '(');
-	if (p == NULL)
-		return -1;
-	*p = '\0';
-	end = strchr(++p, ')');
-	if (end == NULL)
-		return -1;
-	*end = '\0';
-	if (strchr(++end, '{') == NULL)
-		return -1;
-	while (isspace((unsigned char)*p))
-		p++;
-
-	/*
-	 * If default group - no other options.
-	 */
-	if (strcmp(p, "default") == 0) {
-		attr_dir = NPF_RULE_DEFAULT | (NPF_RULE_IN | NPF_RULE_OUT);
-		goto done;
-	}
-
-	PARSE_FIRST_TOKEN();
-
-	/* Name of the group (mandatory). */
-	if (strcmp(p, "name") == 0) {
-		PARSE_NEXT_TOKEN()
-		if (*p != '"')
-			return -1;
-		rname = ++p;
-		if ((p = strchr(rname, '"')) == NULL)
-			return -1;
-		*p = '\0';
-		PARSE_NEXT_TOKEN_NOCHECK();
-	}
-
-	/* Interface for this group (optional). */
-	if (p && strcmp(p, "interface") == 0) {
-		var_t *ifvar;
-		PARSE_NEXT_TOKEN();
-		if ((ifvar = npfctl_parsevalue(p)) == NULL)
-			return -1;
-		if_idx = npfctl_val_interface(ifvar, p, true);
-		PARSE_NEXT_TOKEN_NOCHECK();
-	}
-
-	/* Direction (optional). */
-	if (p) {
-		if (strcmp(p, "in") == 0)
-			attr_dir = NPF_RULE_IN;
-		else if (strcmp(p, "out") == 0)
-			attr_dir = NPF_RULE_OUT;
-		else
-			return -1;
-	} else {
-		attr_dir = NPF_RULE_IN | NPF_RULE_OUT;
-	}
-done:
-	*rl = npf_rule_create(rname, GROUP_ATTRS | attr_dir, if_idx);
-	if (*rl == NULL) {
-		return -1;
-	}
-	npf_rule_insert(npf_conf, NULL, *rl, NPF_PRI_NEXT);
-	return 0;
-}
-
-/*
- * npfctl_parsetable: parse table definition.
- *
- *	table <num> type <t> [ dynamic | file <path> ]
- */
-static int
-npfctl_parsetable(char *buf)
-{
-	char *p, *sptr, *fname;
-	unsigned int id, type;
-	nl_table_t *tl;
-
-	DPRINTF(("table\t|%s|\n", buf));
-
-	/* Name of the set. */
-	if ((p = strchr(buf, '"')) == NULL) {
-		return PARSE_ERR();
-	}
-	id = atoi(++p);
-	p = strchr(p, '"');
-	*p++ = '\0';
-
-	PARSE_FIRST_TOKEN();
-
-	/* Table type (mandatory). */
-	if (strcmp(p, "type") != 0) {
-		return PARSE_ERR();
-	}
-	PARSE_NEXT_TOKEN_NOCHECK();
-	if (p == NULL) {
-		return PARSE_ERR();
-	}
-	if (strcmp(p, "hash") == 0) {
-		type = NPF_TABLE_HASH;
-	} else if (strcmp(p, "tree") == 0) {
-		type = NPF_TABLE_RBTREE;
-	} else {
-		errx(EXIT_FAILURE, "invalid table type '%s'", p);
-	}
-	*p = '\0';
-
-	/*
-	 * Setup the table.
-	 */
-	tl = npf_table_create(id, type);
-	if (npf_table_insert(npf_conf, tl)) {
-		errx(EXIT_FAILURE, "table '%d' is already defined\n", id);
-	}
-	PARSE_NEXT_TOKEN();
-
-	/* Dynamic. */
-	if (strcmp(p, "dynamic") == 0) {
-		/* No other options. */
-		return 0;
-	}
-
-	/* File. */
-	if (strcmp(p, "file") != 0) {
-		return PARSE_ERR();
-	}
-	PARSE_NEXT_TOKEN();
-	fname = ++p;
-	p = strchr(p, '"');
-	*p = '\0';
-
-	/* Fill the table. */
-	npfctl_fill_table(tl, fname);
-	return 0;
-}
-
-/*
- * npfctl_parse_nat: parse NAT policy definition.
- *
- *	[bi]nat <if> from <net> to <net/addr> -> <ip>
- *	rdr <if> from <net> to <addr> -> <ip>
- *	nat <if> dynamic "<name>"
- */
-static int
-npfctl_parse_nat(char *buf)
-{
-	var_t *ifvar, *from_v, *to_v, *raddr_v;
-	var_t *tports = NULL, *rport_v = NULL;
-	char *p, *sptr, *raddr_s, *rport_s;
-	npf_addr_t raddr;
-	npf_netmask_t dummy;
-	bool binat, rdr;
-	nl_nat_t *nat;
-	u_int if_idx;
-
-	DPRINTF(("[bi]nat/rdr\t|%s|\n", buf));
-	binat = (strncmp(buf, "binat", 5) == 0);
-	rdr = (strncmp(buf, "rdr", 3) == 0);
-
-	if ((p = strchr(buf, ' ')) == NULL) {
-		return PARSE_ERR();
-	}
-	PARSE_FIRST_TOKEN();
-
-	/* <interface> */
-	if ((ifvar = npfctl_parsevalue(p)) == NULL) {
-		return PARSE_ERR();
-	}
-	if_idx = npfctl_val_interface(ifvar, p, true);
-	PARSE_NEXT_TOKEN();
-
-	/* dynamic <name> */
-	if (!binat && !rdr && strcmp(p, "dynamic") == 0) {
-		char *nname;
-
-		/* Parse name. */
-		PARSE_NEXT_TOKEN()
-		if (*p != '"')
-			return PARSE_ERR();
-		nname = ++p;
-		if ((p = strchr(nname, '"')) == NULL)
-			return PARSE_ERR();
-		*p = '\0';
-
-		/* Create a rule and insert into the NAT list. */
-		nat = npf_rule_create(nname, NPF_RULE_PASS | NPF_RULE_FINAL |
-		    NPF_RULE_OUT | NPF_RULE_IN, if_idx);
-		(void)npf_nat_insert(npf_conf, nat, NPF_PRI_NEXT);
-		return 0;
-	}
-
-	/* from <addr> */
-	if (strcmp(p, "from") != 0) {
-		return PARSE_ERR();
-	}
-	PARSE_NEXT_TOKEN();
-	from_v = npfctl_parsevalue(p);
-	PARSE_NEXT_TOKEN();
-
-	/* to <addr> */
-	if (strcmp(p, "to") != 0) {
-		return PARSE_ERR();
-	}
-	PARSE_NEXT_TOKEN();
-	to_v = npfctl_parsevalue(p);
-	PARSE_NEXT_TOKEN();
-
-	if (rdr && strcmp(p, "port") == 0) {
-		PARSE_NEXT_TOKEN();
-		tports = npfctl_parsevalue(p);
-		PARSE_NEXT_TOKEN();
-	}
-
-	/* -> <ip> */
-	if (strcmp(p, "->") != 0) {
-		return PARSE_ERR();
-	}
-	PARSE_NEXT_TOKEN();
-	raddr_v = npfctl_parsevalue(p);
-	raddr_s = npfctl_val_single(raddr_v, p);
-	npfctl_parse_cidr(raddr_s, npfctl_get_addrfamily(raddr_s), &raddr, &dummy);
-
-	if (rdr) {
-		PARSE_NEXT_TOKEN();
-		if (strcmp(p, "port") != 0) {
-			return PARSE_ERR();
-		}
-		PARSE_NEXT_TOKEN();
-		rport_v = npfctl_parsevalue(p);
-		rport_s = npfctl_val_single(rport_v, p);
-	}
-
-	/*
-	 * Setup NAT policy (rule as filter and extra info), which is
-	 * Outbound NAT (NPF_NATOUT).  Unless it is a redirect rule,
-	 * in which case it is Inbound NAT with specified port.
-	 *
-	 * XXX mess
-	 */
-	if (!rdr) {
-		nat = npf_nat_create(NPF_NATOUT,
-		    binat ? 0 : (NPF_NAT_PORTS | NPF_NAT_PORTMAP),
-		    if_idx, &raddr, AF_INET, 0);
-	} else {
-		in_port_t rport;
-		bool range;
-
-		if (!npfctl_parse_port(rport_s, &range, &rport, &rport)) {
-			errx(EXIT_FAILURE, "invalid service '%s'", rport_s);
-		}
-		if (range) {
-			errx(EXIT_FAILURE, "range is not supported for 'rdr'");
-		}
-		nat = npf_nat_create(NPF_NATIN, NPF_NAT_PORTS,
-		    if_idx, &raddr, AF_INET, rport);
-	}
-	npfctl_rule_ncode(nat, NULL, NULL, -1, -1, from_v, AF_INET, NULL, to_v, tports);
-	(void)npf_nat_insert(npf_conf, nat, NPF_PRI_NEXT);
-
-	/*
-	 * For bi-directional NAT case, create and setup additional
-	 * Inbound NAT (NPF_NATIN) policy.  Note that translation address
-	 * is local IP, and filter criteria is inverted accordingly.
-	 *
-	 * XXX mess
-	 */
-	if (binat) {
-		char *taddr_s = npfctl_val_single(from_v, NULL);
-		npf_addr_t taddr;
-		nl_nat_t *bn;
-
-		npfctl_parse_cidr(taddr_s, npfctl_get_addrfamily(taddr_s), &taddr, &dummy);
-		bn = npf_nat_create(NPF_NATIN, 0, if_idx, &taddr, AF_INET, 0);
-		npfctl_rule_ncode(bn, NULL, NULL, -1, -1,
-		    to_v, AF_INET, NULL, raddr_v, NULL);
-		(void)npf_nat_insert(npf_conf, bn, NPF_PRI_NEXT);
-	}
-	return 0;
-}
-
-/*
- * npfctl_parsevar: parse defined variable.
- *
- * => Assigned value should be with double quotes (").
- * => Value can be an array, use npf_parsevalue().
- * => Insert variable into the global list.
- */
-static int
-npfctl_parsevar(char *buf)
-{
-	char *s = buf, *p, *key;
-	var_t *vr;
-
-	DPRINTF(("def\t|%s|\n", buf));
-
-	if ((p = strpbrk(s, "= \t")) == NULL)
-		return PARSE_ERR();
-
-	/* Validation of '='. */
-	if (*p != '=' && strchr(p, '=') == NULL)
-		return PARSE_ERR();
-	*p = '\0';
-	key = s;
-
-	/* Check for duplicates. */
-	if (npfctl_lookup_varlist(key))
-		return PARSE_ERR();
-
-	/* Parse quotes before. */
-	if ((s = strchr(p + 1, '"')) == NULL)
-		return PARSE_ERR();
-	if ((p = strchr(++s, '"')) == NULL)
-		return PARSE_ERR();
-	*p = '\0';
-
-	if ((vr = npfctl_parsevalue(s)) == NULL)
-		return PARSE_ERR();
-	vr->v_key = xstrdup(key);
-	vr->v_next = var_list;
-	var_list = vr;
-	return 0;
-}
-
-/*
- * npf_parseline: main function parsing a single configuration line.
- *
- * Distinguishes 'group', rule (in-group), 'procedure', in-procedure,
- * 'table' and definitions.  Tracks begin-end of the group and procedure
- * i.e. in-group or in-procedure states.
- */
-int
-npf_parseline(char *buf)
-{
-	static nl_rule_t *curgr = NULL;
-	static nl_rproc_t *currp = NULL;
-	char *p = buf;
-	int ret;
-
-	/* Skip empty lines and comments. */
-	while (isspace((unsigned char)*p))
-		p++;
-	if (*p == '\0' || *p == '\n' || *p == '#')
-		return 0;
-
-	/* At first, check if inside the group or rproc. */
-	if (curgr) {
-		/* End of the group. */
-		if (*p == '}') {
-			curgr = NULL;
-			return 0;
-		}
-		/* Rule. */
-		ret = npfctl_parserule(p, curgr);
-
-	} else if (currp) {
-		/* End of the procedure. */
-		if (*p == '}') {
-			currp = NULL;
-			return 0;
-		}
-		/* Procedure contents. */
-		ret = npfctl_parserproc_lines(p, currp);
-
-	} else if (strncmp(p, "group", 5) == 0) {
-		/* Group. */
-		ret = npfctl_parsegroup(p, &curgr);
-
-	} else if (strncmp(p, "procedure", 9) == 0) {
-		/* Rule procedure. */
-		ret = npfctl_parserproc(p, &currp);
-
-	} else if (strncmp(p, "table", 5) == 0) {
-		/* Table. */
-		ret = npfctl_parsetable(p);
-
-	} else if (strncmp(p, "nat", 3) == 0 || strncmp(p, "rdr", 3) == 0 ||
-	    strncmp(p, "binat", 5) == 0) {
-		/* NAT policy. */
-		ret = npfctl_parse_nat(p);
-
-	} else {
-		/* Defined variable or syntax error. */
-		ret = npfctl_parsevar(p);
-	}
-	return ret;
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr.sbin/npf/npfctl/npf_scan.l	Sun Jan 08 21:34:21 2012 +0000
@@ -0,0 +1,155 @@
+/*	$NetBSD: npf_scan.l,v 1.1 2012/01/08 21:34:21 rmind Exp $	*/
+
+/*-
+ * Copyright (c) 2011-2012 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Martin Husemann.
+ *
+ * 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.
+ */
+
+%{
+#include <stdio.h>
+#include <err.h>
+
+#include "npfctl.h"
+#include "npf_parse.h"
+
+int	yycolumn;
+
+#define	YY_USER_ACTION	yycolumn += yyleng;
+
+%}
+
+%option noyywrap nounput noinput
+
+ID	[a-zA-Z_][a-zA-Z_0-9]*
+NID	[a-zA-Z_0-9]+
+NUMBER	[0-9]+
+
+%%
+table			return TABLE;
+type			return TYPE;
+hash			return HASH;
+tree			return TREE;
+dynamic			return TDYNAMIC;
+file			return TFILE;
+nat			return NAT;
+binat			return BINAT;
+"->"			return ARROW;
+"-"			return MINUS;
+rdr			return RDR;
+procedure		return PROCEDURE;
+\n			yylineno++; yycolumn = 0; return SEPLINE;
+;			return SEPLINE;
+name			return NAME;
+group			return GROUP;
+default			return DEFAULT;
+in			return IN;
+out			return OUT;
+interface		return INTERFACE;
+all			return ALL;
+block			return BLOCK;
+pass			return PASS;
+keep			return KEEP;
+state			return STATE;
+apply			return APPLY;
+quick			return QUICK;
+on			return ON;
+inet6			return INET6;
+inet4			return INET;
+inet			return INET;
+proto			return PROTO;
+family			return FAMILY;
+tcp			return TCP;
+icmp			return ICMP;
+udp			return UDP;
+return-rst		return RETURNRST;
+return-icmp		return RETURNICMP;
+return			return RETURN;
+from			return FROM;
+to			return TO;
+port			return PORT;
+flags			return FLAGS;
+icmp-type		return ICMPTYPE;
+code			return CODE;
+any			return ANY;
+
+"/"			return SLASH;
+"{"			return CURLY_OPEN;
+"}"			return CURLY_CLOSE;
+"("			return PAR_OPEN;
+")"			return PAR_CLOSE;
+","			return COMMA;
+"="			return EQ;
+
+"0x"[0-9a-fA-F]+ {
+			char *endp, *buf = zalloc(yyleng + 1);
+			buf[yyleng] = 0;
+			yylval.num = strtoul(buf+2, &endp, 16);
+			free(buf);
+			return HEX;
+		}
+
+[0-9a-fA-F]+":"[0-9a-fA-F:]* {
+			yylval.str = xstrndup(yytext, yyleng);
+			return IPV6ADDR;
+		}
+
+{NUMBER}"."[0-9][0-9.]* {
+			yylval.str = xstrndup(yytext, yyleng);
+			return IPV4ADDR;
+		}
+
+{NUMBER}	{
+			char *endp, *buf = xstrndup(yytext, yyleng);
+			yylval.num = strtoul(buf, &endp, 10);
+			free(buf);
+			return NUM;
+		}
+
+"<"{NID}">"	{
+			yylval.str = xstrndup(yytext + 1, yyleng - 2);
+			return TABLE_ID;
+		}
+
+"$"{NID}	{
+			yylval.str = xstrndup(yytext + 1, yyleng - 1);
+			return VAR_ID;
+		}
+
+{ID}		{
+			yylval.str = xstrndup(yytext, yyleng);
+			return IDENTIFIER;
+		}
+
+\"[^\"]*\"	{
+			yylval.str = xstrndup(yytext + 1, yyleng - 2);
+			return STRING;
+		}
+
+#.*$		/* drop comment until end of line */
+[ \t]		/* eat whitespace */
+
+:		return COLON;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr.sbin/npf/npfctl/npf_var.c	Sun Jan 08 21:34:21 2012 +0000
@@ -0,0 +1,213 @@
+/*	$NetBSD: npf_var.c,v 1.1 2012/01/08 21:34:21 rmind Exp $	*/
+
+/*-
+ * Copyright (c) 2011-2012 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Christos Zoulas.
+ *
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+__RCSID("$NetBSD: npf_var.c,v 1.1 2012/01/08 21:34:21 rmind Exp $");
+
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#define _NPFVAR_PRIVATE
+#include "npfctl.h"
+
+typedef struct npf_element {
+	void *		e_data;
+	struct npf_element *e_next;
+} npf_element_t;
+
+struct npfvar {
+	char *		v_key;
+	npf_element_t *	v_elements;
+	npf_element_t *	v_last;
+	int		v_type;
+	size_t		v_count;
+	void *		v_next;
+};
+
+static npfvar_t *	var_list = NULL;
+
+npfvar_t *
+npfvar_create(const char *name)
+{
+	npfvar_t *vp = zalloc(sizeof(*vp));
+	vp->v_key = xstrdup(name);
+	return vp;
+}
+
+npfvar_t *
+npfvar_lookup(const char *key)
+{
+	for (npfvar_t *it = var_list; it != NULL; it = it->v_next)
+		if (strcmp(it->v_key, key) == 0)
+			return it;
+	return NULL;
+}
+
+const char *
+npfvar_type(size_t t)
+{
+	if (t >= __arraycount(npfvar_types)) {
+		return "unknown";
+	}
+	return npfvar_types[t];
+}
+
+void
+npfvar_add(npfvar_t *vp)
+{
+	vp->v_next = var_list;
+	var_list = vp;
+}
+
+npfvar_t *
+npfvar_add_element(npfvar_t *vp, int type, const void *data, size_t len)
+{
+	npf_element_t *el;
+
+	if (vp->v_count == 0) {
+		vp->v_type = type;
+	} else if (NPFVAR_TYPE(vp->v_type) != NPFVAR_TYPE(type)) {
+		yyerror("element type '%s' does not match variable type '%s'",
+		    npfvar_type(type), npfvar_type(vp->v_type));
+		return NULL;
+	}
+	vp->v_count++;
+	el = zalloc(sizeof(*el));
+	el->e_data = zalloc(len);
+	memcpy(el->e_data, data, len);
+
+	/* Preserve order of insertion. */
+	if (vp->v_elements == NULL) {
+		vp->v_elements = el;
+	} else {
+		vp->v_last->e_next = el;
+	}
+	vp->v_last = el;
+	return vp;
+}
+
+npfvar_t *
+npfvar_add_elements(npfvar_t *vp, npfvar_t *vp2)
+{
+	if (vp2 == NULL)
+		return vp;
+	if (vp == NULL)
+		return vp2;
+
+	if (vp->v_elements == NULL) {
+		if (vp2->v_elements) {
+			vp->v_type = vp2->v_type;
+			vp->v_elements = vp2->v_elements;
+		}
+	} else if (vp2->v_elements) {
+		if (NPFVAR_TYPE(vp->v_type) != NPFVAR_TYPE(vp2->v_type)) {
+			yyerror("variable '%s' type '%s' does not match "
+			    "variable '%s' type '%s'", vp->v_key,
+			    npfvar_type(vp->v_type),
+			    vp2->v_key, npfvar_type(vp2->v_type));
+			return NULL;
+		}
+		vp->v_last->e_next = vp2->v_elements;
+	}
+	if (vp2->v_elements) {
+		vp->v_last = vp2->v_last;
+		vp->v_count += vp2->v_count;
+		vp2->v_elements = NULL;
+		vp2->v_count = 0;
+		vp2->v_last = NULL;
+	}
+	npfvar_destroy(vp2);
+	return vp;
+}
+
+static void
+npfvar_free_elements(npf_element_t *el)
+{
+	if (el == NULL)
+		return;
+	npfvar_free_elements(el->e_next);
+	free(el->e_data);
+	free(el);
+}
+
+void
+npfvar_destroy(npfvar_t *vp)
+{
+	npfvar_free_elements(vp->v_elements);
+	free(vp->v_key);
+	free(vp);
+}
+
+char *
+npfvar_expand_string(const npfvar_t *vp)
+{
+	return npfvar_get_data(vp, NPFVAR_STRING, 0);
+}
+
+size_t
+npfvar_get_count(const npfvar_t *vp)
+{
+	return vp ? vp->v_count : 0;
+}
+
+int
+npfvar_get_type(const npfvar_t *vp)
+{
+	return vp ? vp->v_type : -1;
+}
+
+void *
+npfvar_get_data(const npfvar_t *vp, int type, size_t idx)
+{
+	npf_element_t *el;
+
+	if (vp == NULL)
+		return NULL;
+
+	if (NPFVAR_TYPE(vp->v_type) != NPFVAR_TYPE(type)) {
+		yyerror("variable '%s' is of type '%s' not '%s'", vp->v_key,
+		    npfvar_type(vp->v_type), npfvar_type(type));
+		return NULL;
+	}
+
+	if (vp->v_count <= idx) {
+		yyerror("variable '%s' has only %zu elements, requested %zu",
+		    vp->v_key, vp->v_count, idx);
+		return NULL;
+	}
+
+	el = vp->v_elements;
+	while (idx--) {
+		el = el->e_next;
+	}
+	return el->e_data;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr.sbin/npf/npfctl/npf_var.h	Sun Jan 08 21:34:21 2012 +0000
@@ -0,0 +1,75 @@
+/*	$NetBSD: npf_var.h,v 1.1 2012/01/08 21:34:21 rmind Exp $	*/
+
+/*-
+ * Copyright (c) 2011-2012 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Christos Zoulas.
+ *
+ * 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.
+ */
+
+#ifndef _NPF_VAR_H_
+#define _NPF_VAR_H_
+
+#define	NPFVAR_STRING		0
+#define	NPFVAR_IDENTIFIER	1
+#define	NPFVAR_VAR_ID		2
+#define NPFVAR_NUM		3
+
+/* Note: primitive types are equivalent. */
+#define NPFVAR_PRIM		NPFVAR_NUM
+#define NPFVAR_TYPE(x)		(((x) & ~NPFVAR_PRIM) ? (x) : 0)
+
+#define	NPFVAR_TABLE		4
+#define	NPFVAR_FAM		5
+#define	NPFVAR_PORT_RANGE	6
+#define	NPFVAR_TCPFLAG		7
+#define	NPFVAR_ICMP		8
+#define	NPFVAR_PROC_OP		9
+#define	NPFVAR_MODULE_ARG	10
+
+#ifdef _NPFVAR_PRIVATE
+static const char *npfvar_types[ ] = {
+	"string", "identifier", "var_id", "num", "table", "fam", "port_range",
+	"tcpflag", "icmp", "proc_op", "module_arg"
+};
+#endif
+
+struct npfvar;
+typedef struct npfvar npfvar_t;
+
+npfvar_t *	npfvar_create(const char *);
+npfvar_t *	npfvar_lookup(const char *);
+const char *	npfvar_type(size_t);
+void		npfvar_add(npfvar_t *);
+npfvar_t *	npfvar_add_element(npfvar_t *, int, const void *, size_t);
+npfvar_t *	npfvar_add_elements(npfvar_t *, npfvar_t *);
+void		npfvar_destroy(npfvar_t *);
+
+char *		npfvar_expand_string(const npfvar_t *);
+size_t		npfvar_get_count(const npfvar_t *);
+int		npfvar_get_type(const npfvar_t *);
+void *		npfvar_get_data(const npfvar_t *, int, size_t);
+
+#endif
--- a/usr.sbin/npf/npfctl/npfctl.c	Sun Jan 08 21:20:40 2012 +0000
+++ b/usr.sbin/npf/npfctl/npfctl.c	Sun Jan 08 21:34:21 2012 +0000
@@ -1,7 +1,7 @@
-/*	$NetBSD: npfctl.c,v 1.7 2011/11/04 01:00:28 zoltan Exp $	*/
+/*	$NetBSD: npfctl.c,v 1.8 2012/01/08 21:34:21 rmind Exp $	*/
 
 /*-
- * Copyright (c) 2009-2011 The NetBSD Foundation, Inc.
+ * Copyright (c) 2009-2012 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.7 2011/11/04 01:00:28 zoltan Exp $");
+__RCSID("$NetBSD: npfctl.c,v 1.8 2012/01/08 21:34:21 rmind Exp $");
 
 #include <sys/ioctl.h>
 #include <sys/stat.h>
@@ -45,6 +45,11 @@
 
 #include "npfctl.h"
 
+extern int		yylineno, yycolumn;
+extern const char *	yyfilename;
+extern int		yyparse(void);
+extern void		yyrestart(FILE *);
+
 #define	NPFCTL_START		1
 #define	NPFCTL_STOP		2
 #define	NPFCTL_RELOAD		3
@@ -77,9 +82,8 @@
 void *
 zalloc(size_t sz)
 {
-	void *p;
+	void *p = malloc(sz);
 
-	p = malloc(sz);
 	if (p == NULL) {
 		err(EXIT_FAILURE, "zalloc");
 	}
@@ -87,14 +91,36 @@
 	return p;
 }
 
+void *
+xrealloc(void *ptr, size_t size)
+{
+	void *p = realloc(ptr, size);
+
+	if (p == NULL) {
+		err(EXIT_FAILURE, "xrealloc");
+	}
+	return p;
+}
+
 char *
 xstrdup(const char *s)
 {
+	char *p = strdup(s);
+
+	if (p == NULL) {
+		err(EXIT_FAILURE, "xstrdup");
+	}
+	return p;
+}
+
+char *
+xstrndup(const char *s, size_t len)
+{
 	char *p;
 
-	p = strdup(s);
+	p = strndup(s, len);
 	if (p == NULL) {
-		err(EXIT_FAILURE, "xstrdup");
+		err(EXIT_FAILURE, "xstrndup");
 	}
 	return p;
 }
@@ -123,37 +149,24 @@
 static void
 npfctl_parsecfg(const char *cfg)
 {
-	char *buf, *p;
 	FILE *fp;
-	size_t n;
-	int l;
 
 	fp = fopen(cfg, "r");
 	if (fp == NULL) {
 		err(EXIT_FAILURE, "open '%s'", cfg);
 	}
-	l = 0;
-	buf = NULL;
-	while (getline(&buf, &n, fp) != -1) {
-		l++;
-		p = strpbrk(buf, "#\n");
-		if (p != NULL) {
-			*p = '\0';
-		}
-		if (npf_parseline(buf)) {
-			fprintf(stderr, "invalid syntax at line %d\n", l);
-			exit(EXIT_FAILURE);
-		}
-	}
-	if (buf != NULL) {
-		free(buf);
-	}
+	yyrestart(fp);
+	yylineno = 1;
+	yycolumn = 0;
+	yyfilename = cfg;
+	yyparse();
+	fclose(fp);
 }
 
 static int
 npfctl_print_stats(int fd)
 {
-	uint64_t *st = malloc(NPF_STATS_SIZE);
+	uint64_t *st = zalloc(NPF_STATS_SIZE);
 
 	if (ioctl(fd, IOC_NPF_STATS, &st) != 0) {
 		err(EXIT_FAILURE, "ioctl(IOC_NPF_STATS)");
@@ -221,18 +234,12 @@
 		ret = ioctl(fd, IOC_NPF_SWITCH, &boolval);
 		break;
 	case NPFCTL_RELOAD:
-		npfctl_init_data();
+		npfctl_config_init(false);
 		npfctl_parsecfg(argc < 3 ? NPF_CONF_PATH : argv[2]);
-		ret = npfctl_ioctl_send(fd);
+		ret = npfctl_config_send(fd);
 		break;
 	case NPFCTL_FLUSH:
-		/* Pass empty configuration to flush. */
-		npfctl_init_data();
-		ret = npfctl_ioctl_send(fd);
-		if (ret) {
-			break;
-		}
-		ret = npf_sessions_send(fd, NULL);
+		ret = npfctl_config_flush(fd);
 		break;
 	case NPFCTL_TABLE:
 		if (argc < 5) {
@@ -252,12 +259,17 @@
 			tbl.nct_action = 0;
 			arg = argv[3];
 		}
-		if (!npfctl_parse_cidr(arg,
-		    npfctl_get_addrfamily(arg),
-		    &tbl.nct_addr, &tbl.nct_mask)) {
+		fam_addr_mask_t *fam = npfctl_parse_cidr(arg);
+		if (fam == NULL) {
 			errx(EXIT_FAILURE, "invalid CIDR '%s'", arg);
 		}
+		memcpy(&tbl.nct_addr, &fam->fam_addr, sizeof(npf_addr_t));
+		tbl.nct_mask = fam->fam_mask;
 		ret = ioctl(fd, IOC_NPF_TABLE, &tbl);
+		if (tbl.nct_action == 0) {
+			printf("%s\n", ret ? "not found" : "found");
+			exit(ret ? EXIT_FAILURE : EXIT_SUCCESS);
+		}
 		break;
 	case NPFCTL_STATS:
 		ret = npfctl_print_stats(fd);
@@ -287,28 +299,26 @@
 main(int argc, char **argv)
 {
 	char *cmd;
-	int n;
 
 	if (argc < 2) {
 		usage();
 	}
 	cmd = argv[1];
 
-#ifdef _NPF_TESTING
-	/* Special testing case. */
-	npfctl_init_data();
-	npfctl_parsecfg("npf.conf");
-	npfctl_ioctl_send(0);
-	return 0;
-#endif
+	if (strcmp(cmd, "debug") == 0) {
+		const char *cfg = argc > 2 ? argv[2] : "npf.conf";
+		npfctl_config_init(true);
+		npfctl_parsecfg(cfg);
+		npfctl_config_send(0);
+		return EXIT_SUCCESS;
+	}
 
 	/* Find and call the subroutine */
-	for (n = 0; operations[n].cmd != NULL; n++) {
+	for (int n = 0; operations[n].cmd != NULL; n++) {
 		if (strcmp(cmd, operations[n].cmd) != 0)
 			continue;
 		npfctl(operations[n].action, argc, argv);
-		return 0;
+		return EXIT_SUCCESS;
 	}
 	usage();
-	return 0;
 }
--- a/usr.sbin/npf/npfctl/npfctl.h	Sun Jan 08 21:20:40 2012 +0000
+++ b/usr.sbin/npf/npfctl/npfctl.h	Sun Jan 08 21:34:21 2012 +0000
@@ -1,7 +1,7 @@
-/*	$NetBSD: npfctl.h,v 1.7 2011/11/04 01:00:28 zoltan Exp $	*/
+/*	$NetBSD: npfctl.h,v 1.8 2012/01/08 21:34:21 rmind Exp $	*/
 
 /*-
- * Copyright (c) 2009-2011 The NetBSD Foundation, Inc.
+ * Copyright (c) 2009-2012 The NetBSD Foundation, Inc.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -29,9 +29,9 @@
 #ifndef _NPFCTL_H_
 #define _NPFCTL_H_
 
-#include <sys/types.h>
 #include <stdio.h>
 #include <stdbool.h>
+#include <inttypes.h>
 
 #include <net/npf_ncode.h>
 #include <net/npf.h>
@@ -39,67 +39,119 @@
 #define	_NPF_PRIVATE
 #include <npf.h>
 
-#ifdef DEBUG
-#define	DPRINTF(x)	printf x
-#else
-#define	DPRINTF(x)
-#endif
+#include "npf_var.h"
 
 #define	NPF_DEV_PATH	"/dev/npf"
 #define	NPF_CONF_PATH	"/etc/npf.conf"
 #define	NPF_SESSDB_PATH	"/var/db/npf_sessions.db"
 
-typedef struct {
-	char *		e_data;
-	void *		e_next;
-} element_t;
+typedef struct fam_addr_mask {
+	sa_family_t	fam_family;
+	npf_addr_t	fam_addr;
+	npf_netmask_t	fam_mask;
+	npfvar_t *	fam_interface;
+} fam_addr_mask_t;
 
-#define	VAR_SINGLE	1
-#define	VAR_ARRAY	2
-#define	VAR_TABLE	3
+typedef struct port_range {
+	in_port_t	pr_start;
+	in_port_t	pr_end;
+} port_range_t;
+
+typedef struct filt_opts {
+	npfvar_t *	fo_from;
+	npfvar_t *	fo_from_port_range;
+	npfvar_t *	fo_to;
+	npfvar_t *	fo_to_port_range;
+} filt_opts_t;
 
-typedef struct {
-	char *		v_key;
-	element_t *	v_elements;
-	int		v_type;
-	int		v_count;
-	void *		v_next;
-} var_t;
+typedef struct opt_proto {
+	int		op_proto;
+	npfvar_t *	op_opts;
+} opt_proto_t;
+
+typedef struct rule_group {
+	const char *	rg_name;
+	int		rg_attr;
+	u_int		rg_ifnum;
+} rule_group_t;
 
-extern nl_config_t *	npf_conf;
+typedef struct proc_op {
+	const char *	po_name;
+	npfvar_t *	po_opts;
+} proc_op_t;
 
+typedef struct module_arg {
+	const char *	ma_name;
+	npfvar_t *	ma_opts;
+} module_arg_t;
+
+void		yyerror(const char *, ...) __printflike(1, 2);
 void *		zalloc(size_t);
+void *		xrealloc(void *, size_t);
 char *		xstrdup(const char *);
-
-void		npfctl_init_data(void);
-int		npfctl_ioctl_send(int);
+char *		xstrndup(const char *, size_t);
 
-struct ifaddrs *npfctl_getif(char *, unsigned int *, bool, sa_family_t);
-void		npfctl_create_mask(sa_family_t, u_int, npf_addr_t *);
-sa_family_t	npfctl_get_addrfamily(const char *);
-sa_family_t	npfctl_parse_cidr(char *, sa_family_t, npf_addr_t *, npf_netmask_t *);
-bool		npfctl_parse_port(char *, bool *, in_port_t *, in_port_t *);
+bool		npfctl_table_exists_p(const char *);
+in_port_t	npfctl_portno(const char *);
+uint8_t		npfctl_icmpcode(uint8_t, const char *);
+uint8_t		npfctl_icmptype(const char *);
+unsigned long   npfctl_find_ifindex(const char *);
+npfvar_t *	npfctl_parse_tcpflag(const char *);
+npfvar_t *	npfctl_parse_table_id(const char *);
+npfvar_t * 	npfctl_parse_icmpcode(const char *);
+npfvar_t * 	npfctl_parse_icmp(uint8_t, uint8_t);
+npfvar_t *	npfctl_parse_iface(const char *);
+npfvar_t *	npfctl_parse_port_range(in_port_t, in_port_t);
+npfvar_t *	npfctl_parse_fam_addr_mask(const char *, const char *,
+		    unsigned long *);
+fam_addr_mask_t *npfctl_parse_cidr(char *);
 
-void		npfctl_fill_table(nl_table_t *, char *);
+/*
+ * N-code generation interface.
+ */
+
+typedef struct nc_ctx nc_ctx_t;
 
-void		npfctl_rule_ncode(nl_rule_t *, char *, char *,
-		    int, int, var_t *, sa_family_t, var_t *, var_t *, var_t *);
+#define	NC_MATCH_DST		0x01
+#define	NC_MATCH_SRC		0x02
 
-size_t		npfctl_calc_ncsize(int []);
-size_t		npfctl_failure_offset(int []);
+#define	NC_MATCH_TCP		0x04
+#define	NC_MATCH_UDP		0x08
+
+nc_ctx_t *	npfctl_ncgen_create(void);
+void *		npfctl_ncgen_complete(nc_ctx_t *, size_t *);
+void		npfctl_ncgen_print(const void *, size_t);
 
-void		npfctl_gennc_ether(void **, int, uint16_t);
-void		npfctl_gennc_v4cidr(void **, int,
-		    const npf_addr_t *, const npf_netmask_t, bool);
-void		npfctl_gennc_v6cidr(void **, int,
-		    const npf_addr_t *, const npf_netmask_t, bool);
-void		npfctl_gennc_icmp(void **, int, int, int);
-void		npfctl_gennc_tcpfl(void **, int , uint8_t, uint8_t);
-void		npfctl_gennc_ports(void **, int,
-		    in_port_t, in_port_t, bool, bool);
-void		npfctl_gennc_tbl(void **, int, u_int , bool);
-void		npfctl_gennc_complete(void **);
+void		npfctl_ncgen_group(nc_ctx_t *);
+void		npfctl_ncgen_endgroup(nc_ctx_t *);
+
+void		npfctl_gennc_v4cidr(nc_ctx_t *, int, const npf_addr_t *,
+		    const npf_netmask_t);
+void		npfctl_gennc_v6cidr(nc_ctx_t *, int, const npf_addr_t *,
+		    const npf_netmask_t);
+void		npfctl_gennc_ports(nc_ctx_t *, int, in_port_t, in_port_t);
+void		npfctl_gennc_icmp(nc_ctx_t *, int, int);
+void		npfctl_gennc_tbl(nc_ctx_t *, int, u_int);
+void		npfctl_gennc_tcpfl(nc_ctx_t *, uint8_t, uint8_t);
+
+/*
+ * Configuration building interface.
+ */
 
-int		npf_parseline(char *);
+#define	NPFCTL_RDR		1
+#define	NPFCTL_BINAT		2
+#define	NPFCTL_NAT		3
+
+void		npfctl_config_init(bool);
+int		npfctl_config_send(int);
+int		npfctl_config_flush(int);
+
+void		npfctl_build_rproc(const char *, npfvar_t *);
+void		npfctl_build_group(const char *, int, u_int);
+void		npfctl_build_rule(int, u_int, sa_family_t,
+		    const opt_proto_t *, const filt_opts_t *, const char *);
+void		npfctl_build_nat(int, u_int, const filt_opts_t *,
+		    npfvar_t *, npfvar_t *);
+void		npfctl_build_table(const char *, u_int, const char *);
 
 #endif