npfctl(8): add show-config command. Also, update syntax. trunk
authorrmind <rmind@NetBSD.org>
Wed, 30 May 2012 21:30:07 +0000
branchtrunk
changeset 211312 eec10c99ae2c
parent 211311 7a3c85e12159
child 211313 26a7a6991d9b
npfctl(8): add show-config command. Also, update syntax.
usr.sbin/npf/npfctl/Makefile
usr.sbin/npf/npfctl/npf.conf.5
usr.sbin/npf/npfctl/npf_data.c
usr.sbin/npf/npfctl/npf_disassemble.c
usr.sbin/npf/npfctl/npf_ncgen.c
usr.sbin/npf/npfctl/npf_parse.y
usr.sbin/npf/npfctl/npf_scan.l
usr.sbin/npf/npfctl/npf_var.c
usr.sbin/npf/npfctl/npfctl.c
usr.sbin/npf/npfctl/npfctl.h
--- a/usr.sbin/npf/npfctl/Makefile	Wed May 30 20:15:56 2012 +0000
+++ b/usr.sbin/npf/npfctl/Makefile	Wed May 30 21:30:07 2012 +0000
@@ -1,4 +1,4 @@
-# $NetBSD: Makefile,v 1.6 2012/03/10 22:21:50 christos Exp $
+# $NetBSD: Makefile,v 1.7 2012/05/30 21:30:07 rmind Exp $
 
 PROG=		npfctl
 MAN=		npfctl.8 npf.conf.5
@@ -9,7 +9,6 @@
 CPPFLAGS+=	-I${.CURDIR}
 SRCS+=		npf_scan.l npf_parse.y
 YHEADER=	1
-YFLAGS+=	-v
 
 LDADD+=		-lnpf -lprop -lutil -ly
 DPADD+=		${LIBNPF} ${LIBPROP} ${LIBUTIL}
--- a/usr.sbin/npf/npfctl/npf.conf.5	Wed May 30 20:15:56 2012 +0000
+++ b/usr.sbin/npf/npfctl/npf.conf.5	Wed May 30 21:30:07 2012 +0000
@@ -1,4 +1,4 @@
-.\"    $NetBSD: npf.conf.5,v 1.9 2012/02/06 00:41:36 rmind Exp $
+.\"    $NetBSD: npf.conf.5,v 1.10 2012/05/30 21:30:07 rmind Exp $
 .\"
 .\" Copyright (c) 2009-2012 The NetBSD Foundation, Inc.
 .\" All rights reserved.
@@ -27,7 +27,7 @@
 .\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 .\" POSSIBILITY OF SUCH DAMAGE.
 .\"
-.Dd February 5, 2012
+.Dd May 27, 2012
 .Dt NPF.CONF 5
 .Os
 .Sh NAME
@@ -64,13 +64,13 @@
 Rules defined first are accordingly inspected first.
 All rules in the group are inspected sequentially, and the last matching
 dictates the action to be taken.
-Rules, however, may be explicitly marked as final (that is, "quick").
+Rules, however, may be explicitly marked as final.
 In such cases, processing stops after encountering the first matching rule
 marked as final.
 If there is no matching rule in the custom group, then rules in the default
 group will be inspected.
 .Pp
-Stateful filtering is supported using the "keep state" keyword.
+Stateful filtering is supported using the "stateful" keyword.
 In such cases, state (a session) is created and any further packets
 of the connection are tracked.
 Packets in backwards stream, after having been confirmed to belong to
@@ -155,9 +155,9 @@
 
 ruleset		= "{" rule1 \*[Lt]newline\*[Gt], rule2 \*[Lt]newline\*[Gt], ... "}"
 
-rule		= ( "block" block-opts | "pass" ) [ "in" | out" ] [ "quick" ]
+rule		= ( "block" block-opts | "pass" ) [ "stateful" ] [ "in" | out" ] [ "final" ]
 		  [ "on" iface ] [ "family" fam-opt ] [ "proto" \*[Lt]protocol\*[Gt] ]
-		  ( "all" | filt-opts ) [ "keep state" ] [ "apply" rproc ] }
+		  ( "all" | filt-opts ) [ "apply" rproc ] }
 
 fam-opt		= [ "inet" | "inet6" ]
 block-opts	= [ "return-rst" | "return-icmp" | "return" ]
@@ -197,20 +197,20 @@
 }
 
 group (name "external", interface $ext_if) {
-	block in quick from \*[Lt]1\*[Gt]
-	pass out quick from $ext_if keep state apply "rid"
+	block in final from \*[Lt]1\*[Gt]
+	pass stateful out final from $ext_if apply "rid"
 
-	pass in quick family inet proto tcp to $ext_if port ssh apply "log"
-	pass in quick proto tcp to $ext_if port $services_tcp
-	pass in quick proto udp to $ext_if port $services_udp
-	pass in quick proto tcp to $ext_if port 49151-65535	# Passive FTP
-	pass in quick proto udp to $ext_if port 33434-33600	# Traceroute
+	pass in final family inet proto tcp to $ext_if port ssh apply "log"
+	pass in final proto tcp to $ext_if port $services_tcp
+	pass in final proto udp to $ext_if port $services_udp
+	pass in final proto tcp to $ext_if port 49151-65535	# Passive FTP
+	pass in final proto udp to $ext_if port 33434-33600	# Traceroute
 }
 
 group (name "internal", interface $int_if) {
 	block in all
-	pass in quick from \*[Lt]2\*[Gt]
-	pass out quick all
+	pass in final from \*[Lt]2\*[Gt]
+	pass out final all
 }
 
 group (default) {
--- a/usr.sbin/npf/npfctl/npf_data.c	Wed May 30 20:15:56 2012 +0000
+++ b/usr.sbin/npf/npfctl/npf_data.c	Wed May 30 21:30:07 2012 +0000
@@ -1,4 +1,4 @@
-/*	$NetBSD: npf_data.c,v 1.11 2012/02/26 21:50:05 christos Exp $	*/
+/*	$NetBSD: npf_data.c,v 1.12 2012/05/30 21:30:07 rmind Exp $	*/
 
 /*-
  * Copyright (c) 2009-2012 The NetBSD Foundation, Inc.
@@ -31,7 +31,7 @@
  */
 
 #include <sys/cdefs.h>
-__RCSID("$NetBSD: npf_data.c,v 1.11 2012/02/26 21:50:05 christos Exp $");
+__RCSID("$NetBSD: npf_data.c,v 1.12 2012/05/30 21:30:07 rmind Exp $");
 
 #include <sys/types.h>
 #include <sys/null.h>
@@ -211,7 +211,7 @@
 
 /*
  * npfctl_parse_port_range: create a port-range variable.  Note that the
- * passed port numbers are in network byte order.
+ * passed port numbers should be in host byte order.
  */
 npfvar_t *
 npfctl_parse_port_range(in_port_t s, in_port_t e)
@@ -219,8 +219,8 @@
 	npfvar_t *vp = npfvar_create(".port_range");
 	port_range_t pr;
 
-	pr.pr_start = s;
-	pr.pr_end = e;
+	pr.pr_start = htons(s);
+	pr.pr_end = htons(e);
 
 	if (!npfvar_add_element(vp, NPFVAR_PORT_RANGE, &pr, sizeof(pr)))
 		goto out;
@@ -235,14 +235,15 @@
 npfctl_parse_port_range_variable(const char *v)
 {
 	npfvar_t *vp = npfvar_lookup(v);
-	in_port_t p;
-	port_range_t *pr;
 	size_t count = npfvar_get_count(vp);
 	npfvar_t *pvp = npfvar_create(".port_range");
+	port_range_t *pr;
+	in_port_t p;
 
 	for (size_t i = 0; i < count; i++) {
 		int type = npfvar_get_type(vp, i);
 		void *data = npfvar_get_data(vp, type, i);
+
 		switch (type) {
 		case NPFVAR_IDENTIFIER:
 		case NPFVAR_STRING:
@@ -261,13 +262,11 @@
 		default:
 			yyerror("wrong variable '%s' type '%s' for port range",
 			    v, npfvar_type(type));
-			goto out;
+			npfvar_destroy(pvp);
+			return NULL;
 		}
 	}
 	return pvp;
-out:
-	npfvar_destroy(pvp);
-	return NULL;
 }
 
 npfvar_t *
@@ -350,7 +349,7 @@
 /*
  * npfctl_portno: convert port identifier (string) to a number.
  *
- * => Returns port number in network byte order.
+ * => Returns port number in host byte order.
  */
 in_port_t
 npfctl_portno(const char *port)
@@ -383,7 +382,7 @@
 	}
 out:
 	freeaddrinfo(rai);
-	return p;
+	return ntohs(p);
 }
 
 npfvar_t *
--- a/usr.sbin/npf/npfctl/npf_disassemble.c	Wed May 30 20:15:56 2012 +0000
+++ b/usr.sbin/npf/npfctl/npf_disassemble.c	Wed May 30 21:30:07 2012 +0000
@@ -1,4 +1,4 @@
-/*	$NetBSD: npf_disassemble.c,v 1.3 2012/03/12 15:32:02 christos Exp $	*/
+/*	$NetBSD: npf_disassemble.c,v 1.4 2012/05/30 21:30:07 rmind Exp $	*/
 
 /*-
  * Copyright (c) 2012 The NetBSD Foundation, Inc.
@@ -30,15 +30,17 @@
  */
 
 #include <sys/cdefs.h>
-__RCSID("$NetBSD: npf_disassemble.c,v 1.3 2012/03/12 15:32:02 christos Exp $");
+__RCSID("$NetBSD: npf_disassemble.c,v 1.4 2012/05/30 21:30:07 rmind Exp $");
 
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
+#include <errno.h>
 #include <err.h>
 #include <sys/socket.h>
 #include <netinet/in.h>
+#include <net/if.h>
 
 #include <util.h>
 
@@ -47,6 +49,21 @@
 
 #include "npfctl.h"
 
+enum {
+	NPF_SHOW_SRCADDR,
+	NPF_SHOW_DSTADDR,
+	NPF_SHOW_SRCPORT,
+	NPF_SHOW_DSTPORT,
+	NPF_SHOW_ICMP,
+	NPF_SHOW_TCPF,
+	NPF_SHOW_COUNT,
+};
+
+struct nc_inf {
+	npfvar_t *	nci_vlist[NPF_SHOW_COUNT];
+	bool		nci_srcdst;
+};
+
 #define ADVANCE(n, rv) \
 	do { \
 		if (len < sizeof(*pc) * (n)) { \
@@ -84,10 +101,24 @@
 	return q;
 }
 
+static void
+npfctl_ncode_add_vp(char *buf, nc_inf_t *nci, unsigned idx)
+{
+	npfvar_t *vl = nci->nci_vlist[idx];
+
+	if (vl == NULL) {
+		vl = npfvar_create(".list");
+		nci->nci_vlist[idx] = vl;
+	}
+	npfvar_t *vp = npfvar_create(".string");
+	npfvar_add_element(vp, NPFVAR_STRING, buf, strlen(buf) + 1);
+	npfvar_add_elements(vl, vp);
+}
+
 static const char *
 npfctl_ncode_operand(char *buf, size_t bufsiz, uint8_t op, const uint32_t *st,
     const uint32_t *ipc, const uint32_t **pcv, size_t *lenv,
-    const uint32_t ***t, size_t *l, size_t *m)
+    const uint32_t ***t, size_t *l, size_t *m, nc_inf_t *nci)
 {
 	const uint32_t *pc = *pcv;
 	size_t len = *lenv;
@@ -99,14 +130,14 @@
 
 	case NPF_OPERAND_REGISTER:
 		if (*pc & ~0x3) {
-			warnx("Bad register operand 0x%x at offset %td",
+			warnx("invalid register operand 0x%x at offset %td",
 			    *pc, pc - st);
 			return NULL;
 		}
 		snprintf(buf, bufsiz, "R%d", *pc);
 		ADVANCE(1, NULL);
 		break;
-				
+
 	case NPF_OPERAND_KEY:
 		snprintf(buf, bufsiz, "key=<0x%x>", *pc);
 		ADVANCE(1, NULL);
@@ -119,12 +150,15 @@
 
 	case NPF_OPERAND_SD:
 		if (*pc & ~0x1) {
-			warnx("Bad src/dst operand 0x%x at offset %td",
+			warnx("invalid src/dst operand 0x%x at offset %td",
 			    *pc, pc - st);
 			return NULL;
 		}
-		snprintf(buf, bufsiz, "%s", *pc == NPF_OPERAND_SD_SRC ?
-		    "SRC" : "DST");
+		bool srcdst = (*pc == NPF_OPERAND_SD_SRC);
+		if (nci) {
+			nci->nci_srcdst = srcdst;
+		}
+		snprintf(buf, bufsiz, "%s", srcdst ? "SRC" : "DST");
 		ADVANCE(1, NULL);
 		break;
 
@@ -140,8 +174,11 @@
 		sin->sin_family = AF_INET;
 		sin->sin_port = 0;
 		memcpy(&sin->sin_addr, pc, sizeof(sin->sin_addr));
-		sockaddr_snprintf(buf, bufsiz, "%a", (struct sockaddr *)(void *)
-		    sin);
+		sockaddr_snprintf(buf, bufsiz, "%a", (struct sockaddr *)sin);
+		if (nci) {
+			npfctl_ncode_add_vp(buf, nci, nci->nci_srcdst ?
+			    NPF_SHOW_SRCADDR : NPF_SHOW_DSTADDR);
+		}
 		ADVANCE(sizeof(sin->sin_addr) / sizeof(*pc), NULL);
 		break;
 	}
@@ -151,8 +188,11 @@
 		sin6->sin6_family = AF_INET6;
 		sin6->sin6_port = 0;
 		memcpy(&sin6->sin6_addr, pc, sizeof(sin6->sin6_addr));
-		sockaddr_snprintf(buf, bufsiz, "%a", (struct sockaddr *)(void *)
-		    sin6);
+		sockaddr_snprintf(buf, bufsiz, "%a", (struct sockaddr *)sin6);
+		if (nci) {
+			npfctl_ncode_add_vp(buf, nci, nci->nci_srcdst ?
+			    NPF_SHOW_SRCADDR : NPF_SHOW_DSTADDR);
+		}
 		ADVANCE(sizeof(sin6->sin6_addr) / sizeof(*pc), NULL);
 		break;
 	}
@@ -161,47 +201,71 @@
 		ADVANCE(1, NULL);
 		break;
 
-	case NPF_OPERAND_SUBNET:
+	case NPF_OPERAND_SUBNET: {
 		snprintf(buf, bufsiz, "/%d", *pc);
+		if (nci) {
+			npfctl_ncode_add_vp(buf, nci, nci->nci_srcdst ?
+			    NPF_SHOW_SRCADDR : NPF_SHOW_DSTADDR);
+		}
 		ADVANCE(1, NULL);
 		break;
-
+	}
 	case NPF_OPERAND_LENGTH:
 		snprintf(buf, bufsiz, "length=%d", *pc);
 		ADVANCE(1, NULL);
 		break;
 
 	case NPF_OPERAND_TABLE_ID:
+		if (nci) {
+			snprintf(buf, bufsiz, "<%d>", *pc);
+			npfctl_ncode_add_vp(buf, nci, nci->nci_srcdst ?
+			    NPF_SHOW_SRCADDR : NPF_SHOW_DSTADDR);
+		}
 		snprintf(buf, bufsiz, "id=%d", *pc);
 		ADVANCE(1, NULL);
 		break;
 
 	case NPF_OPERAND_ICMP4_TYPE_CODE:
 		if (*pc & ~0xffff) {
-			warnx("Bad icmp/type operand 0x%x at offset %td",
+			warnx("invalid icmp/type operand 0x%x at offset %td",
 			    *pc, pc - st);
 			return NULL;
 		}
-		snprintf(buf, bufsiz, "type=%d, code=%d", *pc >> 8,
-		    *pc & 0xff);
+		snprintf(buf, bufsiz, "type=%d, code=%d", *pc >> 8, *pc & 0xff);
+		if (nci) {
+			npfctl_ncode_add_vp(buf, nci, NPF_SHOW_ICMP);
+		}
 		ADVANCE(1, NULL);
 		break;
 
 	case NPF_OPERAND_TCP_FLAGS_MASK:
 		if (*pc & ~0xffff) {
-			warnx("Bad flags/mask operand 0x%x at offset %td",
+			warnx("invalid flags/mask operand 0x%x at offset %td",
 			    *pc, pc - st);
 			return NULL;
 		}
-		snprintf(buf, bufsiz, "type=%d, code=%d", *pc >> 8,
-		    *pc & 0xff);
+		snprintf(buf, bufsiz, "type=%d, code=%d", *pc >> 8, *pc & 0xff);
+		if (nci) {
+			npfctl_ncode_add_vp(buf, nci, NPF_SHOW_TCPF);
+		}
 		ADVANCE(1, NULL);
 		break;
 
-	case NPF_OPERAND_PORT_RANGE:
-		snprintf(buf, bufsiz, "%d-%d", (*pc >> 16), *pc & 0xffff);
+	case NPF_OPERAND_PORT_RANGE: {
+		in_port_t p1 = ntohs(*pc >> 16), p2 = ntohs(*pc & 0xffff);
+
+		if (p1 == p2) {
+			snprintf(buf, bufsiz, "%d", p1);
+		} else {
+			snprintf(buf, bufsiz, "%d-%d", p1, p2);
+		}
+		if (nci) {
+			npfctl_ncode_add_vp(buf, nci, nci->nci_srcdst ?
+			    NPF_SHOW_SRCPORT : NPF_SHOW_DSTPORT);
+		}
 		ADVANCE(1, NULL);
 		break;
+	}
 	default:
 		warnx("invalid operand %d at offset %td", op, pc - st);
 		return NULL;
@@ -213,7 +277,7 @@
 }
 
 int
-npfctl_ncode_disassemble(FILE *fp, const void *v, size_t len)
+npfctl_ncode_disassemble(FILE *fp, const void *v, size_t len, nc_inf_t *nci)
 {
 	const uint32_t *ipc, *pc = v;
 	const uint32_t *st = v;
@@ -228,7 +292,7 @@
 	while (len) {
 		/* Get the opcode */
 		if (*pc & ~0xff) {
-			warnx("bad opcode 0x%x at offset (%td)", *pc,
+			warnx("invalid opcode 0x%x at offset (%td)", *pc,
 			    pc - st);
 			goto out;
 		}
@@ -238,26 +302,186 @@
 			    pc - st);
 			goto out;
 		}
+
 		ipc = pc;
 		target = npfctl_ncode_get_target(pc, targ, tlen);
-		if (target != (size_t)~0)
-			printf("%zu:", target);
-		fprintf(fp, "\t%s", ni->name);
+		if (fp) {
+			if (target != (size_t)~0)
+				fprintf(fp, "%zu:", target);
+			fprintf(fp, "\t%s", ni->name);
+		}
 		ADVANCE(1, -1);
+
 		for (size_t i = 0; i < __arraycount(ni->op); i++) {
 			const char *op;
 			if (ni->op[i] == NPF_OPERAND_NONE)
 				break;
 			op = npfctl_ncode_operand(buf, sizeof(buf), ni->op[i],
-			    st, ipc, &pc, &len, &targ, &tlen, &mlen);
+			    st, ipc, &pc, &len, &targ, &tlen, &mlen, nci);
 			if (op == NULL)
 				goto out;
-			fprintf(fp, "%s%s", i == 0 ? " " : ", ", op);
+			if (fp) {
+				fprintf(fp, "%s%s", i == 0 ? " " : ", ", op);
+			}
 		}
-		fprintf(fp, "\n");
+		if (fp) {
+			fprintf(fp, "\n");
+		}
 	}
 	error = 0;
 out:
 	free(targ);
 	return error;
 }
+
+static void
+npfctl_show_fromto(const char *name, npfvar_t *vl, bool showany)
+{
+	size_t count = npfvar_get_count(vl), last = count - 1;
+	bool one = (count == 1);
+
+	if (count == 0) {
+		if (showany) {
+			printf("%s all ", name);
+		}
+		return;
+	}
+	printf("%s%s ", name, one ? "" : " {");
+
+	for (size_t i = 0; i < count; i++) {
+		char *s = npfvar_get_data(vl, NPFVAR_STRING, i);
+		printf("%s%s ", s, i == last ? (one ? "" : " }") : ",");
+	}
+	npfvar_destroy(vl);
+}
+
+static void
+npfctl_show_ncode(const void *nc, size_t len)
+{
+	nc_inf_t nci;
+
+	memset(&nci, 0, sizeof(nc_inf_t));
+
+	if (npfctl_ncode_disassemble(NULL, nc, len, (void *)&nci) == 0) {
+		npfctl_show_fromto("from", nci.nci_vlist[NPF_SHOW_SRCADDR], true);
+		npfctl_show_fromto("port", nci.nci_vlist[NPF_SHOW_SRCPORT], false);
+		npfctl_show_fromto("to", nci.nci_vlist[NPF_SHOW_DSTADDR], true);
+		npfctl_show_fromto("port", nci.nci_vlist[NPF_SHOW_DSTPORT], false);
+	} else {
+		printf("<< ncode >> ");
+	}
+}
+
+#define	NPF_RSTICMP		(NPF_RULE_RETRST | NPF_RULE_RETICMP)
+
+static const struct attr_keyword_mapent {
+	uint32_t	mask;
+	uint32_t	flags;
+	const char *	onmatch;
+	const char *	nomatch;
+} attr_keyword_map[] = {
+	{ NPF_RULE_PASS,	NPF_RULE_PASS,	"pass",		"block"	},
+	{ NPF_RSTICMP,		NPF_RSTICMP,	"return",	NULL	},
+	{ NPF_RSTICMP,		NPF_RULE_RETRST,"return-rst",	NULL	},
+	{ NPF_RSTICMP,		NPF_RULE_RETICMP,"return-icmp",	NULL	},
+	{ NPF_RULE_KEEPSTATE,	NPF_RULE_KEEPSTATE,"stateful",	NULL	},
+	{ NPF_RULE_DIMASK,	NPF_RULE_IN,	"in",		NULL	},
+	{ NPF_RULE_DIMASK,	NPF_RULE_OUT,	"out",		NULL	},
+	{ NPF_RULE_FINAL,	NPF_RULE_FINAL,	"final",	NULL	},
+};
+
+static void
+npfctl_show_rule(nl_rule_t *nrl, unsigned nlevel)
+{
+	rule_group_t rg;
+	const char *rproc;
+	const void *nc;
+	size_t nclen;
+
+	_npf_rule_getinfo(nrl, &rg.rg_name, &rg.rg_attr, &rg.rg_ifnum);
+
+	/* Get the interface, if any. */
+	char ifnamebuf[IFNAMSIZ], *ifname = NULL;
+	if (rg.rg_ifnum) {
+		ifname = if_indextoname(rg.rg_ifnum, ifnamebuf);
+	}
+
+	/*
+	 * If zero level, then it is a group.
+	 */
+	if (nlevel == 0) {
+		static bool ingroup = false;
+		const char *rname = rg.rg_name;
+
+		if (ingroup) {
+			puts("}");
+		}
+		ingroup = true;
+		if (rg.rg_attr & NPF_RULE_DEFAULT) {
+			puts("group (default) {");
+			return;
+		}
+		printf("group (name \"%s\"", rname == NULL ? "" : rname);
+		if (ifname) {
+			printf(", interface %s", ifname);
+		}
+		puts(") {");
+		return;
+	}
+
+	/*
+	 * Rule case.  First, unparse the attributes.
+	 */
+	while (nlevel--) {
+		printf("\t");
+	}
+	for (unsigned i = 0; i < __arraycount(attr_keyword_map); i++) {
+		const struct attr_keyword_mapent *ak = &attr_keyword_map[i];
+
+		if ((rg.rg_attr & ak->mask) == ak->flags) {
+			printf("%s ", ak->onmatch);
+		} else if (ak->nomatch) {
+			printf("%s ", ak->nomatch);
+		}
+	}
+
+	if (ifname) {
+		printf("on %s ", ifname);
+	}
+
+	nc = _npf_rule_ncode(nrl, &nclen);
+	if (nc) {
+		npfctl_show_ncode(nc, nclen);
+	} else {
+		printf("all ");
+	}
+	/* apply <rproc> */
+
+	if ((rproc = _npf_rule_rproc(nrl)) != NULL) {
+		printf("apply \"%s\"", rproc);
+	}
+	puts("");
+}
+
+int
+npfctl_config_show(int fd)
+{
+	nl_config_t *ncf;
+	bool active, loaded;
+	int error = 0;
+
+	ncf = npf_config_retrieve(fd, &active, &loaded);
+	if (ncf == NULL) {
+		return errno;
+	}
+	printf("Filtering:\t%s\nConfiguration:\t%s\n\n",
+	    active ? "active" : "inactive",
+	    loaded ? "loaded" : "empty");
+
+	if (loaded) {
+		error = _npf_rule_foreach(ncf, npfctl_show_rule);
+		puts("}");
+	}
+	npf_config_destroy(ncf);
+	return error;
+}
--- a/usr.sbin/npf/npfctl/npf_ncgen.c	Wed May 30 20:15:56 2012 +0000
+++ b/usr.sbin/npf/npfctl/npf_ncgen.c	Wed May 30 21:30:07 2012 +0000
@@ -1,4 +1,4 @@
-/*	$NetBSD: npf_ncgen.c,v 1.8 2012/03/10 22:21:50 christos Exp $	*/
+/*	$NetBSD: npf_ncgen.c,v 1.9 2012/05/30 21:30:07 rmind Exp $	*/
 
 /*-
  * Copyright (c) 2009-2012 The NetBSD Foundation, Inc.
@@ -34,7 +34,7 @@
  */
 
 #include <sys/cdefs.h>
-__RCSID("$NetBSD: npf_ncgen.c,v 1.8 2012/03/10 22:21:50 christos Exp $");
+__RCSID("$NetBSD: npf_ncgen.c,v 1.9 2012/05/30 21:30:07 rmind Exp $");
 
 #include <stdlib.h>
 #include <stddef.h>
@@ -388,14 +388,5 @@
 void
 npfctl_ncgen_print(const void *code, size_t len)
 {
-#if 0
-	const uint32_t *op = code;
-
-	while (len) {
-		printf("\t> |0x%02x|\n", (u_int)*op++);
-		len -= sizeof(*op);
-	}
-#else
-	npfctl_ncode_disassemble(stdout, code, len);
-#endif
+	npfctl_ncode_disassemble(stdout, code, len, NULL);
 }
--- a/usr.sbin/npf/npfctl/npf_parse.y	Wed May 30 20:15:56 2012 +0000
+++ b/usr.sbin/npf/npfctl/npf_parse.y	Wed May 30 21:30:07 2012 +0000
@@ -1,4 +1,4 @@
-/*	$NetBSD: npf_parse.y,v 1.6 2012/02/26 22:04:42 christos Exp $	*/
+/*	$NetBSD: npf_parse.y,v 1.7 2012/05/30 21:30:07 rmind Exp $	*/
 
 /*-
  * Copyright (c) 2011-2012 The NetBSD Foundation, Inc.
@@ -95,7 +95,6 @@
 %token			INET
 %token			INET6
 %token			INTERFACE
-%token			KEEP
 %token			MINUS
 %token			NAT
 %token			NAME
@@ -108,14 +107,14 @@
 %token			PROCEDURE
 %token			PROTO
 %token			FAMILY
-%token			QUICK
+%token			FINAL
 %token			RDR
 %token			RETURN
 %token			RETURNICMP
 %token			RETURNRST
 %token			SEPLINE
 %token			SLASH
-%token			STATE
+%token			STATEFUL
 %token			TABLE
 %token			TCP
 %token			TO
@@ -135,9 +134,9 @@
 
 %type	<str>		addr, iface_name, moduleargname, list_elem, table_store
 %type	<str>		opt_apply
-%type	<num>		ifindex, port, opt_quick, on_iface
+%type	<num>		ifindex, port, opt_final, on_iface
 %type	<num>		block_or_pass, rule_dir, block_opts, family, opt_family
-%type	<num>		opt_keep_state, icmp_type, table_type
+%type	<num>		opt_stateful, icmp_type, table_type
 %type	<var>		addr_or_iface, port_range, icmp_type_and_code
 %type	<var>		filt_addr, addr_and_mask, tcp_flags, tcp_flags_and_mask
 %type	<var>		modulearg_opts, procs, proc_op, modulearg, moduleargs
@@ -421,15 +420,15 @@
 	;
 
 rule
-	: block_or_pass rule_dir opt_quick on_iface opt_family
-	  opt_proto all_or_filt_opts opt_keep_state opt_apply
+	: block_or_pass opt_stateful rule_dir opt_final on_iface opt_family
+	  opt_proto all_or_filt_opts opt_apply
 	{
 		/*
 		 * Arguments: attributes, interface index, address
 		 * family, protocol options, filter options.
 		 */
-		npfctl_build_rule($1 | $2 | $3 | $8, $4,
-		    $5, &$6, &$7, $9);
+		npfctl_build_rule($1 | $2 | $3 | $4, $5,
+		    $6, &$7, &$8, $9);
 	}
 	|
 	;
@@ -445,8 +444,8 @@
 	|			{ $$ = NPF_RULE_IN | NPF_RULE_OUT; }
 	;
 
-opt_quick
-	: QUICK			{ $$ = NPF_RULE_FINAL; }
+opt_final
+	: FINAL			{ $$ = NPF_RULE_FINAL; }
 	|			{ $$ = 0; }
 	;
 
@@ -499,8 +498,8 @@
 	| filt_opts	{ $$ = $1; }
 	;
 
-opt_keep_state
-	: KEEP STATE	{ $$ = NPF_RULE_KEEPSTATE; }
+opt_stateful
+	: STATEFUL	{ $$ = NPF_RULE_KEEPSTATE; }
 	|		{ $$ = 0; }
 	;
 
@@ -621,7 +620,7 @@
 	;
 
 port
-	: NUM		{ $$ = htons($1); }
+	: NUM		{ $$ = $1; }
 	| IDENTIFIER	{ $$ = npfctl_portno($1); }
 	;
 
--- a/usr.sbin/npf/npfctl/npf_scan.l	Wed May 30 20:15:56 2012 +0000
+++ b/usr.sbin/npf/npfctl/npf_scan.l	Wed May 30 21:30:07 2012 +0000
@@ -1,4 +1,4 @@
-/*	$NetBSD: npf_scan.l,v 1.1 2012/01/08 21:34:21 rmind Exp $	*/
+/*	$NetBSD: npf_scan.l,v 1.2 2012/05/30 21:30:07 rmind Exp $	*/
 
 /*-
  * Copyright (c) 2011-2012 The NetBSD Foundation, Inc.
@@ -72,10 +72,10 @@
 all			return ALL;
 block			return BLOCK;
 pass			return PASS;
-keep			return KEEP;
-state			return STATE;
+stateful		return STATEFUL;
 apply			return APPLY;
-quick			return QUICK;
+final			return FINAL;
+quick			return FINAL;
 on			return ON;
 inet6			return INET6;
 inet4			return INET;
--- a/usr.sbin/npf/npfctl/npf_var.c	Wed May 30 20:15:56 2012 +0000
+++ b/usr.sbin/npf/npfctl/npf_var.c	Wed May 30 21:30:07 2012 +0000
@@ -1,4 +1,4 @@
-/*	$NetBSD: npf_var.c,v 1.4 2012/02/26 21:50:05 christos Exp $	*/
+/*	$NetBSD: npf_var.c,v 1.5 2012/05/30 21:30:07 rmind Exp $	*/
 
 /*-
  * Copyright (c) 2011-2012 The NetBSD Foundation, Inc.
@@ -30,7 +30,7 @@
  */
 
 #include <sys/cdefs.h>
-__RCSID("$NetBSD: npf_var.c,v 1.4 2012/02/26 21:50:05 christos Exp $");
+__RCSID("$NetBSD: npf_var.c,v 1.5 2012/05/30 21:30:07 rmind Exp $");
 
 #include <stdlib.h>
 #include <string.h>
@@ -62,6 +62,7 @@
 {
 	npfvar_t *vp = zalloc(sizeof(*vp));
 	vp->v_key = xstrdup(name);
+	var_num++;
 	return vp;
 }
 
@@ -88,7 +89,6 @@
 {
 	vp->v_next = var_list;
 	var_list = vp;
-	var_num++;
 }
 
 npfvar_t *
@@ -169,6 +169,7 @@
 	npfvar_free_elements(vp->v_elements);
 	free(vp->v_key);
 	free(vp);
+	var_num--;
 }
 
 char *
--- a/usr.sbin/npf/npfctl/npfctl.c	Wed May 30 20:15:56 2012 +0000
+++ b/usr.sbin/npf/npfctl/npfctl.c	Wed May 30 21:30:07 2012 +0000
@@ -1,4 +1,4 @@
-/*	$NetBSD: npfctl.c,v 1.10 2012/02/05 00:37:13 rmind Exp $	*/
+/*	$NetBSD: npfctl.c,v 1.11 2012/05/30 21:30:07 rmind Exp $	*/
 
 /*-
  * Copyright (c) 2009-2012 The NetBSD Foundation, Inc.
@@ -30,7 +30,7 @@
  */
 
 #include <sys/cdefs.h>
-__RCSID("$NetBSD: npfctl.c,v 1.10 2012/02/05 00:37:13 rmind Exp $");
+__RCSID("$NetBSD: npfctl.c,v 1.11 2012/05/30 21:30:07 rmind Exp $");
 
 #include <sys/ioctl.h>
 #include <sys/stat.h>
@@ -50,14 +50,17 @@
 extern int		yyparse(void);
 extern void		yyrestart(FILE *);
 
-#define	NPFCTL_START		1
-#define	NPFCTL_STOP		2
-#define	NPFCTL_RELOAD		3
-#define	NPFCTL_FLUSH		4
-#define	NPFCTL_TABLE		5
-#define	NPFCTL_STATS		6
-#define	NPFCTL_SESSIONS_SAVE	7
-#define	NPFCTL_SESSIONS_LOAD	8
+enum {
+	NPFCTL_START,
+	NPFCTL_STOP,
+	NPFCTL_RELOAD,
+	NPFCTL_SHOWCONF,
+	NPFCTL_FLUSH,
+	NPFCTL_TABLE,
+	NPFCTL_STATS,
+	NPFCTL_SESSIONS_SAVE,
+	NPFCTL_SESSIONS_LOAD,
+};
 
 static struct operations_s {
 	const char *		cmd;
@@ -67,6 +70,7 @@
 	{	"start",		NPFCTL_START		},
 	{	"stop",			NPFCTL_STOP		},
 	{	"reload",		NPFCTL_RELOAD		},
+	{	"show",			NPFCTL_SHOWCONF,	},
 	{	"flush",		NPFCTL_FLUSH		},
 	/* Table */
 	{	"table",		NPFCTL_TABLE		},
@@ -263,6 +267,9 @@
 		npfctl_parsecfg(argc < 3 ? NPF_CONF_PATH : argv[2]);
 		ret = npfctl_config_send(fd);
 		break;
+	case NPFCTL_SHOWCONF:
+		ret = npfctl_config_show(fd);
+		break;
 	case NPFCTL_FLUSH:
 		ret = npf_config_flush(fd);
 		break;
--- a/usr.sbin/npf/npfctl/npfctl.h	Wed May 30 20:15:56 2012 +0000
+++ b/usr.sbin/npf/npfctl/npfctl.h	Wed May 30 21:30:07 2012 +0000
@@ -1,4 +1,4 @@
-/*	$NetBSD: npfctl.h,v 1.13 2012/03/10 22:21:50 christos Exp $	*/
+/*	$NetBSD: npfctl.h,v 1.14 2012/05/30 21:30:07 rmind Exp $	*/
 
 /*-
  * Copyright (c) 2009-2012 The NetBSD Foundation, Inc.
@@ -71,7 +71,7 @@
 
 typedef struct rule_group {
 	const char *	rg_name;
-	int		rg_attr;
+	uint32_t	rg_attr;
 	u_int		rg_ifnum;
 } rule_group_t;
 
@@ -146,6 +146,7 @@
 
 void		npfctl_config_init(bool);
 int		npfctl_config_send(int);
+int		npfctl_config_show(int);
 
 void		npfctl_build_rproc(const char *, npfvar_t *);
 void		npfctl_build_group(const char *, int, u_int);
@@ -154,6 +155,13 @@
 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 *);
-int		npfctl_ncode_disassemble(FILE *, const void *, size_t);
+
+/*
+ * N-code disassembler.
+ */
+typedef struct nc_inf nc_inf_t;
+
+int		npfctl_ncode_disassemble(FILE *, const void *, size_t,
+		    nc_inf_t *);
 
 #endif