[rtems-libbsd commit] pfctl: Import sources from FreeBSD.

Sebastian Huber sebh at rtems.org
Tue Aug 2 11:22:00 UTC 2016


Module:    rtems-libbsd
Branch:    master
Commit:    6e9a8eadf596c28959b9b9a8dcd64ba5a526c75d
Changeset: http://git.rtems.org/rtems-libbsd/commit/?id=6e9a8eadf596c28959b9b9a8dcd64ba5a526c75d

Author:    Christian Mauderer <Christian.Mauderer at embedded-brains.de>
Date:      Tue Jul  5 16:07:37 2016 +0200

pfctl: Import sources from FreeBSD.

---

 freebsd/contrib/pf/pfctl/parse.y          | 6083 +++++++++++++++++++++++++++++
 freebsd/contrib/pf/pfctl/pf_print_state.c |  382 ++
 freebsd/contrib/pf/pfctl/pfctl.c          | 2410 ++++++++++++
 freebsd/contrib/pf/pfctl/pfctl.h          |  130 +
 freebsd/contrib/pf/pfctl/pfctl_altq.c     | 1260 ++++++
 freebsd/contrib/pf/pfctl/pfctl_optimize.c | 1657 ++++++++
 freebsd/contrib/pf/pfctl/pfctl_osfp.c     | 1110 ++++++
 freebsd/contrib/pf/pfctl/pfctl_parser.c   | 1754 +++++++++
 freebsd/contrib/pf/pfctl/pfctl_parser.h   |  305 ++
 freebsd/contrib/pf/pfctl/pfctl_qstats.c   |  451 +++
 freebsd/contrib/pf/pfctl/pfctl_radix.c    |  587 +++
 freebsd/contrib/pf/pfctl/pfctl_table.c    |  637 +++
 12 files changed, 16766 insertions(+)

diff --git a/freebsd/contrib/pf/pfctl/parse.y b/freebsd/contrib/pf/pfctl/parse.y
new file mode 100644
index 0000000..1448177
--- /dev/null
+++ b/freebsd/contrib/pf/pfctl/parse.y
@@ -0,0 +1,6083 @@
+/*	$OpenBSD: parse.y,v 1.554 2008/10/17 12:59:53 henning Exp $	*/
+
+/*
+ * Copyright (c) 2001 Markus Friedl.  All rights reserved.
+ * Copyright (c) 2001 Daniel Hartmeier.  All rights reserved.
+ * Copyright (c) 2001 Theo de Raadt.  All rights reserved.
+ * Copyright (c) 2002,2003 Henning Brauer. 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 AUTHOR ``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 AUTHOR 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.
+ */
+%{
+#ifdef __rtems__
+#include <machine/rtems-bsd-user-space.h>
+#define	pf_find_or_create_ruleset _bsd_pf_find_or_create_ruleset
+#define	pf_anchor_setup _bsd_pf_anchor_setup
+#define	pf_remove_if_empty_ruleset _bsd_pf_remove_if_empty_ruleset
+#endif /* __rtems__ */
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#ifdef __FreeBSD__
+#include <sys/sysctl.h>
+#endif
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/ip_icmp.h>
+#include <netinet/icmp6.h>
+#include <net/pfvar.h>
+#include <arpa/inet.h>
+#include <altq/altq.h>
+#include <altq/altq_cbq.h>
+#include <altq/altq_priq.h>
+#include <altq/altq_hfsc.h>
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <netdb.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <string.h>
+#include <ctype.h>
+#include <math.h>
+#include <err.h>
+#include <limits.h>
+#include <pwd.h>
+#include <grp.h>
+#include <md5.h>
+
+#include "pfctl_parser.h"
+#include "pfctl.h"
+
+static struct pfctl	*pf = NULL;
+static int		 debug = 0;
+static int		 rulestate = 0;
+static u_int16_t	 returnicmpdefault =
+			    (ICMP_UNREACH << 8) | ICMP_UNREACH_PORT;
+static u_int16_t	 returnicmp6default =
+			    (ICMP6_DST_UNREACH << 8) | ICMP6_DST_UNREACH_NOPORT;
+static int		 blockpolicy = PFRULE_DROP;
+static int		 require_order = 1;
+static int		 default_statelock;
+
+TAILQ_HEAD(files, file)		 files = TAILQ_HEAD_INITIALIZER(files);
+static struct file {
+	TAILQ_ENTRY(file)	 entry;
+	FILE			*stream;
+	char			*name;
+	int			 lineno;
+	int			 errors;
+} *file;
+struct file	*pushfile(const char *, int);
+int		 popfile(void);
+int		 check_file_secrecy(int, const char *);
+int		 yyparse(void);
+int		 yylex(void);
+int		 yyerror(const char *, ...);
+int		 kw_cmp(const void *, const void *);
+int		 lookup(char *);
+int		 lgetc(int);
+int		 lungetc(int);
+int		 findeol(void);
+
+TAILQ_HEAD(symhead, sym)	 symhead = TAILQ_HEAD_INITIALIZER(symhead);
+struct sym {
+	TAILQ_ENTRY(sym)	 entry;
+	int			 used;
+	int			 persist;
+	char			*nam;
+	char			*val;
+};
+int		 symset(const char *, const char *, int);
+char		*symget(const char *);
+
+int		 atoul(char *, u_long *);
+
+enum {
+	PFCTL_STATE_NONE,
+	PFCTL_STATE_OPTION,
+	PFCTL_STATE_SCRUB,
+	PFCTL_STATE_QUEUE,
+	PFCTL_STATE_NAT,
+	PFCTL_STATE_FILTER
+};
+
+struct node_proto {
+	u_int8_t		 proto;
+	struct node_proto	*next;
+	struct node_proto	*tail;
+};
+
+struct node_port {
+	u_int16_t		 port[2];
+	u_int8_t		 op;
+	struct node_port	*next;
+	struct node_port	*tail;
+};
+
+struct node_uid {
+	uid_t			 uid[2];
+	u_int8_t		 op;
+	struct node_uid		*next;
+	struct node_uid		*tail;
+};
+
+struct node_gid {
+	gid_t			 gid[2];
+	u_int8_t		 op;
+	struct node_gid		*next;
+	struct node_gid		*tail;
+};
+
+struct node_icmp {
+	u_int8_t		 code;
+	u_int8_t		 type;
+	u_int8_t		 proto;
+	struct node_icmp	*next;
+	struct node_icmp	*tail;
+};
+
+enum	{ PF_STATE_OPT_MAX, PF_STATE_OPT_NOSYNC, PF_STATE_OPT_SRCTRACK,
+	    PF_STATE_OPT_MAX_SRC_STATES, PF_STATE_OPT_MAX_SRC_CONN,
+	    PF_STATE_OPT_MAX_SRC_CONN_RATE, PF_STATE_OPT_MAX_SRC_NODES,
+	    PF_STATE_OPT_OVERLOAD, PF_STATE_OPT_STATELOCK,
+	    PF_STATE_OPT_TIMEOUT, PF_STATE_OPT_SLOPPY, 
+	    PF_STATE_OPT_PFLOW };
+
+enum	{ PF_SRCTRACK_NONE, PF_SRCTRACK, PF_SRCTRACK_GLOBAL, PF_SRCTRACK_RULE };
+
+struct node_state_opt {
+	int			 type;
+	union {
+		u_int32_t	 max_states;
+		u_int32_t	 max_src_states;
+		u_int32_t	 max_src_conn;
+		struct {
+			u_int32_t	limit;
+			u_int32_t	seconds;
+		}		 max_src_conn_rate;
+		struct {
+			u_int8_t	flush;
+			char		tblname[PF_TABLE_NAME_SIZE];
+		}		 overload;
+		u_int32_t	 max_src_nodes;
+		u_int8_t	 src_track;
+		u_int32_t	 statelock;
+		struct {
+			int		number;
+			u_int32_t	seconds;
+		}		 timeout;
+	}			 data;
+	struct node_state_opt	*next;
+	struct node_state_opt	*tail;
+};
+
+struct peer {
+	struct node_host	*host;
+	struct node_port	*port;
+};
+
+struct node_queue {
+	char			 queue[PF_QNAME_SIZE];
+	char			 parent[PF_QNAME_SIZE];
+	char			 ifname[IFNAMSIZ];
+	int			 scheduler;
+	struct node_queue	*next;
+	struct node_queue	*tail;
+}	*queues = NULL;
+
+struct node_qassign {
+	char		*qname;
+	char		*pqname;
+};
+
+struct filter_opts {
+	int			 marker;
+#define FOM_FLAGS	0x01
+#define FOM_ICMP	0x02
+#define FOM_TOS		0x04
+#define FOM_KEEP	0x08
+#define FOM_SRCTRACK	0x10
+	struct node_uid		*uid;
+	struct node_gid		*gid;
+	struct {
+		u_int8_t	 b1;
+		u_int8_t	 b2;
+		u_int16_t	 w;
+		u_int16_t	 w2;
+	} flags;
+	struct node_icmp	*icmpspec;
+	u_int32_t		 tos;
+	u_int32_t		 prob;
+	struct {
+		int			 action;
+		struct node_state_opt	*options;
+	} keep;
+	int			 fragment;
+	int			 allowopts;
+	char			*label;
+	struct node_qassign	 queues;
+	char			*tag;
+	char			*match_tag;
+	u_int8_t		 match_tag_not;
+	u_int			 rtableid;
+	struct {
+		struct node_host	*addr;
+		u_int16_t		port;
+	}			 divert;
+} filter_opts;
+
+struct antispoof_opts {
+	char			*label;
+	u_int			 rtableid;
+} antispoof_opts;
+
+struct scrub_opts {
+	int			 marker;
+#define SOM_MINTTL	0x01
+#define SOM_MAXMSS	0x02
+#define SOM_FRAGCACHE	0x04
+#define SOM_SETTOS	0x08
+	int			 nodf;
+	int			 minttl;
+	int			 maxmss;
+	int			 settos;
+	int			 fragcache;
+	int			 randomid;
+	int			 reassemble_tcp;
+	char			*match_tag;
+	u_int8_t		 match_tag_not;
+	u_int			 rtableid;
+} scrub_opts;
+
+struct queue_opts {
+	int			marker;
+#define QOM_BWSPEC	0x01
+#define QOM_SCHEDULER	0x02
+#define QOM_PRIORITY	0x04
+#define QOM_TBRSIZE	0x08
+#define QOM_QLIMIT	0x10
+	struct node_queue_bw	queue_bwspec;
+	struct node_queue_opt	scheduler;
+	int			priority;
+	int			tbrsize;
+	int			qlimit;
+} queue_opts;
+
+struct table_opts {
+	int			flags;
+	int			init_addr;
+	struct node_tinithead	init_nodes;
+} table_opts;
+
+struct pool_opts {
+	int			 marker;
+#define POM_TYPE		0x01
+#define POM_STICKYADDRESS	0x02
+	u_int8_t		 opts;
+	int			 type;
+	int			 staticport;
+	struct pf_poolhashkey	*key;
+
+} pool_opts;
+
+
+struct node_hfsc_opts	 hfsc_opts;
+struct node_state_opt	*keep_state_defaults = NULL;
+
+int		 disallow_table(struct node_host *, const char *);
+int		 disallow_urpf_failed(struct node_host *, const char *);
+int		 disallow_alias(struct node_host *, const char *);
+int		 rule_consistent(struct pf_rule *, int);
+int		 filter_consistent(struct pf_rule *, int);
+int		 nat_consistent(struct pf_rule *);
+int		 rdr_consistent(struct pf_rule *);
+int		 process_tabledef(char *, struct table_opts *);
+void		 expand_label_str(char *, size_t, const char *, const char *);
+void		 expand_label_if(const char *, char *, size_t, const char *);
+void		 expand_label_addr(const char *, char *, size_t, u_int8_t,
+		    struct node_host *);
+void		 expand_label_port(const char *, char *, size_t,
+		    struct node_port *);
+void		 expand_label_proto(const char *, char *, size_t, u_int8_t);
+void		 expand_label_nr(const char *, char *, size_t);
+void		 expand_label(char *, size_t, const char *, u_int8_t,
+		    struct node_host *, struct node_port *, struct node_host *,
+		    struct node_port *, u_int8_t);
+void		 expand_rule(struct pf_rule *, struct node_if *,
+		    struct node_host *, struct node_proto *, struct node_os *,
+		    struct node_host *, struct node_port *, struct node_host *,
+		    struct node_port *, struct node_uid *, struct node_gid *,
+		    struct node_icmp *, const char *);
+int		 expand_altq(struct pf_altq *, struct node_if *,
+		    struct node_queue *, struct node_queue_bw bwspec,
+		    struct node_queue_opt *);
+int		 expand_queue(struct pf_altq *, struct node_if *,
+		    struct node_queue *, struct node_queue_bw,
+		    struct node_queue_opt *);
+int		 expand_skip_interface(struct node_if *);
+
+int	 check_rulestate(int);
+int	 getservice(char *);
+int	 rule_label(struct pf_rule *, char *);
+int	 rt_tableid_max(void);
+
+void	 mv_rules(struct pf_ruleset *, struct pf_ruleset *);
+void	 decide_address_family(struct node_host *, sa_family_t *);
+void	 remove_invalid_hosts(struct node_host **, sa_family_t *);
+int	 invalid_redirect(struct node_host *, sa_family_t);
+u_int16_t parseicmpspec(char *, sa_family_t);
+
+TAILQ_HEAD(loadanchorshead, loadanchors)
+    loadanchorshead = TAILQ_HEAD_INITIALIZER(loadanchorshead);
+
+struct loadanchors {
+	TAILQ_ENTRY(loadanchors)	 entries;
+	char				*anchorname;
+	char				*filename;
+};
+
+typedef struct {
+	union {
+		int64_t			 number;
+		double			 probability;
+		int			 i;
+		char			*string;
+		u_int			 rtableid;
+		struct {
+			u_int8_t	 b1;
+			u_int8_t	 b2;
+			u_int16_t	 w;
+			u_int16_t	 w2;
+		}			 b;
+		struct range {
+			int		 a;
+			int		 b;
+			int		 t;
+		}			 range;
+		struct node_if		*interface;
+		struct node_proto	*proto;
+		struct node_icmp	*icmp;
+		struct node_host	*host;
+		struct node_os		*os;
+		struct node_port	*port;
+		struct node_uid		*uid;
+		struct node_gid		*gid;
+		struct node_state_opt	*state_opt;
+		struct peer		 peer;
+		struct {
+			struct peer	 src, dst;
+			struct node_os	*src_os;
+		}			 fromto;
+		struct {
+			struct node_host	*host;
+			u_int8_t		 rt;
+			u_int8_t		 pool_opts;
+			sa_family_t		 af;
+			struct pf_poolhashkey	*key;
+		}			 route;
+		struct redirection {
+			struct node_host	*host;
+			struct range		 rport;
+		}			*redirection;
+		struct {
+			int			 action;
+			struct node_state_opt	*options;
+		}			 keep_state;
+		struct {
+			u_int8_t	 log;
+			u_int8_t	 logif;
+			u_int8_t	 quick;
+		}			 logquick;
+		struct {
+			int		 neg;
+			char		*name;
+		}			 tagged;
+		struct pf_poolhashkey	*hashkey;
+		struct node_queue	*queue;
+		struct node_queue_opt	 queue_options;
+		struct node_queue_bw	 queue_bwspec;
+		struct node_qassign	 qassign;
+		struct filter_opts	 filter_opts;
+		struct antispoof_opts	 antispoof_opts;
+		struct queue_opts	 queue_opts;
+		struct scrub_opts	 scrub_opts;
+		struct table_opts	 table_opts;
+		struct pool_opts	 pool_opts;
+		struct node_hfsc_opts	 hfsc_opts;
+	} v;
+	int lineno;
+} YYSTYPE;
+
+#define PPORT_RANGE	1
+#define PPORT_STAR	2
+int	parseport(char *, struct range *r, int);
+
+#define DYNIF_MULTIADDR(addr) ((addr).type == PF_ADDR_DYNIFTL && \
+	(!((addr).iflags & PFI_AFLAG_NOALIAS) ||		 \
+	!isdigit((addr).v.ifname[strlen((addr).v.ifname)-1])))
+
+%}
+
+%token	PASS BLOCK SCRUB RETURN IN OS OUT LOG QUICK ON FROM TO FLAGS
+%token	RETURNRST RETURNICMP RETURNICMP6 PROTO INET INET6 ALL ANY ICMPTYPE
+%token	ICMP6TYPE CODE KEEP MODULATE STATE PORT RDR NAT BINAT ARROW NODF
+%token	MINTTL ERROR ALLOWOPTS FASTROUTE FILENAME ROUTETO DUPTO REPLYTO NO LABEL
+%token	NOROUTE URPFFAILED FRAGMENT USER GROUP MAXMSS MAXIMUM TTL TOS DROP TABLE
+%token	REASSEMBLE FRAGDROP FRAGCROP ANCHOR NATANCHOR RDRANCHOR BINATANCHOR
+%token	SET OPTIMIZATION TIMEOUT LIMIT LOGINTERFACE BLOCKPOLICY RANDOMID
+%token	REQUIREORDER SYNPROXY FINGERPRINTS NOSYNC DEBUG SKIP HOSTID
+%token	ANTISPOOF FOR INCLUDE
+%token	BITMASK RANDOM SOURCEHASH ROUNDROBIN STATICPORT PROBABILITY
+%token	ALTQ CBQ PRIQ HFSC BANDWIDTH TBRSIZE LINKSHARE REALTIME UPPERLIMIT
+%token	QUEUE PRIORITY QLIMIT RTABLE
+%token	LOAD RULESET_OPTIMIZATION
+%token	STICKYADDRESS MAXSRCSTATES MAXSRCNODES SOURCETRACK GLOBAL RULE
+%token	MAXSRCCONN MAXSRCCONNRATE OVERLOAD FLUSH SLOPPY PFLOW
+%token	TAGGED TAG IFBOUND FLOATING STATEPOLICY STATEDEFAULTS ROUTE SETTOS
+%token	DIVERTTO DIVERTREPLY
+%token	<v.string>		STRING
+%token	<v.number>		NUMBER
+%token	<v.i>			PORTBINARY
+%type	<v.interface>		interface if_list if_item_not if_item
+%type	<v.number>		number icmptype icmp6type uid gid
+%type	<v.number>		tos not yesno
+%type	<v.probability>		probability
+%type	<v.i>			no dir af fragcache optimizer
+%type	<v.i>			sourcetrack flush unaryop statelock
+%type	<v.b>			action nataction natpasslog scrubaction
+%type	<v.b>			flags flag blockspec
+%type	<v.range>		portplain portstar portrange
+%type	<v.hashkey>		hashkey
+%type	<v.proto>		proto proto_list proto_item
+%type	<v.number>		protoval
+%type	<v.icmp>		icmpspec
+%type	<v.icmp>		icmp_list icmp_item
+%type	<v.icmp>		icmp6_list icmp6_item
+%type	<v.number>		reticmpspec reticmp6spec
+%type	<v.fromto>		fromto
+%type	<v.peer>		ipportspec from to
+%type	<v.host>		ipspec toipspec xhost host dynaddr host_list
+%type	<v.host>		redir_host_list redirspec
+%type	<v.host>		route_host route_host_list routespec
+%type	<v.os>			os xos os_list
+%type	<v.port>		portspec port_list port_item
+%type	<v.uid>			uids uid_list uid_item
+%type	<v.gid>			gids gid_list gid_item
+%type	<v.route>		route
+%type	<v.redirection>		redirection redirpool
+%type	<v.string>		label stringall tag anchorname
+%type	<v.string>		string varstring numberstring
+%type	<v.keep_state>		keep
+%type	<v.state_opt>		state_opt_spec state_opt_list state_opt_item
+%type	<v.logquick>		logquick quick log logopts logopt
+%type	<v.interface>		antispoof_ifspc antispoof_iflst antispoof_if
+%type	<v.qassign>		qname
+%type	<v.queue>		qassign qassign_list qassign_item
+%type	<v.queue_options>	scheduler
+%type	<v.number>		cbqflags_list cbqflags_item
+%type	<v.number>		priqflags_list priqflags_item
+%type	<v.hfsc_opts>		hfscopts_list hfscopts_item hfsc_opts
+%type	<v.queue_bwspec>	bandwidth
+%type	<v.filter_opts>		filter_opts filter_opt filter_opts_l
+%type	<v.antispoof_opts>	antispoof_opts antispoof_opt antispoof_opts_l
+%type	<v.queue_opts>		queue_opts queue_opt queue_opts_l
+%type	<v.scrub_opts>		scrub_opts scrub_opt scrub_opts_l
+%type	<v.table_opts>		table_opts table_opt table_opts_l
+%type	<v.pool_opts>		pool_opts pool_opt pool_opts_l
+%type	<v.tagged>		tagged
+%type	<v.rtableid>		rtable
+%%
+
+ruleset		: /* empty */
+		| ruleset include '\n'
+		| ruleset '\n'
+		| ruleset option '\n'
+		| ruleset scrubrule '\n'
+		| ruleset natrule '\n'
+		| ruleset binatrule '\n'
+		| ruleset pfrule '\n'
+		| ruleset anchorrule '\n'
+		| ruleset loadrule '\n'
+		| ruleset altqif '\n'
+		| ruleset queuespec '\n'
+		| ruleset varset '\n'
+		| ruleset antispoof '\n'
+		| ruleset tabledef '\n'
+		| '{' fakeanchor '}' '\n';
+		| ruleset error '\n'		{ file->errors++; }
+		;
+
+include		: INCLUDE STRING		{
+			struct file	*nfile;
+
+			if ((nfile = pushfile($2, 0)) == NULL) {
+				yyerror("failed to include file %s", $2);
+				free($2);
+				YYERROR;
+			}
+			free($2);
+
+			file = nfile;
+			lungetc('\n');
+		}
+		;
+
+/*
+ * apply to previouslys specified rule: must be careful to note
+ * what that is: pf or nat or binat or rdr
+ */
+fakeanchor	: fakeanchor '\n'
+		| fakeanchor anchorrule '\n'
+		| fakeanchor binatrule '\n'
+		| fakeanchor natrule '\n'
+		| fakeanchor pfrule '\n'
+		| fakeanchor error '\n'
+		;
+
+optimizer	: string	{
+			if (!strcmp($1, "none"))
+				$$ = 0;
+			else if (!strcmp($1, "basic"))
+				$$ = PF_OPTIMIZE_BASIC;
+			else if (!strcmp($1, "profile"))
+				$$ = PF_OPTIMIZE_BASIC | PF_OPTIMIZE_PROFILE;
+			else {
+				yyerror("unknown ruleset-optimization %s", $1);
+				YYERROR;
+			}
+		}
+		;
+
+option		: SET OPTIMIZATION STRING		{
+			if (check_rulestate(PFCTL_STATE_OPTION)) {
+				free($3);
+				YYERROR;
+			}
+			if (pfctl_set_optimization(pf, $3) != 0) {
+				yyerror("unknown optimization %s", $3);
+				free($3);
+				YYERROR;
+			}
+			free($3);
+		}
+		| SET RULESET_OPTIMIZATION optimizer {
+			if (!(pf->opts & PF_OPT_OPTIMIZE)) {
+				pf->opts |= PF_OPT_OPTIMIZE;
+				pf->optimize = $3;
+			}
+		}
+		| SET TIMEOUT timeout_spec
+		| SET TIMEOUT '{' optnl timeout_list '}'
+		| SET LIMIT limit_spec
+		| SET LIMIT '{' optnl limit_list '}'
+		| SET LOGINTERFACE stringall		{
+			if (check_rulestate(PFCTL_STATE_OPTION)) {
+				free($3);
+				YYERROR;
+			}
+			if (pfctl_set_logif(pf, $3) != 0) {
+				yyerror("error setting loginterface %s", $3);
+				free($3);
+				YYERROR;
+			}
+			free($3);
+		}
+		| SET HOSTID number {
+			if ($3 == 0 || $3 > UINT_MAX) {
+				yyerror("hostid must be non-zero");
+				YYERROR;
+			}
+			if (pfctl_set_hostid(pf, $3) != 0) {
+				yyerror("error setting hostid %08x", $3);
+				YYERROR;
+			}
+		}
+		| SET BLOCKPOLICY DROP	{
+			if (pf->opts & PF_OPT_VERBOSE)
+				printf("set block-policy drop\n");
+			if (check_rulestate(PFCTL_STATE_OPTION))
+				YYERROR;
+			blockpolicy = PFRULE_DROP;
+		}
+		| SET BLOCKPOLICY RETURN {
+			if (pf->opts & PF_OPT_VERBOSE)
+				printf("set block-policy return\n");
+			if (check_rulestate(PFCTL_STATE_OPTION))
+				YYERROR;
+			blockpolicy = PFRULE_RETURN;
+		}
+		| SET REQUIREORDER yesno {
+			if (pf->opts & PF_OPT_VERBOSE)
+				printf("set require-order %s\n",
+				    $3 == 1 ? "yes" : "no");
+			require_order = $3;
+		}
+		| SET FINGERPRINTS STRING {
+			if (pf->opts & PF_OPT_VERBOSE)
+				printf("set fingerprints \"%s\"\n", $3);
+			if (check_rulestate(PFCTL_STATE_OPTION)) {
+				free($3);
+				YYERROR;
+			}
+			if (!pf->anchor->name[0]) {
+				if (pfctl_file_fingerprints(pf->dev,
+				    pf->opts, $3)) {
+					yyerror("error loading "
+					    "fingerprints %s", $3);
+					free($3);
+					YYERROR;
+				}
+			}
+			free($3);
+		}
+		| SET STATEPOLICY statelock {
+			if (pf->opts & PF_OPT_VERBOSE)
+				switch ($3) {
+				case 0:
+					printf("set state-policy floating\n");
+					break;
+				case PFRULE_IFBOUND:
+					printf("set state-policy if-bound\n");
+					break;
+				}
+			default_statelock = $3;
+		}
+		| SET DEBUG STRING {
+			if (check_rulestate(PFCTL_STATE_OPTION)) {
+				free($3);
+				YYERROR;
+			}
+			if (pfctl_set_debug(pf, $3) != 0) {
+				yyerror("error setting debuglevel %s", $3);
+				free($3);
+				YYERROR;
+			}
+			free($3);
+		}
+		| SET SKIP interface {
+			if (expand_skip_interface($3) != 0) {
+				yyerror("error setting skip interface(s)");
+				YYERROR;
+			}
+		}
+		| SET STATEDEFAULTS state_opt_list {
+			if (keep_state_defaults != NULL) {
+				yyerror("cannot redefine state-defaults");
+				YYERROR;
+			}
+			keep_state_defaults = $3;
+		}
+		;
+
+stringall	: STRING	{ $$ = $1; }
+		| ALL		{
+			if (($$ = strdup("all")) == NULL) {
+				err(1, "stringall: strdup");
+			}
+		}
+		;
+
+string		: STRING string				{
+			if (asprintf(&$$, "%s %s", $1, $2) == -1)
+				err(1, "string: asprintf");
+			free($1);
+			free($2);
+		}
+		| STRING
+		;
+
+varstring	: numberstring varstring 		{
+			if (asprintf(&$$, "%s %s", $1, $2) == -1)
+				err(1, "string: asprintf");
+			free($1);
+			free($2);
+		}
+		| numberstring
+		;
+
+numberstring	: NUMBER				{
+			char	*s;
+			if (asprintf(&s, "%lld", (long long)$1) == -1) {
+				yyerror("string: asprintf");
+				YYERROR;
+			}
+			$$ = s;
+		}
+		| STRING
+		;
+
+varset		: STRING '=' varstring	{
+			if (pf->opts & PF_OPT_VERBOSE)
+				printf("%s = \"%s\"\n", $1, $3);
+			if (symset($1, $3, 0) == -1)
+				err(1, "cannot store variable %s", $1);
+			free($1);
+			free($3);
+		}
+		;
+
+anchorname	: STRING			{ $$ = $1; }
+		| /* empty */			{ $$ = NULL; }
+		;
+
+pfa_anchorlist	: /* empty */
+		| pfa_anchorlist '\n'
+		| pfa_anchorlist pfrule '\n'
+		| pfa_anchorlist anchorrule '\n'
+		;
+
+pfa_anchor	: '{'
+		{
+			char ta[PF_ANCHOR_NAME_SIZE];
+			struct pf_ruleset *rs;
+
+			/* steping into a brace anchor */
+			pf->asd++;
+			pf->bn++;
+			pf->brace = 1;
+
+			/* create a holding ruleset in the root */
+			snprintf(ta, PF_ANCHOR_NAME_SIZE, "_%d", pf->bn);
+			rs = pf_find_or_create_ruleset(ta);
+			if (rs == NULL)
+				err(1, "pfa_anchor: pf_find_or_create_ruleset");
+			pf->astack[pf->asd] = rs->anchor;
+			pf->anchor = rs->anchor;
+		} '\n' pfa_anchorlist '}'
+		{
+			pf->alast = pf->anchor;
+			pf->asd--;
+			pf->anchor = pf->astack[pf->asd];
+		}
+		| /* empty */
+		;
+
+anchorrule	: ANCHOR anchorname dir quick interface af proto fromto
+		    filter_opts pfa_anchor
+		{
+			struct pf_rule	r;
+			struct node_proto	*proto;
+
+			if (check_rulestate(PFCTL_STATE_FILTER)) {
+				if ($2)
+					free($2);
+				YYERROR;
+			}
+
+			if ($2 && ($2[0] == '_' || strstr($2, "/_") != NULL)) {
+				free($2);
+				yyerror("anchor names beginning with '_' "
+				    "are reserved for internal use");
+				YYERROR;
+			}
+
+			memset(&r, 0, sizeof(r));
+			if (pf->astack[pf->asd + 1]) {
+				/* move inline rules into relative location */
+				pf_anchor_setup(&r,
+				    &pf->astack[pf->asd]->ruleset,
+				    $2 ? $2 : pf->alast->name);
+		
+				if (r.anchor == NULL)
+					err(1, "anchorrule: unable to "
+					    "create ruleset");
+
+				if (pf->alast != r.anchor) {
+					if (r.anchor->match) {
+						yyerror("inline anchor '%s' "
+						    "already exists",
+						    r.anchor->name);
+						YYERROR;
+					}
+					mv_rules(&pf->alast->ruleset,
+					    &r.anchor->ruleset);
+				}
+				pf_remove_if_empty_ruleset(&pf->alast->ruleset);
+				pf->alast = r.anchor;
+			} else {
+				if (!$2) {
+					yyerror("anchors without explicit "
+					    "rules must specify a name");
+					YYERROR;
+				}
+			}
+			r.direction = $3;
+			r.quick = $4.quick;
+			r.af = $6;
+			r.prob = $9.prob;
+			r.rtableid = $9.rtableid;
+
+			if ($9.tag)
+				if (strlcpy(r.tagname, $9.tag,
+				    PF_TAG_NAME_SIZE) >= PF_TAG_NAME_SIZE) {
+					yyerror("tag too long, max %u chars",
+					    PF_TAG_NAME_SIZE - 1);
+					YYERROR;
+				}
+			if ($9.match_tag)
+				if (strlcpy(r.match_tagname, $9.match_tag,
+				    PF_TAG_NAME_SIZE) >= PF_TAG_NAME_SIZE) {
+					yyerror("tag too long, max %u chars",
+					    PF_TAG_NAME_SIZE - 1);
+					YYERROR;
+				}
+			r.match_tag_not = $9.match_tag_not;
+			if (rule_label(&r, $9.label))
+				YYERROR;
+			free($9.label);
+			r.flags = $9.flags.b1;
+			r.flagset = $9.flags.b2;
+			if (($9.flags.b1 & $9.flags.b2) != $9.flags.b1) {
+				yyerror("flags always false");
+				YYERROR;
+			}
+			if ($9.flags.b1 || $9.flags.b2 || $8.src_os) {
+				for (proto = $7; proto != NULL &&
+				    proto->proto != IPPROTO_TCP;
+				    proto = proto->next)
+					;	/* nothing */
+				if (proto == NULL && $7 != NULL) {
+					if ($9.flags.b1 || $9.flags.b2)
+						yyerror(
+						    "flags only apply to tcp");
+					if ($8.src_os)
+						yyerror(
+						    "OS fingerprinting only "
+						    "applies to tcp");
+					YYERROR;
+				}
+			}
+
+			r.tos = $9.tos;
+
+			if ($9.keep.action) {
+				yyerror("cannot specify state handling "
+				    "on anchors");
+				YYERROR;
+			}
+
+			if ($9.match_tag)
+				if (strlcpy(r.match_tagname, $9.match_tag,
+				    PF_TAG_NAME_SIZE) >= PF_TAG_NAME_SIZE) {
+					yyerror("tag too long, max %u chars",
+					    PF_TAG_NAME_SIZE - 1);
+					YYERROR;
+				}
+			r.match_tag_not = $9.match_tag_not;
+
+			decide_address_family($8.src.host, &r.af);
+			decide_address_family($8.dst.host, &r.af);
+
+			expand_rule(&r, $5, NULL, $7, $8.src_os,
+			    $8.src.host, $8.src.port, $8.dst.host, $8.dst.port,
+			    $9.uid, $9.gid, $9.icmpspec,
+			    pf->astack[pf->asd + 1] ? pf->alast->name : $2);
+			free($2);
+			pf->astack[pf->asd + 1] = NULL;
+		}
+		| NATANCHOR string interface af proto fromto rtable {
+			struct pf_rule	r;
+
+			if (check_rulestate(PFCTL_STATE_NAT)) {
+				free($2);
+				YYERROR;
+			}
+
+			memset(&r, 0, sizeof(r));
+			r.action = PF_NAT;
+			r.af = $4;
+			r.rtableid = $7;
+
+			decide_address_family($6.src.host, &r.af);
+			decide_address_family($6.dst.host, &r.af);
+
+			expand_rule(&r, $3, NULL, $5, $6.src_os,
+			    $6.src.host, $6.src.port, $6.dst.host, $6.dst.port,
+			    0, 0, 0, $2);
+			free($2);
+		}
+		| RDRANCHOR string interface af proto fromto rtable {
+			struct pf_rule	r;
+
+			if (check_rulestate(PFCTL_STATE_NAT)) {
+				free($2);
+				YYERROR;
+			}
+
+			memset(&r, 0, sizeof(r));
+			r.action = PF_RDR;
+			r.af = $4;
+			r.rtableid = $7;
+
+			decide_address_family($6.src.host, &r.af);
+			decide_address_family($6.dst.host, &r.af);
+
+			if ($6.src.port != NULL) {
+				yyerror("source port parameter not supported"
+				    " in rdr-anchor");
+				YYERROR;
+			}
+			if ($6.dst.port != NULL) {
+				if ($6.dst.port->next != NULL) {
+					yyerror("destination port list "
+					    "expansion not supported in "
+					    "rdr-anchor");
+					YYERROR;
+				} else if ($6.dst.port->op != PF_OP_EQ) {
+					yyerror("destination port operators"
+					    " not supported in rdr-anchor");
+					YYERROR;
+				}
+				r.dst.port[0] = $6.dst.port->port[0];
+				r.dst.port[1] = $6.dst.port->port[1];
+				r.dst.port_op = $6.dst.port->op;
+			}
+
+			expand_rule(&r, $3, NULL, $5, $6.src_os,
+			    $6.src.host, $6.src.port, $6.dst.host, $6.dst.port,
+			    0, 0, 0, $2);
+			free($2);
+		}
+		| BINATANCHOR string interface af proto fromto rtable {
+			struct pf_rule	r;
+
+			if (check_rulestate(PFCTL_STATE_NAT)) {
+				free($2);
+				YYERROR;
+			}
+
+			memset(&r, 0, sizeof(r));
+			r.action = PF_BINAT;
+			r.af = $4;
+			r.rtableid = $7;
+			if ($5 != NULL) {
+				if ($5->next != NULL) {
+					yyerror("proto list expansion"
+					    " not supported in binat-anchor");
+					YYERROR;
+				}
+				r.proto = $5->proto;
+				free($5);
+			}
+
+			if ($6.src.host != NULL || $6.src.port != NULL ||
+			    $6.dst.host != NULL || $6.dst.port != NULL) {
+				yyerror("fromto parameter not supported"
+				    " in binat-anchor");
+				YYERROR;
+			}
+
+			decide_address_family($6.src.host, &r.af);
+			decide_address_family($6.dst.host, &r.af);
+
+			pfctl_add_rule(pf, &r, $2);
+			free($2);
+		}
+		;
+
+loadrule	: LOAD ANCHOR string FROM string	{
+			struct loadanchors	*loadanchor;
+
+			if (strlen(pf->anchor->name) + 1 +
+			    strlen($3) >= MAXPATHLEN) {
+				yyerror("anchorname %s too long, max %u\n",
+				    $3, MAXPATHLEN - 1);
+				free($3);
+				YYERROR;
+			}
+			loadanchor = calloc(1, sizeof(struct loadanchors));
+			if (loadanchor == NULL)
+				err(1, "loadrule: calloc");
+			if ((loadanchor->anchorname = malloc(MAXPATHLEN)) ==
+			    NULL)
+				err(1, "loadrule: malloc");
+			if (pf->anchor->name[0])
+				snprintf(loadanchor->anchorname, MAXPATHLEN,
+				    "%s/%s", pf->anchor->name, $3);
+			else
+				strlcpy(loadanchor->anchorname, $3, MAXPATHLEN);
+			if ((loadanchor->filename = strdup($5)) == NULL)
+				err(1, "loadrule: strdup");
+
+			TAILQ_INSERT_TAIL(&loadanchorshead, loadanchor,
+			    entries);
+
+			free($3);
+			free($5);
+		};
+
+scrubaction	: no SCRUB {
+			$$.b2 = $$.w = 0;
+			if ($1)
+				$$.b1 = PF_NOSCRUB;
+			else
+				$$.b1 = PF_SCRUB;
+		}
+		;
+
+scrubrule	: scrubaction dir logquick interface af proto fromto scrub_opts
+		{
+			struct pf_rule	r;
+
+			if (check_rulestate(PFCTL_STATE_SCRUB))
+				YYERROR;
+
+			memset(&r, 0, sizeof(r));
+
+			r.action = $1.b1;
+			r.direction = $2;
+
+			r.log = $3.log;
+			r.logif = $3.logif;
+			if ($3.quick) {
+				yyerror("scrub rules do not support 'quick'");
+				YYERROR;
+			}
+
+			r.af = $5;
+			if ($8.nodf)
+				r.rule_flag |= PFRULE_NODF;
+			if ($8.randomid)
+				r.rule_flag |= PFRULE_RANDOMID;
+			if ($8.reassemble_tcp) {
+				if (r.direction != PF_INOUT) {
+					yyerror("reassemble tcp rules can not "
+					    "specify direction");
+					YYERROR;
+				}
+				r.rule_flag |= PFRULE_REASSEMBLE_TCP;
+			}
+			if ($8.minttl)
+				r.min_ttl = $8.minttl;
+			if ($8.maxmss)
+				r.max_mss = $8.maxmss;
+			if ($8.marker & SOM_SETTOS) {
+				r.rule_flag |= PFRULE_SET_TOS;
+				r.set_tos = $8.settos;
+			}
+			if ($8.fragcache)
+				r.rule_flag |= $8.fragcache;
+			if ($8.match_tag)
+				if (strlcpy(r.match_tagname, $8.match_tag,
+				    PF_TAG_NAME_SIZE) >= PF_TAG_NAME_SIZE) {
+					yyerror("tag too long, max %u chars",
+					    PF_TAG_NAME_SIZE - 1);
+					YYERROR;
+				}
+			r.match_tag_not = $8.match_tag_not;
+			r.rtableid = $8.rtableid;
+
+			expand_rule(&r, $4, NULL, $6, $7.src_os,
+			    $7.src.host, $7.src.port, $7.dst.host, $7.dst.port,
+			    NULL, NULL, NULL, "");
+		}
+		;
+
+scrub_opts	:	{
+				bzero(&scrub_opts, sizeof scrub_opts);
+				scrub_opts.rtableid = -1;
+			}
+		    scrub_opts_l
+			{ $$ = scrub_opts; }
+		| /* empty */ {
+			bzero(&scrub_opts, sizeof scrub_opts);
+			scrub_opts.rtableid = -1;
+			$$ = scrub_opts;
+		}
+		;
+
+scrub_opts_l	: scrub_opts_l scrub_opt
+		| scrub_opt
+		;
+
+scrub_opt	: NODF	{
+			if (scrub_opts.nodf) {
+				yyerror("no-df cannot be respecified");
+				YYERROR;
+			}
+			scrub_opts.nodf = 1;
+		}
+		| MINTTL NUMBER {
+			if (scrub_opts.marker & SOM_MINTTL) {
+				yyerror("min-ttl cannot be respecified");
+				YYERROR;
+			}
+			if ($2 < 0 || $2 > 255) {
+				yyerror("illegal min-ttl value %d", $2);
+				YYERROR;
+			}
+			scrub_opts.marker |= SOM_MINTTL;
+			scrub_opts.minttl = $2;
+		}
+		| MAXMSS NUMBER {
+			if (scrub_opts.marker & SOM_MAXMSS) {
+				yyerror("max-mss cannot be respecified");
+				YYERROR;
+			}
+			if ($2 < 0 || $2 > 65535) {
+				yyerror("illegal max-mss value %d", $2);
+				YYERROR;
+			}
+			scrub_opts.marker |= SOM_MAXMSS;
+			scrub_opts.maxmss = $2;
+		}
+		| SETTOS tos {
+			if (scrub_opts.marker & SOM_SETTOS) {
+				yyerror("set-tos cannot be respecified");
+				YYERROR;
+			}
+			scrub_opts.marker |= SOM_SETTOS;
+			scrub_opts.settos = $2;
+		}
+		| fragcache {
+			if (scrub_opts.marker & SOM_FRAGCACHE) {
+				yyerror("fragcache cannot be respecified");
+				YYERROR;
+			}
+			scrub_opts.marker |= SOM_FRAGCACHE;
+			scrub_opts.fragcache = $1;
+		}
+		| REASSEMBLE STRING {
+			if (strcasecmp($2, "tcp") != 0) {
+				yyerror("scrub reassemble supports only tcp, "
+				    "not '%s'", $2);
+				free($2);
+				YYERROR;
+			}
+			free($2);
+			if (scrub_opts.reassemble_tcp) {
+				yyerror("reassemble tcp cannot be respecified");
+				YYERROR;
+			}
+			scrub_opts.reassemble_tcp = 1;
+		}
+		| RANDOMID {
+			if (scrub_opts.randomid) {
+				yyerror("random-id cannot be respecified");
+				YYERROR;
+			}
+			scrub_opts.randomid = 1;
+		}
+		| RTABLE NUMBER				{
+			if ($2 < 0 || $2 > rt_tableid_max()) {
+				yyerror("invalid rtable id");
+				YYERROR;
+			}
+			scrub_opts.rtableid = $2;
+		}
+		| not TAGGED string			{
+			scrub_opts.match_tag = $3;
+			scrub_opts.match_tag_not = $1;
+		}
+		;
+
+fragcache	: FRAGMENT REASSEMBLE	{ $$ = 0; /* default */ }
+		| FRAGMENT FRAGCROP	{ $$ = PFRULE_FRAGCROP; }
+		| FRAGMENT FRAGDROP	{ $$ = PFRULE_FRAGDROP; }
+		;
+
+antispoof	: ANTISPOOF logquick antispoof_ifspc af antispoof_opts {
+			struct pf_rule		 r;
+			struct node_host	*h = NULL, *hh;
+			struct node_if		*i, *j;
+
+			if (check_rulestate(PFCTL_STATE_FILTER))
+				YYERROR;
+
+			for (i = $3; i; i = i->next) {
+				bzero(&r, sizeof(r));
+
+				r.action = PF_DROP;
+				r.direction = PF_IN;
+				r.log = $2.log;
+				r.logif = $2.logif;
+				r.quick = $2.quick;
+				r.af = $4;
+				if (rule_label(&r, $5.label))
+					YYERROR;
+				r.rtableid = $5.rtableid;
+				j = calloc(1, sizeof(struct node_if));
+				if (j == NULL)
+					err(1, "antispoof: calloc");
+				if (strlcpy(j->ifname, i->ifname,
+				    sizeof(j->ifname)) >= sizeof(j->ifname)) {
+					free(j);
+					yyerror("interface name too long");
+					YYERROR;
+				}
+				j->not = 1;
+				if (i->dynamic) {
+					h = calloc(1, sizeof(*h));
+					if (h == NULL)
+						err(1, "address: calloc");
+					h->addr.type = PF_ADDR_DYNIFTL;
+					set_ipmask(h, 128);
+					if (strlcpy(h->addr.v.ifname, i->ifname,
+					    sizeof(h->addr.v.ifname)) >=
+					    sizeof(h->addr.v.ifname)) {
+						free(h);
+						yyerror(
+						    "interface name too long");
+						YYERROR;
+					}
+					hh = malloc(sizeof(*hh));
+					if (hh == NULL)
+						 err(1, "address: malloc");
+					bcopy(h, hh, sizeof(*hh));
+					h->addr.iflags = PFI_AFLAG_NETWORK;
+				} else {
+					h = ifa_lookup(j->ifname,
+					    PFI_AFLAG_NETWORK);
+					hh = NULL;
+				}
+
+				if (h != NULL)
+					expand_rule(&r, j, NULL, NULL, NULL, h,
+					    NULL, NULL, NULL, NULL, NULL,
+					    NULL, "");
+
+				if ((i->ifa_flags & IFF_LOOPBACK) == 0) {
+					bzero(&r, sizeof(r));
+
+					r.action = PF_DROP;
+					r.direction = PF_IN;
+					r.log = $2.log;
+					r.logif = $2.logif;
+					r.quick = $2.quick;
+					r.af = $4;
+					if (rule_label(&r, $5.label))
+						YYERROR;
+					r.rtableid = $5.rtableid;
+					if (hh != NULL)
+						h = hh;
+					else
+						h = ifa_lookup(i->ifname, 0);
+					if (h != NULL)
+						expand_rule(&r, NULL, NULL,
+						    NULL, NULL, h, NULL, NULL,
+						    NULL, NULL, NULL, NULL, "");
+				} else
+					free(hh);
+			}
+			free($5.label);
+		}
+		;
+
+antispoof_ifspc	: FOR antispoof_if			{ $$ = $2; }
+		| FOR '{' optnl antispoof_iflst '}'	{ $$ = $4; }
+		;
+
+antispoof_iflst	: antispoof_if optnl			{ $$ = $1; }
+		| antispoof_iflst comma antispoof_if optnl {
+			$1->tail->next = $3;
+			$1->tail = $3;
+			$$ = $1;
+		}
+		;
+
+antispoof_if	: if_item				{ $$ = $1; }
+		| '(' if_item ')'			{
+			$2->dynamic = 1;
+			$$ = $2;
+		}
+		;
+
+antispoof_opts	:	{
+				bzero(&antispoof_opts, sizeof antispoof_opts);
+				antispoof_opts.rtableid = -1;
+			}
+		    antispoof_opts_l
+			{ $$ = antispoof_opts; }
+		| /* empty */	{
+			bzero(&antispoof_opts, sizeof antispoof_opts);
+			antispoof_opts.rtableid = -1;
+			$$ = antispoof_opts;
+		}
+		;
+
+antispoof_opts_l	: antispoof_opts_l antispoof_opt
+			| antispoof_opt
+			;
+
+antispoof_opt	: label	{
+			if (antispoof_opts.label) {
+				yyerror("label cannot be redefined");
+				YYERROR;
+			}
+			antispoof_opts.label = $1;
+		}
+		| RTABLE NUMBER				{
+			if ($2 < 0 || $2 > rt_tableid_max()) {
+				yyerror("invalid rtable id");
+				YYERROR;
+			}
+			antispoof_opts.rtableid = $2;
+		}
+		;
+
+not		: '!'		{ $$ = 1; }
+		| /* empty */	{ $$ = 0; }
+		;
+
+tabledef	: TABLE '<' STRING '>' table_opts {
+			struct node_host	 *h, *nh;
+			struct node_tinit	 *ti, *nti;
+
+			if (strlen($3) >= PF_TABLE_NAME_SIZE) {
+				yyerror("table name too long, max %d chars",
+				    PF_TABLE_NAME_SIZE - 1);
+				free($3);
+				YYERROR;
+			}
+			if (pf->loadopt & PFCTL_FLAG_TABLE)
+				if (process_tabledef($3, &$5)) {
+					free($3);
+					YYERROR;
+				}
+			free($3);
+			for (ti = SIMPLEQ_FIRST(&$5.init_nodes);
+			    ti != SIMPLEQ_END(&$5.init_nodes); ti = nti) {
+				if (ti->file)
+					free(ti->file);
+				for (h = ti->host; h != NULL; h = nh) {
+					nh = h->next;
+					free(h);
+				}
+				nti = SIMPLEQ_NEXT(ti, entries);
+				free(ti);
+			}
+		}
+		;
+
+table_opts	:	{
+			bzero(&table_opts, sizeof table_opts);
+			SIMPLEQ_INIT(&table_opts.init_nodes);
+		}
+		    table_opts_l
+			{ $$ = table_opts; }
+		| /* empty */
+			{
+			bzero(&table_opts, sizeof table_opts);
+			SIMPLEQ_INIT(&table_opts.init_nodes);
+			$$ = table_opts;
+		}
+		;
+
+table_opts_l	: table_opts_l table_opt
+		| table_opt
+		;
+
+table_opt	: STRING		{
+			if (!strcmp($1, "const"))
+				table_opts.flags |= PFR_TFLAG_CONST;
+			else if (!strcmp($1, "persist"))
+				table_opts.flags |= PFR_TFLAG_PERSIST;
+			else if (!strcmp($1, "counters"))
+				table_opts.flags |= PFR_TFLAG_COUNTERS;
+			else {
+				yyerror("invalid table option '%s'", $1);
+				free($1);
+				YYERROR;
+			}
+			free($1);
+		}
+		| '{' optnl '}'		{ table_opts.init_addr = 1; }
+		| '{' optnl host_list '}'	{
+			struct node_host	*n;
+			struct node_tinit	*ti;
+
+			for (n = $3; n != NULL; n = n->next) {
+				switch (n->addr.type) {
+				case PF_ADDR_ADDRMASK:
+					continue; /* ok */
+				case PF_ADDR_RANGE:
+					yyerror("address ranges are not "
+					    "permitted inside tables");
+					break;
+				case PF_ADDR_DYNIFTL:
+					yyerror("dynamic addresses are not "
+					    "permitted inside tables");
+					break;
+				case PF_ADDR_TABLE:
+					yyerror("tables cannot contain tables");
+					break;
+				case PF_ADDR_NOROUTE:
+					yyerror("\"no-route\" is not permitted "
+					    "inside tables");
+					break;
+				case PF_ADDR_URPFFAILED:
+					yyerror("\"urpf-failed\" is not "
+					    "permitted inside tables");
+					break;
+				default:
+					yyerror("unknown address type %d",
+					    n->addr.type);
+				}
+				YYERROR;
+			}
+			if (!(ti = calloc(1, sizeof(*ti))))
+				err(1, "table_opt: calloc");
+			ti->host = $3;
+			SIMPLEQ_INSERT_TAIL(&table_opts.init_nodes, ti,
+			    entries);
+			table_opts.init_addr = 1;
+		}
+		| FILENAME STRING	{
+			struct node_tinit	*ti;
+
+			if (!(ti = calloc(1, sizeof(*ti))))
+				err(1, "table_opt: calloc");
+			ti->file = $2;
+			SIMPLEQ_INSERT_TAIL(&table_opts.init_nodes, ti,
+			    entries);
+			table_opts.init_addr = 1;
+		}
+		;
+
+altqif		: ALTQ interface queue_opts QUEUE qassign {
+			struct pf_altq	a;
+
+			if (check_rulestate(PFCTL_STATE_QUEUE))
+				YYERROR;
+
+			memset(&a, 0, sizeof(a));
+			if ($3.scheduler.qtype == ALTQT_NONE) {
+				yyerror("no scheduler specified!");
+				YYERROR;
+			}
+			a.scheduler = $3.scheduler.qtype;
+			a.qlimit = $3.qlimit;
+			a.tbrsize = $3.tbrsize;
+			if ($5 == NULL) {
+				yyerror("no child queues specified");
+				YYERROR;
+			}
+			if (expand_altq(&a, $2, $5, $3.queue_bwspec,
+			    &$3.scheduler))
+				YYERROR;
+		}
+		;
+
+queuespec	: QUEUE STRING interface queue_opts qassign {
+			struct pf_altq	a;
+
+			if (check_rulestate(PFCTL_STATE_QUEUE)) {
+				free($2);
+				YYERROR;
+			}
+
+			memset(&a, 0, sizeof(a));
+
+			if (strlcpy(a.qname, $2, sizeof(a.qname)) >=
+			    sizeof(a.qname)) {
+				yyerror("queue name too long (max "
+				    "%d chars)", PF_QNAME_SIZE-1);
+				free($2);
+				YYERROR;
+			}
+			free($2);
+			if ($4.tbrsize) {
+				yyerror("cannot specify tbrsize for queue");
+				YYERROR;
+			}
+			if ($4.priority > 255) {
+				yyerror("priority out of range: max 255");
+				YYERROR;
+			}
+			a.priority = $4.priority;
+			a.qlimit = $4.qlimit;
+			a.scheduler = $4.scheduler.qtype;
+			if (expand_queue(&a, $3, $5, $4.queue_bwspec,
+			    &$4.scheduler)) {
+				yyerror("errors in queue definition");
+				YYERROR;
+			}
+		}
+		;
+
+queue_opts	:	{
+			bzero(&queue_opts, sizeof queue_opts);
+			queue_opts.priority = DEFAULT_PRIORITY;
+			queue_opts.qlimit = DEFAULT_QLIMIT;
+			queue_opts.scheduler.qtype = ALTQT_NONE;
+			queue_opts.queue_bwspec.bw_percent = 100;
+		}
+		    queue_opts_l
+			{ $$ = queue_opts; }
+		| /* empty */ {
+			bzero(&queue_opts, sizeof queue_opts);
+			queue_opts.priority = DEFAULT_PRIORITY;
+			queue_opts.qlimit = DEFAULT_QLIMIT;
+			queue_opts.scheduler.qtype = ALTQT_NONE;
+			queue_opts.queue_bwspec.bw_percent = 100;
+			$$ = queue_opts;
+		}
+		;
+
+queue_opts_l	: queue_opts_l queue_opt
+		| queue_opt
+		;
+
+queue_opt	: BANDWIDTH bandwidth	{
+			if (queue_opts.marker & QOM_BWSPEC) {
+				yyerror("bandwidth cannot be respecified");
+				YYERROR;
+			}
+			queue_opts.marker |= QOM_BWSPEC;
+			queue_opts.queue_bwspec = $2;
+		}
+		| PRIORITY NUMBER	{
+			if (queue_opts.marker & QOM_PRIORITY) {
+				yyerror("priority cannot be respecified");
+				YYERROR;
+			}
+			if ($2 < 0 || $2 > 255) {
+				yyerror("priority out of range: max 255");
+				YYERROR;
+			}
+			queue_opts.marker |= QOM_PRIORITY;
+			queue_opts.priority = $2;
+		}
+		| QLIMIT NUMBER	{
+			if (queue_opts.marker & QOM_QLIMIT) {
+				yyerror("qlimit cannot be respecified");
+				YYERROR;
+			}
+			if ($2 < 0 || $2 > 65535) {
+				yyerror("qlimit out of range: max 65535");
+				YYERROR;
+			}
+			queue_opts.marker |= QOM_QLIMIT;
+			queue_opts.qlimit = $2;
+		}
+		| scheduler	{
+			if (queue_opts.marker & QOM_SCHEDULER) {
+				yyerror("scheduler cannot be respecified");
+				YYERROR;
+			}
+			queue_opts.marker |= QOM_SCHEDULER;
+			queue_opts.scheduler = $1;
+		}
+		| TBRSIZE NUMBER	{
+			if (queue_opts.marker & QOM_TBRSIZE) {
+				yyerror("tbrsize cannot be respecified");
+				YYERROR;
+			}
+			if ($2 < 0 || $2 > 65535) {
+				yyerror("tbrsize too big: max 65535");
+				YYERROR;
+			}
+			queue_opts.marker |= QOM_TBRSIZE;
+			queue_opts.tbrsize = $2;
+		}
+		;
+
+bandwidth	: STRING {
+			double	 bps;
+			char	*cp;
+
+			$$.bw_percent = 0;
+
+			bps = strtod($1, &cp);
+			if (cp != NULL) {
+				if (!strcmp(cp, "b"))
+					; /* nothing */
+				else if (!strcmp(cp, "Kb"))
+					bps *= 1000;
+				else if (!strcmp(cp, "Mb"))
+					bps *= 1000 * 1000;
+				else if (!strcmp(cp, "Gb"))
+					bps *= 1000 * 1000 * 1000;
+				else if (!strcmp(cp, "%")) {
+					if (bps < 0 || bps > 100) {
+						yyerror("bandwidth spec "
+						    "out of range");
+						free($1);
+						YYERROR;
+					}
+					$$.bw_percent = bps;
+					bps = 0;
+				} else {
+					yyerror("unknown unit %s", cp);
+					free($1);
+					YYERROR;
+				}
+			}
+			free($1);
+			$$.bw_absolute = (u_int32_t)bps;
+		}
+		| NUMBER {
+			if ($1 < 0 || $1 > UINT_MAX) {
+				yyerror("bandwidth number too big");
+				YYERROR;
+			}
+			$$.bw_percent = 0;
+			$$.bw_absolute = $1;
+		}
+		;
+
+scheduler	: CBQ				{
+			$$.qtype = ALTQT_CBQ;
+			$$.data.cbq_opts.flags = 0;
+		}
+		| CBQ '(' cbqflags_list ')'	{
+			$$.qtype = ALTQT_CBQ;
+			$$.data.cbq_opts.flags = $3;
+		}
+		| PRIQ				{
+			$$.qtype = ALTQT_PRIQ;
+			$$.data.priq_opts.flags = 0;
+		}
+		| PRIQ '(' priqflags_list ')'	{
+			$$.qtype = ALTQT_PRIQ;
+			$$.data.priq_opts.flags = $3;
+		}
+		| HFSC				{
+			$$.qtype = ALTQT_HFSC;
+			bzero(&$$.data.hfsc_opts,
+			    sizeof(struct node_hfsc_opts));
+		}
+		| HFSC '(' hfsc_opts ')'	{
+			$$.qtype = ALTQT_HFSC;
+			$$.data.hfsc_opts = $3;
+		}
+		;
+
+cbqflags_list	: cbqflags_item				{ $$ |= $1; }
+		| cbqflags_list comma cbqflags_item	{ $$ |= $3; }
+		;
+
+cbqflags_item	: STRING	{
+			if (!strcmp($1, "default"))
+				$$ = CBQCLF_DEFCLASS;
+			else if (!strcmp($1, "borrow"))
+				$$ = CBQCLF_BORROW;
+			else if (!strcmp($1, "red"))
+				$$ = CBQCLF_RED;
+			else if (!strcmp($1, "ecn"))
+				$$ = CBQCLF_RED|CBQCLF_ECN;
+			else if (!strcmp($1, "rio"))
+				$$ = CBQCLF_RIO;
+			else {
+				yyerror("unknown cbq flag \"%s\"", $1);
+				free($1);
+				YYERROR;
+			}
+			free($1);
+		}
+		;
+
+priqflags_list	: priqflags_item			{ $$ |= $1; }
+		| priqflags_list comma priqflags_item	{ $$ |= $3; }
+		;
+
+priqflags_item	: STRING	{
+			if (!strcmp($1, "default"))
+				$$ = PRCF_DEFAULTCLASS;
+			else if (!strcmp($1, "red"))
+				$$ = PRCF_RED;
+			else if (!strcmp($1, "ecn"))
+				$$ = PRCF_RED|PRCF_ECN;
+			else if (!strcmp($1, "rio"))
+				$$ = PRCF_RIO;
+			else {
+				yyerror("unknown priq flag \"%s\"", $1);
+				free($1);
+				YYERROR;
+			}
+			free($1);
+		}
+		;
+
+hfsc_opts	:	{
+				bzero(&hfsc_opts,
+				    sizeof(struct node_hfsc_opts));
+			}
+		    hfscopts_list				{
+			$$ = hfsc_opts;
+		}
+		;
+
+hfscopts_list	: hfscopts_item
+		| hfscopts_list comma hfscopts_item
+		;
+
+hfscopts_item	: LINKSHARE bandwidth				{
+			if (hfsc_opts.linkshare.used) {
+				yyerror("linkshare already specified");
+				YYERROR;
+			}
+			hfsc_opts.linkshare.m2 = $2;
+			hfsc_opts.linkshare.used = 1;
+		}
+		| LINKSHARE '(' bandwidth comma NUMBER comma bandwidth ')'
+		    {
+			if ($5 < 0 || $5 > INT_MAX) {
+				yyerror("timing in curve out of range");
+				YYERROR;
+			}
+			if (hfsc_opts.linkshare.used) {
+				yyerror("linkshare already specified");
+				YYERROR;
+			}
+			hfsc_opts.linkshare.m1 = $3;
+			hfsc_opts.linkshare.d = $5;
+			hfsc_opts.linkshare.m2 = $7;
+			hfsc_opts.linkshare.used = 1;
+		}
+		| REALTIME bandwidth				{
+			if (hfsc_opts.realtime.used) {
+				yyerror("realtime already specified");
+				YYERROR;
+			}
+			hfsc_opts.realtime.m2 = $2;
+			hfsc_opts.realtime.used = 1;
+		}
+		| REALTIME '(' bandwidth comma NUMBER comma bandwidth ')'
+		    {
+			if ($5 < 0 || $5 > INT_MAX) {
+				yyerror("timing in curve out of range");
+				YYERROR;
+			}
+			if (hfsc_opts.realtime.used) {
+				yyerror("realtime already specified");
+				YYERROR;
+			}
+			hfsc_opts.realtime.m1 = $3;
+			hfsc_opts.realtime.d = $5;
+			hfsc_opts.realtime.m2 = $7;
+			hfsc_opts.realtime.used = 1;
+		}
+		| UPPERLIMIT bandwidth				{
+			if (hfsc_opts.upperlimit.used) {
+				yyerror("upperlimit already specified");
+				YYERROR;
+			}
+			hfsc_opts.upperlimit.m2 = $2;
+			hfsc_opts.upperlimit.used = 1;
+		}
+		| UPPERLIMIT '(' bandwidth comma NUMBER comma bandwidth ')'
+		    {
+			if ($5 < 0 || $5 > INT_MAX) {
+				yyerror("timing in curve out of range");
+				YYERROR;
+			}
+			if (hfsc_opts.upperlimit.used) {
+				yyerror("upperlimit already specified");
+				YYERROR;
+			}
+			hfsc_opts.upperlimit.m1 = $3;
+			hfsc_opts.upperlimit.d = $5;
+			hfsc_opts.upperlimit.m2 = $7;
+			hfsc_opts.upperlimit.used = 1;
+		}
+		| STRING	{
+			if (!strcmp($1, "default"))
+				hfsc_opts.flags |= HFCF_DEFAULTCLASS;
+			else if (!strcmp($1, "red"))
+				hfsc_opts.flags |= HFCF_RED;
+			else if (!strcmp($1, "ecn"))
+				hfsc_opts.flags |= HFCF_RED|HFCF_ECN;
+			else if (!strcmp($1, "rio"))
+				hfsc_opts.flags |= HFCF_RIO;
+			else {
+				yyerror("unknown hfsc flag \"%s\"", $1);
+				free($1);
+				YYERROR;
+			}
+			free($1);
+		}
+		;
+
+qassign		: /* empty */		{ $$ = NULL; }
+		| qassign_item		{ $$ = $1; }
+		| '{' optnl qassign_list '}'	{ $$ = $3; }
+		;
+
+qassign_list	: qassign_item optnl		{ $$ = $1; }
+		| qassign_list comma qassign_item optnl	{
+			$1->tail->next = $3;
+			$1->tail = $3;
+			$$ = $1;
+		}
+		;
+
+qassign_item	: STRING			{
+			$$ = calloc(1, sizeof(struct node_queue));
+			if ($$ == NULL)
+				err(1, "qassign_item: calloc");
+			if (strlcpy($$->queue, $1, sizeof($$->queue)) >=
+			    sizeof($$->queue)) {
+				yyerror("queue name '%s' too long (max "
+				    "%d chars)", $1, sizeof($$->queue)-1);
+				free($1);
+				free($$);
+				YYERROR;
+			}
+			free($1);
+			$$->next = NULL;
+			$$->tail = $$;
+		}
+		;
+
+pfrule		: action dir logquick interface route af proto fromto
+		    filter_opts
+		{
+			struct pf_rule		 r;
+			struct node_state_opt	*o;
+			struct node_proto	*proto;
+			int			 srctrack = 0;
+			int			 statelock = 0;
+			int			 adaptive = 0;
+			int			 defaults = 0;
+
+			if (check_rulestate(PFCTL_STATE_FILTER))
+				YYERROR;
+
+			memset(&r, 0, sizeof(r));
+
+			r.action = $1.b1;
+			switch ($1.b2) {
+			case PFRULE_RETURNRST:
+				r.rule_flag |= PFRULE_RETURNRST;
+				r.return_ttl = $1.w;
+				break;
+			case PFRULE_RETURNICMP:
+				r.rule_flag |= PFRULE_RETURNICMP;
+				r.return_icmp = $1.w;
+				r.return_icmp6 = $1.w2;
+				break;
+			case PFRULE_RETURN:
+				r.rule_flag |= PFRULE_RETURN;
+				r.return_icmp = $1.w;
+				r.return_icmp6 = $1.w2;
+				break;
+			}
+			r.direction = $2;
+			r.log = $3.log;
+			r.logif = $3.logif;
+			r.quick = $3.quick;
+			r.prob = $9.prob;
+			r.rtableid = $9.rtableid;
+
+			r.af = $6;
+			if ($9.tag)
+				if (strlcpy(r.tagname, $9.tag,
+				    PF_TAG_NAME_SIZE) >= PF_TAG_NAME_SIZE) {
+					yyerror("tag too long, max %u chars",
+					    PF_TAG_NAME_SIZE - 1);
+					YYERROR;
+				}
+			if ($9.match_tag)
+				if (strlcpy(r.match_tagname, $9.match_tag,
+				    PF_TAG_NAME_SIZE) >= PF_TAG_NAME_SIZE) {
+					yyerror("tag too long, max %u chars",
+					    PF_TAG_NAME_SIZE - 1);
+					YYERROR;
+				}
+			r.match_tag_not = $9.match_tag_not;
+			if (rule_label(&r, $9.label))
+				YYERROR;
+			free($9.label);
+			r.flags = $9.flags.b1;
+			r.flagset = $9.flags.b2;
+			if (($9.flags.b1 & $9.flags.b2) != $9.flags.b1) {
+				yyerror("flags always false");
+				YYERROR;
+			}
+			if ($9.flags.b1 || $9.flags.b2 || $8.src_os) {
+				for (proto = $7; proto != NULL &&
+				    proto->proto != IPPROTO_TCP;
+				    proto = proto->next)
+					;	/* nothing */
+				if (proto == NULL && $7 != NULL) {
+					if ($9.flags.b1 || $9.flags.b2)
+						yyerror(
+						    "flags only apply to tcp");
+					if ($8.src_os)
+						yyerror(
+						    "OS fingerprinting only "
+						    "apply to tcp");
+					YYERROR;
+				}
+#if 0
+				if (($9.flags.b1 & parse_flags("S")) == 0 &&
+				    $8.src_os) {
+					yyerror("OS fingerprinting requires "
+					    "the SYN TCP flag (flags S/SA)");
+					YYERROR;
+				}
+#endif
+			}
+
+			r.tos = $9.tos;
+			r.keep_state = $9.keep.action;
+			o = $9.keep.options;
+
+			/* 'keep state' by default on pass rules. */
+			if (!r.keep_state && !r.action &&
+			    !($9.marker & FOM_KEEP)) {
+				r.keep_state = PF_STATE_NORMAL;
+				o = keep_state_defaults;
+				defaults = 1;
+			}
+
+			while (o) {
+				struct node_state_opt	*p = o;
+
+				switch (o->type) {
+				case PF_STATE_OPT_MAX:
+					if (r.max_states) {
+						yyerror("state option 'max' "
+						    "multiple definitions");
+						YYERROR;
+					}
+					r.max_states = o->data.max_states;
+					break;
+				case PF_STATE_OPT_NOSYNC:
+					if (r.rule_flag & PFRULE_NOSYNC) {
+						yyerror("state option 'sync' "
+						    "multiple definitions");
+						YYERROR;
+					}
+					r.rule_flag |= PFRULE_NOSYNC;
+					break;
+				case PF_STATE_OPT_SRCTRACK:
+					if (srctrack) {
+						yyerror("state option "
+						    "'source-track' "
+						    "multiple definitions");
+						YYERROR;
+					}
+					srctrack =  o->data.src_track;
+					r.rule_flag |= PFRULE_SRCTRACK;
+					break;
+				case PF_STATE_OPT_MAX_SRC_STATES:
+					if (r.max_src_states) {
+						yyerror("state option "
+						    "'max-src-states' "
+						    "multiple definitions");
+						YYERROR;
+					}
+					if (o->data.max_src_states == 0) {
+						yyerror("'max-src-states' must "
+						    "be > 0");
+						YYERROR;
+					}
+					r.max_src_states =
+					    o->data.max_src_states;
+					r.rule_flag |= PFRULE_SRCTRACK;
+					break;
+				case PF_STATE_OPT_OVERLOAD:
+					if (r.overload_tblname[0]) {
+						yyerror("multiple 'overload' "
+						    "table definitions");
+						YYERROR;
+					}
+					if (strlcpy(r.overload_tblname,
+					    o->data.overload.tblname,
+					    PF_TABLE_NAME_SIZE) >=
+					    PF_TABLE_NAME_SIZE) {
+						yyerror("state option: "
+						    "strlcpy");
+						YYERROR;
+					}
+					r.flush = o->data.overload.flush;
+					break;
+				case PF_STATE_OPT_MAX_SRC_CONN:
+					if (r.max_src_conn) {
+						yyerror("state option "
+						    "'max-src-conn' "
+						    "multiple definitions");
+						YYERROR;
+					}
+					if (o->data.max_src_conn == 0) {
+						yyerror("'max-src-conn' "
+						    "must be > 0");
+						YYERROR;
+					}
+					r.max_src_conn =
+					    o->data.max_src_conn;
+					r.rule_flag |= PFRULE_SRCTRACK |
+					    PFRULE_RULESRCTRACK;
+					break;
+				case PF_STATE_OPT_MAX_SRC_CONN_RATE:
+					if (r.max_src_conn_rate.limit) {
+						yyerror("state option "
+						    "'max-src-conn-rate' "
+						    "multiple definitions");
+						YYERROR;
+					}
+					if (!o->data.max_src_conn_rate.limit ||
+					    !o->data.max_src_conn_rate.seconds) {
+						yyerror("'max-src-conn-rate' "
+						    "values must be > 0");
+						YYERROR;
+					}
+					if (o->data.max_src_conn_rate.limit >
+					    PF_THRESHOLD_MAX) {
+						yyerror("'max-src-conn-rate' "
+						    "maximum rate must be < %u",
+						    PF_THRESHOLD_MAX);
+						YYERROR;
+					}
+					r.max_src_conn_rate.limit =
+					    o->data.max_src_conn_rate.limit;
+					r.max_src_conn_rate.seconds =
+					    o->data.max_src_conn_rate.seconds;
+					r.rule_flag |= PFRULE_SRCTRACK |
+					    PFRULE_RULESRCTRACK;
+					break;
+				case PF_STATE_OPT_MAX_SRC_NODES:
+					if (r.max_src_nodes) {
+						yyerror("state option "
+						    "'max-src-nodes' "
+						    "multiple definitions");
+						YYERROR;
+					}
+					if (o->data.max_src_nodes == 0) {
+						yyerror("'max-src-nodes' must "
+						    "be > 0");
+						YYERROR;
+					}
+					r.max_src_nodes =
+					    o->data.max_src_nodes;
+					r.rule_flag |= PFRULE_SRCTRACK |
+					    PFRULE_RULESRCTRACK;
+					break;
+				case PF_STATE_OPT_STATELOCK:
+					if (statelock) {
+						yyerror("state locking option: "
+						    "multiple definitions");
+						YYERROR;
+					}
+					statelock = 1;
+					r.rule_flag |= o->data.statelock;
+					break;
+				case PF_STATE_OPT_SLOPPY:
+					if (r.rule_flag & PFRULE_STATESLOPPY) {
+						yyerror("state sloppy option: "
+						    "multiple definitions");
+						YYERROR;
+					}
+					r.rule_flag |= PFRULE_STATESLOPPY;
+					break;
+				case PF_STATE_OPT_PFLOW:
+					if (r.rule_flag & PFRULE_PFLOW) {
+						yyerror("state pflow "
+						    "option: multiple "
+						    "definitions");
+						YYERROR;
+					}
+					r.rule_flag |= PFRULE_PFLOW;
+					break;
+				case PF_STATE_OPT_TIMEOUT:
+					if (o->data.timeout.number ==
+					    PFTM_ADAPTIVE_START ||
+					    o->data.timeout.number ==
+					    PFTM_ADAPTIVE_END)
+						adaptive = 1;
+					if (r.timeout[o->data.timeout.number]) {
+						yyerror("state timeout %s "
+						    "multiple definitions",
+						    pf_timeouts[o->data.
+						    timeout.number].name);
+						YYERROR;
+					}
+					r.timeout[o->data.timeout.number] =
+					    o->data.timeout.seconds;
+				}
+				o = o->next;
+				if (!defaults)
+					free(p);
+			}
+
+			/* 'flags S/SA' by default on stateful rules */
+			if (!r.action && !r.flags && !r.flagset &&
+			    !$9.fragment && !($9.marker & FOM_FLAGS) &&
+			    r.keep_state) {
+				r.flags = parse_flags("S");
+				r.flagset =  parse_flags("SA");
+			}
+			if (!adaptive && r.max_states) {
+				r.timeout[PFTM_ADAPTIVE_START] =
+				    (r.max_states / 10) * 6;
+				r.timeout[PFTM_ADAPTIVE_END] =
+				    (r.max_states / 10) * 12;
+			}
+			if (r.rule_flag & PFRULE_SRCTRACK) {
+				if (srctrack == PF_SRCTRACK_GLOBAL &&
+				    r.max_src_nodes) {
+					yyerror("'max-src-nodes' is "
+					    "incompatible with "
+					    "'source-track global'");
+					YYERROR;
+				}
+				if (srctrack == PF_SRCTRACK_GLOBAL &&
+				    r.max_src_conn) {
+					yyerror("'max-src-conn' is "
+					    "incompatible with "
+					    "'source-track global'");
+					YYERROR;
+				}
+				if (srctrack == PF_SRCTRACK_GLOBAL &&
+				    r.max_src_conn_rate.seconds) {
+					yyerror("'max-src-conn-rate' is "
+					    "incompatible with "
+					    "'source-track global'");
+					YYERROR;
+				}
+				if (r.timeout[PFTM_SRC_NODE] <
+				    r.max_src_conn_rate.seconds)
+					r.timeout[PFTM_SRC_NODE] =
+					    r.max_src_conn_rate.seconds;
+				r.rule_flag |= PFRULE_SRCTRACK;
+				if (srctrack == PF_SRCTRACK_RULE)
+					r.rule_flag |= PFRULE_RULESRCTRACK;
+			}
+			if (r.keep_state && !statelock)
+				r.rule_flag |= default_statelock;
+
+			if ($9.fragment)
+				r.rule_flag |= PFRULE_FRAGMENT;
+			r.allow_opts = $9.allowopts;
+
+			decide_address_family($8.src.host, &r.af);
+			decide_address_family($8.dst.host, &r.af);
+
+			if ($5.rt) {
+				if (!r.direction) {
+					yyerror("direction must be explicit "
+					    "with rules that specify routing");
+					YYERROR;
+				}
+				r.rt = $5.rt;
+				r.rpool.opts = $5.pool_opts;
+				if ($5.key != NULL)
+					memcpy(&r.rpool.key, $5.key,
+					    sizeof(struct pf_poolhashkey));
+			}
+			if (r.rt && r.rt != PF_FASTROUTE) {
+				decide_address_family($5.host, &r.af);
+				remove_invalid_hosts(&$5.host, &r.af);
+				if ($5.host == NULL) {
+					yyerror("no routing address with "
+					    "matching address family found.");
+					YYERROR;
+				}
+				if ((r.rpool.opts & PF_POOL_TYPEMASK) ==
+				    PF_POOL_NONE && ($5.host->next != NULL ||
+				    $5.host->addr.type == PF_ADDR_TABLE ||
+				    DYNIF_MULTIADDR($5.host->addr)))
+					r.rpool.opts |= PF_POOL_ROUNDROBIN;
+				if ((r.rpool.opts & PF_POOL_TYPEMASK) !=
+				    PF_POOL_ROUNDROBIN &&
+				    disallow_table($5.host, "tables are only "
+				    "supported in round-robin routing pools"))
+					YYERROR;
+				if ((r.rpool.opts & PF_POOL_TYPEMASK) !=
+				    PF_POOL_ROUNDROBIN &&
+				    disallow_alias($5.host, "interface (%s) "
+				    "is only supported in round-robin "
+				    "routing pools"))
+					YYERROR;
+				if ($5.host->next != NULL) {
+					if ((r.rpool.opts & PF_POOL_TYPEMASK) !=
+					    PF_POOL_ROUNDROBIN) {
+						yyerror("r.rpool.opts must "
+						    "be PF_POOL_ROUNDROBIN");
+						YYERROR;
+					}
+				}
+			}
+			if ($9.queues.qname != NULL) {
+				if (strlcpy(r.qname, $9.queues.qname,
+				    sizeof(r.qname)) >= sizeof(r.qname)) {
+					yyerror("rule qname too long (max "
+					    "%d chars)", sizeof(r.qname)-1);
+					YYERROR;
+				}
+				free($9.queues.qname);
+			}
+			if ($9.queues.pqname != NULL) {
+				if (strlcpy(r.pqname, $9.queues.pqname,
+				    sizeof(r.pqname)) >= sizeof(r.pqname)) {
+					yyerror("rule pqname too long (max "
+					    "%d chars)", sizeof(r.pqname)-1);
+					YYERROR;
+				}
+				free($9.queues.pqname);
+			}
+#ifdef __FreeBSD__
+			r.divert.port = $9.divert.port;
+#else
+			if ((r.divert.port = $9.divert.port)) {
+				if (r.direction == PF_OUT) {
+					if ($9.divert.addr) {
+						yyerror("address specified "
+						    "for outgoing divert");
+						YYERROR;
+					}
+					bzero(&r.divert.addr,
+					    sizeof(r.divert.addr));
+				} else {
+					if (!$9.divert.addr) {
+						yyerror("no address specified "
+						    "for incoming divert");
+						YYERROR;
+					}
+					if ($9.divert.addr->af != r.af) {
+						yyerror("address family "
+						    "mismatch for divert");
+						YYERROR;
+					}
+					r.divert.addr =
+					    $9.divert.addr->addr.v.a.addr;
+				}
+			}
+#endif
+
+			expand_rule(&r, $4, $5.host, $7, $8.src_os,
+			    $8.src.host, $8.src.port, $8.dst.host, $8.dst.port,
+			    $9.uid, $9.gid, $9.icmpspec, "");
+		}
+		;
+
+filter_opts	:	{
+				bzero(&filter_opts, sizeof filter_opts);
+				filter_opts.rtableid = -1;
+			}
+		    filter_opts_l
+			{ $$ = filter_opts; }
+		| /* empty */	{
+			bzero(&filter_opts, sizeof filter_opts);
+			filter_opts.rtableid = -1;
+			$$ = filter_opts;
+		}
+		;
+
+filter_opts_l	: filter_opts_l filter_opt
+		| filter_opt
+		;
+
+filter_opt	: USER uids {
+			if (filter_opts.uid)
+				$2->tail->next = filter_opts.uid;
+			filter_opts.uid = $2;
+		}
+		| GROUP gids {
+			if (filter_opts.gid)
+				$2->tail->next = filter_opts.gid;
+			filter_opts.gid = $2;
+		}
+		| flags {
+			if (filter_opts.marker & FOM_FLAGS) {
+				yyerror("flags cannot be redefined");
+				YYERROR;
+			}
+			filter_opts.marker |= FOM_FLAGS;
+			filter_opts.flags.b1 |= $1.b1;
+			filter_opts.flags.b2 |= $1.b2;
+			filter_opts.flags.w |= $1.w;
+			filter_opts.flags.w2 |= $1.w2;
+		}
+		| icmpspec {
+			if (filter_opts.marker & FOM_ICMP) {
+				yyerror("icmp-type cannot be redefined");
+				YYERROR;
+			}
+			filter_opts.marker |= FOM_ICMP;
+			filter_opts.icmpspec = $1;
+		}
+		| TOS tos {
+			if (filter_opts.marker & FOM_TOS) {
+				yyerror("tos cannot be redefined");
+				YYERROR;
+			}
+			filter_opts.marker |= FOM_TOS;
+			filter_opts.tos = $2;
+		}
+		| keep {
+			if (filter_opts.marker & FOM_KEEP) {
+				yyerror("modulate or keep cannot be redefined");
+				YYERROR;
+			}
+			filter_opts.marker |= FOM_KEEP;
+			filter_opts.keep.action = $1.action;
+			filter_opts.keep.options = $1.options;
+		}
+		| FRAGMENT {
+			filter_opts.fragment = 1;
+		}
+		| ALLOWOPTS {
+			filter_opts.allowopts = 1;
+		}
+		| label	{
+			if (filter_opts.label) {
+				yyerror("label cannot be redefined");
+				YYERROR;
+			}
+			filter_opts.label = $1;
+		}
+		| qname	{
+			if (filter_opts.queues.qname) {
+				yyerror("queue cannot be redefined");
+				YYERROR;
+			}
+			filter_opts.queues = $1;
+		}
+		| TAG string				{
+			filter_opts.tag = $2;
+		}
+		| not TAGGED string			{
+			filter_opts.match_tag = $3;
+			filter_opts.match_tag_not = $1;
+		}
+		| PROBABILITY probability		{
+			double	p;
+
+			p = floor($2 * UINT_MAX + 0.5);
+			if (p < 0.0 || p > UINT_MAX) {
+				yyerror("invalid probability: %lf", p);
+				YYERROR;
+			}
+			filter_opts.prob = (u_int32_t)p;
+			if (filter_opts.prob == 0)
+				filter_opts.prob = 1;
+		}
+		| RTABLE NUMBER				{
+			if ($2 < 0 || $2 > rt_tableid_max()) {
+				yyerror("invalid rtable id");
+				YYERROR;
+			}
+			filter_opts.rtableid = $2;
+		}
+		| DIVERTTO portplain {
+#ifdef __FreeBSD__
+			filter_opts.divert.port = $2.a;
+			if (!filter_opts.divert.port) {
+				yyerror("invalid divert port: %u", ntohs($2.a));
+				YYERROR;
+			}
+#endif
+		}
+		| DIVERTTO STRING PORT portplain {
+#ifndef __FreeBSD__
+			if ((filter_opts.divert.addr = host($2)) == NULL) {
+				yyerror("could not parse divert address: %s",
+				    $2);
+				free($2);
+				YYERROR;
+			}
+#else
+			if ($2)
+#endif
+			free($2);
+			filter_opts.divert.port = $4.a;
+			if (!filter_opts.divert.port) {
+				yyerror("invalid divert port: %u", ntohs($4.a));
+				YYERROR;
+			}
+		}
+		| DIVERTREPLY {
+#ifdef __FreeBSD__
+			yyerror("divert-reply has no meaning in FreeBSD pf(4)");
+			YYERROR;
+#else
+			filter_opts.divert.port = 1;	/* some random value */
+#endif
+		}
+		;
+
+probability	: STRING				{
+			char	*e;
+			double	 p = strtod($1, &e);
+
+			if (*e == '%') {
+				p *= 0.01;
+				e++;
+			}
+			if (*e) {
+				yyerror("invalid probability: %s", $1);
+				free($1);
+				YYERROR;
+			}
+			free($1);
+			$$ = p;
+		}
+		| NUMBER				{
+			$$ = (double)$1;
+		}
+		;
+
+
+action		: PASS			{ $$.b1 = PF_PASS; $$.b2 = $$.w = 0; }
+		| BLOCK blockspec	{ $$ = $2; $$.b1 = PF_DROP; }
+		;
+
+blockspec	: /* empty */		{
+			$$.b2 = blockpolicy;
+			$$.w = returnicmpdefault;
+			$$.w2 = returnicmp6default;
+		}
+		| DROP			{
+			$$.b2 = PFRULE_DROP;
+			$$.w = 0;
+			$$.w2 = 0;
+		}
+		| RETURNRST		{
+			$$.b2 = PFRULE_RETURNRST;
+			$$.w = 0;
+			$$.w2 = 0;
+		}
+		| RETURNRST '(' TTL NUMBER ')'	{
+			if ($4 < 0 || $4 > 255) {
+				yyerror("illegal ttl value %d", $4);
+				YYERROR;
+			}
+			$$.b2 = PFRULE_RETURNRST;
+			$$.w = $4;
+			$$.w2 = 0;
+		}
+		| RETURNICMP		{
+			$$.b2 = PFRULE_RETURNICMP;
+			$$.w = returnicmpdefault;
+			$$.w2 = returnicmp6default;
+		}
+		| RETURNICMP6		{
+			$$.b2 = PFRULE_RETURNICMP;
+			$$.w = returnicmpdefault;
+			$$.w2 = returnicmp6default;
+		}
+		| RETURNICMP '(' reticmpspec ')'	{
+			$$.b2 = PFRULE_RETURNICMP;
+			$$.w = $3;
+			$$.w2 = returnicmpdefault;
+		}
+		| RETURNICMP6 '(' reticmp6spec ')'	{
+			$$.b2 = PFRULE_RETURNICMP;
+			$$.w = returnicmpdefault;
+			$$.w2 = $3;
+		}
+		| RETURNICMP '(' reticmpspec comma reticmp6spec ')' {
+			$$.b2 = PFRULE_RETURNICMP;
+			$$.w = $3;
+			$$.w2 = $5;
+		}
+		| RETURN {
+			$$.b2 = PFRULE_RETURN;
+			$$.w = returnicmpdefault;
+			$$.w2 = returnicmp6default;
+		}
+		;
+
+reticmpspec	: STRING			{
+			if (!($$ = parseicmpspec($1, AF_INET))) {
+				free($1);
+				YYERROR;
+			}
+			free($1);
+		}
+		| NUMBER			{
+			u_int8_t		icmptype;
+
+			if ($1 < 0 || $1 > 255) {
+				yyerror("invalid icmp code %lu", $1);
+				YYERROR;
+			}
+			icmptype = returnicmpdefault >> 8;
+			$$ = (icmptype << 8 | $1);
+		}
+		;
+
+reticmp6spec	: STRING			{
+			if (!($$ = parseicmpspec($1, AF_INET6))) {
+				free($1);
+				YYERROR;
+			}
+			free($1);
+		}
+		| NUMBER			{
+			u_int8_t		icmptype;
+
+			if ($1 < 0 || $1 > 255) {
+				yyerror("invalid icmp code %lu", $1);
+				YYERROR;
+			}
+			icmptype = returnicmp6default >> 8;
+			$$ = (icmptype << 8 | $1);
+		}
+		;
+
+dir		: /* empty */			{ $$ = PF_INOUT; }
+		| IN				{ $$ = PF_IN; }
+		| OUT				{ $$ = PF_OUT; }
+		;
+
+quick		: /* empty */			{ $$.quick = 0; }
+		| QUICK				{ $$.quick = 1; }
+		;
+
+logquick	: /* empty */	{ $$.log = 0; $$.quick = 0; $$.logif = 0; }
+		| log		{ $$ = $1; $$.quick = 0; }
+		| QUICK		{ $$.quick = 1; $$.log = 0; $$.logif = 0; }
+		| log QUICK	{ $$ = $1; $$.quick = 1; }
+		| QUICK log	{ $$ = $2; $$.quick = 1; }
+		;
+
+log		: LOG			{ $$.log = PF_LOG; $$.logif = 0; }
+		| LOG '(' logopts ')'	{
+			$$.log = PF_LOG | $3.log;
+			$$.logif = $3.logif;
+		}
+		;
+
+logopts		: logopt			{ $$ = $1; }
+		| logopts comma logopt		{
+			$$.log = $1.log | $3.log;
+			$$.logif = $3.logif;
+			if ($$.logif == 0)
+				$$.logif = $1.logif;
+		}
+		;
+
+logopt		: ALL		{ $$.log = PF_LOG_ALL; $$.logif = 0; }
+		| USER		{ $$.log = PF_LOG_SOCKET_LOOKUP; $$.logif = 0; }
+		| GROUP		{ $$.log = PF_LOG_SOCKET_LOOKUP; $$.logif = 0; }
+		| TO string	{
+			const char	*errstr;
+			u_int		 i;
+
+			$$.log = 0;
+			if (strncmp($2, "pflog", 5)) {
+				yyerror("%s: should be a pflog interface", $2);
+				free($2);
+				YYERROR;
+			}
+			i = strtonum($2 + 5, 0, 255, &errstr);
+			if (errstr) {
+				yyerror("%s: %s", $2, errstr);
+				free($2);
+				YYERROR;
+			}
+			free($2);
+			$$.logif = i;
+		}
+		;
+
+interface	: /* empty */			{ $$ = NULL; }
+		| ON if_item_not		{ $$ = $2; }
+		| ON '{' optnl if_list '}'	{ $$ = $4; }
+		;
+
+if_list		: if_item_not optnl		{ $$ = $1; }
+		| if_list comma if_item_not optnl	{
+			$1->tail->next = $3;
+			$1->tail = $3;
+			$$ = $1;
+		}
+		;
+
+if_item_not	: not if_item			{ $$ = $2; $$->not = $1; }
+		;
+
+if_item		: STRING			{
+			struct node_host	*n;
+
+			$$ = calloc(1, sizeof(struct node_if));
+			if ($$ == NULL)
+				err(1, "if_item: calloc");
+			if (strlcpy($$->ifname, $1, sizeof($$->ifname)) >=
+			    sizeof($$->ifname)) {
+				free($1);
+				free($$);
+				yyerror("interface name too long");
+				YYERROR;
+			}
+
+			if ((n = ifa_exists($1)) != NULL)
+				$$->ifa_flags = n->ifa_flags;
+
+			free($1);
+			$$->not = 0;
+			$$->next = NULL;
+			$$->tail = $$;
+		}
+		;
+
+af		: /* empty */			{ $$ = 0; }
+		| INET				{ $$ = AF_INET; }
+		| INET6				{ $$ = AF_INET6; }
+		;
+
+proto		: /* empty */				{ $$ = NULL; }
+		| PROTO proto_item			{ $$ = $2; }
+		| PROTO '{' optnl proto_list '}'	{ $$ = $4; }
+		;
+
+proto_list	: proto_item optnl		{ $$ = $1; }
+		| proto_list comma proto_item optnl	{
+			$1->tail->next = $3;
+			$1->tail = $3;
+			$$ = $1;
+		}
+		;
+
+proto_item	: protoval			{
+			u_int8_t	pr;
+
+			pr = (u_int8_t)$1;
+			if (pr == 0) {
+				yyerror("proto 0 cannot be used");
+				YYERROR;
+			}
+			$$ = calloc(1, sizeof(struct node_proto));
+			if ($$ == NULL)
+				err(1, "proto_item: calloc");
+			$$->proto = pr;
+			$$->next = NULL;
+			$$->tail = $$;
+		}
+		;
+
+protoval	: STRING			{
+			struct protoent	*p;
+
+			p = getprotobyname($1);
+			if (p == NULL) {
+				yyerror("unknown protocol %s", $1);
+				free($1);
+				YYERROR;
+			}
+			$$ = p->p_proto;
+			free($1);
+		}
+		| NUMBER			{
+			if ($1 < 0 || $1 > 255) {
+				yyerror("protocol outside range");
+				YYERROR;
+			}
+		}
+		;
+
+fromto		: ALL				{
+			$$.src.host = NULL;
+			$$.src.port = NULL;
+			$$.dst.host = NULL;
+			$$.dst.port = NULL;
+			$$.src_os = NULL;
+		}
+		| from os to			{
+			$$.src = $1;
+			$$.src_os = $2;
+			$$.dst = $3;
+		}
+		;
+
+os		: /* empty */			{ $$ = NULL; }
+		| OS xos			{ $$ = $2; }
+		| OS '{' optnl os_list '}'	{ $$ = $4; }
+		;
+
+xos		: STRING {
+			$$ = calloc(1, sizeof(struct node_os));
+			if ($$ == NULL)
+				err(1, "os: calloc");
+			$$->os = $1;
+			$$->tail = $$;
+		}
+		;
+
+os_list		: xos optnl 			{ $$ = $1; }
+		| os_list comma xos optnl	{
+			$1->tail->next = $3;
+			$1->tail = $3;
+			$$ = $1;
+		}
+		;
+
+from		: /* empty */			{
+			$$.host = NULL;
+			$$.port = NULL;
+		}
+		| FROM ipportspec		{
+			$$ = $2;
+		}
+		;
+
+to		: /* empty */			{
+			$$.host = NULL;
+			$$.port = NULL;
+		}
+		| TO ipportspec		{
+			if (disallow_urpf_failed($2.host, "\"urpf-failed\" is "
+			    "not permitted in a destination address"))
+				YYERROR;
+			$$ = $2;
+		}
+		;
+
+ipportspec	: ipspec			{
+			$$.host = $1;
+			$$.port = NULL;
+		}
+		| ipspec PORT portspec		{
+			$$.host = $1;
+			$$.port = $3;
+		}
+		| PORT portspec			{
+			$$.host = NULL;
+			$$.port = $2;
+		}
+		;
+
+optnl		: '\n' optnl
+		|
+		;
+
+ipspec		: ANY				{ $$ = NULL; }
+		| xhost				{ $$ = $1; }
+		| '{' optnl host_list '}'	{ $$ = $3; }
+		;
+
+toipspec	: TO ipspec			{ $$ = $2; }
+		| /* empty */			{ $$ = NULL; }
+		;
+
+host_list	: ipspec optnl			{ $$ = $1; }
+		| host_list comma ipspec optnl	{
+			if ($3 == NULL)
+				$$ = $1;
+			else if ($1 == NULL)
+				$$ = $3;
+			else {
+				$1->tail->next = $3;
+				$1->tail = $3->tail;
+				$$ = $1;
+			}
+		}
+		;
+
+xhost		: not host			{
+			struct node_host	*n;
+
+			for (n = $2; n != NULL; n = n->next)
+				n->not = $1;
+			$$ = $2;
+		}
+		| not NOROUTE			{
+			$$ = calloc(1, sizeof(struct node_host));
+			if ($$ == NULL)
+				err(1, "xhost: calloc");
+			$$->addr.type = PF_ADDR_NOROUTE;
+			$$->next = NULL;
+			$$->not = $1;
+			$$->tail = $$;
+		}
+		| not URPFFAILED		{
+			$$ = calloc(1, sizeof(struct node_host));
+			if ($$ == NULL)
+				err(1, "xhost: calloc");
+			$$->addr.type = PF_ADDR_URPFFAILED;
+			$$->next = NULL;
+			$$->not = $1;
+			$$->tail = $$;
+		}
+		;
+
+host		: STRING			{
+			if (($$ = host($1)) == NULL)	{
+				/* error. "any" is handled elsewhere */
+				free($1);
+				yyerror("could not parse host specification");
+				YYERROR;
+			}
+			free($1);
+
+		}
+		| STRING '-' STRING		{
+			struct node_host *b, *e;
+
+			if ((b = host($1)) == NULL || (e = host($3)) == NULL) {
+				free($1);
+				free($3);
+				yyerror("could not parse host specification");
+				YYERROR;
+			}
+			if (b->af != e->af ||
+			    b->addr.type != PF_ADDR_ADDRMASK ||
+			    e->addr.type != PF_ADDR_ADDRMASK ||
+			    unmask(&b->addr.v.a.mask, b->af) !=
+			    (b->af == AF_INET ? 32 : 128) ||
+			    unmask(&e->addr.v.a.mask, e->af) !=
+			    (e->af == AF_INET ? 32 : 128) ||
+			    b->next != NULL || b->not ||
+			    e->next != NULL || e->not) {
+				free(b);
+				free(e);
+				free($1);
+				free($3);
+				yyerror("invalid address range");
+				YYERROR;
+			}
+			memcpy(&b->addr.v.a.mask, &e->addr.v.a.addr,
+			    sizeof(b->addr.v.a.mask));
+			b->addr.type = PF_ADDR_RANGE;
+			$$ = b;
+			free(e);
+			free($1);
+			free($3);
+		}
+		| STRING '/' NUMBER		{
+			char	*buf;
+
+			if (asprintf(&buf, "%s/%lld", $1, (long long)$3) == -1)
+				err(1, "host: asprintf");
+			free($1);
+			if (($$ = host(buf)) == NULL)	{
+				/* error. "any" is handled elsewhere */
+				free(buf);
+				yyerror("could not parse host specification");
+				YYERROR;
+			}
+			free(buf);
+		}
+		| NUMBER '/' NUMBER		{
+			char	*buf;
+
+			/* ie. for 10/8 parsing */
+#ifdef __FreeBSD__
+			if (asprintf(&buf, "%lld/%lld", (long long)$1, (long long)$3) == -1)
+#else
+			if (asprintf(&buf, "%lld/%lld", $1, $3) == -1)
+#endif
+				err(1, "host: asprintf");
+			if (($$ = host(buf)) == NULL)	{
+				/* error. "any" is handled elsewhere */
+				free(buf);
+				yyerror("could not parse host specification");
+				YYERROR;
+			}
+			free(buf);
+		}
+		| dynaddr
+		| dynaddr '/' NUMBER		{
+			struct node_host	*n;
+
+			if ($3 < 0 || $3 > 128) {
+				yyerror("bit number too big");
+				YYERROR;
+			}
+			$$ = $1;
+			for (n = $1; n != NULL; n = n->next)
+				set_ipmask(n, $3);
+		}
+		| '<' STRING '>'	{
+			if (strlen($2) >= PF_TABLE_NAME_SIZE) {
+				yyerror("table name '%s' too long", $2);
+				free($2);
+				YYERROR;
+			}
+			$$ = calloc(1, sizeof(struct node_host));
+			if ($$ == NULL)
+				err(1, "host: calloc");
+			$$->addr.type = PF_ADDR_TABLE;
+			if (strlcpy($$->addr.v.tblname, $2,
+			    sizeof($$->addr.v.tblname)) >=
+			    sizeof($$->addr.v.tblname))
+				errx(1, "host: strlcpy");
+			free($2);
+			$$->next = NULL;
+			$$->tail = $$;
+		}
+		| ROUTE	STRING		{
+			$$ = calloc(1, sizeof(struct node_host));
+			if ($$ == NULL) {
+				free($2);
+				err(1, "host: calloc");
+			}
+			$$->addr.type = PF_ADDR_RTLABEL;
+			if (strlcpy($$->addr.v.rtlabelname, $2,
+			    sizeof($$->addr.v.rtlabelname)) >=
+			    sizeof($$->addr.v.rtlabelname)) {
+				yyerror("route label too long, max %u chars",
+				    sizeof($$->addr.v.rtlabelname) - 1);
+				free($2);
+				free($$);
+				YYERROR;
+			}
+			$$->next = NULL;
+			$$->tail = $$;
+			free($2);
+		}
+		;
+
+number		: NUMBER
+		| STRING		{
+			u_long	ulval;
+
+			if (atoul($1, &ulval) == -1) {
+				yyerror("%s is not a number", $1);
+				free($1);
+				YYERROR;
+			} else
+				$$ = ulval;
+			free($1);
+		}
+		;
+
+dynaddr		: '(' STRING ')'		{
+			int	 flags = 0;
+			char	*p, *op;
+
+			op = $2;
+			if (!isalpha(op[0])) {
+				yyerror("invalid interface name '%s'", op);
+				free(op);
+				YYERROR;
+			}
+			while ((p = strrchr($2, ':')) != NULL) {
+				if (!strcmp(p+1, "network"))
+					flags |= PFI_AFLAG_NETWORK;
+				else if (!strcmp(p+1, "broadcast"))
+					flags |= PFI_AFLAG_BROADCAST;
+				else if (!strcmp(p+1, "peer"))
+					flags |= PFI_AFLAG_PEER;
+				else if (!strcmp(p+1, "0"))
+					flags |= PFI_AFLAG_NOALIAS;
+				else {
+					yyerror("interface %s has bad modifier",
+					    $2);
+					free(op);
+					YYERROR;
+				}
+				*p = '\0';
+			}
+			if (flags & (flags - 1) & PFI_AFLAG_MODEMASK) {
+				free(op);
+				yyerror("illegal combination of "
+				    "interface modifiers");
+				YYERROR;
+			}
+			$$ = calloc(1, sizeof(struct node_host));
+			if ($$ == NULL)
+				err(1, "address: calloc");
+			$$->af = 0;
+			set_ipmask($$, 128);
+			$$->addr.type = PF_ADDR_DYNIFTL;
+			$$->addr.iflags = flags;
+			if (strlcpy($$->addr.v.ifname, $2,
+			    sizeof($$->addr.v.ifname)) >=
+			    sizeof($$->addr.v.ifname)) {
+				free(op);
+				free($$);
+				yyerror("interface name too long");
+				YYERROR;
+			}
+			free(op);
+			$$->next = NULL;
+			$$->tail = $$;
+		}
+		;
+
+portspec	: port_item			{ $$ = $1; }
+		| '{' optnl port_list '}'	{ $$ = $3; }
+		;
+
+port_list	: port_item optnl		{ $$ = $1; }
+		| port_list comma port_item optnl	{
+			$1->tail->next = $3;
+			$1->tail = $3;
+			$$ = $1;
+		}
+		;
+
+port_item	: portrange			{
+			$$ = calloc(1, sizeof(struct node_port));
+			if ($$ == NULL)
+				err(1, "port_item: calloc");
+			$$->port[0] = $1.a;
+			$$->port[1] = $1.b;
+			if ($1.t)
+				$$->op = PF_OP_RRG;
+			else
+				$$->op = PF_OP_EQ;
+			$$->next = NULL;
+			$$->tail = $$;
+		}
+		| unaryop portrange	{
+			if ($2.t) {
+				yyerror("':' cannot be used with an other "
+				    "port operator");
+				YYERROR;
+			}
+			$$ = calloc(1, sizeof(struct node_port));
+			if ($$ == NULL)
+				err(1, "port_item: calloc");
+			$$->port[0] = $2.a;
+			$$->port[1] = $2.b;
+			$$->op = $1;
+			$$->next = NULL;
+			$$->tail = $$;
+		}
+		| portrange PORTBINARY portrange	{
+			if ($1.t || $3.t) {
+				yyerror("':' cannot be used with an other "
+				    "port operator");
+				YYERROR;
+			}
+			$$ = calloc(1, sizeof(struct node_port));
+			if ($$ == NULL)
+				err(1, "port_item: calloc");
+			$$->port[0] = $1.a;
+			$$->port[1] = $3.a;
+			$$->op = $2;
+			$$->next = NULL;
+			$$->tail = $$;
+		}
+		;
+
+portplain	: numberstring			{
+			if (parseport($1, &$$, 0) == -1) {
+				free($1);
+				YYERROR;
+			}
+			free($1);
+		}
+		;
+
+portrange	: numberstring			{
+			if (parseport($1, &$$, PPORT_RANGE) == -1) {
+				free($1);
+				YYERROR;
+			}
+			free($1);
+		}
+		;
+
+uids		: uid_item			{ $$ = $1; }
+		| '{' optnl uid_list '}'	{ $$ = $3; }
+		;
+
+uid_list	: uid_item optnl		{ $$ = $1; }
+		| uid_list comma uid_item optnl	{
+			$1->tail->next = $3;
+			$1->tail = $3;
+			$$ = $1;
+		}
+		;
+
+uid_item	: uid				{
+			$$ = calloc(1, sizeof(struct node_uid));
+			if ($$ == NULL)
+				err(1, "uid_item: calloc");
+			$$->uid[0] = $1;
+			$$->uid[1] = $1;
+			$$->op = PF_OP_EQ;
+			$$->next = NULL;
+			$$->tail = $$;
+		}
+		| unaryop uid			{
+			if ($2 == UID_MAX && $1 != PF_OP_EQ && $1 != PF_OP_NE) {
+				yyerror("user unknown requires operator = or "
+				    "!=");
+				YYERROR;
+			}
+			$$ = calloc(1, sizeof(struct node_uid));
+			if ($$ == NULL)
+				err(1, "uid_item: calloc");
+			$$->uid[0] = $2;
+			$$->uid[1] = $2;
+			$$->op = $1;
+			$$->next = NULL;
+			$$->tail = $$;
+		}
+		| uid PORTBINARY uid		{
+			if ($1 == UID_MAX || $3 == UID_MAX) {
+				yyerror("user unknown requires operator = or "
+				    "!=");
+				YYERROR;
+			}
+			$$ = calloc(1, sizeof(struct node_uid));
+			if ($$ == NULL)
+				err(1, "uid_item: calloc");
+			$$->uid[0] = $1;
+			$$->uid[1] = $3;
+			$$->op = $2;
+			$$->next = NULL;
+			$$->tail = $$;
+		}
+		;
+
+uid		: STRING			{
+			if (!strcmp($1, "unknown"))
+				$$ = UID_MAX;
+			else {
+				struct passwd	*pw;
+
+				if ((pw = getpwnam($1)) == NULL) {
+					yyerror("unknown user %s", $1);
+					free($1);
+					YYERROR;
+				}
+				$$ = pw->pw_uid;
+			}
+			free($1);
+		}
+		| NUMBER			{
+			if ($1 < 0 || $1 >= UID_MAX) {
+				yyerror("illegal uid value %lu", $1);
+				YYERROR;
+			}
+			$$ = $1;
+		}
+		;
+
+gids		: gid_item			{ $$ = $1; }
+		| '{' optnl gid_list '}'	{ $$ = $3; }
+		;
+
+gid_list	: gid_item optnl		{ $$ = $1; }
+		| gid_list comma gid_item optnl	{
+			$1->tail->next = $3;
+			$1->tail = $3;
+			$$ = $1;
+		}
+		;
+
+gid_item	: gid				{
+			$$ = calloc(1, sizeof(struct node_gid));
+			if ($$ == NULL)
+				err(1, "gid_item: calloc");
+			$$->gid[0] = $1;
+			$$->gid[1] = $1;
+			$$->op = PF_OP_EQ;
+			$$->next = NULL;
+			$$->tail = $$;
+		}
+		| unaryop gid			{
+			if ($2 == GID_MAX && $1 != PF_OP_EQ && $1 != PF_OP_NE) {
+				yyerror("group unknown requires operator = or "
+				    "!=");
+				YYERROR;
+			}
+			$$ = calloc(1, sizeof(struct node_gid));
+			if ($$ == NULL)
+				err(1, "gid_item: calloc");
+			$$->gid[0] = $2;
+			$$->gid[1] = $2;
+			$$->op = $1;
+			$$->next = NULL;
+			$$->tail = $$;
+		}
+		| gid PORTBINARY gid		{
+			if ($1 == GID_MAX || $3 == GID_MAX) {
+				yyerror("group unknown requires operator = or "
+				    "!=");
+				YYERROR;
+			}
+			$$ = calloc(1, sizeof(struct node_gid));
+			if ($$ == NULL)
+				err(1, "gid_item: calloc");
+			$$->gid[0] = $1;
+			$$->gid[1] = $3;
+			$$->op = $2;
+			$$->next = NULL;
+			$$->tail = $$;
+		}
+		;
+
+gid		: STRING			{
+			if (!strcmp($1, "unknown"))
+				$$ = GID_MAX;
+			else {
+				struct group	*grp;
+
+				if ((grp = getgrnam($1)) == NULL) {
+					yyerror("unknown group %s", $1);
+					free($1);
+					YYERROR;
+				}
+				$$ = grp->gr_gid;
+			}
+			free($1);
+		}
+		| NUMBER			{
+			if ($1 < 0 || $1 >= GID_MAX) {
+				yyerror("illegal gid value %lu", $1);
+				YYERROR;
+			}
+			$$ = $1;
+		}
+		;
+
+flag		: STRING			{
+			int	f;
+
+			if ((f = parse_flags($1)) < 0) {
+				yyerror("bad flags %s", $1);
+				free($1);
+				YYERROR;
+			}
+			free($1);
+			$$.b1 = f;
+		}
+		;
+
+flags		: FLAGS flag '/' flag	{ $$.b1 = $2.b1; $$.b2 = $4.b1; }
+		| FLAGS '/' flag	{ $$.b1 = 0; $$.b2 = $3.b1; }
+		| FLAGS ANY		{ $$.b1 = 0; $$.b2 = 0; }
+		;
+
+icmpspec	: ICMPTYPE icmp_item			{ $$ = $2; }
+		| ICMPTYPE '{' optnl icmp_list '}'	{ $$ = $4; }
+		| ICMP6TYPE icmp6_item			{ $$ = $2; }
+		| ICMP6TYPE '{' optnl icmp6_list '}'	{ $$ = $4; }
+		;
+
+icmp_list	: icmp_item optnl		{ $$ = $1; }
+		| icmp_list comma icmp_item optnl {
+			$1->tail->next = $3;
+			$1->tail = $3;
+			$$ = $1;
+		}
+		;
+
+icmp6_list	: icmp6_item optnl		{ $$ = $1; }
+		| icmp6_list comma icmp6_item optnl {
+			$1->tail->next = $3;
+			$1->tail = $3;
+			$$ = $1;
+		}
+		;
+
+icmp_item	: icmptype		{
+			$$ = calloc(1, sizeof(struct node_icmp));
+			if ($$ == NULL)
+				err(1, "icmp_item: calloc");
+			$$->type = $1;
+			$$->code = 0;
+			$$->proto = IPPROTO_ICMP;
+			$$->next = NULL;
+			$$->tail = $$;
+		}
+		| icmptype CODE STRING	{
+			const struct icmpcodeent	*p;
+
+			if ((p = geticmpcodebyname($1-1, $3, AF_INET)) == NULL) {
+				yyerror("unknown icmp-code %s", $3);
+				free($3);
+				YYERROR;
+			}
+
+			free($3);
+			$$ = calloc(1, sizeof(struct node_icmp));
+			if ($$ == NULL)
+				err(1, "icmp_item: calloc");
+			$$->type = $1;
+			$$->code = p->code + 1;
+			$$->proto = IPPROTO_ICMP;
+			$$->next = NULL;
+			$$->tail = $$;
+		}
+		| icmptype CODE NUMBER	{
+			if ($3 < 0 || $3 > 255) {
+				yyerror("illegal icmp-code %lu", $3);
+				YYERROR;
+			}
+			$$ = calloc(1, sizeof(struct node_icmp));
+			if ($$ == NULL)
+				err(1, "icmp_item: calloc");
+			$$->type = $1;
+			$$->code = $3 + 1;
+			$$->proto = IPPROTO_ICMP;
+			$$->next = NULL;
+			$$->tail = $$;
+		}
+		;
+
+icmp6_item	: icmp6type		{
+			$$ = calloc(1, sizeof(struct node_icmp));
+			if ($$ == NULL)
+				err(1, "icmp_item: calloc");
+			$$->type = $1;
+			$$->code = 0;
+			$$->proto = IPPROTO_ICMPV6;
+			$$->next = NULL;
+			$$->tail = $$;
+		}
+		| icmp6type CODE STRING	{
+			const struct icmpcodeent	*p;
+
+			if ((p = geticmpcodebyname($1-1, $3, AF_INET6)) == NULL) {
+				yyerror("unknown icmp6-code %s", $3);
+				free($3);
+				YYERROR;
+			}
+			free($3);
+
+			$$ = calloc(1, sizeof(struct node_icmp));
+			if ($$ == NULL)
+				err(1, "icmp_item: calloc");
+			$$->type = $1;
+			$$->code = p->code + 1;
+			$$->proto = IPPROTO_ICMPV6;
+			$$->next = NULL;
+			$$->tail = $$;
+		}
+		| icmp6type CODE NUMBER	{
+			if ($3 < 0 || $3 > 255) {
+				yyerror("illegal icmp-code %lu", $3);
+				YYERROR;
+			}
+			$$ = calloc(1, sizeof(struct node_icmp));
+			if ($$ == NULL)
+				err(1, "icmp_item: calloc");
+			$$->type = $1;
+			$$->code = $3 + 1;
+			$$->proto = IPPROTO_ICMPV6;
+			$$->next = NULL;
+			$$->tail = $$;
+		}
+		;
+
+icmptype	: STRING			{
+			const struct icmptypeent	*p;
+
+			if ((p = geticmptypebyname($1, AF_INET)) == NULL) {
+				yyerror("unknown icmp-type %s", $1);
+				free($1);
+				YYERROR;
+			}
+			$$ = p->type + 1;
+			free($1);
+		}
+		| NUMBER			{
+			if ($1 < 0 || $1 > 255) {
+				yyerror("illegal icmp-type %lu", $1);
+				YYERROR;
+			}
+			$$ = $1 + 1;
+		}
+		;
+
+icmp6type	: STRING			{
+			const struct icmptypeent	*p;
+
+			if ((p = geticmptypebyname($1, AF_INET6)) ==
+			    NULL) {
+				yyerror("unknown icmp6-type %s", $1);
+				free($1);
+				YYERROR;
+			}
+			$$ = p->type + 1;
+			free($1);
+		}
+		| NUMBER			{
+			if ($1 < 0 || $1 > 255) {
+				yyerror("illegal icmp6-type %lu", $1);
+				YYERROR;
+			}
+			$$ = $1 + 1;
+		}
+		;
+
+tos	: STRING			{
+			if (!strcmp($1, "lowdelay"))
+				$$ = IPTOS_LOWDELAY;
+			else if (!strcmp($1, "throughput"))
+				$$ = IPTOS_THROUGHPUT;
+			else if (!strcmp($1, "reliability"))
+				$$ = IPTOS_RELIABILITY;
+			else if ($1[0] == '0' && $1[1] == 'x')
+				$$ = strtoul($1, NULL, 16);
+			else
+				$$ = 0;		/* flag bad argument */
+			if (!$$ || $$ > 255) {
+				yyerror("illegal tos value %s", $1);
+				free($1);
+				YYERROR;
+			}
+			free($1);
+		}
+		| NUMBER			{
+			$$ = $1;
+			if (!$$ || $$ > 255) {
+				yyerror("illegal tos value %s", $1);
+				YYERROR;
+			}
+		}
+		;
+
+sourcetrack	: SOURCETRACK		{ $$ = PF_SRCTRACK; }
+		| SOURCETRACK GLOBAL	{ $$ = PF_SRCTRACK_GLOBAL; }
+		| SOURCETRACK RULE	{ $$ = PF_SRCTRACK_RULE; }
+		;
+
+statelock	: IFBOUND {
+			$$ = PFRULE_IFBOUND;
+		}
+		| FLOATING {
+			$$ = 0;
+		}
+		;
+
+keep		: NO STATE			{
+			$$.action = 0;
+			$$.options = NULL;
+		}
+		| KEEP STATE state_opt_spec	{
+			$$.action = PF_STATE_NORMAL;
+			$$.options = $3;
+		}
+		| MODULATE STATE state_opt_spec {
+			$$.action = PF_STATE_MODULATE;
+			$$.options = $3;
+		}
+		| SYNPROXY STATE state_opt_spec {
+			$$.action = PF_STATE_SYNPROXY;
+			$$.options = $3;
+		}
+		;
+
+flush		: /* empty */			{ $$ = 0; }
+		| FLUSH				{ $$ = PF_FLUSH; }
+		| FLUSH GLOBAL			{
+			$$ = PF_FLUSH | PF_FLUSH_GLOBAL;
+		}
+		;
+
+state_opt_spec	: '(' state_opt_list ')'	{ $$ = $2; }
+		| /* empty */			{ $$ = NULL; }
+		;
+
+state_opt_list	: state_opt_item		{ $$ = $1; }
+		| state_opt_list comma state_opt_item {
+			$1->tail->next = $3;
+			$1->tail = $3;
+			$$ = $1;
+		}
+		;
+
+state_opt_item	: MAXIMUM NUMBER		{
+			if ($2 < 0 || $2 > UINT_MAX) {
+				yyerror("only positive values permitted");
+				YYERROR;
+			}
+			$$ = calloc(1, sizeof(struct node_state_opt));
+			if ($$ == NULL)
+				err(1, "state_opt_item: calloc");
+			$$->type = PF_STATE_OPT_MAX;
+			$$->data.max_states = $2;
+			$$->next = NULL;
+			$$->tail = $$;
+		}
+		| NOSYNC				{
+			$$ = calloc(1, sizeof(struct node_state_opt));
+			if ($$ == NULL)
+				err(1, "state_opt_item: calloc");
+			$$->type = PF_STATE_OPT_NOSYNC;
+			$$->next = NULL;
+			$$->tail = $$;
+		}
+		| MAXSRCSTATES NUMBER			{
+			if ($2 < 0 || $2 > UINT_MAX) {
+				yyerror("only positive values permitted");
+				YYERROR;
+			}
+			$$ = calloc(1, sizeof(struct node_state_opt));
+			if ($$ == NULL)
+				err(1, "state_opt_item: calloc");
+			$$->type = PF_STATE_OPT_MAX_SRC_STATES;
+			$$->data.max_src_states = $2;
+			$$->next = NULL;
+			$$->tail = $$;
+		}
+		| MAXSRCCONN NUMBER			{
+			if ($2 < 0 || $2 > UINT_MAX) {
+				yyerror("only positive values permitted");
+				YYERROR;
+			}
+			$$ = calloc(1, sizeof(struct node_state_opt));
+			if ($$ == NULL)
+				err(1, "state_opt_item: calloc");
+			$$->type = PF_STATE_OPT_MAX_SRC_CONN;
+			$$->data.max_src_conn = $2;
+			$$->next = NULL;
+			$$->tail = $$;
+		}
+		| MAXSRCCONNRATE NUMBER '/' NUMBER	{
+			if ($2 < 0 || $2 > UINT_MAX ||
+			    $4 < 0 || $4 > UINT_MAX) {
+				yyerror("only positive values permitted");
+				YYERROR;
+			}
+			$$ = calloc(1, sizeof(struct node_state_opt));
+			if ($$ == NULL)
+				err(1, "state_opt_item: calloc");
+			$$->type = PF_STATE_OPT_MAX_SRC_CONN_RATE;
+			$$->data.max_src_conn_rate.limit = $2;
+			$$->data.max_src_conn_rate.seconds = $4;
+			$$->next = NULL;
+			$$->tail = $$;
+		}
+		| OVERLOAD '<' STRING '>' flush		{
+			if (strlen($3) >= PF_TABLE_NAME_SIZE) {
+				yyerror("table name '%s' too long", $3);
+				free($3);
+				YYERROR;
+			}
+			$$ = calloc(1, sizeof(struct node_state_opt));
+			if ($$ == NULL)
+				err(1, "state_opt_item: calloc");
+			if (strlcpy($$->data.overload.tblname, $3,
+			    PF_TABLE_NAME_SIZE) >= PF_TABLE_NAME_SIZE)
+				errx(1, "state_opt_item: strlcpy");
+			free($3);
+			$$->type = PF_STATE_OPT_OVERLOAD;
+			$$->data.overload.flush = $5;
+			$$->next = NULL;
+			$$->tail = $$;
+		}
+		| MAXSRCNODES NUMBER			{
+			if ($2 < 0 || $2 > UINT_MAX) {
+				yyerror("only positive values permitted");
+				YYERROR;
+			}
+			$$ = calloc(1, sizeof(struct node_state_opt));
+			if ($$ == NULL)
+				err(1, "state_opt_item: calloc");
+			$$->type = PF_STATE_OPT_MAX_SRC_NODES;
+			$$->data.max_src_nodes = $2;
+			$$->next = NULL;
+			$$->tail = $$;
+		}
+		| sourcetrack {
+			$$ = calloc(1, sizeof(struct node_state_opt));
+			if ($$ == NULL)
+				err(1, "state_opt_item: calloc");
+			$$->type = PF_STATE_OPT_SRCTRACK;
+			$$->data.src_track = $1;
+			$$->next = NULL;
+			$$->tail = $$;
+		}
+		| statelock {
+			$$ = calloc(1, sizeof(struct node_state_opt));
+			if ($$ == NULL)
+				err(1, "state_opt_item: calloc");
+			$$->type = PF_STATE_OPT_STATELOCK;
+			$$->data.statelock = $1;
+			$$->next = NULL;
+			$$->tail = $$;
+		}
+		| SLOPPY {
+			$$ = calloc(1, sizeof(struct node_state_opt));
+			if ($$ == NULL)
+				err(1, "state_opt_item: calloc");
+			$$->type = PF_STATE_OPT_SLOPPY;
+			$$->next = NULL;
+			$$->tail = $$;
+		}
+		| PFLOW {
+			$$ = calloc(1, sizeof(struct node_state_opt));
+			if ($$ == NULL)
+				err(1, "state_opt_item: calloc");
+			$$->type = PF_STATE_OPT_PFLOW;
+			$$->next = NULL;
+			$$->tail = $$;
+		}
+		| STRING NUMBER			{
+			int	i;
+
+			if ($2 < 0 || $2 > UINT_MAX) {
+				yyerror("only positive values permitted");
+				YYERROR;
+			}
+			for (i = 0; pf_timeouts[i].name &&
+			    strcmp(pf_timeouts[i].name, $1); ++i)
+				;	/* nothing */
+			if (!pf_timeouts[i].name) {
+				yyerror("illegal timeout name %s", $1);
+				free($1);
+				YYERROR;
+			}
+			if (strchr(pf_timeouts[i].name, '.') == NULL) {
+				yyerror("illegal state timeout %s", $1);
+				free($1);
+				YYERROR;
+			}
+			free($1);
+			$$ = calloc(1, sizeof(struct node_state_opt));
+			if ($$ == NULL)
+				err(1, "state_opt_item: calloc");
+			$$->type = PF_STATE_OPT_TIMEOUT;
+			$$->data.timeout.number = pf_timeouts[i].timeout;
+			$$->data.timeout.seconds = $2;
+			$$->next = NULL;
+			$$->tail = $$;
+		}
+		;
+
+label		: LABEL STRING			{
+			$$ = $2;
+		}
+		;
+
+qname		: QUEUE STRING				{
+			$$.qname = $2;
+			$$.pqname = NULL;
+		}
+		| QUEUE '(' STRING ')'			{
+			$$.qname = $3;
+			$$.pqname = NULL;
+		}
+		| QUEUE '(' STRING comma STRING ')'	{
+			$$.qname = $3;
+			$$.pqname = $5;
+		}
+		;
+
+no		: /* empty */			{ $$ = 0; }
+		| NO				{ $$ = 1; }
+		;
+
+portstar	: numberstring			{
+			if (parseport($1, &$$, PPORT_RANGE|PPORT_STAR) == -1) {
+				free($1);
+				YYERROR;
+			}
+			free($1);
+		}
+		;
+
+redirspec	: host				{ $$ = $1; }
+		| '{' optnl redir_host_list '}'	{ $$ = $3; }
+		;
+
+redir_host_list	: host optnl			{ $$ = $1; }
+		| redir_host_list comma host optnl {
+			$1->tail->next = $3;
+			$1->tail = $3->tail;
+			$$ = $1;
+		}
+		;
+
+redirpool	: /* empty */			{ $$ = NULL; }
+		| ARROW redirspec		{
+			$$ = calloc(1, sizeof(struct redirection));
+			if ($$ == NULL)
+				err(1, "redirection: calloc");
+			$$->host = $2;
+			$$->rport.a = $$->rport.b = $$->rport.t = 0;
+		}
+		| ARROW redirspec PORT portstar	{
+			$$ = calloc(1, sizeof(struct redirection));
+			if ($$ == NULL)
+				err(1, "redirection: calloc");
+			$$->host = $2;
+			$$->rport = $4;
+		}
+		;
+
+hashkey		: /* empty */
+		{
+			$$ = calloc(1, sizeof(struct pf_poolhashkey));
+			if ($$ == NULL)
+				err(1, "hashkey: calloc");
+			$$->key32[0] = arc4random();
+			$$->key32[1] = arc4random();
+			$$->key32[2] = arc4random();
+			$$->key32[3] = arc4random();
+		}
+		| string
+		{
+			if (!strncmp($1, "0x", 2)) {
+				if (strlen($1) != 34) {
+					free($1);
+					yyerror("hex key must be 128 bits "
+						"(32 hex digits) long");
+					YYERROR;
+				}
+				$$ = calloc(1, sizeof(struct pf_poolhashkey));
+				if ($$ == NULL)
+					err(1, "hashkey: calloc");
+
+				if (sscanf($1, "0x%8x%8x%8x%8x",
+				    &$$->key32[0], &$$->key32[1],
+				    &$$->key32[2], &$$->key32[3]) != 4) {
+					free($$);
+					free($1);
+					yyerror("invalid hex key");
+					YYERROR;
+				}
+			} else {
+				MD5_CTX	context;
+
+				$$ = calloc(1, sizeof(struct pf_poolhashkey));
+				if ($$ == NULL)
+					err(1, "hashkey: calloc");
+				MD5Init(&context);
+				MD5Update(&context, (unsigned char *)$1,
+				    strlen($1));
+				MD5Final((unsigned char *)$$, &context);
+				HTONL($$->key32[0]);
+				HTONL($$->key32[1]);
+				HTONL($$->key32[2]);
+				HTONL($$->key32[3]);
+			}
+			free($1);
+		}
+		;
+
+pool_opts	:	{ bzero(&pool_opts, sizeof pool_opts); }
+		    pool_opts_l
+			{ $$ = pool_opts; }
+		| /* empty */	{
+			bzero(&pool_opts, sizeof pool_opts);
+			$$ = pool_opts;
+		}
+		;
+
+pool_opts_l	: pool_opts_l pool_opt
+		| pool_opt
+		;
+
+pool_opt	: BITMASK	{
+			if (pool_opts.type) {
+				yyerror("pool type cannot be redefined");
+				YYERROR;
+			}
+			pool_opts.type =  PF_POOL_BITMASK;
+		}
+		| RANDOM	{
+			if (pool_opts.type) {
+				yyerror("pool type cannot be redefined");
+				YYERROR;
+			}
+			pool_opts.type = PF_POOL_RANDOM;
+		}
+		| SOURCEHASH hashkey {
+			if (pool_opts.type) {
+				yyerror("pool type cannot be redefined");
+				YYERROR;
+			}
+			pool_opts.type = PF_POOL_SRCHASH;
+			pool_opts.key = $2;
+		}
+		| ROUNDROBIN	{
+			if (pool_opts.type) {
+				yyerror("pool type cannot be redefined");
+				YYERROR;
+			}
+			pool_opts.type = PF_POOL_ROUNDROBIN;
+		}
+		| STATICPORT	{
+			if (pool_opts.staticport) {
+				yyerror("static-port cannot be redefined");
+				YYERROR;
+			}
+			pool_opts.staticport = 1;
+		}
+		| STICKYADDRESS	{
+			if (filter_opts.marker & POM_STICKYADDRESS) {
+				yyerror("sticky-address cannot be redefined");
+				YYERROR;
+			}
+			pool_opts.marker |= POM_STICKYADDRESS;
+			pool_opts.opts |= PF_POOL_STICKYADDR;
+		}
+		;
+
+redirection	: /* empty */			{ $$ = NULL; }
+		| ARROW host			{
+			$$ = calloc(1, sizeof(struct redirection));
+			if ($$ == NULL)
+				err(1, "redirection: calloc");
+			$$->host = $2;
+			$$->rport.a = $$->rport.b = $$->rport.t = 0;
+		}
+		| ARROW host PORT portstar	{
+			$$ = calloc(1, sizeof(struct redirection));
+			if ($$ == NULL)
+				err(1, "redirection: calloc");
+			$$->host = $2;
+			$$->rport = $4;
+		}
+		;
+
+natpasslog	: /* empty */	{ $$.b1 = $$.b2 = 0; $$.w2 = 0; }
+		| PASS		{ $$.b1 = 1; $$.b2 = 0; $$.w2 = 0; }
+		| PASS log	{ $$.b1 = 1; $$.b2 = $2.log; $$.w2 = $2.logif; }
+		| log		{ $$.b1 = 0; $$.b2 = $1.log; $$.w2 = $1.logif; }
+		;
+
+nataction	: no NAT natpasslog {
+			if ($1 && $3.b1) {
+				yyerror("\"pass\" not valid with \"no\"");
+				YYERROR;
+			}
+			if ($1)
+				$$.b1 = PF_NONAT;
+			else
+				$$.b1 = PF_NAT;
+			$$.b2 = $3.b1;
+			$$.w = $3.b2;
+			$$.w2 = $3.w2;
+		}
+		| no RDR natpasslog {
+			if ($1 && $3.b1) {
+				yyerror("\"pass\" not valid with \"no\"");
+				YYERROR;
+			}
+			if ($1)
+				$$.b1 = PF_NORDR;
+			else
+				$$.b1 = PF_RDR;
+			$$.b2 = $3.b1;
+			$$.w = $3.b2;
+			$$.w2 = $3.w2;
+		}
+		;
+
+natrule		: nataction interface af proto fromto tag tagged rtable
+		    redirpool pool_opts
+		{
+			struct pf_rule	r;
+
+			if (check_rulestate(PFCTL_STATE_NAT))
+				YYERROR;
+
+			memset(&r, 0, sizeof(r));
+
+			r.action = $1.b1;
+			r.natpass = $1.b2;
+			r.log = $1.w;
+			r.logif = $1.w2;
+			r.af = $3;
+
+			if (!r.af) {
+				if ($5.src.host && $5.src.host->af &&
+				    !$5.src.host->ifindex)
+					r.af = $5.src.host->af;
+				else if ($5.dst.host && $5.dst.host->af &&
+				    !$5.dst.host->ifindex)
+					r.af = $5.dst.host->af;
+			}
+
+			if ($6 != NULL)
+				if (strlcpy(r.tagname, $6, PF_TAG_NAME_SIZE) >=
+				    PF_TAG_NAME_SIZE) {
+					yyerror("tag too long, max %u chars",
+					    PF_TAG_NAME_SIZE - 1);
+					YYERROR;
+				}
+
+			if ($7.name)
+				if (strlcpy(r.match_tagname, $7.name,
+				    PF_TAG_NAME_SIZE) >= PF_TAG_NAME_SIZE) {
+					yyerror("tag too long, max %u chars",
+					    PF_TAG_NAME_SIZE - 1);
+					YYERROR;
+				}
+			r.match_tag_not = $7.neg;
+			r.rtableid = $8;
+
+			if (r.action == PF_NONAT || r.action == PF_NORDR) {
+				if ($9 != NULL) {
+					yyerror("translation rule with 'no' "
+					    "does not need '->'");
+					YYERROR;
+				}
+			} else {
+				if ($9 == NULL || $9->host == NULL) {
+					yyerror("translation rule requires '-> "
+					    "address'");
+					YYERROR;
+				}
+				if (!r.af && ! $9->host->ifindex)
+					r.af = $9->host->af;
+
+				remove_invalid_hosts(&$9->host, &r.af);
+				if (invalid_redirect($9->host, r.af))
+					YYERROR;
+				if (check_netmask($9->host, r.af))
+					YYERROR;
+
+				r.rpool.proxy_port[0] = ntohs($9->rport.a);
+
+				switch (r.action) {
+				case PF_RDR:
+					if (!$9->rport.b && $9->rport.t &&
+					    $5.dst.port != NULL) {
+						r.rpool.proxy_port[1] =
+						    ntohs($9->rport.a) +
+						    (ntohs(
+						    $5.dst.port->port[1]) -
+						    ntohs(
+						    $5.dst.port->port[0]));
+					} else
+						r.rpool.proxy_port[1] =
+						    ntohs($9->rport.b);
+					break;
+				case PF_NAT:
+					r.rpool.proxy_port[1] =
+					    ntohs($9->rport.b);
+					if (!r.rpool.proxy_port[0] &&
+					    !r.rpool.proxy_port[1]) {
+						r.rpool.proxy_port[0] =
+						    PF_NAT_PROXY_PORT_LOW;
+						r.rpool.proxy_port[1] =
+						    PF_NAT_PROXY_PORT_HIGH;
+					} else if (!r.rpool.proxy_port[1])
+						r.rpool.proxy_port[1] =
+						    r.rpool.proxy_port[0];
+					break;
+				default:
+					break;
+				}
+
+				r.rpool.opts = $10.type;
+				if ((r.rpool.opts & PF_POOL_TYPEMASK) ==
+				    PF_POOL_NONE && ($9->host->next != NULL ||
+				    $9->host->addr.type == PF_ADDR_TABLE ||
+				    DYNIF_MULTIADDR($9->host->addr)))
+					r.rpool.opts = PF_POOL_ROUNDROBIN;
+				if ((r.rpool.opts & PF_POOL_TYPEMASK) !=
+				    PF_POOL_ROUNDROBIN &&
+				    disallow_table($9->host, "tables are only "
+				    "supported in round-robin redirection "
+				    "pools"))
+					YYERROR;
+				if ((r.rpool.opts & PF_POOL_TYPEMASK) !=
+				    PF_POOL_ROUNDROBIN &&
+				    disallow_alias($9->host, "interface (%s) "
+				    "is only supported in round-robin "
+				    "redirection pools"))
+					YYERROR;
+				if ($9->host->next != NULL) {
+					if ((r.rpool.opts & PF_POOL_TYPEMASK) !=
+					    PF_POOL_ROUNDROBIN) {
+						yyerror("only round-robin "
+						    "valid for multiple "
+						    "redirection addresses");
+						YYERROR;
+					}
+				}
+			}
+
+			if ($10.key != NULL)
+				memcpy(&r.rpool.key, $10.key,
+				    sizeof(struct pf_poolhashkey));
+
+			 if ($10.opts)
+				r.rpool.opts |= $10.opts;
+
+			if ($10.staticport) {
+				if (r.action != PF_NAT) {
+					yyerror("the 'static-port' option is "
+					    "only valid with nat rules");
+					YYERROR;
+				}
+				if (r.rpool.proxy_port[0] !=
+				    PF_NAT_PROXY_PORT_LOW &&
+				    r.rpool.proxy_port[1] !=
+				    PF_NAT_PROXY_PORT_HIGH) {
+					yyerror("the 'static-port' option can't"
+					    " be used when specifying a port"
+					    " range");
+					YYERROR;
+				}
+				r.rpool.proxy_port[0] = 0;
+				r.rpool.proxy_port[1] = 0;
+			}
+
+			expand_rule(&r, $2, $9 == NULL ? NULL : $9->host, $4,
+			    $5.src_os, $5.src.host, $5.src.port, $5.dst.host,
+			    $5.dst.port, 0, 0, 0, "");
+			free($9);
+		}
+		;
+
+binatrule	: no BINAT natpasslog interface af proto FROM host toipspec tag
+		    tagged rtable redirection
+		{
+			struct pf_rule		binat;
+			struct pf_pooladdr	*pa;
+
+			if (check_rulestate(PFCTL_STATE_NAT))
+				YYERROR;
+			if (disallow_urpf_failed($9, "\"urpf-failed\" is not "
+			    "permitted as a binat destination"))
+				YYERROR;
+
+			memset(&binat, 0, sizeof(binat));
+
+			if ($1 && $3.b1) {
+				yyerror("\"pass\" not valid with \"no\"");
+				YYERROR;
+			}
+			if ($1)
+				binat.action = PF_NOBINAT;
+			else
+				binat.action = PF_BINAT;
+			binat.natpass = $3.b1;
+			binat.log = $3.b2;
+			binat.logif = $3.w2;
+			binat.af = $5;
+			if (!binat.af && $8 != NULL && $8->af)
+				binat.af = $8->af;
+			if (!binat.af && $9 != NULL && $9->af)
+				binat.af = $9->af;
+
+			if (!binat.af && $13 != NULL && $13->host)
+				binat.af = $13->host->af;
+			if (!binat.af) {
+				yyerror("address family (inet/inet6) "
+				    "undefined");
+				YYERROR;
+			}
+
+			if ($4 != NULL) {
+				memcpy(binat.ifname, $4->ifname,
+				    sizeof(binat.ifname));
+				binat.ifnot = $4->not;
+				free($4);
+			}
+
+			if ($10 != NULL)
+				if (strlcpy(binat.tagname, $10,
+				    PF_TAG_NAME_SIZE) >= PF_TAG_NAME_SIZE) {
+					yyerror("tag too long, max %u chars",
+					    PF_TAG_NAME_SIZE - 1);
+					YYERROR;
+				}
+			if ($11.name)
+				if (strlcpy(binat.match_tagname, $11.name,
+				    PF_TAG_NAME_SIZE) >= PF_TAG_NAME_SIZE) {
+					yyerror("tag too long, max %u chars",
+					    PF_TAG_NAME_SIZE - 1);
+					YYERROR;
+				}
+			binat.match_tag_not = $11.neg;
+			binat.rtableid = $12;
+
+			if ($6 != NULL) {
+				binat.proto = $6->proto;
+				free($6);
+			}
+
+			if ($8 != NULL && disallow_table($8, "invalid use of "
+			    "table <%s> as the source address of a binat rule"))
+				YYERROR;
+			if ($8 != NULL && disallow_alias($8, "invalid use of "
+			    "interface (%s) as the source address of a binat "
+			    "rule"))
+				YYERROR;
+			if ($13 != NULL && $13->host != NULL && disallow_table(
+			    $13->host, "invalid use of table <%s> as the "
+			    "redirect address of a binat rule"))
+				YYERROR;
+			if ($13 != NULL && $13->host != NULL && disallow_alias(
+			    $13->host, "invalid use of interface (%s) as the "
+			    "redirect address of a binat rule"))
+				YYERROR;
+
+			if ($8 != NULL) {
+				if ($8->next) {
+					yyerror("multiple binat ip addresses");
+					YYERROR;
+				}
+				if ($8->addr.type == PF_ADDR_DYNIFTL)
+					$8->af = binat.af;
+				if ($8->af != binat.af) {
+					yyerror("binat ip versions must match");
+					YYERROR;
+				}
+				if (check_netmask($8, binat.af))
+					YYERROR;
+				memcpy(&binat.src.addr, &$8->addr,
+				    sizeof(binat.src.addr));
+				free($8);
+			}
+			if ($9 != NULL) {
+				if ($9->next) {
+					yyerror("multiple binat ip addresses");
+					YYERROR;
+				}
+				if ($9->af != binat.af && $9->af) {
+					yyerror("binat ip versions must match");
+					YYERROR;
+				}
+				if (check_netmask($9, binat.af))
+					YYERROR;
+				memcpy(&binat.dst.addr, &$9->addr,
+				    sizeof(binat.dst.addr));
+				binat.dst.neg = $9->not;
+				free($9);
+			}
+
+			if (binat.action == PF_NOBINAT) {
+				if ($13 != NULL) {
+					yyerror("'no binat' rule does not need"
+					    " '->'");
+					YYERROR;
+				}
+			} else {
+				if ($13 == NULL || $13->host == NULL) {
+					yyerror("'binat' rule requires"
+					    " '-> address'");
+					YYERROR;
+				}
+
+				remove_invalid_hosts(&$13->host, &binat.af);
+				if (invalid_redirect($13->host, binat.af))
+					YYERROR;
+				if ($13->host->next != NULL) {
+					yyerror("binat rule must redirect to "
+					    "a single address");
+					YYERROR;
+				}
+				if (check_netmask($13->host, binat.af))
+					YYERROR;
+
+				if (!PF_AZERO(&binat.src.addr.v.a.mask,
+				    binat.af) &&
+				    !PF_AEQ(&binat.src.addr.v.a.mask,
+				    &$13->host->addr.v.a.mask, binat.af)) {
+					yyerror("'binat' source mask and "
+					    "redirect mask must be the same");
+					YYERROR;
+				}
+
+				TAILQ_INIT(&binat.rpool.list);
+				pa = calloc(1, sizeof(struct pf_pooladdr));
+				if (pa == NULL)
+					err(1, "binat: calloc");
+				pa->addr = $13->host->addr;
+				pa->ifname[0] = 0;
+				TAILQ_INSERT_TAIL(&binat.rpool.list,
+				    pa, entries);
+
+				free($13);
+			}
+
+			pfctl_add_rule(pf, &binat, "");
+		}
+		;
+
+tag		: /* empty */		{ $$ = NULL; }
+		| TAG STRING		{ $$ = $2; }
+		;
+
+tagged		: /* empty */		{ $$.neg = 0; $$.name = NULL; }
+		| not TAGGED string	{ $$.neg = $1; $$.name = $3; }
+		;
+
+rtable		: /* empty */		{ $$ = -1; }
+		| RTABLE NUMBER		{
+			if ($2 < 0 || $2 > rt_tableid_max()) {
+				yyerror("invalid rtable id");
+				YYERROR;
+			}
+			$$ = $2;
+		}
+		;
+
+route_host	: STRING			{
+			$$ = calloc(1, sizeof(struct node_host));
+			if ($$ == NULL)
+				err(1, "route_host: calloc");
+			$$->ifname = $1;
+			set_ipmask($$, 128);
+			$$->next = NULL;
+			$$->tail = $$;
+		}
+		| '(' STRING host ')'		{
+			$$ = $3;
+			$$->ifname = $2;
+		}
+		;
+
+route_host_list	: route_host optnl			{ $$ = $1; }
+		| route_host_list comma route_host optnl {
+			if ($1->af == 0)
+				$1->af = $3->af;
+			if ($1->af != $3->af) {
+				yyerror("all pool addresses must be in the "
+				    "same address family");
+				YYERROR;
+			}
+			$1->tail->next = $3;
+			$1->tail = $3->tail;
+			$$ = $1;
+		}
+		;
+
+routespec	: route_host			{ $$ = $1; }
+		| '{' optnl route_host_list '}'	{ $$ = $3; }
+		;
+
+route		: /* empty */			{
+			$$.host = NULL;
+			$$.rt = 0;
+			$$.pool_opts = 0;
+		}
+		| FASTROUTE {
+			$$.host = NULL;
+			$$.rt = PF_FASTROUTE;
+			$$.pool_opts = 0;
+		}
+		| ROUTETO routespec pool_opts {
+			$$.host = $2;
+			$$.rt = PF_ROUTETO;
+			$$.pool_opts = $3.type | $3.opts;
+			if ($3.key != NULL)
+				$$.key = $3.key;
+		}
+		| REPLYTO routespec pool_opts {
+			$$.host = $2;
+			$$.rt = PF_REPLYTO;
+			$$.pool_opts = $3.type | $3.opts;
+			if ($3.key != NULL)
+				$$.key = $3.key;
+		}
+		| DUPTO routespec pool_opts {
+			$$.host = $2;
+			$$.rt = PF_DUPTO;
+			$$.pool_opts = $3.type | $3.opts;
+			if ($3.key != NULL)
+				$$.key = $3.key;
+		}
+		;
+
+timeout_spec	: STRING NUMBER
+		{
+			if (check_rulestate(PFCTL_STATE_OPTION)) {
+				free($1);
+				YYERROR;
+			}
+			if ($2 < 0 || $2 > UINT_MAX) {
+				yyerror("only positive values permitted");
+				YYERROR;
+			}
+			if (pfctl_set_timeout(pf, $1, $2, 0) != 0) {
+				yyerror("unknown timeout %s", $1);
+				free($1);
+				YYERROR;
+			}
+			free($1);
+		}
+		;
+
+timeout_list	: timeout_list comma timeout_spec optnl
+		| timeout_spec optnl
+		;
+
+limit_spec	: STRING NUMBER
+		{
+			if (check_rulestate(PFCTL_STATE_OPTION)) {
+				free($1);
+				YYERROR;
+			}
+			if ($2 < 0 || $2 > UINT_MAX) {
+				yyerror("only positive values permitted");
+				YYERROR;
+			}
+			if (pfctl_set_limit(pf, $1, $2) != 0) {
+				yyerror("unable to set limit %s %u", $1, $2);
+				free($1);
+				YYERROR;
+			}
+			free($1);
+		}
+		;
+
+limit_list	: limit_list comma limit_spec optnl
+		| limit_spec optnl
+		;
+
+comma		: ','
+		| /* empty */
+		;
+
+yesno		: NO			{ $$ = 0; }
+		| STRING		{
+			if (!strcmp($1, "yes"))
+				$$ = 1;
+			else {
+				yyerror("invalid value '%s', expected 'yes' "
+				    "or 'no'", $1);
+				free($1);
+				YYERROR;
+			}
+			free($1);
+		}
+		;
+
+unaryop		: '='		{ $$ = PF_OP_EQ; }
+		| '!' '='	{ $$ = PF_OP_NE; }
+		| '<' '='	{ $$ = PF_OP_LE; }
+		| '<'		{ $$ = PF_OP_LT; }
+		| '>' '='	{ $$ = PF_OP_GE; }
+		| '>'		{ $$ = PF_OP_GT; }
+		;
+
+%%
+
+int
+yyerror(const char *fmt, ...)
+{
+	va_list		 ap;
+
+	file->errors++;
+	va_start(ap, fmt);
+	fprintf(stderr, "%s:%d: ", file->name, yylval.lineno);
+	vfprintf(stderr, fmt, ap);
+	fprintf(stderr, "\n");
+	va_end(ap);
+	return (0);
+}
+
+int
+disallow_table(struct node_host *h, const char *fmt)
+{
+	for (; h != NULL; h = h->next)
+		if (h->addr.type == PF_ADDR_TABLE) {
+			yyerror(fmt, h->addr.v.tblname);
+			return (1);
+		}
+	return (0);
+}
+
+int
+disallow_urpf_failed(struct node_host *h, const char *fmt)
+{
+	for (; h != NULL; h = h->next)
+		if (h->addr.type == PF_ADDR_URPFFAILED) {
+			yyerror(fmt);
+			return (1);
+		}
+	return (0);
+}
+
+int
+disallow_alias(struct node_host *h, const char *fmt)
+{
+	for (; h != NULL; h = h->next)
+		if (DYNIF_MULTIADDR(h->addr)) {
+			yyerror(fmt, h->addr.v.tblname);
+			return (1);
+		}
+	return (0);
+}
+
+int
+rule_consistent(struct pf_rule *r, int anchor_call)
+{
+	int	problems = 0;
+
+	switch (r->action) {
+	case PF_PASS:
+	case PF_DROP:
+	case PF_SCRUB:
+	case PF_NOSCRUB:
+		problems = filter_consistent(r, anchor_call);
+		break;
+	case PF_NAT:
+	case PF_NONAT:
+		problems = nat_consistent(r);
+		break;
+	case PF_RDR:
+	case PF_NORDR:
+		problems = rdr_consistent(r);
+		break;
+	case PF_BINAT:
+	case PF_NOBINAT:
+	default:
+		break;
+	}
+	return (problems);
+}
+
+int
+filter_consistent(struct pf_rule *r, int anchor_call)
+{
+	int	problems = 0;
+
+	if (r->proto != IPPROTO_TCP && r->proto != IPPROTO_UDP &&
+	    (r->src.port_op || r->dst.port_op)) {
+		yyerror("port only applies to tcp/udp");
+		problems++;
+	}
+	if (r->proto != IPPROTO_ICMP && r->proto != IPPROTO_ICMPV6 &&
+	    (r->type || r->code)) {
+		yyerror("icmp-type/code only applies to icmp");
+		problems++;
+	}
+	if (!r->af && (r->type || r->code)) {
+		yyerror("must indicate address family with icmp-type/code");
+		problems++;
+	}
+	if (r->overload_tblname[0] &&
+	    r->max_src_conn == 0 && r->max_src_conn_rate.seconds == 0) {
+		yyerror("'overload' requires 'max-src-conn' "
+		    "or 'max-src-conn-rate'");
+		problems++;
+	}
+	if ((r->proto == IPPROTO_ICMP && r->af == AF_INET6) ||
+	    (r->proto == IPPROTO_ICMPV6 && r->af == AF_INET)) {
+		yyerror("proto %s doesn't match address family %s",
+		    r->proto == IPPROTO_ICMP ? "icmp" : "icmp6",
+		    r->af == AF_INET ? "inet" : "inet6");
+		problems++;
+	}
+	if (r->allow_opts && r->action != PF_PASS) {
+		yyerror("allow-opts can only be specified for pass rules");
+		problems++;
+	}
+	if (r->rule_flag & PFRULE_FRAGMENT && (r->src.port_op ||
+	    r->dst.port_op || r->flagset || r->type || r->code)) {
+		yyerror("fragments can be filtered only on IP header fields");
+		problems++;
+	}
+	if (r->rule_flag & PFRULE_RETURNRST && r->proto != IPPROTO_TCP) {
+		yyerror("return-rst can only be applied to TCP rules");
+		problems++;
+	}
+	if (r->max_src_nodes && !(r->rule_flag & PFRULE_RULESRCTRACK)) {
+		yyerror("max-src-nodes requires 'source-track rule'");
+		problems++;
+	}
+	if (r->action == PF_DROP && r->keep_state) {
+		yyerror("keep state on block rules doesn't make sense");
+		problems++;
+	}
+	if (r->rule_flag & PFRULE_STATESLOPPY &&
+	    (r->keep_state == PF_STATE_MODULATE ||
+	    r->keep_state == PF_STATE_SYNPROXY)) {
+		yyerror("sloppy state matching cannot be used with "
+		    "synproxy state or modulate state");
+		problems++;
+	}
+	return (-problems);
+}
+
+int
+nat_consistent(struct pf_rule *r)
+{
+	return (0);	/* yeah! */
+}
+
+int
+rdr_consistent(struct pf_rule *r)
+{
+	int			 problems = 0;
+
+	if (r->proto != IPPROTO_TCP && r->proto != IPPROTO_UDP) {
+		if (r->src.port_op) {
+			yyerror("src port only applies to tcp/udp");
+			problems++;
+		}
+		if (r->dst.port_op) {
+			yyerror("dst port only applies to tcp/udp");
+			problems++;
+		}
+		if (r->rpool.proxy_port[0]) {
+			yyerror("rpool port only applies to tcp/udp");
+			problems++;
+		}
+	}
+	if (r->dst.port_op &&
+	    r->dst.port_op != PF_OP_EQ && r->dst.port_op != PF_OP_RRG) {
+		yyerror("invalid port operator for rdr destination port");
+		problems++;
+	}
+	return (-problems);
+}
+
+int
+process_tabledef(char *name, struct table_opts *opts)
+{
+	struct pfr_buffer	 ab;
+	struct node_tinit	*ti;
+
+	bzero(&ab, sizeof(ab));
+	ab.pfrb_type = PFRB_ADDRS;
+	SIMPLEQ_FOREACH(ti, &opts->init_nodes, entries) {
+		if (ti->file)
+			if (pfr_buf_load(&ab, ti->file, 0, append_addr)) {
+				if (errno)
+					yyerror("cannot load \"%s\": %s",
+					    ti->file, strerror(errno));
+				else
+					yyerror("file \"%s\" contains bad data",
+					    ti->file);
+				goto _error;
+			}
+		if (ti->host)
+			if (append_addr_host(&ab, ti->host, 0, 0)) {
+				yyerror("cannot create address buffer: %s",
+				    strerror(errno));
+				goto _error;
+			}
+	}
+	if (pf->opts & PF_OPT_VERBOSE)
+		print_tabledef(name, opts->flags, opts->init_addr,
+		    &opts->init_nodes);
+	if (!(pf->opts & PF_OPT_NOACTION) &&
+	    pfctl_define_table(name, opts->flags, opts->init_addr,
+	    pf->anchor->name, &ab, pf->anchor->ruleset.tticket)) {
+		yyerror("cannot define table %s: %s", name,
+		    pfr_strerror(errno));
+		goto _error;
+	}
+	pf->tdirty = 1;
+	pfr_buf_clear(&ab);
+	return (0);
+_error:
+	pfr_buf_clear(&ab);
+	return (-1);
+}
+
+struct keywords {
+	const char	*k_name;
+	int		 k_val;
+};
+
+/* macro gore, but you should've seen the prior indentation nightmare... */
+
+#define FREE_LIST(T,r) \
+	do { \
+		T *p, *node = r; \
+		while (node != NULL) { \
+			p = node; \
+			node = node->next; \
+			free(p); \
+		} \
+	} while (0)
+
+#define LOOP_THROUGH(T,n,r,C) \
+	do { \
+		T *n; \
+		if (r == NULL) { \
+			r = calloc(1, sizeof(T)); \
+			if (r == NULL) \
+				err(1, "LOOP: calloc"); \
+			r->next = NULL; \
+		} \
+		n = r; \
+		while (n != NULL) { \
+			do { \
+				C; \
+			} while (0); \
+			n = n->next; \
+		} \
+	} while (0)
+
+void
+expand_label_str(char *label, size_t len, const char *srch, const char *repl)
+{
+	char *tmp;
+	char *p, *q;
+
+	if ((tmp = calloc(1, len)) == NULL)
+		err(1, "expand_label_str: calloc");
+	p = q = label;
+	while ((q = strstr(p, srch)) != NULL) {
+		*q = '\0';
+		if ((strlcat(tmp, p, len) >= len) ||
+		    (strlcat(tmp, repl, len) >= len))
+			errx(1, "expand_label: label too long");
+		q += strlen(srch);
+		p = q;
+	}
+	if (strlcat(tmp, p, len) >= len)
+		errx(1, "expand_label: label too long");
+	strlcpy(label, tmp, len);	/* always fits */
+	free(tmp);
+}
+
+void
+expand_label_if(const char *name, char *label, size_t len, const char *ifname)
+{
+	if (strstr(label, name) != NULL) {
+		if (!*ifname)
+			expand_label_str(label, len, name, "any");
+		else
+			expand_label_str(label, len, name, ifname);
+	}
+}
+
+void
+expand_label_addr(const char *name, char *label, size_t len, sa_family_t af,
+    struct node_host *h)
+{
+	char tmp[64], tmp_not[66];
+
+	if (strstr(label, name) != NULL) {
+		switch (h->addr.type) {
+		case PF_ADDR_DYNIFTL:
+			snprintf(tmp, sizeof(tmp), "(%s)", h->addr.v.ifname);
+			break;
+		case PF_ADDR_TABLE:
+			snprintf(tmp, sizeof(tmp), "<%s>", h->addr.v.tblname);
+			break;
+		case PF_ADDR_NOROUTE:
+			snprintf(tmp, sizeof(tmp), "no-route");
+			break;
+		case PF_ADDR_URPFFAILED:
+			snprintf(tmp, sizeof(tmp), "urpf-failed");
+			break;
+		case PF_ADDR_ADDRMASK:
+			if (!af || (PF_AZERO(&h->addr.v.a.addr, af) &&
+			    PF_AZERO(&h->addr.v.a.mask, af)))
+				snprintf(tmp, sizeof(tmp), "any");
+			else {
+				char	a[48];
+				int	bits;
+
+				if (inet_ntop(af, &h->addr.v.a.addr, a,
+				    sizeof(a)) == NULL)
+					snprintf(tmp, sizeof(tmp), "?");
+				else {
+					bits = unmask(&h->addr.v.a.mask, af);
+					if ((af == AF_INET && bits < 32) ||
+					    (af == AF_INET6 && bits < 128))
+						snprintf(tmp, sizeof(tmp),
+						    "%s/%d", a, bits);
+					else
+						snprintf(tmp, sizeof(tmp),
+						    "%s", a);
+				}
+			}
+			break;
+		default:
+			snprintf(tmp, sizeof(tmp), "?");
+			break;
+		}
+
+		if (h->not) {
+			snprintf(tmp_not, sizeof(tmp_not), "! %s", tmp);
+			expand_label_str(label, len, name, tmp_not);
+		} else
+			expand_label_str(label, len, name, tmp);
+	}
+}
+
+void
+expand_label_port(const char *name, char *label, size_t len,
+    struct node_port *port)
+{
+	char	 a1[6], a2[6], op[13] = "";
+
+	if (strstr(label, name) != NULL) {
+		snprintf(a1, sizeof(a1), "%u", ntohs(port->port[0]));
+		snprintf(a2, sizeof(a2), "%u", ntohs(port->port[1]));
+		if (!port->op)
+			;
+		else if (port->op == PF_OP_IRG)
+			snprintf(op, sizeof(op), "%s><%s", a1, a2);
+		else if (port->op == PF_OP_XRG)
+			snprintf(op, sizeof(op), "%s<>%s", a1, a2);
+		else if (port->op == PF_OP_EQ)
+			snprintf(op, sizeof(op), "%s", a1);
+		else if (port->op == PF_OP_NE)
+			snprintf(op, sizeof(op), "!=%s", a1);
+		else if (port->op == PF_OP_LT)
+			snprintf(op, sizeof(op), "<%s", a1);
+		else if (port->op == PF_OP_LE)
+			snprintf(op, sizeof(op), "<=%s", a1);
+		else if (port->op == PF_OP_GT)
+			snprintf(op, sizeof(op), ">%s", a1);
+		else if (port->op == PF_OP_GE)
+			snprintf(op, sizeof(op), ">=%s", a1);
+		expand_label_str(label, len, name, op);
+	}
+}
+
+void
+expand_label_proto(const char *name, char *label, size_t len, u_int8_t proto)
+{
+	struct protoent *pe;
+	char n[4];
+
+	if (strstr(label, name) != NULL) {
+		pe = getprotobynumber(proto);
+		if (pe != NULL)
+			expand_label_str(label, len, name, pe->p_name);
+		else {
+			snprintf(n, sizeof(n), "%u", proto);
+			expand_label_str(label, len, name, n);
+		}
+	}
+}
+
+void
+expand_label_nr(const char *name, char *label, size_t len)
+{
+	char n[11];
+
+	if (strstr(label, name) != NULL) {
+		snprintf(n, sizeof(n), "%u", pf->anchor->match);
+		expand_label_str(label, len, name, n);
+	}
+}
+
+void
+expand_label(char *label, size_t len, const char *ifname, sa_family_t af,
+    struct node_host *src_host, struct node_port *src_port,
+    struct node_host *dst_host, struct node_port *dst_port,
+    u_int8_t proto)
+{
+	expand_label_if("$if", label, len, ifname);
+	expand_label_addr("$srcaddr", label, len, af, src_host);
+	expand_label_addr("$dstaddr", label, len, af, dst_host);
+	expand_label_port("$srcport", label, len, src_port);
+	expand_label_port("$dstport", label, len, dst_port);
+	expand_label_proto("$proto", label, len, proto);
+	expand_label_nr("$nr", label, len);
+}
+
+int
+expand_altq(struct pf_altq *a, struct node_if *interfaces,
+    struct node_queue *nqueues, struct node_queue_bw bwspec,
+    struct node_queue_opt *opts)
+{
+	struct pf_altq		 pa, pb;
+	char			 qname[PF_QNAME_SIZE];
+	struct node_queue	*n;
+	struct node_queue_bw	 bw;
+	int			 errs = 0;
+
+	if ((pf->loadopt & PFCTL_FLAG_ALTQ) == 0) {
+		FREE_LIST(struct node_if, interfaces);
+		FREE_LIST(struct node_queue, nqueues);
+		return (0);
+	}
+
+	LOOP_THROUGH(struct node_if, interface, interfaces,
+		memcpy(&pa, a, sizeof(struct pf_altq));
+		if (strlcpy(pa.ifname, interface->ifname,
+		    sizeof(pa.ifname)) >= sizeof(pa.ifname))
+			errx(1, "expand_altq: strlcpy");
+
+		if (interface->not) {
+			yyerror("altq on ! <interface> is not supported");
+			errs++;
+		} else {
+			if (eval_pfaltq(pf, &pa, &bwspec, opts))
+				errs++;
+			else
+				if (pfctl_add_altq(pf, &pa))
+					errs++;
+
+			if (pf->opts & PF_OPT_VERBOSE) {
+				print_altq(&pf->paltq->altq, 0,
+				    &bwspec, opts);
+				if (nqueues && nqueues->tail) {
+					printf("queue { ");
+					LOOP_THROUGH(struct node_queue, queue,
+					    nqueues,
+						printf("%s ",
+						    queue->queue);
+					);
+					printf("}");
+				}
+				printf("\n");
+			}
+
+			if (pa.scheduler == ALTQT_CBQ ||
+			    pa.scheduler == ALTQT_HFSC) {
+				/* now create a root queue */
+				memset(&pb, 0, sizeof(struct pf_altq));
+				if (strlcpy(qname, "root_", sizeof(qname)) >=
+				    sizeof(qname))
+					errx(1, "expand_altq: strlcpy");
+				if (strlcat(qname, interface->ifname,
+				    sizeof(qname)) >= sizeof(qname))
+					errx(1, "expand_altq: strlcat");
+				if (strlcpy(pb.qname, qname,
+				    sizeof(pb.qname)) >= sizeof(pb.qname))
+					errx(1, "expand_altq: strlcpy");
+				if (strlcpy(pb.ifname, interface->ifname,
+				    sizeof(pb.ifname)) >= sizeof(pb.ifname))
+					errx(1, "expand_altq: strlcpy");
+				pb.qlimit = pa.qlimit;
+				pb.scheduler = pa.scheduler;
+				bw.bw_absolute = pa.ifbandwidth;
+				bw.bw_percent = 0;
+				if (eval_pfqueue(pf, &pb, &bw, opts))
+					errs++;
+				else
+					if (pfctl_add_altq(pf, &pb))
+						errs++;
+			}
+
+			LOOP_THROUGH(struct node_queue, queue, nqueues,
+				n = calloc(1, sizeof(struct node_queue));
+				if (n == NULL)
+					err(1, "expand_altq: calloc");
+				if (pa.scheduler == ALTQT_CBQ ||
+				    pa.scheduler == ALTQT_HFSC)
+					if (strlcpy(n->parent, qname,
+					    sizeof(n->parent)) >=
+					    sizeof(n->parent))
+						errx(1, "expand_altq: strlcpy");
+				if (strlcpy(n->queue, queue->queue,
+				    sizeof(n->queue)) >= sizeof(n->queue))
+					errx(1, "expand_altq: strlcpy");
+				if (strlcpy(n->ifname, interface->ifname,
+				    sizeof(n->ifname)) >= sizeof(n->ifname))
+					errx(1, "expand_altq: strlcpy");
+				n->scheduler = pa.scheduler;
+				n->next = NULL;
+				n->tail = n;
+				if (queues == NULL)
+					queues = n;
+				else {
+					queues->tail->next = n;
+					queues->tail = n;
+				}
+			);
+		}
+	);
+	FREE_LIST(struct node_if, interfaces);
+	FREE_LIST(struct node_queue, nqueues);
+
+	return (errs);
+}
+
+int
+expand_queue(struct pf_altq *a, struct node_if *interfaces,
+    struct node_queue *nqueues, struct node_queue_bw bwspec,
+    struct node_queue_opt *opts)
+{
+	struct node_queue	*n, *nq;
+	struct pf_altq		 pa;
+	u_int8_t		 found = 0;
+	u_int8_t		 errs = 0;
+
+	if ((pf->loadopt & PFCTL_FLAG_ALTQ) == 0) {
+		FREE_LIST(struct node_queue, nqueues);
+		return (0);
+	}
+
+	if (queues == NULL) {
+		yyerror("queue %s has no parent", a->qname);
+		FREE_LIST(struct node_queue, nqueues);
+		return (1);
+	}
+
+	LOOP_THROUGH(struct node_if, interface, interfaces,
+		LOOP_THROUGH(struct node_queue, tqueue, queues,
+			if (!strncmp(a->qname, tqueue->queue, PF_QNAME_SIZE) &&
+			    (interface->ifname[0] == 0 ||
+			    (!interface->not && !strncmp(interface->ifname,
+			    tqueue->ifname, IFNAMSIZ)) ||
+			    (interface->not && strncmp(interface->ifname,
+			    tqueue->ifname, IFNAMSIZ)))) {
+				/* found ourself in queues */
+				found++;
+
+				memcpy(&pa, a, sizeof(struct pf_altq));
+
+				if (pa.scheduler != ALTQT_NONE &&
+				    pa.scheduler != tqueue->scheduler) {
+					yyerror("exactly one scheduler type "
+					    "per interface allowed");
+					return (1);
+				}
+				pa.scheduler = tqueue->scheduler;
+
+				/* scheduler dependent error checking */
+				switch (pa.scheduler) {
+				case ALTQT_PRIQ:
+					if (nqueues != NULL) {
+						yyerror("priq queues cannot "
+						    "have child queues");
+						return (1);
+					}
+					if (bwspec.bw_absolute > 0 ||
+					    bwspec.bw_percent < 100) {
+						yyerror("priq doesn't take "
+						    "bandwidth");
+						return (1);
+					}
+					break;
+				default:
+					break;
+				}
+
+				if (strlcpy(pa.ifname, tqueue->ifname,
+				    sizeof(pa.ifname)) >= sizeof(pa.ifname))
+					errx(1, "expand_queue: strlcpy");
+				if (strlcpy(pa.parent, tqueue->parent,
+				    sizeof(pa.parent)) >= sizeof(pa.parent))
+					errx(1, "expand_queue: strlcpy");
+
+				if (eval_pfqueue(pf, &pa, &bwspec, opts))
+					errs++;
+				else
+					if (pfctl_add_altq(pf, &pa))
+						errs++;
+
+				for (nq = nqueues; nq != NULL; nq = nq->next) {
+					if (!strcmp(a->qname, nq->queue)) {
+						yyerror("queue cannot have "
+						    "itself as child");
+						errs++;
+						continue;
+					}
+					n = calloc(1,
+					    sizeof(struct node_queue));
+					if (n == NULL)
+						err(1, "expand_queue: calloc");
+					if (strlcpy(n->parent, a->qname,
+					    sizeof(n->parent)) >=
+					    sizeof(n->parent))
+						errx(1, "expand_queue strlcpy");
+					if (strlcpy(n->queue, nq->queue,
+					    sizeof(n->queue)) >=
+					    sizeof(n->queue))
+						errx(1, "expand_queue strlcpy");
+					if (strlcpy(n->ifname, tqueue->ifname,
+					    sizeof(n->ifname)) >=
+					    sizeof(n->ifname))
+						errx(1, "expand_queue strlcpy");
+					n->scheduler = tqueue->scheduler;
+					n->next = NULL;
+					n->tail = n;
+					if (queues == NULL)
+						queues = n;
+					else {
+						queues->tail->next = n;
+						queues->tail = n;
+					}
+				}
+				if ((pf->opts & PF_OPT_VERBOSE) && (
+				    (found == 1 && interface->ifname[0] == 0) ||
+				    (found > 0 && interface->ifname[0] != 0))) {
+					print_queue(&pf->paltq->altq, 0,
+					    &bwspec, interface->ifname[0] != 0,
+					    opts);
+					if (nqueues && nqueues->tail) {
+						printf("{ ");
+						LOOP_THROUGH(struct node_queue,
+						    queue, nqueues,
+							printf("%s ",
+							    queue->queue);
+						);
+						printf("}");
+					}
+					printf("\n");
+				}
+			}
+		);
+	);
+
+	FREE_LIST(struct node_queue, nqueues);
+	FREE_LIST(struct node_if, interfaces);
+
+	if (!found) {
+		yyerror("queue %s has no parent", a->qname);
+		errs++;
+	}
+
+	if (errs)
+		return (1);
+	else
+		return (0);
+}
+
+void
+expand_rule(struct pf_rule *r,
+    struct node_if *interfaces, struct node_host *rpool_hosts,
+    struct node_proto *protos, struct node_os *src_oses,
+    struct node_host *src_hosts, struct node_port *src_ports,
+    struct node_host *dst_hosts, struct node_port *dst_ports,
+    struct node_uid *uids, struct node_gid *gids, struct node_icmp *icmp_types,
+    const char *anchor_call)
+{
+	sa_family_t		 af = r->af;
+	int			 added = 0, error = 0;
+	char			 ifname[IF_NAMESIZE];
+	char			 label[PF_RULE_LABEL_SIZE];
+	char			 tagname[PF_TAG_NAME_SIZE];
+	char			 match_tagname[PF_TAG_NAME_SIZE];
+	struct pf_pooladdr	*pa;
+	struct node_host	*h;
+	u_int8_t		 flags, flagset, keep_state;
+
+	if (strlcpy(label, r->label, sizeof(label)) >= sizeof(label))
+		errx(1, "expand_rule: strlcpy");
+	if (strlcpy(tagname, r->tagname, sizeof(tagname)) >= sizeof(tagname))
+		errx(1, "expand_rule: strlcpy");
+	if (strlcpy(match_tagname, r->match_tagname, sizeof(match_tagname)) >=
+	    sizeof(match_tagname))
+		errx(1, "expand_rule: strlcpy");
+	flags = r->flags;
+	flagset = r->flagset;
+	keep_state = r->keep_state;
+
+	LOOP_THROUGH(struct node_if, interface, interfaces,
+	LOOP_THROUGH(struct node_proto, proto, protos,
+	LOOP_THROUGH(struct node_icmp, icmp_type, icmp_types,
+	LOOP_THROUGH(struct node_host, src_host, src_hosts,
+	LOOP_THROUGH(struct node_port, src_port, src_ports,
+	LOOP_THROUGH(struct node_os, src_os, src_oses,
+	LOOP_THROUGH(struct node_host, dst_host, dst_hosts,
+	LOOP_THROUGH(struct node_port, dst_port, dst_ports,
+	LOOP_THROUGH(struct node_uid, uid, uids,
+	LOOP_THROUGH(struct node_gid, gid, gids,
+
+		r->af = af;
+		/* for link-local IPv6 address, interface must match up */
+		if ((r->af && src_host->af && r->af != src_host->af) ||
+		    (r->af && dst_host->af && r->af != dst_host->af) ||
+		    (src_host->af && dst_host->af &&
+		    src_host->af != dst_host->af) ||
+		    (src_host->ifindex && dst_host->ifindex &&
+		    src_host->ifindex != dst_host->ifindex) ||
+		    (src_host->ifindex && *interface->ifname &&
+		    src_host->ifindex != if_nametoindex(interface->ifname)) ||
+		    (dst_host->ifindex && *interface->ifname &&
+		    dst_host->ifindex != if_nametoindex(interface->ifname)))
+			continue;
+		if (!r->af && src_host->af)
+			r->af = src_host->af;
+		else if (!r->af && dst_host->af)
+			r->af = dst_host->af;
+
+		if (*interface->ifname)
+			strlcpy(r->ifname, interface->ifname,
+			    sizeof(r->ifname));
+		else if (if_indextoname(src_host->ifindex, ifname))
+			strlcpy(r->ifname, ifname, sizeof(r->ifname));
+		else if (if_indextoname(dst_host->ifindex, ifname))
+			strlcpy(r->ifname, ifname, sizeof(r->ifname));
+		else
+			memset(r->ifname, '\0', sizeof(r->ifname));
+
+		if (strlcpy(r->label, label, sizeof(r->label)) >=
+		    sizeof(r->label))
+			errx(1, "expand_rule: strlcpy");
+		if (strlcpy(r->tagname, tagname, sizeof(r->tagname)) >=
+		    sizeof(r->tagname))
+			errx(1, "expand_rule: strlcpy");
+		if (strlcpy(r->match_tagname, match_tagname,
+		    sizeof(r->match_tagname)) >= sizeof(r->match_tagname))
+			errx(1, "expand_rule: strlcpy");
+		expand_label(r->label, PF_RULE_LABEL_SIZE, r->ifname, r->af,
+		    src_host, src_port, dst_host, dst_port, proto->proto);
+		expand_label(r->tagname, PF_TAG_NAME_SIZE, r->ifname, r->af,
+		    src_host, src_port, dst_host, dst_port, proto->proto);
+		expand_label(r->match_tagname, PF_TAG_NAME_SIZE, r->ifname,
+		    r->af, src_host, src_port, dst_host, dst_port,
+		    proto->proto);
+
+		error += check_netmask(src_host, r->af);
+		error += check_netmask(dst_host, r->af);
+
+		r->ifnot = interface->not;
+		r->proto = proto->proto;
+		r->src.addr = src_host->addr;
+		r->src.neg = src_host->not;
+		r->src.port[0] = src_port->port[0];
+		r->src.port[1] = src_port->port[1];
+		r->src.port_op = src_port->op;
+		r->dst.addr = dst_host->addr;
+		r->dst.neg = dst_host->not;
+		r->dst.port[0] = dst_port->port[0];
+		r->dst.port[1] = dst_port->port[1];
+		r->dst.port_op = dst_port->op;
+		r->uid.op = uid->op;
+		r->uid.uid[0] = uid->uid[0];
+		r->uid.uid[1] = uid->uid[1];
+		r->gid.op = gid->op;
+		r->gid.gid[0] = gid->gid[0];
+		r->gid.gid[1] = gid->gid[1];
+		r->type = icmp_type->type;
+		r->code = icmp_type->code;
+
+		if ((keep_state == PF_STATE_MODULATE ||
+		    keep_state == PF_STATE_SYNPROXY) &&
+		    r->proto && r->proto != IPPROTO_TCP)
+			r->keep_state = PF_STATE_NORMAL;
+		else
+			r->keep_state = keep_state;
+
+		if (r->proto && r->proto != IPPROTO_TCP) {
+			r->flags = 0;
+			r->flagset = 0;
+		} else {
+			r->flags = flags;
+			r->flagset = flagset;
+		}
+		if (icmp_type->proto && r->proto != icmp_type->proto) {
+			yyerror("icmp-type mismatch");
+			error++;
+		}
+
+		if (src_os && src_os->os) {
+			r->os_fingerprint = pfctl_get_fingerprint(src_os->os);
+			if ((pf->opts & PF_OPT_VERBOSE2) &&
+			    r->os_fingerprint == PF_OSFP_NOMATCH)
+				fprintf(stderr,
+				    "warning: unknown '%s' OS fingerprint\n",
+				    src_os->os);
+		} else {
+			r->os_fingerprint = PF_OSFP_ANY;
+		}
+
+		TAILQ_INIT(&r->rpool.list);
+		for (h = rpool_hosts; h != NULL; h = h->next) {
+			pa = calloc(1, sizeof(struct pf_pooladdr));
+			if (pa == NULL)
+				err(1, "expand_rule: calloc");
+			pa->addr = h->addr;
+			if (h->ifname != NULL) {
+				if (strlcpy(pa->ifname, h->ifname,
+				    sizeof(pa->ifname)) >=
+				    sizeof(pa->ifname))
+					errx(1, "expand_rule: strlcpy");
+			} else
+				pa->ifname[0] = 0;
+			TAILQ_INSERT_TAIL(&r->rpool.list, pa, entries);
+		}
+
+		if (rule_consistent(r, anchor_call[0]) < 0 || error)
+			yyerror("skipping rule due to errors");
+		else {
+			r->nr = pf->astack[pf->asd]->match++;
+			pfctl_add_rule(pf, r, anchor_call);
+			added++;
+		}
+
+	))))))))));
+
+	FREE_LIST(struct node_if, interfaces);
+	FREE_LIST(struct node_proto, protos);
+	FREE_LIST(struct node_host, src_hosts);
+	FREE_LIST(struct node_port, src_ports);
+	FREE_LIST(struct node_os, src_oses);
+	FREE_LIST(struct node_host, dst_hosts);
+	FREE_LIST(struct node_port, dst_ports);
+	FREE_LIST(struct node_uid, uids);
+	FREE_LIST(struct node_gid, gids);
+	FREE_LIST(struct node_icmp, icmp_types);
+	FREE_LIST(struct node_host, rpool_hosts);
+
+	if (!added)
+		yyerror("rule expands to no valid combination");
+}
+
+int
+expand_skip_interface(struct node_if *interfaces)
+{
+	int	errs = 0;
+
+	if (!interfaces || (!interfaces->next && !interfaces->not &&
+	    !strcmp(interfaces->ifname, "none"))) {
+		if (pf->opts & PF_OPT_VERBOSE)
+			printf("set skip on none\n");
+		errs = pfctl_set_interface_flags(pf, "", PFI_IFLAG_SKIP, 0);
+		return (errs);
+	}
+
+	if (pf->opts & PF_OPT_VERBOSE)
+		printf("set skip on {");
+	LOOP_THROUGH(struct node_if, interface, interfaces,
+		if (pf->opts & PF_OPT_VERBOSE)
+			printf(" %s", interface->ifname);
+		if (interface->not) {
+			yyerror("skip on ! <interface> is not supported");
+			errs++;
+		} else
+			errs += pfctl_set_interface_flags(pf,
+			    interface->ifname, PFI_IFLAG_SKIP, 1);
+	);
+	if (pf->opts & PF_OPT_VERBOSE)
+		printf(" }\n");
+
+	FREE_LIST(struct node_if, interfaces);
+
+	if (errs)
+		return (1);
+	else
+		return (0);
+}
+
+#undef FREE_LIST
+#undef LOOP_THROUGH
+
+int
+check_rulestate(int desired_state)
+{
+	if (require_order && (rulestate > desired_state)) {
+		yyerror("Rules must be in order: options, normalization, "
+		    "queueing, translation, filtering");
+		return (1);
+	}
+	rulestate = desired_state;
+	return (0);
+}
+
+int
+kw_cmp(const void *k, const void *e)
+{
+	return (strcmp(k, ((const struct keywords *)e)->k_name));
+}
+
+int
+lookup(char *s)
+{
+	/* this has to be sorted always */
+	static const struct keywords keywords[] = {
+		{ "all",		ALL},
+		{ "allow-opts",		ALLOWOPTS},
+		{ "altq",		ALTQ},
+		{ "anchor",		ANCHOR},
+		{ "antispoof",		ANTISPOOF},
+		{ "any",		ANY},
+		{ "bandwidth",		BANDWIDTH},
+		{ "binat",		BINAT},
+		{ "binat-anchor",	BINATANCHOR},
+		{ "bitmask",		BITMASK},
+		{ "block",		BLOCK},
+		{ "block-policy",	BLOCKPOLICY},
+		{ "cbq",		CBQ},
+		{ "code",		CODE},
+		{ "crop",		FRAGCROP},
+		{ "debug",		DEBUG},
+		{ "divert-reply",	DIVERTREPLY},
+		{ "divert-to",		DIVERTTO},
+		{ "drop",		DROP},
+		{ "drop-ovl",		FRAGDROP},
+		{ "dup-to",		DUPTO},
+		{ "fastroute",		FASTROUTE},
+		{ "file",		FILENAME},
+		{ "fingerprints",	FINGERPRINTS},
+		{ "flags",		FLAGS},
+		{ "floating",		FLOATING},
+		{ "flush",		FLUSH},
+		{ "for",		FOR},
+		{ "fragment",		FRAGMENT},
+		{ "from",		FROM},
+		{ "global",		GLOBAL},
+		{ "group",		GROUP},
+		{ "hfsc",		HFSC},
+		{ "hostid",		HOSTID},
+		{ "icmp-type",		ICMPTYPE},
+		{ "icmp6-type",		ICMP6TYPE},
+		{ "if-bound",		IFBOUND},
+		{ "in",			IN},
+		{ "include",		INCLUDE},
+		{ "inet",		INET},
+		{ "inet6",		INET6},
+		{ "keep",		KEEP},
+		{ "label",		LABEL},
+		{ "limit",		LIMIT},
+		{ "linkshare",		LINKSHARE},
+		{ "load",		LOAD},
+		{ "log",		LOG},
+		{ "loginterface",	LOGINTERFACE},
+		{ "max",		MAXIMUM},
+		{ "max-mss",		MAXMSS},
+		{ "max-src-conn",	MAXSRCCONN},
+		{ "max-src-conn-rate",	MAXSRCCONNRATE},
+		{ "max-src-nodes",	MAXSRCNODES},
+		{ "max-src-states",	MAXSRCSTATES},
+		{ "min-ttl",		MINTTL},
+		{ "modulate",		MODULATE},
+		{ "nat",		NAT},
+		{ "nat-anchor",		NATANCHOR},
+		{ "no",			NO},
+		{ "no-df",		NODF},
+		{ "no-route",		NOROUTE},
+		{ "no-sync",		NOSYNC},
+		{ "on",			ON},
+		{ "optimization",	OPTIMIZATION},
+		{ "os",			OS},
+		{ "out",		OUT},
+		{ "overload",		OVERLOAD},
+		{ "pass",		PASS},
+		{ "pflow",		PFLOW},
+		{ "port",		PORT},
+		{ "priority",		PRIORITY},
+		{ "priq",		PRIQ},
+		{ "probability",	PROBABILITY},
+		{ "proto",		PROTO},
+		{ "qlimit",		QLIMIT},
+		{ "queue",		QUEUE},
+		{ "quick",		QUICK},
+		{ "random",		RANDOM},
+		{ "random-id",		RANDOMID},
+		{ "rdr",		RDR},
+		{ "rdr-anchor",		RDRANCHOR},
+		{ "realtime",		REALTIME},
+		{ "reassemble",		REASSEMBLE},
+		{ "reply-to",		REPLYTO},
+		{ "require-order",	REQUIREORDER},
+		{ "return",		RETURN},
+		{ "return-icmp",	RETURNICMP},
+		{ "return-icmp6",	RETURNICMP6},
+		{ "return-rst",		RETURNRST},
+		{ "round-robin",	ROUNDROBIN},
+		{ "route",		ROUTE},
+		{ "route-to",		ROUTETO},
+		{ "rtable",		RTABLE},
+		{ "rule",		RULE},
+		{ "ruleset-optimization",	RULESET_OPTIMIZATION},
+		{ "scrub",		SCRUB},
+		{ "set",		SET},
+		{ "set-tos",		SETTOS},
+		{ "skip",		SKIP},
+		{ "sloppy",		SLOPPY},
+		{ "source-hash",	SOURCEHASH},
+		{ "source-track",	SOURCETRACK},
+		{ "state",		STATE},
+		{ "state-defaults",	STATEDEFAULTS},
+		{ "state-policy",	STATEPOLICY},
+		{ "static-port",	STATICPORT},
+		{ "sticky-address",	STICKYADDRESS},
+		{ "synproxy",		SYNPROXY},
+		{ "table",		TABLE},
+		{ "tag",		TAG},
+		{ "tagged",		TAGGED},
+		{ "tbrsize",		TBRSIZE},
+		{ "timeout",		TIMEOUT},
+		{ "to",			TO},
+		{ "tos",		TOS},
+		{ "ttl",		TTL},
+		{ "upperlimit",		UPPERLIMIT},
+		{ "urpf-failed",	URPFFAILED},
+		{ "user",		USER},
+	};
+	const struct keywords	*p;
+
+	p = bsearch(s, keywords, sizeof(keywords)/sizeof(keywords[0]),
+	    sizeof(keywords[0]), kw_cmp);
+
+	if (p) {
+		if (debug > 1)
+			fprintf(stderr, "%s: %d\n", s, p->k_val);
+		return (p->k_val);
+	} else {
+		if (debug > 1)
+			fprintf(stderr, "string: %s\n", s);
+		return (STRING);
+	}
+}
+
+#define MAXPUSHBACK	128
+
+char	*parsebuf;
+int	 parseindex;
+char	 pushback_buffer[MAXPUSHBACK];
+int	 pushback_index = 0;
+
+int
+lgetc(int quotec)
+{
+	int		c, next;
+
+	if (parsebuf) {
+		/* Read character from the parsebuffer instead of input. */
+		if (parseindex >= 0) {
+			c = parsebuf[parseindex++];
+			if (c != '\0')
+				return (c);
+			parsebuf = NULL;
+		} else
+			parseindex++;
+	}
+
+	if (pushback_index)
+		return (pushback_buffer[--pushback_index]);
+
+	if (quotec) {
+		if ((c = getc(file->stream)) == EOF) {
+			yyerror("reached end of file while parsing quoted string");
+			if (popfile() == EOF)
+				return (EOF);
+			return (quotec);
+		}
+		return (c);
+	}
+
+	while ((c = getc(file->stream)) == '\\') {
+		next = getc(file->stream);
+		if (next != '\n') {
+			c = next;
+			break;
+		}
+		yylval.lineno = file->lineno;
+		file->lineno++;
+	}
+
+	while (c == EOF) {
+		if (popfile() == EOF)
+			return (EOF);
+		c = getc(file->stream);
+	}
+	return (c);
+}
+
+int
+lungetc(int c)
+{
+	if (c == EOF)
+		return (EOF);
+	if (parsebuf) {
+		parseindex--;
+		if (parseindex >= 0)
+			return (c);
+	}
+	if (pushback_index < MAXPUSHBACK-1)
+		return (pushback_buffer[pushback_index++] = c);
+	else
+		return (EOF);
+}
+
+int
+findeol(void)
+{
+	int	c;
+
+	parsebuf = NULL;
+
+	/* skip to either EOF or the first real EOL */
+	while (1) {
+		if (pushback_index)
+			c = pushback_buffer[--pushback_index];
+		else
+			c = lgetc(0);
+		if (c == '\n') {
+			file->lineno++;
+			break;
+		}
+		if (c == EOF)
+			break;
+	}
+	return (ERROR);
+}
+
+int
+yylex(void)
+{
+	char	 buf[8096];
+	char	*p, *val;
+	int	 quotec, next, c;
+	int	 token;
+
+top:
+	p = buf;
+	while ((c = lgetc(0)) == ' ' || c == '\t')
+		; /* nothing */
+
+	yylval.lineno = file->lineno;
+	if (c == '#')
+		while ((c = lgetc(0)) != '\n' && c != EOF)
+			; /* nothing */
+	if (c == '$' && parsebuf == NULL) {
+		while (1) {
+			if ((c = lgetc(0)) == EOF)
+				return (0);
+
+			if (p + 1 >= buf + sizeof(buf) - 1) {
+				yyerror("string too long");
+				return (findeol());
+			}
+			if (isalnum(c) || c == '_') {
+				*p++ = (char)c;
+				continue;
+			}
+			*p = '\0';
+			lungetc(c);
+			break;
+		}
+		val = symget(buf);
+		if (val == NULL) {
+			yyerror("macro '%s' not defined", buf);
+			return (findeol());
+		}
+		parsebuf = val;
+		parseindex = 0;
+		goto top;
+	}
+
+	switch (c) {
+	case '\'':
+	case '"':
+		quotec = c;
+		while (1) {
+			if ((c = lgetc(quotec)) == EOF)
+				return (0);
+			if (c == '\n') {
+				file->lineno++;
+				continue;
+			} else if (c == '\\') {
+				if ((next = lgetc(quotec)) == EOF)
+					return (0);
+				if (next == quotec || c == ' ' || c == '\t')
+					c = next;
+				else if (next == '\n')
+					continue;
+				else
+					lungetc(next);
+			} else if (c == quotec) {
+				*p = '\0';
+				break;
+			}
+			if (p + 1 >= buf + sizeof(buf) - 1) {
+				yyerror("string too long");
+				return (findeol());
+			}
+			*p++ = (char)c;
+		}
+		yylval.v.string = strdup(buf);
+		if (yylval.v.string == NULL)
+			err(1, "yylex: strdup");
+		return (STRING);
+	case '<':
+		next = lgetc(0);
+		if (next == '>') {
+			yylval.v.i = PF_OP_XRG;
+			return (PORTBINARY);
+		}
+		lungetc(next);
+		break;
+	case '>':
+		next = lgetc(0);
+		if (next == '<') {
+			yylval.v.i = PF_OP_IRG;
+			return (PORTBINARY);
+		}
+		lungetc(next);
+		break;
+	case '-':
+		next = lgetc(0);
+		if (next == '>')
+			return (ARROW);
+		lungetc(next);
+		break;
+	}
+
+#define allowed_to_end_number(x) \
+	(isspace(x) || x == ')' || x ==',' || x == '/' || x == '}' || x == '=')
+
+	if (c == '-' || isdigit(c)) {
+		do {
+			*p++ = c;
+			if ((unsigned)(p-buf) >= sizeof(buf)) {
+				yyerror("string too long");
+				return (findeol());
+			}
+		} while ((c = lgetc(0)) != EOF && isdigit(c));
+		lungetc(c);
+		if (p == buf + 1 && buf[0] == '-')
+			goto nodigits;
+		if (c == EOF || allowed_to_end_number(c)) {
+			const char *errstr = NULL;
+
+			*p = '\0';
+			yylval.v.number = strtonum(buf, LLONG_MIN,
+			    LLONG_MAX, &errstr);
+			if (errstr) {
+				yyerror("\"%s\" invalid number: %s",
+				    buf, errstr);
+				return (findeol());
+			}
+			return (NUMBER);
+		} else {
+nodigits:
+			while (p > buf + 1)
+				lungetc(*--p);
+			c = *--p;
+			if (c == '-')
+				return (c);
+		}
+	}
+
+#define allowed_in_string(x) \
+	(isalnum(x) || (ispunct(x) && x != '(' && x != ')' && \
+	x != '{' && x != '}' && x != '<' && x != '>' && \
+	x != '!' && x != '=' && x != '/' && x != '#' && \
+	x != ','))
+
+	if (isalnum(c) || c == ':' || c == '_') {
+		do {
+			*p++ = c;
+			if ((unsigned)(p-buf) >= sizeof(buf)) {
+				yyerror("string too long");
+				return (findeol());
+			}
+		} while ((c = lgetc(0)) != EOF && (allowed_in_string(c)));
+		lungetc(c);
+		*p = '\0';
+		if ((token = lookup(buf)) == STRING)
+			if ((yylval.v.string = strdup(buf)) == NULL)
+				err(1, "yylex: strdup");
+		return (token);
+	}
+	if (c == '\n') {
+		yylval.lineno = file->lineno;
+		file->lineno++;
+	}
+	if (c == EOF)
+		return (0);
+	return (c);
+}
+
+int
+check_file_secrecy(int fd, const char *fname)
+{
+	struct stat	st;
+
+	if (fstat(fd, &st)) {
+		warn("cannot stat %s", fname);
+		return (-1);
+	}
+	if (st.st_uid != 0 && st.st_uid != getuid()) {
+		warnx("%s: owner not root or current user", fname);
+		return (-1);
+	}
+	if (st.st_mode & (S_IRWXG | S_IRWXO)) {
+		warnx("%s: group/world readable/writeable", fname);
+		return (-1);
+	}
+	return (0);
+}
+
+struct file *
+pushfile(const char *name, int secret)
+{
+	struct file	*nfile;
+
+	if ((nfile = calloc(1, sizeof(struct file))) == NULL ||
+	    (nfile->name = strdup(name)) == NULL) {
+		warn("malloc");
+		return (NULL);
+	}
+	if (TAILQ_FIRST(&files) == NULL && strcmp(nfile->name, "-") == 0) {
+		nfile->stream = stdin;
+		free(nfile->name);
+		if ((nfile->name = strdup("stdin")) == NULL) {
+			warn("strdup");
+			free(nfile);
+			return (NULL);
+		}
+	} else if ((nfile->stream = fopen(nfile->name, "r")) == NULL) {
+		warn("%s", nfile->name);
+		free(nfile->name);
+		free(nfile);
+		return (NULL);
+	} else if (secret &&
+	    check_file_secrecy(fileno(nfile->stream), nfile->name)) {
+		fclose(nfile->stream);
+		free(nfile->name);
+		free(nfile);
+		return (NULL);
+	}
+	nfile->lineno = 1;
+	TAILQ_INSERT_TAIL(&files, nfile, entry);
+	return (nfile);
+}
+
+int
+popfile(void)
+{
+	struct file	*prev;
+
+	if ((prev = TAILQ_PREV(file, files, entry)) != NULL) {
+		prev->errors += file->errors;
+		TAILQ_REMOVE(&files, file, entry);
+		fclose(file->stream);
+		free(file->name);
+		free(file);
+		file = prev;
+		return (0);
+	}
+	return (EOF);
+}
+
+int
+parse_config(char *filename, struct pfctl *xpf)
+{
+	int		 errors = 0;
+	struct sym	*sym;
+
+	pf = xpf;
+	errors = 0;
+	rulestate = PFCTL_STATE_NONE;
+	returnicmpdefault = (ICMP_UNREACH << 8) | ICMP_UNREACH_PORT;
+	returnicmp6default =
+	    (ICMP6_DST_UNREACH << 8) | ICMP6_DST_UNREACH_NOPORT;
+	blockpolicy = PFRULE_DROP;
+	require_order = 1;
+
+	if ((file = pushfile(filename, 0)) == NULL) {
+		warn("cannot open the main config file!");
+		return (-1);
+	}
+
+	yyparse();
+	errors = file->errors;
+	popfile();
+
+	/* Free macros and check which have not been used. */
+	while ((sym = TAILQ_FIRST(&symhead))) {
+		if ((pf->opts & PF_OPT_VERBOSE2) && !sym->used)
+			fprintf(stderr, "warning: macro '%s' not "
+			    "used\n", sym->nam);
+		free(sym->nam);
+		free(sym->val);
+		TAILQ_REMOVE(&symhead, sym, entry);
+		free(sym);
+	}
+
+	return (errors ? -1 : 0);
+}
+
+int
+symset(const char *nam, const char *val, int persist)
+{
+	struct sym	*sym;
+
+	for (sym = TAILQ_FIRST(&symhead); sym && strcmp(nam, sym->nam);
+	    sym = TAILQ_NEXT(sym, entry))
+		;	/* nothing */
+
+	if (sym != NULL) {
+		if (sym->persist == 1)
+			return (0);
+		else {
+			free(sym->nam);
+			free(sym->val);
+			TAILQ_REMOVE(&symhead, sym, entry);
+			free(sym);
+		}
+	}
+	if ((sym = calloc(1, sizeof(*sym))) == NULL)
+		return (-1);
+
+	sym->nam = strdup(nam);
+	if (sym->nam == NULL) {
+		free(sym);
+		return (-1);
+	}
+	sym->val = strdup(val);
+	if (sym->val == NULL) {
+		free(sym->nam);
+		free(sym);
+		return (-1);
+	}
+	sym->used = 0;
+	sym->persist = persist;
+	TAILQ_INSERT_TAIL(&symhead, sym, entry);
+	return (0);
+}
+
+int
+pfctl_cmdline_symset(char *s)
+{
+	char	*sym, *val;
+	int	 ret;
+
+	if ((val = strrchr(s, '=')) == NULL)
+		return (-1);
+
+	if ((sym = malloc(strlen(s) - strlen(val) + 1)) == NULL)
+		err(1, "pfctl_cmdline_symset: malloc");
+
+	strlcpy(sym, s, strlen(s) - strlen(val) + 1);
+
+	ret = symset(sym, val + 1, 1);
+	free(sym);
+
+	return (ret);
+}
+
+char *
+symget(const char *nam)
+{
+	struct sym	*sym;
+
+	TAILQ_FOREACH(sym, &symhead, entry)
+		if (strcmp(nam, sym->nam) == 0) {
+			sym->used = 1;
+			return (sym->val);
+		}
+	return (NULL);
+}
+
+void
+mv_rules(struct pf_ruleset *src, struct pf_ruleset *dst)
+{
+	int i;
+	struct pf_rule *r;
+
+	for (i = 0; i < PF_RULESET_MAX; ++i) {
+		while ((r = TAILQ_FIRST(src->rules[i].active.ptr))
+		    != NULL) {
+			TAILQ_REMOVE(src->rules[i].active.ptr, r, entries);
+			TAILQ_INSERT_TAIL(dst->rules[i].active.ptr, r, entries);
+			dst->anchor->match++;
+		}
+		src->anchor->match = 0;
+		while ((r = TAILQ_FIRST(src->rules[i].inactive.ptr))
+		    != NULL) {
+			TAILQ_REMOVE(src->rules[i].inactive.ptr, r, entries);
+			TAILQ_INSERT_TAIL(dst->rules[i].inactive.ptr,
+				r, entries);
+		}
+	}
+}
+
+void
+decide_address_family(struct node_host *n, sa_family_t *af)
+{
+	if (*af != 0 || n == NULL)
+		return;
+	*af = n->af;
+	while ((n = n->next) != NULL) {
+		if (n->af != *af) {
+			*af = 0;
+			return;
+		}
+	}
+}
+
+void
+remove_invalid_hosts(struct node_host **nh, sa_family_t *af)
+{
+	struct node_host	*n = *nh, *prev = NULL;
+
+	while (n != NULL) {
+		if (*af && n->af && n->af != *af) {
+			/* unlink and free n */
+			struct node_host *next = n->next;
+
+			/* adjust tail pointer */
+			if (n == (*nh)->tail)
+				(*nh)->tail = prev;
+			/* adjust previous node's next pointer */
+			if (prev == NULL)
+				*nh = next;
+			else
+				prev->next = next;
+			/* free node */
+			if (n->ifname != NULL)
+				free(n->ifname);
+			free(n);
+			n = next;
+		} else {
+			if (n->af && !*af)
+				*af = n->af;
+			prev = n;
+			n = n->next;
+		}
+	}
+}
+
+int
+invalid_redirect(struct node_host *nh, sa_family_t af)
+{
+	if (!af) {
+		struct node_host *n;
+
+		/* tables and dyniftl are ok without an address family */
+		for (n = nh; n != NULL; n = n->next) {
+			if (n->addr.type != PF_ADDR_TABLE &&
+			    n->addr.type != PF_ADDR_DYNIFTL) {
+				yyerror("address family not given and "
+				    "translation address expands to multiple "
+				    "address families");
+				return (1);
+			}
+		}
+	}
+	if (nh == NULL) {
+		yyerror("no translation address with matching address family "
+		    "found.");
+		return (1);
+	}
+	return (0);
+}
+
+int
+atoul(char *s, u_long *ulvalp)
+{
+	u_long	 ulval;
+	char	*ep;
+
+	errno = 0;
+	ulval = strtoul(s, &ep, 0);
+	if (s[0] == '\0' || *ep != '\0')
+		return (-1);
+	if (errno == ERANGE && ulval == ULONG_MAX)
+		return (-1);
+	*ulvalp = ulval;
+	return (0);
+}
+
+int
+getservice(char *n)
+{
+	struct servent	*s;
+	u_long		 ulval;
+
+	if (atoul(n, &ulval) == 0) {
+		if (ulval > 65535) {
+			yyerror("illegal port value %lu", ulval);
+			return (-1);
+		}
+		return (htons(ulval));
+	} else {
+		s = getservbyname(n, "tcp");
+		if (s == NULL)
+			s = getservbyname(n, "udp");
+		if (s == NULL) {
+			yyerror("unknown port %s", n);
+			return (-1);
+		}
+		return (s->s_port);
+	}
+}
+
+int
+rule_label(struct pf_rule *r, char *s)
+{
+	if (s) {
+		if (strlcpy(r->label, s, sizeof(r->label)) >=
+		    sizeof(r->label)) {
+			yyerror("rule label too long (max %d chars)",
+			    sizeof(r->label)-1);
+			return (-1);
+		}
+	}
+	return (0);
+}
+
+u_int16_t
+parseicmpspec(char *w, sa_family_t af)
+{
+	const struct icmpcodeent	*p;
+	u_long				 ulval;
+	u_int8_t			 icmptype;
+
+	if (af == AF_INET)
+		icmptype = returnicmpdefault >> 8;
+	else
+		icmptype = returnicmp6default >> 8;
+
+	if (atoul(w, &ulval) == -1) {
+		if ((p = geticmpcodebyname(icmptype, w, af)) == NULL) {
+			yyerror("unknown icmp code %s", w);
+			return (0);
+		}
+		ulval = p->code;
+	}
+	if (ulval > 255) {
+		yyerror("invalid icmp code %lu", ulval);
+		return (0);
+	}
+	return (icmptype << 8 | ulval);
+}
+
+int
+parseport(char *port, struct range *r, int extensions)
+{
+	char	*p = strchr(port, ':');
+
+	if (p == NULL) {
+		if ((r->a = getservice(port)) == -1)
+			return (-1);
+		r->b = 0;
+		r->t = PF_OP_NONE;
+		return (0);
+	}
+	if ((extensions & PPORT_STAR) && !strcmp(p+1, "*")) {
+		*p = 0;
+		if ((r->a = getservice(port)) == -1)
+			return (-1);
+		r->b = 0;
+		r->t = PF_OP_IRG;
+		return (0);
+	}
+	if ((extensions & PPORT_RANGE)) {
+		*p++ = 0;
+		if ((r->a = getservice(port)) == -1 ||
+		    (r->b = getservice(p)) == -1)
+			return (-1);
+		if (r->a == r->b) {
+			r->b = 0;
+			r->t = PF_OP_NONE;
+		} else
+			r->t = PF_OP_RRG;
+		return (0);
+	}
+	return (-1);
+}
+
+int
+pfctl_load_anchors(int dev, struct pfctl *pf, struct pfr_buffer *trans)
+{
+	struct loadanchors	*la;
+
+	TAILQ_FOREACH(la, &loadanchorshead, entries) {
+		if (pf->opts & PF_OPT_VERBOSE)
+			fprintf(stderr, "\nLoading anchor %s from %s\n",
+			    la->anchorname, la->filename);
+		if (pfctl_rules(dev, la->filename, pf->opts, pf->optimize,
+		    la->anchorname, trans) == -1)
+			return (-1);
+	}
+
+	return (0);
+}
+
+int
+rt_tableid_max(void)
+{
+#ifdef __FreeBSD__
+	int fibs;
+	size_t l = sizeof(fibs);
+
+        if (sysctlbyname("net.fibs", &fibs, &l, NULL, 0) == -1)
+		fibs = 16;	/* XXX RT_MAXFIBS, at least limit it some. */
+	/*
+	 * As the OpenBSD code only compares > and not >= we need to adjust
+	 * here given we only accept values of 0..n and want to avoid #ifdefs
+	 * in the grammer.
+	 */
+	return (fibs - 1);
+#else
+	return (RT_TABLEID_MAX);
+#endif
+}
diff --git a/freebsd/contrib/pf/pfctl/pf_print_state.c b/freebsd/contrib/pf/pfctl/pf_print_state.c
new file mode 100644
index 0000000..1b2a597
--- /dev/null
+++ b/freebsd/contrib/pf/pfctl/pf_print_state.c
@@ -0,0 +1,382 @@
+#include <machine/rtems-bsd-user-space.h>
+
+/*	$OpenBSD: pf_print_state.c,v 1.52 2008/08/12 16:40:18 david Exp $	*/
+
+/*
+ * Copyright (c) 2001 Daniel Hartmeier
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *    - Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *    - 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 COPYRIGHT HOLDERS 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
+ * COPYRIGHT HOLDERS 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>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#ifdef __FreeBSD__
+#include <sys/endian.h>
+#define	betoh64	be64toh
+#endif
+#include <net/if.h>
+#define TCPSTATES
+#include <netinet/tcp_fsm.h>
+#include <net/pfvar.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+
+#include <stdio.h>
+#include <string.h>
+
+#include "pfctl_parser.h"
+#include "pfctl.h"
+
+void	print_name(struct pf_addr *, sa_family_t);
+
+void
+print_addr(struct pf_addr_wrap *addr, sa_family_t af, int verbose)
+{
+	switch (addr->type) {
+	case PF_ADDR_DYNIFTL:
+		printf("(%s", addr->v.ifname);
+		if (addr->iflags & PFI_AFLAG_NETWORK)
+			printf(":network");
+		if (addr->iflags & PFI_AFLAG_BROADCAST)
+			printf(":broadcast");
+		if (addr->iflags & PFI_AFLAG_PEER)
+			printf(":peer");
+		if (addr->iflags & PFI_AFLAG_NOALIAS)
+			printf(":0");
+		if (verbose) {
+			if (addr->p.dyncnt <= 0)
+				printf(":*");
+			else
+				printf(":%d", addr->p.dyncnt);
+		}
+		printf(")");
+		break;
+	case PF_ADDR_TABLE:
+		if (verbose)
+			if (addr->p.tblcnt == -1)
+				printf("<%s:*>", addr->v.tblname);
+			else
+				printf("<%s:%d>", addr->v.tblname,
+				    addr->p.tblcnt);
+		else
+			printf("<%s>", addr->v.tblname);
+		return;
+	case PF_ADDR_RANGE: {
+		char buf[48];
+
+		if (inet_ntop(af, &addr->v.a.addr, buf, sizeof(buf)) == NULL)
+			printf("?");
+		else
+			printf("%s", buf);
+		if (inet_ntop(af, &addr->v.a.mask, buf, sizeof(buf)) == NULL)
+			printf(" - ?");
+		else
+			printf(" - %s", buf);
+		break;
+	}
+	case PF_ADDR_ADDRMASK:
+		if (PF_AZERO(&addr->v.a.addr, AF_INET6) &&
+		    PF_AZERO(&addr->v.a.mask, AF_INET6))
+			printf("any");
+		else {
+			char buf[48];
+
+			if (inet_ntop(af, &addr->v.a.addr, buf,
+			    sizeof(buf)) == NULL)
+				printf("?");
+			else
+				printf("%s", buf);
+		}
+		break;
+	case PF_ADDR_NOROUTE:
+		printf("no-route");
+		return;
+	case PF_ADDR_URPFFAILED:
+		printf("urpf-failed");
+		return;
+	case PF_ADDR_RTLABEL:
+		printf("route \"%s\"", addr->v.rtlabelname);
+		return;
+	default:
+		printf("?");
+		return;
+	}
+
+	/* mask if not _both_ address and mask are zero */
+	if (addr->type != PF_ADDR_RANGE &&
+	    !(PF_AZERO(&addr->v.a.addr, AF_INET6) &&
+	    PF_AZERO(&addr->v.a.mask, AF_INET6))) {
+		int bits = unmask(&addr->v.a.mask, af);
+
+		if (bits != (af == AF_INET ? 32 : 128))
+			printf("/%d", bits);
+	}
+}
+
+void
+print_name(struct pf_addr *addr, sa_family_t af)
+{
+	char host[NI_MAXHOST];
+
+	strlcpy(host, "?", sizeof(host));
+	switch (af) {
+	case AF_INET: {
+		struct sockaddr_in sin;
+
+		memset(&sin, 0, sizeof(sin));
+		sin.sin_len = sizeof(sin);
+		sin.sin_family = AF_INET;
+		sin.sin_addr = addr->v4;
+		getnameinfo((struct sockaddr *)&sin, sin.sin_len,
+		    host, sizeof(host), NULL, 0, NI_NOFQDN);
+		break;
+	}
+	case AF_INET6: {
+		struct sockaddr_in6 sin6;
+
+		memset(&sin6, 0, sizeof(sin6));
+		sin6.sin6_len = sizeof(sin6);
+		sin6.sin6_family = AF_INET6;
+		sin6.sin6_addr = addr->v6;
+		getnameinfo((struct sockaddr *)&sin6, sin6.sin6_len,
+		    host, sizeof(host), NULL, 0, NI_NOFQDN);
+		break;
+	}
+	}
+	printf("%s", host);
+}
+
+void
+print_host(struct pf_addr *addr, u_int16_t port, sa_family_t af, int opts)
+{
+	if (opts & PF_OPT_USEDNS)
+		print_name(addr, af);
+	else {
+		struct pf_addr_wrap aw;
+
+		memset(&aw, 0, sizeof(aw));
+		aw.v.a.addr = *addr;
+		if (af == AF_INET)
+			aw.v.a.mask.addr32[0] = 0xffffffff;
+		else {
+			memset(&aw.v.a.mask, 0xff, sizeof(aw.v.a.mask));
+			af = AF_INET6;
+		}
+		print_addr(&aw, af, opts & PF_OPT_VERBOSE2);
+	}
+
+	if (port) {
+		if (af == AF_INET)
+			printf(":%u", ntohs(port));
+		else
+			printf("[%u]", ntohs(port));
+	}
+}
+
+void
+print_seq(struct pfsync_state_peer *p)
+{
+	if (p->seqdiff)
+		printf("[%u + %u](+%u)", ntohl(p->seqlo),
+		    ntohl(p->seqhi) - ntohl(p->seqlo), ntohl(p->seqdiff));
+	else
+		printf("[%u + %u]", ntohl(p->seqlo),
+		    ntohl(p->seqhi) - ntohl(p->seqlo));
+}
+
+void
+print_state(struct pfsync_state *s, int opts)
+{
+	struct pfsync_state_peer *src, *dst;
+	struct pfsync_state_key *sk, *nk;
+	struct protoent *p;
+	int min, sec;
+
+	if (s->direction == PF_OUT) {
+		src = &s->src;
+		dst = &s->dst;
+		sk = &s->key[PF_SK_STACK];
+		nk = &s->key[PF_SK_WIRE];
+		if (s->proto == IPPROTO_ICMP || s->proto == IPPROTO_ICMPV6) 
+			sk->port[0] = nk->port[0];
+	} else {
+		src = &s->dst;
+		dst = &s->src;
+		sk = &s->key[PF_SK_WIRE];
+		nk = &s->key[PF_SK_STACK];
+		if (s->proto == IPPROTO_ICMP || s->proto == IPPROTO_ICMPV6) 
+			sk->port[1] = nk->port[1];
+	}
+	printf("%s ", s->ifname);
+	if ((p = getprotobynumber(s->proto)) != NULL)
+		printf("%s ", p->p_name);
+	else
+		printf("%u ", s->proto);
+
+	print_host(&nk->addr[1], nk->port[1], s->af, opts);
+	if (PF_ANEQ(&nk->addr[1], &sk->addr[1], s->af) ||
+	    nk->port[1] != sk->port[1]) {
+		printf(" (");
+		print_host(&sk->addr[1], sk->port[1], s->af, opts);
+		printf(")");
+	}
+	if (s->direction == PF_OUT)
+		printf(" -> ");
+	else
+		printf(" <- ");
+	print_host(&nk->addr[0], nk->port[0], s->af, opts);
+	if (PF_ANEQ(&nk->addr[0], &sk->addr[0], s->af) ||
+	    nk->port[0] != sk->port[0]) {
+		printf(" (");
+		print_host(&sk->addr[0], sk->port[0], s->af, opts);
+		printf(")");
+	}
+
+	printf("    ");
+	if (s->proto == IPPROTO_TCP) {
+		if (src->state <= TCPS_TIME_WAIT &&
+		    dst->state <= TCPS_TIME_WAIT)
+			printf("   %s:%s\n", tcpstates[src->state],
+			    tcpstates[dst->state]);
+		else if (src->state == PF_TCPS_PROXY_SRC ||
+		    dst->state == PF_TCPS_PROXY_SRC)
+			printf("   PROXY:SRC\n");
+		else if (src->state == PF_TCPS_PROXY_DST ||
+		    dst->state == PF_TCPS_PROXY_DST)
+			printf("   PROXY:DST\n");
+		else
+			printf("   <BAD STATE LEVELS %u:%u>\n",
+			    src->state, dst->state);
+		if (opts & PF_OPT_VERBOSE) {
+			printf("   ");
+			print_seq(src);
+			if (src->wscale && dst->wscale)
+				printf(" wscale %u",
+				    src->wscale & PF_WSCALE_MASK);
+			printf("  ");
+			print_seq(dst);
+			if (src->wscale && dst->wscale)
+				printf(" wscale %u",
+				    dst->wscale & PF_WSCALE_MASK);
+			printf("\n");
+		}
+	} else if (s->proto == IPPROTO_UDP && src->state < PFUDPS_NSTATES &&
+	    dst->state < PFUDPS_NSTATES) {
+		const char *states[] = PFUDPS_NAMES;
+
+		printf("   %s:%s\n", states[src->state], states[dst->state]);
+	} else if (s->proto != IPPROTO_ICMP && src->state < PFOTHERS_NSTATES &&
+	    dst->state < PFOTHERS_NSTATES) {
+		/* XXX ICMP doesn't really have state levels */
+		const char *states[] = PFOTHERS_NAMES;
+
+		printf("   %s:%s\n", states[src->state], states[dst->state]);
+	} else {
+		printf("   %u:%u\n", src->state, dst->state);
+	}
+
+	if (opts & PF_OPT_VERBOSE) {
+		u_int64_t packets[2];
+		u_int64_t bytes[2];
+		u_int32_t creation = ntohl(s->creation);
+		u_int32_t expire = ntohl(s->expire);
+
+		sec = creation % 60;
+		creation /= 60;
+		min = creation % 60;
+		creation /= 60;
+		printf("   age %.2u:%.2u:%.2u", creation, min, sec);
+		sec = expire % 60;
+		expire /= 60;
+		min = expire % 60;
+		expire /= 60;
+		printf(", expires in %.2u:%.2u:%.2u", expire, min, sec);
+
+		bcopy(s->packets[0], &packets[0], sizeof(u_int64_t));
+		bcopy(s->packets[1], &packets[1], sizeof(u_int64_t));
+		bcopy(s->bytes[0], &bytes[0], sizeof(u_int64_t));
+		bcopy(s->bytes[1], &bytes[1], sizeof(u_int64_t));
+		printf(", %llu:%llu pkts, %llu:%llu bytes",
+#ifdef __FreeBSD__
+		    (unsigned long long)betoh64(packets[0]),
+		    (unsigned long long)betoh64(packets[1]),
+		    (unsigned long long)betoh64(bytes[0]),
+		    (unsigned long long)betoh64(bytes[1]));
+#else
+		    betoh64(packets[0]),
+		    betoh64(packets[1]),
+		    betoh64(bytes[0]),
+		    betoh64(bytes[1]));
+#endif
+		if (ntohl(s->anchor) != -1)
+			printf(", anchor %u", ntohl(s->anchor));
+		if (ntohl(s->rule) != -1)
+			printf(", rule %u", ntohl(s->rule));
+		if (s->state_flags & PFSTATE_SLOPPY)
+			printf(", sloppy");
+		if (s->state_flags & PFSTATE_PFLOW)
+			printf(", pflow");
+		if (s->sync_flags & PFSYNC_FLAG_SRCNODE)
+			printf(", source-track");
+		if (s->sync_flags & PFSYNC_FLAG_NATSRCNODE)
+			printf(", sticky-address");
+		printf("\n");
+	}
+	if (opts & PF_OPT_VERBOSE2) {
+		u_int64_t id;
+
+		bcopy(&s->id, &id, sizeof(u_int64_t));
+		printf("   id: %016llx creatorid: %08x",
+#ifdef __FreeBSD__
+		    (unsigned long long)betoh64(id), ntohl(s->creatorid));
+#else
+		    betoh64(id), ntohl(s->creatorid));
+#endif
+		printf("\n");
+	}
+}
+
+int
+unmask(struct pf_addr *m, sa_family_t af)
+{
+	int i = 31, j = 0, b = 0;
+	u_int32_t tmp;
+
+	while (j < 4 && m->addr32[j] == 0xffffffff) {
+		b += 32;
+		j++;
+	}
+	if (j < 4) {
+		tmp = ntohl(m->addr32[j]);
+		for (i = 31; tmp & (1 << i); --i)
+			b++;
+	}
+	return (b);
+}
diff --git a/freebsd/contrib/pf/pfctl/pfctl.c b/freebsd/contrib/pf/pfctl/pfctl.c
new file mode 100644
index 0000000..2d57839
--- /dev/null
+++ b/freebsd/contrib/pf/pfctl/pfctl.c
@@ -0,0 +1,2410 @@
+#include <machine/rtems-bsd-user-space.h>
+
+/*	$OpenBSD: pfctl.c,v 1.278 2008/08/31 20:18:17 jmc Exp $ */
+
+/*
+ * Copyright (c) 2001 Daniel Hartmeier
+ * Copyright (c) 2002,2003 Henning Brauer
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *    - Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *    - 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 COPYRIGHT HOLDERS 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
+ * COPYRIGHT HOLDERS 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>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+
+#ifdef __FreeBSD__
+#include <sys/endian.h>
+#endif
+
+#include <net/if.h>
+#include <netinet/in.h>
+#include <net/pfvar.h>
+#include <arpa/inet.h>
+#include <altq/altq.h>
+#include <sys/sysctl.h>
+
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "pfctl_parser.h"
+#include "pfctl.h"
+
+void	 usage(void);
+int	 pfctl_enable(int, int);
+int	 pfctl_disable(int, int);
+int	 pfctl_clear_stats(int, int);
+int	 pfctl_clear_interface_flags(int, int);
+int	 pfctl_clear_rules(int, int, char *);
+int	 pfctl_clear_nat(int, int, char *);
+int	 pfctl_clear_altq(int, int);
+int	 pfctl_clear_src_nodes(int, int);
+int	 pfctl_clear_states(int, const char *, int);
+void	 pfctl_addrprefix(char *, struct pf_addr *);
+int	 pfctl_kill_src_nodes(int, const char *, int);
+int	 pfctl_net_kill_states(int, const char *, int);
+int	 pfctl_label_kill_states(int, const char *, int);
+int	 pfctl_id_kill_states(int, const char *, int);
+void	 pfctl_init_options(struct pfctl *);
+int	 pfctl_load_options(struct pfctl *);
+int	 pfctl_load_limit(struct pfctl *, unsigned int, unsigned int);
+int	 pfctl_load_timeout(struct pfctl *, unsigned int, unsigned int);
+int	 pfctl_load_debug(struct pfctl *, unsigned int);
+int	 pfctl_load_logif(struct pfctl *, char *);
+int	 pfctl_load_hostid(struct pfctl *, unsigned int);
+int	 pfctl_get_pool(int, struct pf_pool *, u_int32_t, u_int32_t, int,
+	    char *);
+void	 pfctl_print_rule_counters(struct pf_rule *, int);
+int	 pfctl_show_rules(int, char *, int, enum pfctl_show, char *, int);
+int	 pfctl_show_nat(int, int, char *);
+int	 pfctl_show_src_nodes(int, int);
+int	 pfctl_show_states(int, const char *, int);
+int	 pfctl_show_status(int, int);
+int	 pfctl_show_timeouts(int, int);
+int	 pfctl_show_limits(int, int);
+void	 pfctl_debug(int, u_int32_t, int);
+int	 pfctl_test_altqsupport(int, int);
+int	 pfctl_show_anchors(int, int, char *);
+int	 pfctl_ruleset_trans(struct pfctl *, char *, struct pf_anchor *);
+int	 pfctl_load_ruleset(struct pfctl *, char *,
+		struct pf_ruleset *, int, int);
+int	 pfctl_load_rule(struct pfctl *, char *, struct pf_rule *, int);
+const char	*pfctl_lookup_option(char *, const char **);
+
+struct pf_anchor_global	 pf_anchors;
+struct pf_anchor	 pf_main_anchor;
+
+const char	*clearopt;
+char		*rulesopt;
+const char	*showopt;
+const char	*debugopt;
+char		*anchoropt;
+const char	*optiopt = NULL;
+char		*pf_device = "/dev/pf";
+char		*ifaceopt;
+char		*tableopt;
+const char	*tblcmdopt;
+int		 src_node_killers;
+char		*src_node_kill[2];
+int		 state_killers;
+char		*state_kill[2];
+int		 loadopt;
+int		 altqsupport;
+
+int		 dev = -1;
+int		 first_title = 1;
+int		 labels = 0;
+
+#define INDENT(d, o)	do {						\
+				if (o) {				\
+					int i;				\
+					for (i=0; i < d; i++)		\
+						printf("  ");		\
+				}					\
+			} while (0);					\
+
+
+static const struct {
+	const char	*name;
+	int		index;
+} pf_limits[] = {
+	{ "states",		PF_LIMIT_STATES },
+	{ "src-nodes",		PF_LIMIT_SRC_NODES },
+	{ "frags",		PF_LIMIT_FRAGS },
+	{ "tables",		PF_LIMIT_TABLES },
+	{ "table-entries",	PF_LIMIT_TABLE_ENTRIES },
+	{ NULL,			0 }
+};
+
+struct pf_hint {
+	const char	*name;
+	int		timeout;
+};
+static const struct pf_hint pf_hint_normal[] = {
+	{ "tcp.first",		2 * 60 },
+	{ "tcp.opening",	30 },
+	{ "tcp.established",	24 * 60 * 60 },
+	{ "tcp.closing",	15 * 60 },
+	{ "tcp.finwait",	45 },
+	{ "tcp.closed",		90 },
+	{ "tcp.tsdiff",		30 },
+	{ NULL,			0 }
+};
+static const struct pf_hint pf_hint_satellite[] = {
+	{ "tcp.first",		3 * 60 },
+	{ "tcp.opening",	30 + 5 },
+	{ "tcp.established",	24 * 60 * 60 },
+	{ "tcp.closing",	15 * 60 + 5 },
+	{ "tcp.finwait",	45 + 5 },
+	{ "tcp.closed",		90 + 5 },
+	{ "tcp.tsdiff",		60 },
+	{ NULL,			0 }
+};
+static const struct pf_hint pf_hint_conservative[] = {
+	{ "tcp.first",		60 * 60 },
+	{ "tcp.opening",	15 * 60 },
+	{ "tcp.established",	5 * 24 * 60 * 60 },
+	{ "tcp.closing",	60 * 60 },
+	{ "tcp.finwait",	10 * 60 },
+	{ "tcp.closed",		3 * 60 },
+	{ "tcp.tsdiff",		60 },
+	{ NULL,			0 }
+};
+static const struct pf_hint pf_hint_aggressive[] = {
+	{ "tcp.first",		30 },
+	{ "tcp.opening",	5 },
+	{ "tcp.established",	5 * 60 * 60 },
+	{ "tcp.closing",	60 },
+	{ "tcp.finwait",	30 },
+	{ "tcp.closed",		30 },
+	{ "tcp.tsdiff",		10 },
+	{ NULL,			0 }
+};
+
+static const struct {
+	const char *name;
+	const struct pf_hint *hint;
+} pf_hints[] = {
+	{ "normal",		pf_hint_normal },
+	{ "satellite",		pf_hint_satellite },
+	{ "high-latency",	pf_hint_satellite },
+	{ "conservative",	pf_hint_conservative },
+	{ "aggressive",		pf_hint_aggressive },
+	{ NULL,			NULL }
+};
+
+static const char *clearopt_list[] = {
+	"nat", "queue", "rules", "Sources",
+	"states", "info", "Tables", "osfp", "all", NULL
+};
+
+static const char *showopt_list[] = {
+	"nat", "queue", "rules", "Anchors", "Sources", "states", "info",
+	"Interfaces", "labels", "timeouts", "memory", "Tables", "osfp",
+	"all", NULL
+};
+
+static const char *tblcmdopt_list[] = {
+	"kill", "flush", "add", "delete", "load", "replace", "show",
+	"test", "zero", "expire", NULL
+};
+
+static const char *debugopt_list[] = {
+	"none", "urgent", "misc", "loud", NULL
+};
+
+static const char *optiopt_list[] = {
+	"none", "basic", "profile", NULL
+};
+
+void
+usage(void)
+{
+	extern char *__progname;
+
+	fprintf(stderr, "usage: %s [-AdeghmNnOPqRrvz] ", __progname);
+	fprintf(stderr, "[-a anchor] [-D macro=value] [-F modifier]\n");
+	fprintf(stderr, "\t[-f file] [-i interface] [-K host | network]\n");
+	fprintf(stderr, "\t[-k host | network | label | id] ");
+	fprintf(stderr, "[-o level] [-p device]\n");
+	fprintf(stderr, "\t[-s modifier] ");
+	fprintf(stderr, "[-t table -T command [address ...]] [-x level]\n");
+	exit(1);
+}
+
+int
+pfctl_enable(int dev, int opts)
+{
+	if (ioctl(dev, DIOCSTART)) {
+		if (errno == EEXIST)
+			errx(1, "pf already enabled");
+#ifdef __FreeBSD__
+		else if (errno == ESRCH)
+			errx(1, "pfil registeration failed");
+#endif
+		else
+			err(1, "DIOCSTART");
+	}
+	if ((opts & PF_OPT_QUIET) == 0)
+		fprintf(stderr, "pf enabled\n");
+
+	if (altqsupport && ioctl(dev, DIOCSTARTALTQ))
+		if (errno != EEXIST)
+			err(1, "DIOCSTARTALTQ");
+
+	return (0);
+}
+
+int
+pfctl_disable(int dev, int opts)
+{
+	if (ioctl(dev, DIOCSTOP)) {
+		if (errno == ENOENT)
+			errx(1, "pf not enabled");
+		else
+			err(1, "DIOCSTOP");
+	}
+	if ((opts & PF_OPT_QUIET) == 0)
+		fprintf(stderr, "pf disabled\n");
+
+	if (altqsupport && ioctl(dev, DIOCSTOPALTQ))
+			if (errno != ENOENT)
+				err(1, "DIOCSTOPALTQ");
+
+	return (0);
+}
+
+int
+pfctl_clear_stats(int dev, int opts)
+{
+	if (ioctl(dev, DIOCCLRSTATUS))
+		err(1, "DIOCCLRSTATUS");
+	if ((opts & PF_OPT_QUIET) == 0)
+		fprintf(stderr, "pf: statistics cleared\n");
+	return (0);
+}
+
+int
+pfctl_clear_interface_flags(int dev, int opts)
+{
+	struct pfioc_iface	pi;
+
+	if ((opts & PF_OPT_NOACTION) == 0) {
+		bzero(&pi, sizeof(pi));
+		pi.pfiio_flags = PFI_IFLAG_SKIP;
+
+		if (ioctl(dev, DIOCCLRIFFLAG, &pi))
+			err(1, "DIOCCLRIFFLAG");
+		if ((opts & PF_OPT_QUIET) == 0)
+			fprintf(stderr, "pf: interface flags reset\n");
+	}
+	return (0);
+}
+
+int
+pfctl_clear_rules(int dev, int opts, char *anchorname)
+{
+	struct pfr_buffer t;
+
+	memset(&t, 0, sizeof(t));
+	t.pfrb_type = PFRB_TRANS;
+	if (pfctl_add_trans(&t, PF_RULESET_SCRUB, anchorname) ||
+	    pfctl_add_trans(&t, PF_RULESET_FILTER, anchorname) ||
+	    pfctl_trans(dev, &t, DIOCXBEGIN, 0) ||
+	    pfctl_trans(dev, &t, DIOCXCOMMIT, 0))
+		err(1, "pfctl_clear_rules");
+	if ((opts & PF_OPT_QUIET) == 0)
+		fprintf(stderr, "rules cleared\n");
+	return (0);
+}
+
+int
+pfctl_clear_nat(int dev, int opts, char *anchorname)
+{
+	struct pfr_buffer t;
+
+	memset(&t, 0, sizeof(t));
+	t.pfrb_type = PFRB_TRANS;
+	if (pfctl_add_trans(&t, PF_RULESET_NAT, anchorname) ||
+	    pfctl_add_trans(&t, PF_RULESET_BINAT, anchorname) ||
+	    pfctl_add_trans(&t, PF_RULESET_RDR, anchorname) ||
+	    pfctl_trans(dev, &t, DIOCXBEGIN, 0) ||
+	    pfctl_trans(dev, &t, DIOCXCOMMIT, 0))
+		err(1, "pfctl_clear_nat");
+	if ((opts & PF_OPT_QUIET) == 0)
+		fprintf(stderr, "nat cleared\n");
+	return (0);
+}
+
+int
+pfctl_clear_altq(int dev, int opts)
+{
+	struct pfr_buffer t;
+
+	if (!altqsupport)
+		return (-1);
+	memset(&t, 0, sizeof(t));
+	t.pfrb_type = PFRB_TRANS;
+	if (pfctl_add_trans(&t, PF_RULESET_ALTQ, "") ||
+	    pfctl_trans(dev, &t, DIOCXBEGIN, 0) ||
+	    pfctl_trans(dev, &t, DIOCXCOMMIT, 0))
+		err(1, "pfctl_clear_altq");
+	if ((opts & PF_OPT_QUIET) == 0)
+		fprintf(stderr, "altq cleared\n");
+	return (0);
+}
+
+int
+pfctl_clear_src_nodes(int dev, int opts)
+{
+	if (ioctl(dev, DIOCCLRSRCNODES))
+		err(1, "DIOCCLRSRCNODES");
+	if ((opts & PF_OPT_QUIET) == 0)
+		fprintf(stderr, "source tracking entries cleared\n");
+	return (0);
+}
+
+int
+pfctl_clear_states(int dev, const char *iface, int opts)
+{
+	struct pfioc_state_kill psk;
+
+	memset(&psk, 0, sizeof(psk));
+	if (iface != NULL && strlcpy(psk.psk_ifname, iface,
+	    sizeof(psk.psk_ifname)) >= sizeof(psk.psk_ifname))
+		errx(1, "invalid interface: %s", iface);
+
+	if (ioctl(dev, DIOCCLRSTATES, &psk))
+		err(1, "DIOCCLRSTATES");
+	if ((opts & PF_OPT_QUIET) == 0)
+		fprintf(stderr, "%d states cleared\n", psk.psk_killed);
+	return (0);
+}
+
+void
+pfctl_addrprefix(char *addr, struct pf_addr *mask)
+{
+	char *p;
+	const char *errstr;
+	int prefix, ret_ga, q, r;
+	struct addrinfo hints, *res;
+
+	if ((p = strchr(addr, '/')) == NULL)
+		return;
+
+	*p++ = '\0';
+	prefix = strtonum(p, 0, 128, &errstr);
+	if (errstr)
+		errx(1, "prefix is %s: %s", errstr, p);
+
+	bzero(&hints, sizeof(hints));
+	/* prefix only with numeric addresses */
+	hints.ai_flags |= AI_NUMERICHOST;
+
+	if ((ret_ga = getaddrinfo(addr, NULL, &hints, &res))) {
+		errx(1, "getaddrinfo: %s", gai_strerror(ret_ga));
+		/* NOTREACHED */
+	}
+
+	if (res->ai_family == AF_INET && prefix > 32)
+		errx(1, "prefix too long for AF_INET");
+	else if (res->ai_family == AF_INET6 && prefix > 128)
+		errx(1, "prefix too long for AF_INET6");
+
+	q = prefix >> 3;
+	r = prefix & 7;
+	switch (res->ai_family) {
+	case AF_INET:
+		bzero(&mask->v4, sizeof(mask->v4));
+		mask->v4.s_addr = htonl((u_int32_t)
+		    (0xffffffffffULL << (32 - prefix)));
+		break;
+	case AF_INET6:
+		bzero(&mask->v6, sizeof(mask->v6));
+		if (q > 0)
+			memset((void *)&mask->v6, 0xff, q);
+		if (r > 0)
+			*((u_char *)&mask->v6 + q) =
+			    (0xff00 >> r) & 0xff;
+		break;
+	}
+	freeaddrinfo(res);
+}
+
+int
+pfctl_kill_src_nodes(int dev, const char *iface, int opts)
+{
+	struct pfioc_src_node_kill psnk;
+	struct addrinfo *res[2], *resp[2];
+	struct sockaddr last_src, last_dst;
+	int killed, sources, dests;
+	int ret_ga;
+
+	killed = sources = dests = 0;
+
+	memset(&psnk, 0, sizeof(psnk));
+	memset(&psnk.psnk_src.addr.v.a.mask, 0xff,
+	    sizeof(psnk.psnk_src.addr.v.a.mask));
+	memset(&last_src, 0xff, sizeof(last_src));
+	memset(&last_dst, 0xff, sizeof(last_dst));
+
+	pfctl_addrprefix(src_node_kill[0], &psnk.psnk_src.addr.v.a.mask);
+
+	if ((ret_ga = getaddrinfo(src_node_kill[0], NULL, NULL, &res[0]))) {
+		errx(1, "getaddrinfo: %s", gai_strerror(ret_ga));
+		/* NOTREACHED */
+	}
+	for (resp[0] = res[0]; resp[0]; resp[0] = resp[0]->ai_next) {
+		if (resp[0]->ai_addr == NULL)
+			continue;
+		/* We get lots of duplicates.  Catch the easy ones */
+		if (memcmp(&last_src, resp[0]->ai_addr, sizeof(last_src)) == 0)
+			continue;
+		last_src = *(struct sockaddr *)resp[0]->ai_addr;
+
+		psnk.psnk_af = resp[0]->ai_family;
+		sources++;
+
+		if (psnk.psnk_af == AF_INET)
+			psnk.psnk_src.addr.v.a.addr.v4 =
+			    ((struct sockaddr_in *)resp[0]->ai_addr)->sin_addr;
+		else if (psnk.psnk_af == AF_INET6)
+			psnk.psnk_src.addr.v.a.addr.v6 =
+			    ((struct sockaddr_in6 *)resp[0]->ai_addr)->
+			    sin6_addr;
+		else
+			errx(1, "Unknown address family %d", psnk.psnk_af);
+
+		if (src_node_killers > 1) {
+			dests = 0;
+			memset(&psnk.psnk_dst.addr.v.a.mask, 0xff,
+			    sizeof(psnk.psnk_dst.addr.v.a.mask));
+			memset(&last_dst, 0xff, sizeof(last_dst));
+			pfctl_addrprefix(src_node_kill[1],
+			    &psnk.psnk_dst.addr.v.a.mask);
+			if ((ret_ga = getaddrinfo(src_node_kill[1], NULL, NULL,
+			    &res[1]))) {
+				errx(1, "getaddrinfo: %s",
+				    gai_strerror(ret_ga));
+				/* NOTREACHED */
+			}
+			for (resp[1] = res[1]; resp[1];
+			    resp[1] = resp[1]->ai_next) {
+				if (resp[1]->ai_addr == NULL)
+					continue;
+				if (psnk.psnk_af != resp[1]->ai_family)
+					continue;
+
+				if (memcmp(&last_dst, resp[1]->ai_addr,
+				    sizeof(last_dst)) == 0)
+					continue;
+				last_dst = *(struct sockaddr *)resp[1]->ai_addr;
+
+				dests++;
+
+				if (psnk.psnk_af == AF_INET)
+					psnk.psnk_dst.addr.v.a.addr.v4 =
+					    ((struct sockaddr_in *)resp[1]->
+					    ai_addr)->sin_addr;
+				else if (psnk.psnk_af == AF_INET6)
+					psnk.psnk_dst.addr.v.a.addr.v6 =
+					    ((struct sockaddr_in6 *)resp[1]->
+					    ai_addr)->sin6_addr;
+				else
+					errx(1, "Unknown address family %d",
+					    psnk.psnk_af);
+
+				if (ioctl(dev, DIOCKILLSRCNODES, &psnk))
+					err(1, "DIOCKILLSRCNODES");
+				killed += psnk.psnk_killed;
+			}
+			freeaddrinfo(res[1]);
+		} else {
+			if (ioctl(dev, DIOCKILLSRCNODES, &psnk))
+				err(1, "DIOCKILLSRCNODES");
+			killed += psnk.psnk_killed;
+		}
+	}
+
+	freeaddrinfo(res[0]);
+
+	if ((opts & PF_OPT_QUIET) == 0)
+		fprintf(stderr, "killed %d src nodes from %d sources and %d "
+		    "destinations\n", killed, sources, dests);
+	return (0);
+}
+
+int
+pfctl_net_kill_states(int dev, const char *iface, int opts)
+{
+	struct pfioc_state_kill psk;
+	struct addrinfo *res[2], *resp[2];
+	struct sockaddr last_src, last_dst;
+	int killed, sources, dests;
+	int ret_ga;
+
+	killed = sources = dests = 0;
+
+	memset(&psk, 0, sizeof(psk));
+	memset(&psk.psk_src.addr.v.a.mask, 0xff,
+	    sizeof(psk.psk_src.addr.v.a.mask));
+	memset(&last_src, 0xff, sizeof(last_src));
+	memset(&last_dst, 0xff, sizeof(last_dst));
+	if (iface != NULL && strlcpy(psk.psk_ifname, iface,
+	    sizeof(psk.psk_ifname)) >= sizeof(psk.psk_ifname))
+		errx(1, "invalid interface: %s", iface);
+
+	pfctl_addrprefix(state_kill[0], &psk.psk_src.addr.v.a.mask);
+
+	if ((ret_ga = getaddrinfo(state_kill[0], NULL, NULL, &res[0]))) {
+		errx(1, "getaddrinfo: %s", gai_strerror(ret_ga));
+		/* NOTREACHED */
+	}
+	for (resp[0] = res[0]; resp[0]; resp[0] = resp[0]->ai_next) {
+		if (resp[0]->ai_addr == NULL)
+			continue;
+		/* We get lots of duplicates.  Catch the easy ones */
+		if (memcmp(&last_src, resp[0]->ai_addr, sizeof(last_src)) == 0)
+			continue;
+		last_src = *(struct sockaddr *)resp[0]->ai_addr;
+
+		psk.psk_af = resp[0]->ai_family;
+		sources++;
+
+		if (psk.psk_af == AF_INET)
+			psk.psk_src.addr.v.a.addr.v4 =
+			    ((struct sockaddr_in *)resp[0]->ai_addr)->sin_addr;
+		else if (psk.psk_af == AF_INET6)
+			psk.psk_src.addr.v.a.addr.v6 =
+			    ((struct sockaddr_in6 *)resp[0]->ai_addr)->
+			    sin6_addr;
+		else
+			errx(1, "Unknown address family %d", psk.psk_af);
+
+		if (state_killers > 1) {
+			dests = 0;
+			memset(&psk.psk_dst.addr.v.a.mask, 0xff,
+			    sizeof(psk.psk_dst.addr.v.a.mask));
+			memset(&last_dst, 0xff, sizeof(last_dst));
+			pfctl_addrprefix(state_kill[1],
+			    &psk.psk_dst.addr.v.a.mask);
+			if ((ret_ga = getaddrinfo(state_kill[1], NULL, NULL,
+			    &res[1]))) {
+				errx(1, "getaddrinfo: %s",
+				    gai_strerror(ret_ga));
+				/* NOTREACHED */
+			}
+			for (resp[1] = res[1]; resp[1];
+			    resp[1] = resp[1]->ai_next) {
+				if (resp[1]->ai_addr == NULL)
+					continue;
+				if (psk.psk_af != resp[1]->ai_family)
+					continue;
+
+				if (memcmp(&last_dst, resp[1]->ai_addr,
+				    sizeof(last_dst)) == 0)
+					continue;
+				last_dst = *(struct sockaddr *)resp[1]->ai_addr;
+
+				dests++;
+
+				if (psk.psk_af == AF_INET)
+					psk.psk_dst.addr.v.a.addr.v4 =
+					    ((struct sockaddr_in *)resp[1]->
+					    ai_addr)->sin_addr;
+				else if (psk.psk_af == AF_INET6)
+					psk.psk_dst.addr.v.a.addr.v6 =
+					    ((struct sockaddr_in6 *)resp[1]->
+					    ai_addr)->sin6_addr;
+				else
+					errx(1, "Unknown address family %d",
+					    psk.psk_af);
+
+				if (ioctl(dev, DIOCKILLSTATES, &psk))
+					err(1, "DIOCKILLSTATES");
+				killed += psk.psk_killed;
+			}
+			freeaddrinfo(res[1]);
+		} else {
+			if (ioctl(dev, DIOCKILLSTATES, &psk))
+				err(1, "DIOCKILLSTATES");
+			killed += psk.psk_killed;
+		}
+	}
+
+	freeaddrinfo(res[0]);
+
+	if ((opts & PF_OPT_QUIET) == 0)
+		fprintf(stderr, "killed %d states from %d sources and %d "
+		    "destinations\n", killed, sources, dests);
+	return (0);
+}
+
+int
+pfctl_label_kill_states(int dev, const char *iface, int opts)
+{
+	struct pfioc_state_kill psk;
+
+	if (state_killers != 2 || (strlen(state_kill[1]) == 0)) {
+		warnx("no label specified");
+		usage();
+	}
+	memset(&psk, 0, sizeof(psk));
+	if (iface != NULL && strlcpy(psk.psk_ifname, iface,
+	    sizeof(psk.psk_ifname)) >= sizeof(psk.psk_ifname))
+		errx(1, "invalid interface: %s", iface);
+
+	if (strlcpy(psk.psk_label, state_kill[1], sizeof(psk.psk_label)) >=
+	    sizeof(psk.psk_label))
+		errx(1, "label too long: %s", state_kill[1]);
+
+	if (ioctl(dev, DIOCKILLSTATES, &psk))
+		err(1, "DIOCKILLSTATES");
+
+	if ((opts & PF_OPT_QUIET) == 0)
+		fprintf(stderr, "killed %d states\n", psk.psk_killed);
+
+	return (0);
+}
+
+int
+pfctl_id_kill_states(int dev, const char *iface, int opts)
+{
+	struct pfioc_state_kill psk;
+	
+	if (state_killers != 2 || (strlen(state_kill[1]) == 0)) {
+		warnx("no id specified");
+		usage();
+	}
+
+	memset(&psk, 0, sizeof(psk));
+	if ((sscanf(state_kill[1], "%jx/%x",
+	    &psk.psk_pfcmp.id, &psk.psk_pfcmp.creatorid)) == 2)
+		HTONL(psk.psk_pfcmp.creatorid);
+	else if ((sscanf(state_kill[1], "%jx", &psk.psk_pfcmp.id)) == 1) {
+		psk.psk_pfcmp.creatorid = 0;
+	} else {
+		warnx("wrong id format specified");
+		usage();
+	}
+	if (psk.psk_pfcmp.id == 0) {
+		warnx("cannot kill id 0");
+		usage();
+	}
+
+	psk.psk_pfcmp.id = htobe64(psk.psk_pfcmp.id);
+	if (ioctl(dev, DIOCKILLSTATES, &psk))
+		err(1, "DIOCKILLSTATES");
+
+	if ((opts & PF_OPT_QUIET) == 0)
+		fprintf(stderr, "killed %d states\n", psk.psk_killed);
+
+	return (0);
+}
+
+int
+pfctl_get_pool(int dev, struct pf_pool *pool, u_int32_t nr,
+    u_int32_t ticket, int r_action, char *anchorname)
+{
+	struct pfioc_pooladdr pp;
+	struct pf_pooladdr *pa;
+	u_int32_t pnr, mpnr;
+
+	memset(&pp, 0, sizeof(pp));
+	memcpy(pp.anchor, anchorname, sizeof(pp.anchor));
+	pp.r_action = r_action;
+	pp.r_num = nr;
+	pp.ticket = ticket;
+	if (ioctl(dev, DIOCGETADDRS, &pp)) {
+		warn("DIOCGETADDRS");
+		return (-1);
+	}
+	mpnr = pp.nr;
+	TAILQ_INIT(&pool->list);
+	for (pnr = 0; pnr < mpnr; ++pnr) {
+		pp.nr = pnr;
+		if (ioctl(dev, DIOCGETADDR, &pp)) {
+			warn("DIOCGETADDR");
+			return (-1);
+		}
+		pa = calloc(1, sizeof(struct pf_pooladdr));
+		if (pa == NULL)
+			err(1, "calloc");
+		bcopy(&pp.addr, pa, sizeof(struct pf_pooladdr));
+		TAILQ_INSERT_TAIL(&pool->list, pa, entries);
+	}
+
+	return (0);
+}
+
+void
+pfctl_move_pool(struct pf_pool *src, struct pf_pool *dst)
+{
+	struct pf_pooladdr *pa;
+
+	while ((pa = TAILQ_FIRST(&src->list)) != NULL) {
+		TAILQ_REMOVE(&src->list, pa, entries);
+		TAILQ_INSERT_TAIL(&dst->list, pa, entries);
+	}
+}
+
+void
+pfctl_clear_pool(struct pf_pool *pool)
+{
+	struct pf_pooladdr *pa;
+
+	while ((pa = TAILQ_FIRST(&pool->list)) != NULL) {
+		TAILQ_REMOVE(&pool->list, pa, entries);
+		free(pa);
+	}
+}
+
+void
+pfctl_print_rule_counters(struct pf_rule *rule, int opts)
+{
+	if (opts & PF_OPT_DEBUG) {
+		const char *t[PF_SKIP_COUNT] = { "i", "d", "f",
+		    "p", "sa", "sp", "da", "dp" };
+		int i;
+
+		printf("  [ Skip steps: ");
+		for (i = 0; i < PF_SKIP_COUNT; ++i) {
+			if (rule->skip[i].nr == rule->nr + 1)
+				continue;
+			printf("%s=", t[i]);
+			if (rule->skip[i].nr == -1)
+				printf("end ");
+			else
+				printf("%u ", rule->skip[i].nr);
+		}
+		printf("]\n");
+
+		printf("  [ queue: qname=%s qid=%u pqname=%s pqid=%u ]\n",
+		    rule->qname, rule->qid, rule->pqname, rule->pqid);
+	}
+	if (opts & PF_OPT_VERBOSE) {
+		printf("  [ Evaluations: %-8llu  Packets: %-8llu  "
+			    "Bytes: %-10llu  States: %-6u]\n",
+			    (unsigned long long)rule->evaluations,
+			    (unsigned long long)(rule->packets[0] +
+			    rule->packets[1]),
+			    (unsigned long long)(rule->bytes[0] +
+			    rule->bytes[1]), rule->states_cur);
+		if (!(opts & PF_OPT_DEBUG))
+			printf("  [ Inserted: uid %u pid %u "
+			    "State Creations: %-6u]\n",
+			    (unsigned)rule->cuid, (unsigned)rule->cpid,
+			    rule->states_tot);
+	}
+}
+
+void
+pfctl_print_title(char *title)
+{
+	if (!first_title)
+		printf("\n");
+	first_title = 0;
+	printf("%s\n", title);
+}
+
+int
+pfctl_show_rules(int dev, char *path, int opts, enum pfctl_show format,
+    char *anchorname, int depth)
+{
+	struct pfioc_rule pr;
+	u_int32_t nr, mnr, header = 0;
+	int rule_numbers = opts & (PF_OPT_VERBOSE2 | PF_OPT_DEBUG);
+	int numeric = opts & PF_OPT_NUMERIC;
+	int len = strlen(path);
+	int brace;
+	char *p;
+
+	if (path[0])
+		snprintf(&path[len], MAXPATHLEN - len, "/%s", anchorname);
+	else
+		snprintf(&path[len], MAXPATHLEN - len, "%s", anchorname);
+
+	memset(&pr, 0, sizeof(pr));
+	memcpy(pr.anchor, path, sizeof(pr.anchor));
+	if (opts & PF_OPT_SHOWALL) {
+		pr.rule.action = PF_PASS;
+		if (ioctl(dev, DIOCGETRULES, &pr)) {
+			warn("DIOCGETRULES");
+			goto error;
+		}
+		header++;
+	}
+	pr.rule.action = PF_SCRUB;
+	if (ioctl(dev, DIOCGETRULES, &pr)) {
+		warn("DIOCGETRULES");
+		goto error;
+	}
+	if (opts & PF_OPT_SHOWALL) {
+		if (format == PFCTL_SHOW_RULES && (pr.nr > 0 || header))
+			pfctl_print_title("FILTER RULES:");
+		else if (format == PFCTL_SHOW_LABELS && labels)
+			pfctl_print_title("LABEL COUNTERS:");
+	}
+	mnr = pr.nr;
+	if (opts & PF_OPT_CLRRULECTRS)
+		pr.action = PF_GET_CLR_CNTR;
+
+	for (nr = 0; nr < mnr; ++nr) {
+		pr.nr = nr;
+		if (ioctl(dev, DIOCGETRULE, &pr)) {
+			warn("DIOCGETRULE");
+			goto error;
+		}
+
+		if (pfctl_get_pool(dev, &pr.rule.rpool,
+		    nr, pr.ticket, PF_SCRUB, path) != 0)
+			goto error;
+
+		switch (format) {
+		case PFCTL_SHOW_LABELS:
+			break;
+		case PFCTL_SHOW_RULES:
+			if (pr.rule.label[0] && (opts & PF_OPT_SHOWALL))
+				labels = 1;
+			print_rule(&pr.rule, pr.anchor_call, rule_numbers, numeric);
+			printf("\n");
+			pfctl_print_rule_counters(&pr.rule, opts);
+			break;
+		case PFCTL_SHOW_NOTHING:
+			break;
+		}
+		pfctl_clear_pool(&pr.rule.rpool);
+	}
+	pr.rule.action = PF_PASS;
+	if (ioctl(dev, DIOCGETRULES, &pr)) {
+		warn("DIOCGETRULES");
+		goto error;
+	}
+	mnr = pr.nr;
+	for (nr = 0; nr < mnr; ++nr) {
+		pr.nr = nr;
+		if (ioctl(dev, DIOCGETRULE, &pr)) {
+			warn("DIOCGETRULE");
+			goto error;
+		}
+
+		if (pfctl_get_pool(dev, &pr.rule.rpool,
+		    nr, pr.ticket, PF_PASS, path) != 0)
+			goto error;
+
+		switch (format) {
+		case PFCTL_SHOW_LABELS:
+			if (pr.rule.label[0]) {
+				printf("%s %llu %llu %llu %llu"
+				    " %llu %llu %llu %llu\n",
+				    pr.rule.label,
+				    (unsigned long long)pr.rule.evaluations,
+				    (unsigned long long)(pr.rule.packets[0] +
+				    pr.rule.packets[1]),
+				    (unsigned long long)(pr.rule.bytes[0] +
+				    pr.rule.bytes[1]),
+				    (unsigned long long)pr.rule.packets[0],
+				    (unsigned long long)pr.rule.bytes[0],
+				    (unsigned long long)pr.rule.packets[1],
+				    (unsigned long long)pr.rule.bytes[1],
+				    (unsigned long long)pr.rule.states_tot);
+			}
+			break;
+		case PFCTL_SHOW_RULES:
+			brace = 0;
+			if (pr.rule.label[0] && (opts & PF_OPT_SHOWALL))
+				labels = 1;
+			INDENT(depth, !(opts & PF_OPT_VERBOSE));
+			if (pr.anchor_call[0] &&
+			   ((((p = strrchr(pr.anchor_call, '_')) != NULL) &&
+			   ((void *)p == (void *)pr.anchor_call ||
+			   *(--p) == '/')) || (opts & PF_OPT_RECURSE))) {
+				brace++;
+				if ((p = strrchr(pr.anchor_call, '/')) !=
+				    NULL)
+					p++;
+				else
+					p = &pr.anchor_call[0];
+			} else
+				p = &pr.anchor_call[0];
+		
+			print_rule(&pr.rule, p, rule_numbers, numeric);
+			if (brace)
+				printf(" {\n");
+			else
+				printf("\n");
+			pfctl_print_rule_counters(&pr.rule, opts);
+			if (brace) { 
+				pfctl_show_rules(dev, path, opts, format,
+				    p, depth + 1);
+				INDENT(depth, !(opts & PF_OPT_VERBOSE));
+				printf("}\n");
+			}
+			break;
+		case PFCTL_SHOW_NOTHING:
+			break;
+		}
+		pfctl_clear_pool(&pr.rule.rpool);
+	}
+	path[len] = '\0';
+	return (0);
+
+ error:
+	path[len] = '\0';
+	return (-1);
+}
+
+int
+pfctl_show_nat(int dev, int opts, char *anchorname)
+{
+	struct pfioc_rule pr;
+	u_int32_t mnr, nr;
+	static int nattype[3] = { PF_NAT, PF_RDR, PF_BINAT };
+	int i, dotitle = opts & PF_OPT_SHOWALL;
+
+	memset(&pr, 0, sizeof(pr));
+	memcpy(pr.anchor, anchorname, sizeof(pr.anchor));
+	for (i = 0; i < 3; i++) {
+		pr.rule.action = nattype[i];
+		if (ioctl(dev, DIOCGETRULES, &pr)) {
+			warn("DIOCGETRULES");
+			return (-1);
+		}
+		mnr = pr.nr;
+		for (nr = 0; nr < mnr; ++nr) {
+			pr.nr = nr;
+			if (ioctl(dev, DIOCGETRULE, &pr)) {
+				warn("DIOCGETRULE");
+				return (-1);
+			}
+			if (pfctl_get_pool(dev, &pr.rule.rpool, nr,
+			    pr.ticket, nattype[i], anchorname) != 0)
+				return (-1);
+			if (dotitle) {
+				pfctl_print_title("TRANSLATION RULES:");
+				dotitle = 0;
+			}
+			print_rule(&pr.rule, pr.anchor_call,
+			    opts & PF_OPT_VERBOSE2, opts & PF_OPT_NUMERIC);
+			printf("\n");
+			pfctl_print_rule_counters(&pr.rule, opts);
+			pfctl_clear_pool(&pr.rule.rpool);
+		}
+	}
+	return (0);
+}
+
+int
+pfctl_show_src_nodes(int dev, int opts)
+{
+	struct pfioc_src_nodes psn;
+	struct pf_src_node *p;
+	char *inbuf = NULL, *newinbuf = NULL;
+	unsigned int len = 0;
+	int i;
+
+	memset(&psn, 0, sizeof(psn));
+	for (;;) {
+		psn.psn_len = len;
+		if (len) {
+			newinbuf = realloc(inbuf, len);
+			if (newinbuf == NULL)
+				err(1, "realloc");
+			psn.psn_buf = inbuf = newinbuf;
+		}
+		if (ioctl(dev, DIOCGETSRCNODES, &psn) < 0) {
+			warn("DIOCGETSRCNODES");
+			free(inbuf);
+			return (-1);
+		}
+		if (psn.psn_len + sizeof(struct pfioc_src_nodes) < len)
+			break;
+		if (len == 0 && psn.psn_len == 0)
+			goto done;
+		if (len == 0 && psn.psn_len != 0)
+			len = psn.psn_len;
+		if (psn.psn_len == 0)
+			goto done;	/* no src_nodes */
+		len *= 2;
+	}
+	p = psn.psn_src_nodes;
+	if (psn.psn_len > 0 && (opts & PF_OPT_SHOWALL))
+		pfctl_print_title("SOURCE TRACKING NODES:");
+	for (i = 0; i < psn.psn_len; i += sizeof(*p)) {
+		print_src_node(p, opts);
+		p++;
+	}
+done:
+	free(inbuf);
+	return (0);
+}
+
+int
+pfctl_show_states(int dev, const char *iface, int opts)
+{
+	struct pfioc_states ps;
+	struct pfsync_state *p;
+	char *inbuf = NULL, *newinbuf = NULL;
+	unsigned int len = 0;
+	int i, dotitle = (opts & PF_OPT_SHOWALL);
+
+	memset(&ps, 0, sizeof(ps));
+	for (;;) {
+		ps.ps_len = len;
+		if (len) {
+			newinbuf = realloc(inbuf, len);
+			if (newinbuf == NULL)
+				err(1, "realloc");
+			ps.ps_buf = inbuf = newinbuf;
+		}
+		if (ioctl(dev, DIOCGETSTATES, &ps) < 0) {
+			warn("DIOCGETSTATES");
+			free(inbuf);
+			return (-1);
+		}
+		if (ps.ps_len + sizeof(struct pfioc_states) < len)
+			break;
+		if (len == 0 && ps.ps_len == 0)
+			goto done;
+		if (len == 0 && ps.ps_len != 0)
+			len = ps.ps_len;
+		if (ps.ps_len == 0)
+			goto done;	/* no states */
+		len *= 2;
+	}
+	p = ps.ps_states;
+	for (i = 0; i < ps.ps_len; i += sizeof(*p), p++) {
+		if (iface != NULL && strcmp(p->ifname, iface))
+			continue;
+		if (dotitle) {
+			pfctl_print_title("STATES:");
+			dotitle = 0;
+		}
+		print_state(p, opts);
+	}
+done:
+	free(inbuf);
+	return (0);
+}
+
+int
+pfctl_show_status(int dev, int opts)
+{
+	struct pf_status status;
+
+	if (ioctl(dev, DIOCGETSTATUS, &status)) {
+		warn("DIOCGETSTATUS");
+		return (-1);
+	}
+	if (opts & PF_OPT_SHOWALL)
+		pfctl_print_title("INFO:");
+	print_status(&status, opts);
+	return (0);
+}
+
+int
+pfctl_show_timeouts(int dev, int opts)
+{
+	struct pfioc_tm pt;
+	int i;
+
+	if (opts & PF_OPT_SHOWALL)
+		pfctl_print_title("TIMEOUTS:");
+	memset(&pt, 0, sizeof(pt));
+	for (i = 0; pf_timeouts[i].name; i++) {
+		pt.timeout = pf_timeouts[i].timeout;
+		if (ioctl(dev, DIOCGETTIMEOUT, &pt))
+			err(1, "DIOCGETTIMEOUT");
+		printf("%-20s %10d", pf_timeouts[i].name, pt.seconds);
+		if (pf_timeouts[i].timeout >= PFTM_ADAPTIVE_START &&
+		    pf_timeouts[i].timeout <= PFTM_ADAPTIVE_END)
+			printf(" states");
+		else
+			printf("s");
+		printf("\n");
+	}
+	return (0);
+
+}
+
+int
+pfctl_show_limits(int dev, int opts)
+{
+	struct pfioc_limit pl;
+	int i;
+
+	if (opts & PF_OPT_SHOWALL)
+		pfctl_print_title("LIMITS:");
+	memset(&pl, 0, sizeof(pl));
+	for (i = 0; pf_limits[i].name; i++) {
+		pl.index = pf_limits[i].index;
+		if (ioctl(dev, DIOCGETLIMIT, &pl))
+			err(1, "DIOCGETLIMIT");
+		printf("%-13s ", pf_limits[i].name);
+		if (pl.limit == UINT_MAX)
+			printf("unlimited\n");
+		else
+			printf("hard limit %8u\n", pl.limit);
+	}
+	return (0);
+}
+
+/* callbacks for rule/nat/rdr/addr */
+int
+pfctl_add_pool(struct pfctl *pf, struct pf_pool *p, sa_family_t af)
+{
+	struct pf_pooladdr *pa;
+
+	if ((pf->opts & PF_OPT_NOACTION) == 0) {
+		if (ioctl(pf->dev, DIOCBEGINADDRS, &pf->paddr))
+			err(1, "DIOCBEGINADDRS");
+	}
+
+	pf->paddr.af = af;
+	TAILQ_FOREACH(pa, &p->list, entries) {
+		memcpy(&pf->paddr.addr, pa, sizeof(struct pf_pooladdr));
+		if ((pf->opts & PF_OPT_NOACTION) == 0) {
+			if (ioctl(pf->dev, DIOCADDADDR, &pf->paddr))
+				err(1, "DIOCADDADDR");
+		}
+	}
+	return (0);
+}
+
+int
+pfctl_add_rule(struct pfctl *pf, struct pf_rule *r, const char *anchor_call)
+{
+	u_int8_t		rs_num;
+	struct pf_rule		*rule;
+	struct pf_ruleset	*rs;
+	char 			*p;
+
+	rs_num = pf_get_ruleset_number(r->action);
+	if (rs_num == PF_RULESET_MAX)
+		errx(1, "Invalid rule type %d", r->action);
+
+	rs = &pf->anchor->ruleset;
+
+	if (anchor_call[0] && r->anchor == NULL) {
+		/* 
+		 * Don't make non-brace anchors part of the main anchor pool.
+		 */
+		if ((r->anchor = calloc(1, sizeof(*r->anchor))) == NULL)
+			err(1, "pfctl_add_rule: calloc");
+		
+		pf_init_ruleset(&r->anchor->ruleset);
+		r->anchor->ruleset.anchor = r->anchor;
+		if (strlcpy(r->anchor->path, anchor_call,
+		    sizeof(rule->anchor->path)) >= sizeof(rule->anchor->path))
+			errx(1, "pfctl_add_rule: strlcpy");
+		if ((p = strrchr(anchor_call, '/')) != NULL) {
+			if (!strlen(p))
+				err(1, "pfctl_add_rule: bad anchor name %s",
+				    anchor_call);
+		} else
+			p = (char *)anchor_call;
+		if (strlcpy(r->anchor->name, p,
+		    sizeof(rule->anchor->name)) >= sizeof(rule->anchor->name))
+			errx(1, "pfctl_add_rule: strlcpy");
+	}
+
+	if ((rule = calloc(1, sizeof(*rule))) == NULL)
+		err(1, "calloc");
+	bcopy(r, rule, sizeof(*rule));
+	TAILQ_INIT(&rule->rpool.list);
+	pfctl_move_pool(&r->rpool, &rule->rpool);
+
+	TAILQ_INSERT_TAIL(rs->rules[rs_num].active.ptr, rule, entries);
+	return (0);
+}
+
+int
+pfctl_ruleset_trans(struct pfctl *pf, char *path, struct pf_anchor *a)
+{
+	int osize = pf->trans->pfrb_size;
+
+	if ((pf->loadopt & PFCTL_FLAG_NAT) != 0) {
+		if (pfctl_add_trans(pf->trans, PF_RULESET_NAT, path) ||
+		    pfctl_add_trans(pf->trans, PF_RULESET_BINAT, path) ||
+		    pfctl_add_trans(pf->trans, PF_RULESET_RDR, path))
+			return (1);
+	}
+	if (a == pf->astack[0] && ((altqsupport &&
+	    (pf->loadopt & PFCTL_FLAG_ALTQ) != 0))) {
+		if (pfctl_add_trans(pf->trans, PF_RULESET_ALTQ, path))
+			return (2);
+	}
+	if ((pf->loadopt & PFCTL_FLAG_FILTER) != 0) {
+		if (pfctl_add_trans(pf->trans, PF_RULESET_SCRUB, path) ||
+		    pfctl_add_trans(pf->trans, PF_RULESET_FILTER, path))
+			return (3);
+	}
+	if (pf->loadopt & PFCTL_FLAG_TABLE)
+		if (pfctl_add_trans(pf->trans, PF_RULESET_TABLE, path))
+			return (4);
+	if (pfctl_trans(pf->dev, pf->trans, DIOCXBEGIN, osize))
+		return (5);
+
+	return (0);
+}
+
+int
+pfctl_load_ruleset(struct pfctl *pf, char *path, struct pf_ruleset *rs,
+    int rs_num, int depth)
+{
+	struct pf_rule *r;
+	int		error, len = strlen(path);
+	int		brace = 0;
+
+	pf->anchor = rs->anchor;
+
+	if (path[0])
+		snprintf(&path[len], MAXPATHLEN - len, "/%s", pf->anchor->name);
+	else
+		snprintf(&path[len], MAXPATHLEN - len, "%s", pf->anchor->name);
+
+	if (depth) {
+		if (TAILQ_FIRST(rs->rules[rs_num].active.ptr) != NULL) {
+			brace++;
+			if (pf->opts & PF_OPT_VERBOSE)
+				printf(" {\n");
+			if ((pf->opts & PF_OPT_NOACTION) == 0 &&
+			    (error = pfctl_ruleset_trans(pf,
+			    path, rs->anchor))) {
+				printf("pfctl_load_rulesets: "
+				    "pfctl_ruleset_trans %d\n", error);
+				goto error;
+			}
+		} else if (pf->opts & PF_OPT_VERBOSE)
+			printf("\n");
+
+	}
+
+	if (pf->optimize && rs_num == PF_RULESET_FILTER)
+		pfctl_optimize_ruleset(pf, rs);
+
+	while ((r = TAILQ_FIRST(rs->rules[rs_num].active.ptr)) != NULL) {
+		TAILQ_REMOVE(rs->rules[rs_num].active.ptr, r, entries);
+		if ((error = pfctl_load_rule(pf, path, r, depth)))
+			goto error;
+		if (r->anchor) {
+			if ((error = pfctl_load_ruleset(pf, path,
+			    &r->anchor->ruleset, rs_num, depth + 1)))
+				goto error;
+		} else if (pf->opts & PF_OPT_VERBOSE)
+			printf("\n");
+		free(r);
+	}
+	if (brace && pf->opts & PF_OPT_VERBOSE) {
+		INDENT(depth - 1, (pf->opts & PF_OPT_VERBOSE));
+		printf("}\n");
+	}
+	path[len] = '\0';
+	return (0);
+
+ error:
+	path[len] = '\0';
+	return (error);
+
+}
+
+int
+pfctl_load_rule(struct pfctl *pf, char *path, struct pf_rule *r, int depth)
+{
+	u_int8_t		rs_num = pf_get_ruleset_number(r->action);
+	char			*name;
+	struct pfioc_rule	pr;
+	int			len = strlen(path);
+
+	bzero(&pr, sizeof(pr));
+	/* set up anchor before adding to path for anchor_call */
+	if ((pf->opts & PF_OPT_NOACTION) == 0)
+		pr.ticket = pfctl_get_ticket(pf->trans, rs_num, path);
+	if (strlcpy(pr.anchor, path, sizeof(pr.anchor)) >= sizeof(pr.anchor))
+		errx(1, "pfctl_load_rule: strlcpy");
+
+	if (r->anchor) {
+		if (r->anchor->match) {
+			if (path[0])
+				snprintf(&path[len], MAXPATHLEN - len,
+				    "/%s", r->anchor->name);
+			else
+				snprintf(&path[len], MAXPATHLEN - len,
+				    "%s", r->anchor->name);
+			name = path;
+		} else
+			name = r->anchor->path;
+	} else
+		name = "";
+
+	if ((pf->opts & PF_OPT_NOACTION) == 0) {
+		if (pfctl_add_pool(pf, &r->rpool, r->af))
+			return (1);
+		pr.pool_ticket = pf->paddr.ticket;
+		memcpy(&pr.rule, r, sizeof(pr.rule));
+		if (r->anchor && strlcpy(pr.anchor_call, name,
+		    sizeof(pr.anchor_call)) >= sizeof(pr.anchor_call))
+			errx(1, "pfctl_load_rule: strlcpy");
+		if (ioctl(pf->dev, DIOCADDRULE, &pr))
+			err(1, "DIOCADDRULE");
+	}
+
+	if (pf->opts & PF_OPT_VERBOSE) {
+		INDENT(depth, !(pf->opts & PF_OPT_VERBOSE2));
+		print_rule(r, r->anchor ? r->anchor->name : "",
+		    pf->opts & PF_OPT_VERBOSE2,
+		    pf->opts & PF_OPT_NUMERIC);
+	}
+	path[len] = '\0';
+	pfctl_clear_pool(&r->rpool);
+	return (0);
+}
+
+int
+pfctl_add_altq(struct pfctl *pf, struct pf_altq *a)
+{
+	if (altqsupport &&
+	    (loadopt & PFCTL_FLAG_ALTQ) != 0) {
+		memcpy(&pf->paltq->altq, a, sizeof(struct pf_altq));
+		if ((pf->opts & PF_OPT_NOACTION) == 0) {
+			if (ioctl(pf->dev, DIOCADDALTQ, pf->paltq)) {
+				if (errno == ENXIO)
+					errx(1, "qtype not configured");
+				else if (errno == ENODEV)
+					errx(1, "%s: driver does not support "
+					    "altq", a->ifname);
+				else
+					err(1, "DIOCADDALTQ");
+			}
+		}
+		pfaltq_store(&pf->paltq->altq);
+	}
+	return (0);
+}
+
+int
+pfctl_rules(int dev, char *filename, int opts, int optimize,
+    char *anchorname, struct pfr_buffer *trans)
+{
+#define ERR(x) do { warn(x); goto _error; } while(0)
+#define ERRX(x) do { warnx(x); goto _error; } while(0)
+
+	struct pfr_buffer	*t, buf;
+	struct pfioc_altq	 pa;
+	struct pfctl		 pf;
+	struct pf_ruleset	*rs;
+	struct pfr_table	 trs;
+	char			*path;
+	int			 osize;
+
+	RB_INIT(&pf_anchors);
+	memset(&pf_main_anchor, 0, sizeof(pf_main_anchor));
+	pf_init_ruleset(&pf_main_anchor.ruleset);
+	pf_main_anchor.ruleset.anchor = &pf_main_anchor;
+	if (trans == NULL) {
+		bzero(&buf, sizeof(buf));
+		buf.pfrb_type = PFRB_TRANS;
+		t = &buf;
+		osize = 0;
+	} else {
+		t = trans;
+		osize = t->pfrb_size;
+	}
+
+	memset(&pa, 0, sizeof(pa));
+	memset(&pf, 0, sizeof(pf));
+	memset(&trs, 0, sizeof(trs));
+	if ((path = calloc(1, MAXPATHLEN)) == NULL)
+		ERRX("pfctl_rules: calloc");
+	if (strlcpy(trs.pfrt_anchor, anchorname,
+	    sizeof(trs.pfrt_anchor)) >= sizeof(trs.pfrt_anchor))
+		ERRX("pfctl_rules: strlcpy");
+	pf.dev = dev;
+	pf.opts = opts;
+	pf.optimize = optimize;
+	pf.loadopt = loadopt;
+
+	/* non-brace anchor, create without resolving the path */
+	if ((pf.anchor = calloc(1, sizeof(*pf.anchor))) == NULL)
+		ERRX("pfctl_rules: calloc");
+	rs = &pf.anchor->ruleset;
+	pf_init_ruleset(rs);
+	rs->anchor = pf.anchor;
+	if (strlcpy(pf.anchor->path, anchorname,
+	    sizeof(pf.anchor->path)) >= sizeof(pf.anchor->path))
+		errx(1, "pfctl_add_rule: strlcpy");
+	if (strlcpy(pf.anchor->name, anchorname,
+	    sizeof(pf.anchor->name)) >= sizeof(pf.anchor->name))
+		errx(1, "pfctl_add_rule: strlcpy");
+
+
+	pf.astack[0] = pf.anchor;
+	pf.asd = 0;
+	if (anchorname[0])
+		pf.loadopt &= ~PFCTL_FLAG_ALTQ;
+	pf.paltq = &pa;
+	pf.trans = t;
+	pfctl_init_options(&pf);
+
+	if ((opts & PF_OPT_NOACTION) == 0) {
+		/*
+		 * XXX For the time being we need to open transactions for
+		 * the main ruleset before parsing, because tables are still
+		 * loaded at parse time.
+		 */
+		if (pfctl_ruleset_trans(&pf, anchorname, pf.anchor))
+			ERRX("pfctl_rules");
+		if (altqsupport && (pf.loadopt & PFCTL_FLAG_ALTQ))
+			pa.ticket =
+			    pfctl_get_ticket(t, PF_RULESET_ALTQ, anchorname);
+		if (pf.loadopt & PFCTL_FLAG_TABLE)
+			pf.astack[0]->ruleset.tticket =
+			    pfctl_get_ticket(t, PF_RULESET_TABLE, anchorname);
+	}
+
+	if (parse_config(filename, &pf) < 0) {
+		if ((opts & PF_OPT_NOACTION) == 0)
+			ERRX("Syntax error in config file: "
+			    "pf rules not loaded");
+		else
+			goto _error;
+	}
+
+	if ((pf.loadopt & PFCTL_FLAG_FILTER &&
+	    (pfctl_load_ruleset(&pf, path, rs, PF_RULESET_SCRUB, 0))) ||
+	    (pf.loadopt & PFCTL_FLAG_NAT &&
+	    (pfctl_load_ruleset(&pf, path, rs, PF_RULESET_NAT, 0) ||
+	    pfctl_load_ruleset(&pf, path, rs, PF_RULESET_RDR, 0) ||
+	    pfctl_load_ruleset(&pf, path, rs, PF_RULESET_BINAT, 0))) ||
+	    (pf.loadopt & PFCTL_FLAG_FILTER &&
+	    pfctl_load_ruleset(&pf, path, rs, PF_RULESET_FILTER, 0))) {
+		if ((opts & PF_OPT_NOACTION) == 0)
+			ERRX("Unable to load rules into kernel");
+		else
+			goto _error;
+	}
+
+	if ((altqsupport && (pf.loadopt & PFCTL_FLAG_ALTQ) != 0))
+		if (check_commit_altq(dev, opts) != 0)
+			ERRX("errors in altq config");
+
+	/* process "load anchor" directives */
+	if (!anchorname[0])
+		if (pfctl_load_anchors(dev, &pf, t) == -1)
+			ERRX("load anchors");
+
+	if (trans == NULL && (opts & PF_OPT_NOACTION) == 0) {
+		if (!anchorname[0])
+			if (pfctl_load_options(&pf))
+				goto _error;
+		if (pfctl_trans(dev, t, DIOCXCOMMIT, osize))
+			ERR("DIOCXCOMMIT");
+	}
+	return (0);
+
+_error:
+	if (trans == NULL) {	/* main ruleset */
+		if ((opts & PF_OPT_NOACTION) == 0)
+			if (pfctl_trans(dev, t, DIOCXROLLBACK, osize))
+				err(1, "DIOCXROLLBACK");
+		exit(1);
+	} else {		/* sub ruleset */
+		return (-1);
+	}
+
+#undef ERR
+#undef ERRX
+}
+
+FILE *
+pfctl_fopen(const char *name, const char *mode)
+{
+	struct stat	 st;
+	FILE		*fp;
+
+	fp = fopen(name, mode);
+	if (fp == NULL)
+		return (NULL);
+	if (fstat(fileno(fp), &st)) {
+		fclose(fp);
+		return (NULL);
+	}
+	if (S_ISDIR(st.st_mode)) {
+		fclose(fp);
+		errno = EISDIR;
+		return (NULL);
+	}
+	return (fp);
+}
+
+void
+pfctl_init_options(struct pfctl *pf)
+{
+	int64_t mem;
+	int mib[2];
+	size_t size;
+
+	pf->timeout[PFTM_TCP_FIRST_PACKET] = PFTM_TCP_FIRST_PACKET_VAL;
+	pf->timeout[PFTM_TCP_OPENING] = PFTM_TCP_OPENING_VAL;
+	pf->timeout[PFTM_TCP_ESTABLISHED] = PFTM_TCP_ESTABLISHED_VAL;
+	pf->timeout[PFTM_TCP_CLOSING] = PFTM_TCP_CLOSING_VAL;
+	pf->timeout[PFTM_TCP_FIN_WAIT] = PFTM_TCP_FIN_WAIT_VAL;
+	pf->timeout[PFTM_TCP_CLOSED] = PFTM_TCP_CLOSED_VAL;
+	pf->timeout[PFTM_UDP_FIRST_PACKET] = PFTM_UDP_FIRST_PACKET_VAL;
+	pf->timeout[PFTM_UDP_SINGLE] = PFTM_UDP_SINGLE_VAL;
+	pf->timeout[PFTM_UDP_MULTIPLE] = PFTM_UDP_MULTIPLE_VAL;
+	pf->timeout[PFTM_ICMP_FIRST_PACKET] = PFTM_ICMP_FIRST_PACKET_VAL;
+	pf->timeout[PFTM_ICMP_ERROR_REPLY] = PFTM_ICMP_ERROR_REPLY_VAL;
+	pf->timeout[PFTM_OTHER_FIRST_PACKET] = PFTM_OTHER_FIRST_PACKET_VAL;
+	pf->timeout[PFTM_OTHER_SINGLE] = PFTM_OTHER_SINGLE_VAL;
+	pf->timeout[PFTM_OTHER_MULTIPLE] = PFTM_OTHER_MULTIPLE_VAL;
+	pf->timeout[PFTM_FRAG] = PFTM_FRAG_VAL;
+	pf->timeout[PFTM_INTERVAL] = PFTM_INTERVAL_VAL;
+	pf->timeout[PFTM_SRC_NODE] = PFTM_SRC_NODE_VAL;
+	pf->timeout[PFTM_TS_DIFF] = PFTM_TS_DIFF_VAL;
+	pf->timeout[PFTM_ADAPTIVE_START] = PFSTATE_ADAPT_START;
+	pf->timeout[PFTM_ADAPTIVE_END] = PFSTATE_ADAPT_END;
+
+	pf->limit[PF_LIMIT_STATES] = PFSTATE_HIWAT;
+	pf->limit[PF_LIMIT_FRAGS] = PFFRAG_FRENT_HIWAT;
+	pf->limit[PF_LIMIT_SRC_NODES] = PFSNODE_HIWAT;
+	pf->limit[PF_LIMIT_TABLES] = PFR_KTABLE_HIWAT;
+	pf->limit[PF_LIMIT_TABLE_ENTRIES] = PFR_KENTRY_HIWAT;
+
+	mib[0] = CTL_HW;
+#ifdef __FreeBSD__
+	mib[1] = HW_PHYSMEM;
+#else
+	mib[1] = HW_PHYSMEM64;
+#endif
+	size = sizeof(mem);
+	if (sysctl(mib, 2, &mem, &size, NULL, 0) == -1)
+		err(1, "sysctl");
+	if (mem <= 100*1024*1024)
+		pf->limit[PF_LIMIT_TABLE_ENTRIES] = PFR_KENTRY_HIWAT_SMALL; 
+
+	pf->debug = PF_DEBUG_URGENT;
+}
+
+int
+pfctl_load_options(struct pfctl *pf)
+{
+	int i, error = 0;
+
+	if ((loadopt & PFCTL_FLAG_OPTION) == 0)
+		return (0);
+
+	/* load limits */
+	for (i = 0; i < PF_LIMIT_MAX; i++) {
+		if ((pf->opts & PF_OPT_MERGE) && !pf->limit_set[i])
+			continue;
+		if (pfctl_load_limit(pf, i, pf->limit[i]))
+			error = 1;
+	}
+
+	/*
+	 * If we've set the limit, but haven't explicitly set adaptive
+	 * timeouts, do it now with a start of 60% and end of 120%.
+	 */
+	if (pf->limit_set[PF_LIMIT_STATES] &&
+	    !pf->timeout_set[PFTM_ADAPTIVE_START] &&
+	    !pf->timeout_set[PFTM_ADAPTIVE_END]) {
+		pf->timeout[PFTM_ADAPTIVE_START] =
+			(pf->limit[PF_LIMIT_STATES] / 10) * 6;
+		pf->timeout_set[PFTM_ADAPTIVE_START] = 1;
+		pf->timeout[PFTM_ADAPTIVE_END] =
+			(pf->limit[PF_LIMIT_STATES] / 10) * 12;
+		pf->timeout_set[PFTM_ADAPTIVE_END] = 1;
+	}
+
+	/* load timeouts */
+	for (i = 0; i < PFTM_MAX; i++) {
+		if ((pf->opts & PF_OPT_MERGE) && !pf->timeout_set[i])
+			continue;
+		if (pfctl_load_timeout(pf, i, pf->timeout[i]))
+			error = 1;
+	}
+
+	/* load debug */
+	if (!(pf->opts & PF_OPT_MERGE) || pf->debug_set)
+		if (pfctl_load_debug(pf, pf->debug))
+			error = 1;
+
+	/* load logif */
+	if (!(pf->opts & PF_OPT_MERGE) || pf->ifname_set)
+		if (pfctl_load_logif(pf, pf->ifname))
+			error = 1;
+
+	/* load hostid */
+	if (!(pf->opts & PF_OPT_MERGE) || pf->hostid_set)
+		if (pfctl_load_hostid(pf, pf->hostid))
+			error = 1;
+
+	return (error);
+}
+
+int
+pfctl_set_limit(struct pfctl *pf, const char *opt, unsigned int limit)
+{
+	int i;
+
+
+	for (i = 0; pf_limits[i].name; i++) {
+		if (strcasecmp(opt, pf_limits[i].name) == 0) {
+			pf->limit[pf_limits[i].index] = limit;
+			pf->limit_set[pf_limits[i].index] = 1;
+			break;
+		}
+	}
+	if (pf_limits[i].name == NULL) {
+		warnx("Bad pool name.");
+		return (1);
+	}
+
+	if (pf->opts & PF_OPT_VERBOSE)
+		printf("set limit %s %d\n", opt, limit);
+
+	return (0);
+}
+
+int
+pfctl_load_limit(struct pfctl *pf, unsigned int index, unsigned int limit)
+{
+	struct pfioc_limit pl;
+
+	memset(&pl, 0, sizeof(pl));
+	pl.index = index;
+	pl.limit = limit;
+	if (ioctl(pf->dev, DIOCSETLIMIT, &pl)) {
+		if (errno == EBUSY)
+			warnx("Current pool size exceeds requested hard limit");
+		else
+			warnx("DIOCSETLIMIT");
+		return (1);
+	}
+	return (0);
+}
+
+int
+pfctl_set_timeout(struct pfctl *pf, const char *opt, int seconds, int quiet)
+{
+	int i;
+
+	if ((loadopt & PFCTL_FLAG_OPTION) == 0)
+		return (0);
+
+	for (i = 0; pf_timeouts[i].name; i++) {
+		if (strcasecmp(opt, pf_timeouts[i].name) == 0) {
+			pf->timeout[pf_timeouts[i].timeout] = seconds;
+			pf->timeout_set[pf_timeouts[i].timeout] = 1;
+			break;
+		}
+	}
+
+	if (pf_timeouts[i].name == NULL) {
+		warnx("Bad timeout name.");
+		return (1);
+	}
+
+
+	if (pf->opts & PF_OPT_VERBOSE && ! quiet)
+		printf("set timeout %s %d\n", opt, seconds);
+
+	return (0);
+}
+
+int
+pfctl_load_timeout(struct pfctl *pf, unsigned int timeout, unsigned int seconds)
+{
+	struct pfioc_tm pt;
+
+	memset(&pt, 0, sizeof(pt));
+	pt.timeout = timeout;
+	pt.seconds = seconds;
+	if (ioctl(pf->dev, DIOCSETTIMEOUT, &pt)) {
+		warnx("DIOCSETTIMEOUT");
+		return (1);
+	}
+	return (0);
+}
+
+int
+pfctl_set_optimization(struct pfctl *pf, const char *opt)
+{
+	const struct pf_hint *hint;
+	int i, r;
+
+	if ((loadopt & PFCTL_FLAG_OPTION) == 0)
+		return (0);
+
+	for (i = 0; pf_hints[i].name; i++)
+		if (strcasecmp(opt, pf_hints[i].name) == 0)
+			break;
+
+	hint = pf_hints[i].hint;
+	if (hint == NULL) {
+		warnx("invalid state timeouts optimization");
+		return (1);
+	}
+
+	for (i = 0; hint[i].name; i++)
+		if ((r = pfctl_set_timeout(pf, hint[i].name,
+		    hint[i].timeout, 1)))
+			return (r);
+
+	if (pf->opts & PF_OPT_VERBOSE)
+		printf("set optimization %s\n", opt);
+
+	return (0);
+}
+
+int
+pfctl_set_logif(struct pfctl *pf, char *ifname)
+{
+
+	if ((loadopt & PFCTL_FLAG_OPTION) == 0)
+		return (0);
+
+	if (!strcmp(ifname, "none")) {
+		free(pf->ifname);
+		pf->ifname = NULL;
+	} else {
+		pf->ifname = strdup(ifname);
+		if (!pf->ifname)
+			errx(1, "pfctl_set_logif: strdup");
+	}
+	pf->ifname_set = 1;
+
+	if (pf->opts & PF_OPT_VERBOSE)
+		printf("set loginterface %s\n", ifname);
+
+	return (0);
+}
+
+int
+pfctl_load_logif(struct pfctl *pf, char *ifname)
+{
+	struct pfioc_if pi;
+
+	memset(&pi, 0, sizeof(pi));
+	if (ifname && strlcpy(pi.ifname, ifname,
+	    sizeof(pi.ifname)) >= sizeof(pi.ifname)) {
+		warnx("pfctl_load_logif: strlcpy");
+		return (1);
+	}
+	if (ioctl(pf->dev, DIOCSETSTATUSIF, &pi)) {
+		warnx("DIOCSETSTATUSIF");
+		return (1);
+	}
+	return (0);
+}
+
+int
+pfctl_set_hostid(struct pfctl *pf, u_int32_t hostid)
+{
+	if ((loadopt & PFCTL_FLAG_OPTION) == 0)
+		return (0);
+
+	HTONL(hostid);
+
+	pf->hostid = hostid;
+	pf->hostid_set = 1;
+
+	if (pf->opts & PF_OPT_VERBOSE)
+		printf("set hostid 0x%08x\n", ntohl(hostid));
+
+	return (0);
+}
+
+int
+pfctl_load_hostid(struct pfctl *pf, u_int32_t hostid)
+{
+	if (ioctl(dev, DIOCSETHOSTID, &hostid)) {
+		warnx("DIOCSETHOSTID");
+		return (1);
+	}
+	return (0);
+}
+
+int
+pfctl_set_debug(struct pfctl *pf, char *d)
+{
+	u_int32_t	level;
+
+	if ((loadopt & PFCTL_FLAG_OPTION) == 0)
+		return (0);
+
+	if (!strcmp(d, "none"))
+		pf->debug = PF_DEBUG_NONE;
+	else if (!strcmp(d, "urgent"))
+		pf->debug = PF_DEBUG_URGENT;
+	else if (!strcmp(d, "misc"))
+		pf->debug = PF_DEBUG_MISC;
+	else if (!strcmp(d, "loud"))
+		pf->debug = PF_DEBUG_NOISY;
+	else {
+		warnx("unknown debug level \"%s\"", d);
+		return (-1);
+	}
+
+	pf->debug_set = 1;
+
+	if ((pf->opts & PF_OPT_NOACTION) == 0)
+		if (ioctl(dev, DIOCSETDEBUG, &level))
+			err(1, "DIOCSETDEBUG");
+
+	if (pf->opts & PF_OPT_VERBOSE)
+		printf("set debug %s\n", d);
+
+	return (0);
+}
+
+int
+pfctl_load_debug(struct pfctl *pf, unsigned int level)
+{
+	if (ioctl(pf->dev, DIOCSETDEBUG, &level)) {
+		warnx("DIOCSETDEBUG");
+		return (1);
+	}
+	return (0);
+}
+
+int
+pfctl_set_interface_flags(struct pfctl *pf, char *ifname, int flags, int how)
+{
+	struct pfioc_iface	pi;
+
+	if ((loadopt & PFCTL_FLAG_OPTION) == 0)
+		return (0);
+
+	bzero(&pi, sizeof(pi));
+
+	pi.pfiio_flags = flags;
+
+	if (strlcpy(pi.pfiio_name, ifname, sizeof(pi.pfiio_name)) >=
+	    sizeof(pi.pfiio_name))
+		errx(1, "pfctl_set_interface_flags: strlcpy");
+
+	if ((pf->opts & PF_OPT_NOACTION) == 0) {
+		if (how == 0) {
+			if (ioctl(pf->dev, DIOCCLRIFFLAG, &pi))
+				err(1, "DIOCCLRIFFLAG");
+		} else {
+			if (ioctl(pf->dev, DIOCSETIFFLAG, &pi))
+				err(1, "DIOCSETIFFLAG");
+		}
+	}
+	return (0);
+}
+
+void
+pfctl_debug(int dev, u_int32_t level, int opts)
+{
+	if (ioctl(dev, DIOCSETDEBUG, &level))
+		err(1, "DIOCSETDEBUG");
+	if ((opts & PF_OPT_QUIET) == 0) {
+		fprintf(stderr, "debug level set to '");
+		switch (level) {
+		case PF_DEBUG_NONE:
+			fprintf(stderr, "none");
+			break;
+		case PF_DEBUG_URGENT:
+			fprintf(stderr, "urgent");
+			break;
+		case PF_DEBUG_MISC:
+			fprintf(stderr, "misc");
+			break;
+		case PF_DEBUG_NOISY:
+			fprintf(stderr, "loud");
+			break;
+		default:
+			fprintf(stderr, "<invalid>");
+			break;
+		}
+		fprintf(stderr, "'\n");
+	}
+}
+
+int
+pfctl_test_altqsupport(int dev, int opts)
+{
+	struct pfioc_altq pa;
+
+	if (ioctl(dev, DIOCGETALTQS, &pa)) {
+		if (errno == ENODEV) {
+			if (!(opts & PF_OPT_QUIET))
+				fprintf(stderr, "No ALTQ support in kernel\n"
+				    "ALTQ related functions disabled\n");
+			return (0);
+		} else
+			err(1, "DIOCGETALTQS");
+	}
+	return (1);
+}
+
+int
+pfctl_show_anchors(int dev, int opts, char *anchorname)
+{
+	struct pfioc_ruleset	 pr;
+	u_int32_t		 mnr, nr;
+
+	memset(&pr, 0, sizeof(pr));
+	memcpy(pr.path, anchorname, sizeof(pr.path));
+	if (ioctl(dev, DIOCGETRULESETS, &pr)) {
+		if (errno == EINVAL)
+			fprintf(stderr, "Anchor '%s' not found.\n",
+			    anchorname);
+		else
+			err(1, "DIOCGETRULESETS");
+		return (-1);
+	}
+	mnr = pr.nr;
+	for (nr = 0; nr < mnr; ++nr) {
+		char sub[MAXPATHLEN];
+
+		pr.nr = nr;
+		if (ioctl(dev, DIOCGETRULESET, &pr))
+			err(1, "DIOCGETRULESET");
+		if (!strcmp(pr.name, PF_RESERVED_ANCHOR))
+			continue;
+		sub[0] = 0;
+		if (pr.path[0]) {
+			strlcat(sub, pr.path, sizeof(sub));
+			strlcat(sub, "/", sizeof(sub));
+		}
+		strlcat(sub, pr.name, sizeof(sub));
+		if (sub[0] != '_' || (opts & PF_OPT_VERBOSE))
+			printf("  %s\n", sub);
+		if ((opts & PF_OPT_VERBOSE) && pfctl_show_anchors(dev, opts, sub))
+			return (-1);
+	}
+	return (0);
+}
+
+const char *
+pfctl_lookup_option(char *cmd, const char **list)
+{
+	if (cmd != NULL && *cmd)
+		for (; *list; list++)
+			if (!strncmp(cmd, *list, strlen(cmd)))
+				return (*list);
+	return (NULL);
+}
+
+int
+main(int argc, char *argv[])
+{
+	int	 error = 0;
+	int	 ch;
+	int	 mode = O_RDONLY;
+	int	 opts = 0;
+	int	 optimize = PF_OPTIMIZE_BASIC;
+	char	 anchorname[MAXPATHLEN];
+	char	*path;
+
+	if (argc < 2)
+		usage();
+
+	while ((ch = getopt(argc, argv,
+	    "a:AdD:eqf:F:ghi:k:K:mnNOo:Pp:rRs:t:T:vx:z")) != -1) {
+		switch (ch) {
+		case 'a':
+			anchoropt = optarg;
+			break;
+		case 'd':
+			opts |= PF_OPT_DISABLE;
+			mode = O_RDWR;
+			break;
+		case 'D':
+			if (pfctl_cmdline_symset(optarg) < 0)
+				warnx("could not parse macro definition %s",
+				    optarg);
+			break;
+		case 'e':
+			opts |= PF_OPT_ENABLE;
+			mode = O_RDWR;
+			break;
+		case 'q':
+			opts |= PF_OPT_QUIET;
+			break;
+		case 'F':
+			clearopt = pfctl_lookup_option(optarg, clearopt_list);
+			if (clearopt == NULL) {
+				warnx("Unknown flush modifier '%s'", optarg);
+				usage();
+			}
+			mode = O_RDWR;
+			break;
+		case 'i':
+			ifaceopt = optarg;
+			break;
+		case 'k':
+			if (state_killers >= 2) {
+				warnx("can only specify -k twice");
+				usage();
+				/* NOTREACHED */
+			}
+			state_kill[state_killers++] = optarg;
+			mode = O_RDWR;
+			break;
+		case 'K':
+			if (src_node_killers >= 2) {
+				warnx("can only specify -K twice");
+				usage();
+				/* NOTREACHED */
+			}
+			src_node_kill[src_node_killers++] = optarg;
+			mode = O_RDWR;
+			break;
+		case 'm':
+			opts |= PF_OPT_MERGE;
+			break;
+		case 'n':
+			opts |= PF_OPT_NOACTION;
+			break;
+		case 'N':
+			loadopt |= PFCTL_FLAG_NAT;
+			break;
+		case 'r':
+			opts |= PF_OPT_USEDNS;
+			break;
+		case 'f':
+			rulesopt = optarg;
+			mode = O_RDWR;
+			break;
+		case 'g':
+			opts |= PF_OPT_DEBUG;
+			break;
+		case 'A':
+			loadopt |= PFCTL_FLAG_ALTQ;
+			break;
+		case 'R':
+			loadopt |= PFCTL_FLAG_FILTER;
+			break;
+		case 'o':
+			optiopt = pfctl_lookup_option(optarg, optiopt_list);
+			if (optiopt == NULL) {
+				warnx("Unknown optimization '%s'", optarg);
+				usage();
+			}
+			opts |= PF_OPT_OPTIMIZE;
+			break;
+		case 'O':
+			loadopt |= PFCTL_FLAG_OPTION;
+			break;
+		case 'p':
+			pf_device = optarg;
+			break;
+		case 'P':
+			opts |= PF_OPT_NUMERIC;
+			break;
+		case 's':
+			showopt = pfctl_lookup_option(optarg, showopt_list);
+			if (showopt == NULL) {
+				warnx("Unknown show modifier '%s'", optarg);
+				usage();
+			}
+			break;
+		case 't':
+			tableopt = optarg;
+			break;
+		case 'T':
+			tblcmdopt = pfctl_lookup_option(optarg, tblcmdopt_list);
+			if (tblcmdopt == NULL) {
+				warnx("Unknown table command '%s'", optarg);
+				usage();
+			}
+			break;
+		case 'v':
+			if (opts & PF_OPT_VERBOSE)
+				opts |= PF_OPT_VERBOSE2;
+			opts |= PF_OPT_VERBOSE;
+			break;
+		case 'x':
+			debugopt = pfctl_lookup_option(optarg, debugopt_list);
+			if (debugopt == NULL) {
+				warnx("Unknown debug level '%s'", optarg);
+				usage();
+			}
+			mode = O_RDWR;
+			break;
+		case 'z':
+			opts |= PF_OPT_CLRRULECTRS;
+			mode = O_RDWR;
+			break;
+		case 'h':
+			/* FALLTHROUGH */
+		default:
+			usage();
+			/* NOTREACHED */
+		}
+	}
+
+	if (tblcmdopt != NULL) {
+		argc -= optind;
+		argv += optind;
+		ch = *tblcmdopt;
+		if (ch == 'l') {
+			loadopt |= PFCTL_FLAG_TABLE;
+			tblcmdopt = NULL;
+		} else
+			mode = strchr("acdefkrz", ch) ? O_RDWR : O_RDONLY;
+	} else if (argc != optind) {
+		warnx("unknown command line argument: %s ...", argv[optind]);
+		usage();
+		/* NOTREACHED */
+	}
+	if (loadopt == 0)
+		loadopt = ~0;
+
+	if ((path = calloc(1, MAXPATHLEN)) == NULL)
+		errx(1, "pfctl: calloc");
+	memset(anchorname, 0, sizeof(anchorname));
+	if (anchoropt != NULL) {
+		int len = strlen(anchoropt);
+
+		if (anchoropt[len - 1] == '*') {
+			if (len >= 2 && anchoropt[len - 2] == '/')
+				anchoropt[len - 2] = '\0';
+			else
+				anchoropt[len - 1] = '\0';
+			opts |= PF_OPT_RECURSE;
+		}
+		if (strlcpy(anchorname, anchoropt,
+		    sizeof(anchorname)) >= sizeof(anchorname))
+			errx(1, "anchor name '%s' too long",
+			    anchoropt);
+		loadopt &= PFCTL_FLAG_FILTER|PFCTL_FLAG_NAT|PFCTL_FLAG_TABLE;
+	}
+
+	if ((opts & PF_OPT_NOACTION) == 0) {
+		dev = open(pf_device, mode);
+		if (dev == -1)
+			err(1, "%s", pf_device);
+		altqsupport = pfctl_test_altqsupport(dev, opts);
+	} else {
+		dev = open(pf_device, O_RDONLY);
+		if (dev >= 0)
+			opts |= PF_OPT_DUMMYACTION;
+		/* turn off options */
+		opts &= ~ (PF_OPT_DISABLE | PF_OPT_ENABLE);
+		clearopt = showopt = debugopt = NULL;
+#if defined(__FreeBSD__) && !defined(ENABLE_ALTQ)
+		altqsupport = 0;
+#else
+		altqsupport = 1;
+#endif
+	}
+
+	if (opts & PF_OPT_DISABLE)
+		if (pfctl_disable(dev, opts))
+			error = 1;
+
+	if (showopt != NULL) {
+		switch (*showopt) {
+		case 'A':
+			pfctl_show_anchors(dev, opts, anchorname);
+			break;
+		case 'r':
+			pfctl_load_fingerprints(dev, opts);
+			pfctl_show_rules(dev, path, opts, PFCTL_SHOW_RULES,
+			    anchorname, 0);
+			break;
+		case 'l':
+			pfctl_load_fingerprints(dev, opts);
+			pfctl_show_rules(dev, path, opts, PFCTL_SHOW_LABELS,
+			    anchorname, 0);
+			break;
+		case 'n':
+			pfctl_load_fingerprints(dev, opts);
+			pfctl_show_nat(dev, opts, anchorname);
+			break;
+		case 'q':
+			pfctl_show_altq(dev, ifaceopt, opts,
+			    opts & PF_OPT_VERBOSE2);
+			break;
+		case 's':
+			pfctl_show_states(dev, ifaceopt, opts);
+			break;
+		case 'S':
+			pfctl_show_src_nodes(dev, opts);
+			break;
+		case 'i':
+			pfctl_show_status(dev, opts);
+			break;
+		case 't':
+			pfctl_show_timeouts(dev, opts);
+			break;
+		case 'm':
+			pfctl_show_limits(dev, opts);
+			break;
+		case 'a':
+			opts |= PF_OPT_SHOWALL;
+			pfctl_load_fingerprints(dev, opts);
+
+			pfctl_show_nat(dev, opts, anchorname);
+			pfctl_show_rules(dev, path, opts, 0, anchorname, 0);
+			pfctl_show_altq(dev, ifaceopt, opts, 0);
+			pfctl_show_states(dev, ifaceopt, opts);
+			pfctl_show_src_nodes(dev, opts);
+			pfctl_show_status(dev, opts);
+			pfctl_show_rules(dev, path, opts, 1, anchorname, 0);
+			pfctl_show_timeouts(dev, opts);
+			pfctl_show_limits(dev, opts);
+			pfctl_show_tables(anchorname, opts);
+			pfctl_show_fingerprints(opts);
+			break;
+		case 'T':
+			pfctl_show_tables(anchorname, opts);
+			break;
+		case 'o':
+			pfctl_load_fingerprints(dev, opts);
+			pfctl_show_fingerprints(opts);
+			break;
+		case 'I':
+			pfctl_show_ifaces(ifaceopt, opts);
+			break;
+		}
+	}
+
+	if ((opts & PF_OPT_CLRRULECTRS) && showopt == NULL)
+		pfctl_show_rules(dev, path, opts, PFCTL_SHOW_NOTHING,
+		    anchorname, 0);
+
+	if (clearopt != NULL) {
+		if (anchorname[0] == '_' || strstr(anchorname, "/_") != NULL)
+			errx(1, "anchor names beginning with '_' cannot "
+			    "be modified from the command line");
+
+		switch (*clearopt) {
+		case 'r':
+			pfctl_clear_rules(dev, opts, anchorname);
+			break;
+		case 'n':
+			pfctl_clear_nat(dev, opts, anchorname);
+			break;
+		case 'q':
+			pfctl_clear_altq(dev, opts);
+			break;
+		case 's':
+			pfctl_clear_states(dev, ifaceopt, opts);
+			break;
+		case 'S':
+			pfctl_clear_src_nodes(dev, opts);
+			break;
+		case 'i':
+			pfctl_clear_stats(dev, opts);
+			break;
+		case 'a':
+			pfctl_clear_rules(dev, opts, anchorname);
+			pfctl_clear_nat(dev, opts, anchorname);
+			pfctl_clear_tables(anchorname, opts);
+			if (!*anchorname) {
+				pfctl_clear_altq(dev, opts);
+				pfctl_clear_states(dev, ifaceopt, opts);
+				pfctl_clear_src_nodes(dev, opts);
+				pfctl_clear_stats(dev, opts);
+				pfctl_clear_fingerprints(dev, opts);
+				pfctl_clear_interface_flags(dev, opts);
+			}
+			break;
+		case 'o':
+			pfctl_clear_fingerprints(dev, opts);
+			break;
+		case 'T':
+			pfctl_clear_tables(anchorname, opts);
+			break;
+		}
+	}
+	if (state_killers) {
+		if (!strcmp(state_kill[0], "label"))
+			pfctl_label_kill_states(dev, ifaceopt, opts);
+		else if (!strcmp(state_kill[0], "id"))
+			pfctl_id_kill_states(dev, ifaceopt, opts);
+		else
+			pfctl_net_kill_states(dev, ifaceopt, opts);
+	}
+
+	if (src_node_killers)
+		pfctl_kill_src_nodes(dev, ifaceopt, opts);
+
+	if (tblcmdopt != NULL) {
+		error = pfctl_command_tables(argc, argv, tableopt,
+		    tblcmdopt, rulesopt, anchorname, opts);
+		rulesopt = NULL;
+	}
+	if (optiopt != NULL) {
+		switch (*optiopt) {
+		case 'n':
+			optimize = 0;
+			break;
+		case 'b':
+			optimize |= PF_OPTIMIZE_BASIC;
+			break;
+		case 'o':
+		case 'p':
+			optimize |= PF_OPTIMIZE_PROFILE;
+			break;
+		}
+	}
+
+	if ((rulesopt != NULL) && (loadopt & PFCTL_FLAG_OPTION) &&
+	    !anchorname[0])
+		if (pfctl_clear_interface_flags(dev, opts | PF_OPT_QUIET))
+			error = 1;
+
+	if (rulesopt != NULL && !(opts & (PF_OPT_MERGE|PF_OPT_NOACTION)) &&
+	    !anchorname[0] && (loadopt & PFCTL_FLAG_OPTION))
+		if (pfctl_file_fingerprints(dev, opts, PF_OSFP_FILE))
+			error = 1;
+
+	if (rulesopt != NULL) {
+		if (anchorname[0] == '_' || strstr(anchorname, "/_") != NULL)
+			errx(1, "anchor names beginning with '_' cannot "
+			    "be modified from the command line");
+		if (pfctl_rules(dev, rulesopt, opts, optimize,
+		    anchorname, NULL))
+			error = 1;
+		else if (!(opts & PF_OPT_NOACTION) &&
+		    (loadopt & PFCTL_FLAG_TABLE))
+			warn_namespace_collision(NULL);
+	}
+
+	if (opts & PF_OPT_ENABLE)
+		if (pfctl_enable(dev, opts))
+			error = 1;
+
+	if (debugopt != NULL) {
+		switch (*debugopt) {
+		case 'n':
+			pfctl_debug(dev, PF_DEBUG_NONE, opts);
+			break;
+		case 'u':
+			pfctl_debug(dev, PF_DEBUG_URGENT, opts);
+			break;
+		case 'm':
+			pfctl_debug(dev, PF_DEBUG_MISC, opts);
+			break;
+		case 'l':
+			pfctl_debug(dev, PF_DEBUG_NOISY, opts);
+			break;
+		}
+	}
+
+	exit(error);
+}
diff --git a/freebsd/contrib/pf/pfctl/pfctl.h b/freebsd/contrib/pf/pfctl/pfctl.h
new file mode 100644
index 0000000..2c69bc2
--- /dev/null
+++ b/freebsd/contrib/pf/pfctl/pfctl.h
@@ -0,0 +1,130 @@
+/*	$OpenBSD: pfctl.h,v 1.42 2007/12/05 12:01:47 chl Exp $ */
+
+/*
+ * Copyright (c) 2001 Daniel Hartmeier
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *    - Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *    - 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 COPYRIGHT HOLDERS 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
+ * COPYRIGHT HOLDERS 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.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _PFCTL_H_
+#define _PFCTL_H_
+
+enum pfctl_show { PFCTL_SHOW_RULES, PFCTL_SHOW_LABELS, PFCTL_SHOW_NOTHING };
+
+enum {	PFRB_TABLES = 1, PFRB_TSTATS, PFRB_ADDRS, PFRB_ASTATS,
+	PFRB_IFACES, PFRB_TRANS, PFRB_MAX };
+struct pfr_buffer {
+	int	 pfrb_type;	/* type of content, see enum above */
+	int	 pfrb_size;	/* number of objects in buffer */
+	int	 pfrb_msize;	/* maximum number of objects in buffer */
+	void	*pfrb_caddr;	/* malloc'ated memory area */
+};
+#define PFRB_FOREACH(var, buf)				\
+	for ((var) = pfr_buf_next((buf), NULL);		\
+	    (var) != NULL;				\
+	    (var) = pfr_buf_next((buf), (var)))
+
+int	 pfr_get_fd(void);
+int	 pfr_clr_tables(struct pfr_table *, int *, int);
+int	 pfr_add_tables(struct pfr_table *, int, int *, int);
+int	 pfr_del_tables(struct pfr_table *, int, int *, int);
+int	 pfr_get_tables(struct pfr_table *, struct pfr_table *, int *, int);
+int	 pfr_get_tstats(struct pfr_table *, struct pfr_tstats *, int *, int);
+int	 pfr_clr_tstats(struct pfr_table *, int, int *, int);
+int	 pfr_clr_addrs(struct pfr_table *, int *, int);
+int	 pfr_add_addrs(struct pfr_table *, struct pfr_addr *, int, int *, int);
+int	 pfr_del_addrs(struct pfr_table *, struct pfr_addr *, int, int *, int);
+int	 pfr_set_addrs(struct pfr_table *, struct pfr_addr *, int, int *,
+	    int *, int *, int *, int);
+int	 pfr_get_addrs(struct pfr_table *, struct pfr_addr *, int *, int);
+int	 pfr_get_astats(struct pfr_table *, struct pfr_astats *, int *, int);
+int	 pfr_tst_addrs(struct pfr_table *, struct pfr_addr *, int, int *, int);
+int	 pfr_ina_define(struct pfr_table *, struct pfr_addr *, int, int *,
+	    int *, int, int);
+void	 pfr_buf_clear(struct pfr_buffer *);
+int	 pfr_buf_add(struct pfr_buffer *, const void *);
+void	*pfr_buf_next(struct pfr_buffer *, const void *);
+int	 pfr_buf_grow(struct pfr_buffer *, int);
+int	 pfr_buf_load(struct pfr_buffer *, char *, int,
+	    int (*)(struct pfr_buffer *, char *, int));
+char	*pfr_strerror(int);
+int	 pfi_get_ifaces(const char *, struct pfi_kif *, int *);
+int	 pfi_clr_istats(const char *, int *, int);
+
+void	 pfctl_print_title(char *);
+int	 pfctl_clear_tables(const char *, int);
+int	 pfctl_show_tables(const char *, int);
+int	 pfctl_command_tables(int, char *[], char *, const char *, char *,
+	    const char *, int);
+int	 pfctl_show_altq(int, const char *, int, int);
+void	 warn_namespace_collision(const char *);
+int	 pfctl_show_ifaces(const char *, int);
+FILE	*pfctl_fopen(const char *, const char *);
+
+#ifdef __FreeBSD__
+extern int altqsupport;
+extern int dummynetsupport;
+#define	HTONL(x)		(x) = htonl((__uint32_t)(x))
+#endif
+
+#ifndef DEFAULT_PRIORITY
+#define DEFAULT_PRIORITY	1
+#endif
+
+#ifndef DEFAULT_QLIMIT
+#define DEFAULT_QLIMIT		50
+#endif
+
+/*
+ * generalized service curve used for admission control
+ */
+struct segment {
+	LIST_ENTRY(segment)	_next;
+	double			x, y, d, m;
+};
+
+extern	int loadopt;
+
+int		 check_commit_altq(int, int);
+void		 pfaltq_store(struct pf_altq *);
+struct pf_altq	*pfaltq_lookup(const char *);
+char		*rate2str(double);
+
+void	 print_addr(struct pf_addr_wrap *, sa_family_t, int);
+void	 print_host(struct pf_addr *, u_int16_t p, sa_family_t, int);
+void	 print_seq(struct pfsync_state_peer *);
+void	 print_state(struct pfsync_state *, int);
+int	 unmask(struct pf_addr *, sa_family_t);
+
+int	 pfctl_cmdline_symset(char *);
+int	 pfctl_add_trans(struct pfr_buffer *, int, const char *);
+u_int32_t
+	 pfctl_get_ticket(struct pfr_buffer *, int, const char *);
+int	 pfctl_trans(int, struct pfr_buffer *, u_long, int);
+
+#endif /* _PFCTL_H_ */
diff --git a/freebsd/contrib/pf/pfctl/pfctl_altq.c b/freebsd/contrib/pf/pfctl/pfctl_altq.c
new file mode 100644
index 0000000..4fc225f
--- /dev/null
+++ b/freebsd/contrib/pf/pfctl/pfctl_altq.c
@@ -0,0 +1,1260 @@
+#include <machine/rtems-bsd-user-space.h>
+
+/*	$OpenBSD: pfctl_altq.c,v 1.93 2007/10/15 02:16:35 deraadt Exp $	*/
+
+/*
+ * Copyright (c) 2002
+ *	Sony Computer Science Laboratories Inc.
+ * Copyright (c) 2002, 2003 Henning Brauer <henning at openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+
+#include <net/if.h>
+#include <netinet/in.h>
+#include <net/pfvar.h>
+
+#include <err.h>
+#include <errno.h>
+#include <limits.h>
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <altq/altq.h>
+#include <altq/altq_cbq.h>
+#include <altq/altq_priq.h>
+#include <altq/altq_hfsc.h>
+
+#include "pfctl_parser.h"
+#include "pfctl.h"
+
+#define is_sc_null(sc)	(((sc) == NULL) || ((sc)->m1 == 0 && (sc)->m2 == 0))
+
+TAILQ_HEAD(altqs, pf_altq) altqs = TAILQ_HEAD_INITIALIZER(altqs);
+LIST_HEAD(gen_sc, segment) rtsc, lssc;
+
+struct pf_altq	*qname_to_pfaltq(const char *, const char *);
+u_int32_t	 qname_to_qid(const char *);
+
+static int	eval_pfqueue_cbq(struct pfctl *, struct pf_altq *);
+static int	cbq_compute_idletime(struct pfctl *, struct pf_altq *);
+static int	check_commit_cbq(int, int, struct pf_altq *);
+static int	print_cbq_opts(const struct pf_altq *);
+
+static int	eval_pfqueue_priq(struct pfctl *, struct pf_altq *);
+static int	check_commit_priq(int, int, struct pf_altq *);
+static int	print_priq_opts(const struct pf_altq *);
+
+static int	eval_pfqueue_hfsc(struct pfctl *, struct pf_altq *);
+static int	check_commit_hfsc(int, int, struct pf_altq *);
+static int	print_hfsc_opts(const struct pf_altq *,
+		    const struct node_queue_opt *);
+
+static void		 gsc_add_sc(struct gen_sc *, struct service_curve *);
+static int		 is_gsc_under_sc(struct gen_sc *,
+			     struct service_curve *);
+static void		 gsc_destroy(struct gen_sc *);
+static struct segment	*gsc_getentry(struct gen_sc *, double);
+static int		 gsc_add_seg(struct gen_sc *, double, double, double,
+			     double);
+static double		 sc_x2y(struct service_curve *, double);
+
+#ifdef __FreeBSD__
+u_int32_t	getifspeed(int, char *);
+#else
+u_int32_t	 getifspeed(char *);
+#endif
+u_long		 getifmtu(char *);
+int		 eval_queue_opts(struct pf_altq *, struct node_queue_opt *,
+		     u_int32_t);
+u_int32_t	 eval_bwspec(struct node_queue_bw *, u_int32_t);
+void		 print_hfsc_sc(const char *, u_int, u_int, u_int,
+		     const struct node_hfsc_sc *);
+
+void
+pfaltq_store(struct pf_altq *a)
+{
+	struct pf_altq	*altq;
+
+	if ((altq = malloc(sizeof(*altq))) == NULL)
+		err(1, "malloc");
+	memcpy(altq, a, sizeof(struct pf_altq));
+	TAILQ_INSERT_TAIL(&altqs, altq, entries);
+}
+
+struct pf_altq *
+pfaltq_lookup(const char *ifname)
+{
+	struct pf_altq	*altq;
+
+	TAILQ_FOREACH(altq, &altqs, entries) {
+		if (strncmp(ifname, altq->ifname, IFNAMSIZ) == 0 &&
+		    altq->qname[0] == 0)
+			return (altq);
+	}
+	return (NULL);
+}
+
+struct pf_altq *
+qname_to_pfaltq(const char *qname, const char *ifname)
+{
+	struct pf_altq	*altq;
+
+	TAILQ_FOREACH(altq, &altqs, entries) {
+		if (strncmp(ifname, altq->ifname, IFNAMSIZ) == 0 &&
+		    strncmp(qname, altq->qname, PF_QNAME_SIZE) == 0)
+			return (altq);
+	}
+	return (NULL);
+}
+
+u_int32_t
+qname_to_qid(const char *qname)
+{
+	struct pf_altq	*altq;
+
+	/*
+	 * We guarantee that same named queues on different interfaces
+	 * have the same qid, so we do NOT need to limit matching on
+	 * one interface!
+	 */
+
+	TAILQ_FOREACH(altq, &altqs, entries) {
+		if (strncmp(qname, altq->qname, PF_QNAME_SIZE) == 0)
+			return (altq->qid);
+	}
+	return (0);
+}
+
+void
+print_altq(const struct pf_altq *a, unsigned int level,
+    struct node_queue_bw *bw, struct node_queue_opt *qopts)
+{
+	if (a->qname[0] != 0) {
+		print_queue(a, level, bw, 1, qopts);
+		return;
+	}
+
+#ifdef __FreeBSD__
+	if (a->local_flags & PFALTQ_FLAG_IF_REMOVED)
+		printf("INACTIVE ");
+#endif
+
+	printf("altq on %s ", a->ifname);
+
+	switch (a->scheduler) {
+	case ALTQT_CBQ:
+		if (!print_cbq_opts(a))
+			printf("cbq ");
+		break;
+	case ALTQT_PRIQ:
+		if (!print_priq_opts(a))
+			printf("priq ");
+		break;
+	case ALTQT_HFSC:
+		if (!print_hfsc_opts(a, qopts))
+			printf("hfsc ");
+		break;
+	}
+
+	if (bw != NULL && bw->bw_percent > 0) {
+		if (bw->bw_percent < 100)
+			printf("bandwidth %u%% ", bw->bw_percent);
+	} else
+		printf("bandwidth %s ", rate2str((double)a->ifbandwidth));
+
+	if (a->qlimit != DEFAULT_QLIMIT)
+		printf("qlimit %u ", a->qlimit);
+	printf("tbrsize %u ", a->tbrsize);
+}
+
+void
+print_queue(const struct pf_altq *a, unsigned int level,
+    struct node_queue_bw *bw, int print_interface,
+    struct node_queue_opt *qopts)
+{
+	unsigned int	i;
+
+#ifdef __FreeBSD__
+	if (a->local_flags & PFALTQ_FLAG_IF_REMOVED)
+		printf("INACTIVE ");
+#endif
+	printf("queue ");
+	for (i = 0; i < level; ++i)
+		printf(" ");
+	printf("%s ", a->qname);
+	if (print_interface)
+		printf("on %s ", a->ifname);
+	if (a->scheduler == ALTQT_CBQ || a->scheduler == ALTQT_HFSC) {
+		if (bw != NULL && bw->bw_percent > 0) {
+			if (bw->bw_percent < 100)
+				printf("bandwidth %u%% ", bw->bw_percent);
+		} else
+			printf("bandwidth %s ", rate2str((double)a->bandwidth));
+	}
+	if (a->priority != DEFAULT_PRIORITY)
+		printf("priority %u ", a->priority);
+	if (a->qlimit != DEFAULT_QLIMIT)
+		printf("qlimit %u ", a->qlimit);
+	switch (a->scheduler) {
+	case ALTQT_CBQ:
+		print_cbq_opts(a);
+		break;
+	case ALTQT_PRIQ:
+		print_priq_opts(a);
+		break;
+	case ALTQT_HFSC:
+		print_hfsc_opts(a, qopts);
+		break;
+	}
+}
+
+/*
+ * eval_pfaltq computes the discipline parameters.
+ */
+int
+eval_pfaltq(struct pfctl *pf, struct pf_altq *pa, struct node_queue_bw *bw,
+    struct node_queue_opt *opts)
+{
+	u_int	rate, size, errors = 0;
+
+	if (bw->bw_absolute > 0)
+		pa->ifbandwidth = bw->bw_absolute;
+	else
+#ifdef __FreeBSD__
+		if ((rate = getifspeed(pf->dev, pa->ifname)) == 0) {
+#else
+		if ((rate = getifspeed(pa->ifname)) == 0) {
+#endif
+			fprintf(stderr, "interface %s does not know its bandwidth, "
+			    "please specify an absolute bandwidth\n",
+			    pa->ifname);
+			errors++;
+		} else if ((pa->ifbandwidth = eval_bwspec(bw, rate)) == 0)
+			pa->ifbandwidth = rate;
+
+	errors += eval_queue_opts(pa, opts, pa->ifbandwidth);
+
+	/* if tbrsize is not specified, use heuristics */
+	if (pa->tbrsize == 0) {
+		rate = pa->ifbandwidth;
+		if (rate <= 1 * 1000 * 1000)
+			size = 1;
+		else if (rate <= 10 * 1000 * 1000)
+			size = 4;
+		else if (rate <= 200 * 1000 * 1000)
+			size = 8;
+		else
+			size = 24;
+		size = size * getifmtu(pa->ifname);
+		if (size > 0xffff)
+			size = 0xffff;
+		pa->tbrsize = size;
+	}
+	return (errors);
+}
+
+/*
+ * check_commit_altq does consistency check for each interface
+ */
+int
+check_commit_altq(int dev, int opts)
+{
+	struct pf_altq	*altq;
+	int		 error = 0;
+
+	/* call the discipline check for each interface. */
+	TAILQ_FOREACH(altq, &altqs, entries) {
+		if (altq->qname[0] == 0) {
+			switch (altq->scheduler) {
+			case ALTQT_CBQ:
+				error = check_commit_cbq(dev, opts, altq);
+				break;
+			case ALTQT_PRIQ:
+				error = check_commit_priq(dev, opts, altq);
+				break;
+			case ALTQT_HFSC:
+				error = check_commit_hfsc(dev, opts, altq);
+				break;
+			default:
+				break;
+			}
+		}
+	}
+	return (error);
+}
+
+/*
+ * eval_pfqueue computes the queue parameters.
+ */
+int
+eval_pfqueue(struct pfctl *pf, struct pf_altq *pa, struct node_queue_bw *bw,
+    struct node_queue_opt *opts)
+{
+	/* should be merged with expand_queue */
+	struct pf_altq	*if_pa, *parent, *altq;
+	u_int32_t	 bwsum;
+	int		 error = 0;
+
+	/* find the corresponding interface and copy fields used by queues */
+	if ((if_pa = pfaltq_lookup(pa->ifname)) == NULL) {
+		fprintf(stderr, "altq not defined on %s\n", pa->ifname);
+		return (1);
+	}
+	pa->scheduler = if_pa->scheduler;
+	pa->ifbandwidth = if_pa->ifbandwidth;
+
+	if (qname_to_pfaltq(pa->qname, pa->ifname) != NULL) {
+		fprintf(stderr, "queue %s already exists on interface %s\n",
+		    pa->qname, pa->ifname);
+		return (1);
+	}
+	pa->qid = qname_to_qid(pa->qname);
+
+	parent = NULL;
+	if (pa->parent[0] != 0) {
+		parent = qname_to_pfaltq(pa->parent, pa->ifname);
+		if (parent == NULL) {
+			fprintf(stderr, "parent %s not found for %s\n",
+			    pa->parent, pa->qname);
+			return (1);
+		}
+		pa->parent_qid = parent->qid;
+	}
+	if (pa->qlimit == 0)
+		pa->qlimit = DEFAULT_QLIMIT;
+
+	if (pa->scheduler == ALTQT_CBQ || pa->scheduler == ALTQT_HFSC) {
+		pa->bandwidth = eval_bwspec(bw,
+		    parent == NULL ? 0 : parent->bandwidth);
+
+		if (pa->bandwidth > pa->ifbandwidth) {
+			fprintf(stderr, "bandwidth for %s higher than "
+			    "interface\n", pa->qname);
+			return (1);
+		}
+		/* check the sum of the child bandwidth is under parent's */
+		if (parent != NULL) {
+			if (pa->bandwidth > parent->bandwidth) {
+				warnx("bandwidth for %s higher than parent",
+				    pa->qname);
+				return (1);
+			}
+			bwsum = 0;
+			TAILQ_FOREACH(altq, &altqs, entries) {
+				if (strncmp(altq->ifname, pa->ifname,
+				    IFNAMSIZ) == 0 &&
+				    altq->qname[0] != 0 &&
+				    strncmp(altq->parent, pa->parent,
+				    PF_QNAME_SIZE) == 0)
+					bwsum += altq->bandwidth;
+			}
+			bwsum += pa->bandwidth;
+			if (bwsum > parent->bandwidth) {
+				warnx("the sum of the child bandwidth higher"
+				    " than parent \"%s\"", parent->qname);
+			}
+		}
+	}
+
+	if (eval_queue_opts(pa, opts, parent == NULL? 0 : parent->bandwidth))
+		return (1);
+
+	switch (pa->scheduler) {
+	case ALTQT_CBQ:
+		error = eval_pfqueue_cbq(pf, pa);
+		break;
+	case ALTQT_PRIQ:
+		error = eval_pfqueue_priq(pf, pa);
+		break;
+	case ALTQT_HFSC:
+		error = eval_pfqueue_hfsc(pf, pa);
+		break;
+	default:
+		break;
+	}
+	return (error);
+}
+
+/*
+ * CBQ support functions
+ */
+#define	RM_FILTER_GAIN	5	/* log2 of gain, e.g., 5 => 31/32 */
+#define	RM_NS_PER_SEC	(1000000000)
+
+static int
+eval_pfqueue_cbq(struct pfctl *pf, struct pf_altq *pa)
+{
+	struct cbq_opts	*opts;
+	u_int		 ifmtu;
+
+	if (pa->priority >= CBQ_MAXPRI) {
+		warnx("priority out of range: max %d", CBQ_MAXPRI - 1);
+		return (-1);
+	}
+
+	ifmtu = getifmtu(pa->ifname);
+	opts = &pa->pq_u.cbq_opts;
+
+	if (opts->pktsize == 0) {	/* use default */
+		opts->pktsize = ifmtu;
+		if (opts->pktsize > MCLBYTES)	/* do what TCP does */
+			opts->pktsize &= ~MCLBYTES;
+	} else if (opts->pktsize > ifmtu)
+		opts->pktsize = ifmtu;
+	if (opts->maxpktsize == 0)	/* use default */
+		opts->maxpktsize = ifmtu;
+	else if (opts->maxpktsize > ifmtu)
+		opts->pktsize = ifmtu;
+
+	if (opts->pktsize > opts->maxpktsize)
+		opts->pktsize = opts->maxpktsize;
+
+	if (pa->parent[0] == 0)
+		opts->flags |= (CBQCLF_ROOTCLASS | CBQCLF_WRR);
+
+	cbq_compute_idletime(pf, pa);
+	return (0);
+}
+
+/*
+ * compute ns_per_byte, maxidle, minidle, and offtime
+ */
+static int
+cbq_compute_idletime(struct pfctl *pf, struct pf_altq *pa)
+{
+	struct cbq_opts	*opts;
+	double		 maxidle_s, maxidle, minidle;
+	double		 offtime, nsPerByte, ifnsPerByte, ptime, cptime;
+	double		 z, g, f, gton, gtom;
+	u_int		 minburst, maxburst;
+
+	opts = &pa->pq_u.cbq_opts;
+	ifnsPerByte = (1.0 / (double)pa->ifbandwidth) * RM_NS_PER_SEC * 8;
+	minburst = opts->minburst;
+	maxburst = opts->maxburst;
+
+	if (pa->bandwidth == 0)
+		f = 0.0001;	/* small enough? */
+	else
+		f = ((double) pa->bandwidth / (double) pa->ifbandwidth);
+
+	nsPerByte = ifnsPerByte / f;
+	ptime = (double)opts->pktsize * ifnsPerByte;
+	cptime = ptime * (1.0 - f) / f;
+
+	if (nsPerByte * (double)opts->maxpktsize > (double)INT_MAX) {
+		/*
+		 * this causes integer overflow in kernel!
+		 * (bandwidth < 6Kbps when max_pkt_size=1500)
+		 */
+		if (pa->bandwidth != 0 && (pf->opts & PF_OPT_QUIET) == 0)
+			warnx("queue bandwidth must be larger than %s",
+			    rate2str(ifnsPerByte * (double)opts->maxpktsize /
+			    (double)INT_MAX * (double)pa->ifbandwidth));
+			fprintf(stderr, "cbq: queue %s is too slow!\n",
+			    pa->qname);
+		nsPerByte = (double)(INT_MAX / opts->maxpktsize);
+	}
+
+	if (maxburst == 0) {  /* use default */
+		if (cptime > 10.0 * 1000000)
+			maxburst = 4;
+		else
+			maxburst = 16;
+	}
+	if (minburst == 0)  /* use default */
+		minburst = 2;
+	if (minburst > maxburst)
+		minburst = maxburst;
+
+	z = (double)(1 << RM_FILTER_GAIN);
+	g = (1.0 - 1.0 / z);
+	gton = pow(g, (double)maxburst);
+	gtom = pow(g, (double)(minburst-1));
+	maxidle = ((1.0 / f - 1.0) * ((1.0 - gton) / gton));
+	maxidle_s = (1.0 - g);
+	if (maxidle > maxidle_s)
+		maxidle = ptime * maxidle;
+	else
+		maxidle = ptime * maxidle_s;
+	offtime = cptime * (1.0 + 1.0/(1.0 - g) * (1.0 - gtom) / gtom);
+	minidle = -((double)opts->maxpktsize * (double)nsPerByte);
+
+	/* scale parameters */
+	maxidle = ((maxidle * 8.0) / nsPerByte) *
+	    pow(2.0, (double)RM_FILTER_GAIN);
+	offtime = (offtime * 8.0) / nsPerByte *
+	    pow(2.0, (double)RM_FILTER_GAIN);
+	minidle = ((minidle * 8.0) / nsPerByte) *
+	    pow(2.0, (double)RM_FILTER_GAIN);
+
+	maxidle = maxidle / 1000.0;
+	offtime = offtime / 1000.0;
+	minidle = minidle / 1000.0;
+
+	opts->minburst = minburst;
+	opts->maxburst = maxburst;
+	opts->ns_per_byte = (u_int)nsPerByte;
+	opts->maxidle = (u_int)fabs(maxidle);
+	opts->minidle = (int)minidle;
+	opts->offtime = (u_int)fabs(offtime);
+
+	return (0);
+}
+
+static int
+check_commit_cbq(int dev, int opts, struct pf_altq *pa)
+{
+	struct pf_altq	*altq;
+	int		 root_class, default_class;
+	int		 error = 0;
+
+	/*
+	 * check if cbq has one root queue and one default queue
+	 * for this interface
+	 */
+	root_class = default_class = 0;
+	TAILQ_FOREACH(altq, &altqs, entries) {
+		if (strncmp(altq->ifname, pa->ifname, IFNAMSIZ) != 0)
+			continue;
+		if (altq->qname[0] == 0)  /* this is for interface */
+			continue;
+		if (altq->pq_u.cbq_opts.flags & CBQCLF_ROOTCLASS)
+			root_class++;
+		if (altq->pq_u.cbq_opts.flags & CBQCLF_DEFCLASS)
+			default_class++;
+	}
+	if (root_class != 1) {
+		warnx("should have one root queue on %s", pa->ifname);
+		error++;
+	}
+	if (default_class != 1) {
+		warnx("should have one default queue on %s", pa->ifname);
+		error++;
+	}
+	return (error);
+}
+
+static int
+print_cbq_opts(const struct pf_altq *a)
+{
+	const struct cbq_opts	*opts;
+
+	opts = &a->pq_u.cbq_opts;
+	if (opts->flags) {
+		printf("cbq(");
+		if (opts->flags & CBQCLF_RED)
+			printf(" red");
+		if (opts->flags & CBQCLF_ECN)
+			printf(" ecn");
+		if (opts->flags & CBQCLF_RIO)
+			printf(" rio");
+		if (opts->flags & CBQCLF_CLEARDSCP)
+			printf(" cleardscp");
+		if (opts->flags & CBQCLF_FLOWVALVE)
+			printf(" flowvalve");
+		if (opts->flags & CBQCLF_BORROW)
+			printf(" borrow");
+		if (opts->flags & CBQCLF_WRR)
+			printf(" wrr");
+		if (opts->flags & CBQCLF_EFFICIENT)
+			printf(" efficient");
+		if (opts->flags & CBQCLF_ROOTCLASS)
+			printf(" root");
+		if (opts->flags & CBQCLF_DEFCLASS)
+			printf(" default");
+		printf(" ) ");
+
+		return (1);
+	} else
+		return (0);
+}
+
+/*
+ * PRIQ support functions
+ */
+static int
+eval_pfqueue_priq(struct pfctl *pf, struct pf_altq *pa)
+{
+	struct pf_altq	*altq;
+
+	if (pa->priority >= PRIQ_MAXPRI) {
+		warnx("priority out of range: max %d", PRIQ_MAXPRI - 1);
+		return (-1);
+	}
+	/* the priority should be unique for the interface */
+	TAILQ_FOREACH(altq, &altqs, entries) {
+		if (strncmp(altq->ifname, pa->ifname, IFNAMSIZ) == 0 &&
+		    altq->qname[0] != 0 && altq->priority == pa->priority) {
+			warnx("%s and %s have the same priority",
+			    altq->qname, pa->qname);
+			return (-1);
+		}
+	}
+
+	return (0);
+}
+
+static int
+check_commit_priq(int dev, int opts, struct pf_altq *pa)
+{
+	struct pf_altq	*altq;
+	int		 default_class;
+	int		 error = 0;
+
+	/*
+	 * check if priq has one default class for this interface
+	 */
+	default_class = 0;
+	TAILQ_FOREACH(altq, &altqs, entries) {
+		if (strncmp(altq->ifname, pa->ifname, IFNAMSIZ) != 0)
+			continue;
+		if (altq->qname[0] == 0)  /* this is for interface */
+			continue;
+		if (altq->pq_u.priq_opts.flags & PRCF_DEFAULTCLASS)
+			default_class++;
+	}
+	if (default_class != 1) {
+		warnx("should have one default queue on %s", pa->ifname);
+		error++;
+	}
+	return (error);
+}
+
+static int
+print_priq_opts(const struct pf_altq *a)
+{
+	const struct priq_opts	*opts;
+
+	opts = &a->pq_u.priq_opts;
+
+	if (opts->flags) {
+		printf("priq(");
+		if (opts->flags & PRCF_RED)
+			printf(" red");
+		if (opts->flags & PRCF_ECN)
+			printf(" ecn");
+		if (opts->flags & PRCF_RIO)
+			printf(" rio");
+		if (opts->flags & PRCF_CLEARDSCP)
+			printf(" cleardscp");
+		if (opts->flags & PRCF_DEFAULTCLASS)
+			printf(" default");
+		printf(" ) ");
+
+		return (1);
+	} else
+		return (0);
+}
+
+/*
+ * HFSC support functions
+ */
+static int
+eval_pfqueue_hfsc(struct pfctl *pf, struct pf_altq *pa)
+{
+	struct pf_altq		*altq, *parent;
+	struct hfsc_opts	*opts;
+	struct service_curve	 sc;
+
+	opts = &pa->pq_u.hfsc_opts;
+
+	if (pa->parent[0] == 0) {
+		/* root queue */
+		opts->lssc_m1 = pa->ifbandwidth;
+		opts->lssc_m2 = pa->ifbandwidth;
+		opts->lssc_d = 0;
+		return (0);
+	}
+
+	LIST_INIT(&rtsc);
+	LIST_INIT(&lssc);
+
+	/* if link_share is not specified, use bandwidth */
+	if (opts->lssc_m2 == 0)
+		opts->lssc_m2 = pa->bandwidth;
+
+	if ((opts->rtsc_m1 > 0 && opts->rtsc_m2 == 0) ||
+	    (opts->lssc_m1 > 0 && opts->lssc_m2 == 0) ||
+	    (opts->ulsc_m1 > 0 && opts->ulsc_m2 == 0)) {
+		warnx("m2 is zero for %s", pa->qname);
+		return (-1);
+	}
+
+	if ((opts->rtsc_m1 < opts->rtsc_m2 && opts->rtsc_m1 != 0) ||
+	    (opts->lssc_m1 < opts->lssc_m2 && opts->lssc_m1 != 0) ||
+	    (opts->ulsc_m1 < opts->ulsc_m2 && opts->ulsc_m1 != 0)) {
+		warnx("m1 must be zero for convex curve: %s", pa->qname);
+		return (-1);
+	}
+
+	/*
+	 * admission control:
+	 * for the real-time service curve, the sum of the service curves
+	 * should not exceed 80% of the interface bandwidth.  20% is reserved
+	 * not to over-commit the actual interface bandwidth.
+	 * for the linkshare service curve, the sum of the child service
+	 * curve should not exceed the parent service curve.
+	 * for the upper-limit service curve, the assigned bandwidth should
+	 * be smaller than the interface bandwidth, and the upper-limit should
+	 * be larger than the real-time service curve when both are defined.
+	 */
+	parent = qname_to_pfaltq(pa->parent, pa->ifname);
+	if (parent == NULL)
+		errx(1, "parent %s not found for %s", pa->parent, pa->qname);
+
+	TAILQ_FOREACH(altq, &altqs, entries) {
+		if (strncmp(altq->ifname, pa->ifname, IFNAMSIZ) != 0)
+			continue;
+		if (altq->qname[0] == 0)  /* this is for interface */
+			continue;
+
+		/* if the class has a real-time service curve, add it. */
+		if (opts->rtsc_m2 != 0 && altq->pq_u.hfsc_opts.rtsc_m2 != 0) {
+			sc.m1 = altq->pq_u.hfsc_opts.rtsc_m1;
+			sc.d = altq->pq_u.hfsc_opts.rtsc_d;
+			sc.m2 = altq->pq_u.hfsc_opts.rtsc_m2;
+			gsc_add_sc(&rtsc, &sc);
+		}
+
+		if (strncmp(altq->parent, pa->parent, PF_QNAME_SIZE) != 0)
+			continue;
+
+		/* if the class has a linkshare service curve, add it. */
+		if (opts->lssc_m2 != 0 && altq->pq_u.hfsc_opts.lssc_m2 != 0) {
+			sc.m1 = altq->pq_u.hfsc_opts.lssc_m1;
+			sc.d = altq->pq_u.hfsc_opts.lssc_d;
+			sc.m2 = altq->pq_u.hfsc_opts.lssc_m2;
+			gsc_add_sc(&lssc, &sc);
+		}
+	}
+
+	/* check the real-time service curve.  reserve 20% of interface bw */
+	if (opts->rtsc_m2 != 0) {
+		/* add this queue to the sum */
+		sc.m1 = opts->rtsc_m1;
+		sc.d = opts->rtsc_d;
+		sc.m2 = opts->rtsc_m2;
+		gsc_add_sc(&rtsc, &sc);
+		/* compare the sum with 80% of the interface */
+		sc.m1 = 0;
+		sc.d = 0;
+		sc.m2 = pa->ifbandwidth / 100 * 80;
+		if (!is_gsc_under_sc(&rtsc, &sc)) {
+			warnx("real-time sc exceeds 80%% of the interface "
+			    "bandwidth (%s)", rate2str((double)sc.m2));
+			goto err_ret;
+		}
+	}
+
+	/* check the linkshare service curve. */
+	if (opts->lssc_m2 != 0) {
+		/* add this queue to the child sum */
+		sc.m1 = opts->lssc_m1;
+		sc.d = opts->lssc_d;
+		sc.m2 = opts->lssc_m2;
+		gsc_add_sc(&lssc, &sc);
+		/* compare the sum of the children with parent's sc */
+		sc.m1 = parent->pq_u.hfsc_opts.lssc_m1;
+		sc.d = parent->pq_u.hfsc_opts.lssc_d;
+		sc.m2 = parent->pq_u.hfsc_opts.lssc_m2;
+		if (!is_gsc_under_sc(&lssc, &sc)) {
+			warnx("linkshare sc exceeds parent's sc");
+			goto err_ret;
+		}
+	}
+
+	/* check the upper-limit service curve. */
+	if (opts->ulsc_m2 != 0) {
+		if (opts->ulsc_m1 > pa->ifbandwidth ||
+		    opts->ulsc_m2 > pa->ifbandwidth) {
+			warnx("upper-limit larger than interface bandwidth");
+			goto err_ret;
+		}
+		if (opts->rtsc_m2 != 0 && opts->rtsc_m2 > opts->ulsc_m2) {
+			warnx("upper-limit sc smaller than real-time sc");
+			goto err_ret;
+		}
+	}
+
+	gsc_destroy(&rtsc);
+	gsc_destroy(&lssc);
+
+	return (0);
+
+err_ret:
+	gsc_destroy(&rtsc);
+	gsc_destroy(&lssc);
+	return (-1);
+}
+
+static int
+check_commit_hfsc(int dev, int opts, struct pf_altq *pa)
+{
+	struct pf_altq	*altq, *def = NULL;
+	int		 default_class;
+	int		 error = 0;
+
+	/* check if hfsc has one default queue for this interface */
+	default_class = 0;
+	TAILQ_FOREACH(altq, &altqs, entries) {
+		if (strncmp(altq->ifname, pa->ifname, IFNAMSIZ) != 0)
+			continue;
+		if (altq->qname[0] == 0)  /* this is for interface */
+			continue;
+		if (altq->parent[0] == 0)  /* dummy root */
+			continue;
+		if (altq->pq_u.hfsc_opts.flags & HFCF_DEFAULTCLASS) {
+			default_class++;
+			def = altq;
+		}
+	}
+	if (default_class != 1) {
+		warnx("should have one default queue on %s", pa->ifname);
+		return (1);
+	}
+	/* make sure the default queue is a leaf */
+	TAILQ_FOREACH(altq, &altqs, entries) {
+		if (strncmp(altq->ifname, pa->ifname, IFNAMSIZ) != 0)
+			continue;
+		if (altq->qname[0] == 0)  /* this is for interface */
+			continue;
+		if (strncmp(altq->parent, def->qname, PF_QNAME_SIZE) == 0) {
+			warnx("default queue is not a leaf");
+			error++;
+		}
+	}
+	return (error);
+}
+
+static int
+print_hfsc_opts(const struct pf_altq *a, const struct node_queue_opt *qopts)
+{
+	const struct hfsc_opts		*opts;
+	const struct node_hfsc_sc	*rtsc, *lssc, *ulsc;
+
+	opts = &a->pq_u.hfsc_opts;
+	if (qopts == NULL)
+		rtsc = lssc = ulsc = NULL;
+	else {
+		rtsc = &qopts->data.hfsc_opts.realtime;
+		lssc = &qopts->data.hfsc_opts.linkshare;
+		ulsc = &qopts->data.hfsc_opts.upperlimit;
+	}
+
+	if (opts->flags || opts->rtsc_m2 != 0 || opts->ulsc_m2 != 0 ||
+	    (opts->lssc_m2 != 0 && (opts->lssc_m2 != a->bandwidth ||
+	    opts->lssc_d != 0))) {
+		printf("hfsc(");
+		if (opts->flags & HFCF_RED)
+			printf(" red");
+		if (opts->flags & HFCF_ECN)
+			printf(" ecn");
+		if (opts->flags & HFCF_RIO)
+			printf(" rio");
+		if (opts->flags & HFCF_CLEARDSCP)
+			printf(" cleardscp");
+		if (opts->flags & HFCF_DEFAULTCLASS)
+			printf(" default");
+		if (opts->rtsc_m2 != 0)
+			print_hfsc_sc("realtime", opts->rtsc_m1, opts->rtsc_d,
+			    opts->rtsc_m2, rtsc);
+		if (opts->lssc_m2 != 0 && (opts->lssc_m2 != a->bandwidth ||
+		    opts->lssc_d != 0))
+			print_hfsc_sc("linkshare", opts->lssc_m1, opts->lssc_d,
+			    opts->lssc_m2, lssc);
+		if (opts->ulsc_m2 != 0)
+			print_hfsc_sc("upperlimit", opts->ulsc_m1, opts->ulsc_d,
+			    opts->ulsc_m2, ulsc);
+		printf(" ) ");
+
+		return (1);
+	} else
+		return (0);
+}
+
+/*
+ * admission control using generalized service curve
+ */
+
+/* add a new service curve to a generalized service curve */
+static void
+gsc_add_sc(struct gen_sc *gsc, struct service_curve *sc)
+{
+	if (is_sc_null(sc))
+		return;
+	if (sc->d != 0)
+		gsc_add_seg(gsc, 0.0, 0.0, (double)sc->d, (double)sc->m1);
+	gsc_add_seg(gsc, (double)sc->d, 0.0, INFINITY, (double)sc->m2);
+}
+
+/*
+ * check whether all points of a generalized service curve have
+ * their y-coordinates no larger than a given two-piece linear
+ * service curve.
+ */
+static int
+is_gsc_under_sc(struct gen_sc *gsc, struct service_curve *sc)
+{
+	struct segment	*s, *last, *end;
+	double		 y;
+
+	if (is_sc_null(sc)) {
+		if (LIST_EMPTY(gsc))
+			return (1);
+		LIST_FOREACH(s, gsc, _next) {
+			if (s->m != 0)
+				return (0);
+		}
+		return (1);
+	}
+	/*
+	 * gsc has a dummy entry at the end with x = INFINITY.
+	 * loop through up to this dummy entry.
+	 */
+	end = gsc_getentry(gsc, INFINITY);
+	if (end == NULL)
+		return (1);
+	last = NULL;
+	for (s = LIST_FIRST(gsc); s != end; s = LIST_NEXT(s, _next)) {
+		if (s->y > sc_x2y(sc, s->x))
+			return (0);
+		last = s;
+	}
+	/* last now holds the real last segment */
+	if (last == NULL)
+		return (1);
+	if (last->m > sc->m2)
+		return (0);
+	if (last->x < sc->d && last->m > sc->m1) {
+		y = last->y + (sc->d - last->x) * last->m;
+		if (y > sc_x2y(sc, sc->d))
+			return (0);
+	}
+	return (1);
+}
+
+static void
+gsc_destroy(struct gen_sc *gsc)
+{
+	struct segment	*s;
+
+	while ((s = LIST_FIRST(gsc)) != NULL) {
+		LIST_REMOVE(s, _next);
+		free(s);
+	}
+}
+
+/*
+ * return a segment entry starting at x.
+ * if gsc has no entry starting at x, a new entry is created at x.
+ */
+static struct segment *
+gsc_getentry(struct gen_sc *gsc, double x)
+{
+	struct segment	*new, *prev, *s;
+
+	prev = NULL;
+	LIST_FOREACH(s, gsc, _next) {
+		if (s->x == x)
+			return (s);	/* matching entry found */
+		else if (s->x < x)
+			prev = s;
+		else
+			break;
+	}
+
+	/* we have to create a new entry */
+	if ((new = calloc(1, sizeof(struct segment))) == NULL)
+		return (NULL);
+
+	new->x = x;
+	if (x == INFINITY || s == NULL)
+		new->d = 0;
+	else if (s->x == INFINITY)
+		new->d = INFINITY;
+	else
+		new->d = s->x - x;
+	if (prev == NULL) {
+		/* insert the new entry at the head of the list */
+		new->y = 0;
+		new->m = 0;
+		LIST_INSERT_HEAD(gsc, new, _next);
+	} else {
+		/*
+		 * the start point intersects with the segment pointed by
+		 * prev.  divide prev into 2 segments
+		 */
+		if (x == INFINITY) {
+			prev->d = INFINITY;
+			if (prev->m == 0)
+				new->y = prev->y;
+			else
+				new->y = INFINITY;
+		} else {
+			prev->d = x - prev->x;
+			new->y = prev->d * prev->m + prev->y;
+		}
+		new->m = prev->m;
+		LIST_INSERT_AFTER(prev, new, _next);
+	}
+	return (new);
+}
+
+/* add a segment to a generalized service curve */
+static int
+gsc_add_seg(struct gen_sc *gsc, double x, double y, double d, double m)
+{
+	struct segment	*start, *end, *s;
+	double		 x2;
+
+	if (d == INFINITY)
+		x2 = INFINITY;
+	else
+		x2 = x + d;
+	start = gsc_getentry(gsc, x);
+	end = gsc_getentry(gsc, x2);
+	if (start == NULL || end == NULL)
+		return (-1);
+
+	for (s = start; s != end; s = LIST_NEXT(s, _next)) {
+		s->m += m;
+		s->y += y + (s->x - x) * m;
+	}
+
+	end = gsc_getentry(gsc, INFINITY);
+	for (; s != end; s = LIST_NEXT(s, _next)) {
+		s->y += m * d;
+	}
+
+	return (0);
+}
+
+/* get y-projection of a service curve */
+static double
+sc_x2y(struct service_curve *sc, double x)
+{
+	double	y;
+
+	if (x <= (double)sc->d)
+		/* y belongs to the 1st segment */
+		y = x * (double)sc->m1;
+	else
+		/* y belongs to the 2nd segment */
+		y = (double)sc->d * (double)sc->m1
+			+ (x - (double)sc->d) * (double)sc->m2;
+	return (y);
+}
+
+/*
+ * misc utilities
+ */
+#define	R2S_BUFS	8
+#define	RATESTR_MAX	16
+
+char *
+rate2str(double rate)
+{
+	char		*buf;
+	static char	 r2sbuf[R2S_BUFS][RATESTR_MAX];  /* ring bufer */
+	static int	 idx = 0;
+	int		 i;
+	static const char unit[] = " KMG";
+
+	buf = r2sbuf[idx++];
+	if (idx == R2S_BUFS)
+		idx = 0;
+
+	for (i = 0; rate >= 1000 && i <= 3; i++)
+		rate /= 1000;
+
+	if ((int)(rate * 100) % 100)
+		snprintf(buf, RATESTR_MAX, "%.2f%cb", rate, unit[i]);
+	else
+		snprintf(buf, RATESTR_MAX, "%d%cb", (int)rate, unit[i]);
+
+	return (buf);
+}
+
+#ifdef __FreeBSD__
+/*
+ * XXX
+ * FreeBSD does not have SIOCGIFDATA.
+ * To emulate this, DIOCGIFSPEED ioctl added to pf.
+ */
+u_int32_t
+getifspeed(int pfdev, char *ifname)
+{
+	struct pf_ifspeed io;
+
+	bzero(&io, sizeof io);
+	if (strlcpy(io.ifname, ifname, IFNAMSIZ) >=
+	    sizeof(io.ifname)) 
+		errx(1, "getifspeed: strlcpy");
+	if (ioctl(pfdev, DIOCGIFSPEED, &io) == -1)
+		err(1, "DIOCGIFSPEED");
+	return ((u_int32_t)io.baudrate);
+}
+#else
+u_int32_t
+getifspeed(char *ifname)
+{
+	int		s;
+	struct ifreq	ifr;
+	struct if_data	ifrdat;
+
+	if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
+		err(1, "socket");
+	bzero(&ifr, sizeof(ifr));
+	if (strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)) >=
+	    sizeof(ifr.ifr_name))
+		errx(1, "getifspeed: strlcpy");
+	ifr.ifr_data = (caddr_t)&ifrdat;
+	if (ioctl(s, SIOCGIFDATA, (caddr_t)&ifr) == -1)
+		err(1, "SIOCGIFDATA");
+	if (close(s))
+		err(1, "close");
+	return ((u_int32_t)ifrdat.ifi_baudrate);
+}
+#endif
+
+u_long
+getifmtu(char *ifname)
+{
+	int		s;
+	struct ifreq	ifr;
+
+	if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
+		err(1, "socket");
+	bzero(&ifr, sizeof(ifr));
+	if (strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)) >=
+	    sizeof(ifr.ifr_name))
+		errx(1, "getifmtu: strlcpy");
+	if (ioctl(s, SIOCGIFMTU, (caddr_t)&ifr) == -1)
+#ifdef __FreeBSD__
+		ifr.ifr_mtu = 1500;
+#else
+		err(1, "SIOCGIFMTU");
+#endif
+	if (close(s))
+		err(1, "close");
+	if (ifr.ifr_mtu > 0)
+		return (ifr.ifr_mtu);
+	else {
+		warnx("could not get mtu for %s, assuming 1500", ifname);
+		return (1500);
+	}
+}
+
+int
+eval_queue_opts(struct pf_altq *pa, struct node_queue_opt *opts,
+    u_int32_t ref_bw)
+{
+	int	errors = 0;
+
+	switch (pa->scheduler) {
+	case ALTQT_CBQ:
+		pa->pq_u.cbq_opts = opts->data.cbq_opts;
+		break;
+	case ALTQT_PRIQ:
+		pa->pq_u.priq_opts = opts->data.priq_opts;
+		break;
+	case ALTQT_HFSC:
+		pa->pq_u.hfsc_opts.flags = opts->data.hfsc_opts.flags;
+		if (opts->data.hfsc_opts.linkshare.used) {
+			pa->pq_u.hfsc_opts.lssc_m1 =
+			    eval_bwspec(&opts->data.hfsc_opts.linkshare.m1,
+			    ref_bw);
+			pa->pq_u.hfsc_opts.lssc_m2 =
+			    eval_bwspec(&opts->data.hfsc_opts.linkshare.m2,
+			    ref_bw);
+			pa->pq_u.hfsc_opts.lssc_d =
+			    opts->data.hfsc_opts.linkshare.d;
+		}
+		if (opts->data.hfsc_opts.realtime.used) {
+			pa->pq_u.hfsc_opts.rtsc_m1 =
+			    eval_bwspec(&opts->data.hfsc_opts.realtime.m1,
+			    ref_bw);
+			pa->pq_u.hfsc_opts.rtsc_m2 =
+			    eval_bwspec(&opts->data.hfsc_opts.realtime.m2,
+			    ref_bw);
+			pa->pq_u.hfsc_opts.rtsc_d =
+			    opts->data.hfsc_opts.realtime.d;
+		}
+		if (opts->data.hfsc_opts.upperlimit.used) {
+			pa->pq_u.hfsc_opts.ulsc_m1 =
+			    eval_bwspec(&opts->data.hfsc_opts.upperlimit.m1,
+			    ref_bw);
+			pa->pq_u.hfsc_opts.ulsc_m2 =
+			    eval_bwspec(&opts->data.hfsc_opts.upperlimit.m2,
+			    ref_bw);
+			pa->pq_u.hfsc_opts.ulsc_d =
+			    opts->data.hfsc_opts.upperlimit.d;
+		}
+		break;
+	default:
+		warnx("eval_queue_opts: unknown scheduler type %u",
+		    opts->qtype);
+		errors++;
+		break;
+	}
+
+	return (errors);
+}
+
+u_int32_t
+eval_bwspec(struct node_queue_bw *bw, u_int32_t ref_bw)
+{
+	if (bw->bw_absolute > 0)
+		return (bw->bw_absolute);
+
+	if (bw->bw_percent > 0)
+		return (ref_bw / 100 * bw->bw_percent);
+
+	return (0);
+}
+
+void
+print_hfsc_sc(const char *scname, u_int m1, u_int d, u_int m2,
+    const struct node_hfsc_sc *sc)
+{
+	printf(" %s", scname);
+
+	if (d != 0) {
+		printf("(");
+		if (sc != NULL && sc->m1.bw_percent > 0)
+			printf("%u%%", sc->m1.bw_percent);
+		else
+			printf("%s", rate2str((double)m1));
+		printf(" %u", d);
+	}
+
+	if (sc != NULL && sc->m2.bw_percent > 0)
+		printf(" %u%%", sc->m2.bw_percent);
+	else
+		printf(" %s", rate2str((double)m2));
+
+	if (d != 0)
+		printf(")");
+}
diff --git a/freebsd/contrib/pf/pfctl/pfctl_optimize.c b/freebsd/contrib/pf/pfctl/pfctl_optimize.c
new file mode 100644
index 0000000..b25d5be
--- /dev/null
+++ b/freebsd/contrib/pf/pfctl/pfctl_optimize.c
@@ -0,0 +1,1657 @@
+#include <machine/rtems-bsd-user-space.h>
+
+/*	$OpenBSD: pfctl_optimize.c,v 1.17 2008/05/06 03:45:21 mpf Exp $ */
+
+/*
+ * Copyright (c) 2004 Mike Frantzen <frantzen at openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+
+#include <net/if.h>
+#include <net/pfvar.h>
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <assert.h>
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "pfctl_parser.h"
+#include "pfctl.h"
+
+/* The size at which a table becomes faster than individual rules */
+#define TABLE_THRESHOLD		6
+
+
+/* #define OPT_DEBUG	1 */
+#ifdef OPT_DEBUG
+# define DEBUG(str, v...) \
+	printf("%s: " str "\n", __FUNCTION__ , ## v)
+#else
+# define DEBUG(str, v...) ((void)0)
+#endif
+
+
+/*
+ * A container that lets us sort a superblock to optimize the skip step jumps
+ */
+struct pf_skip_step {
+	int				ps_count;	/* number of items */
+	TAILQ_HEAD( , pf_opt_rule)	ps_rules;
+	TAILQ_ENTRY(pf_skip_step)	ps_entry;
+};
+
+
+/*
+ * A superblock is a block of adjacent rules of similar action.  If there
+ * are five PASS rules in a row, they all become members of a superblock.
+ * Once we have a superblock, we are free to re-order any rules within it
+ * in order to improve performance; if a packet is passed, it doesn't matter
+ * who passed it.
+ */
+struct superblock {
+	TAILQ_HEAD( , pf_opt_rule)		 sb_rules;
+	TAILQ_ENTRY(superblock)			 sb_entry;
+	struct superblock			*sb_profiled_block;
+	TAILQ_HEAD(skiplist, pf_skip_step)	 sb_skipsteps[PF_SKIP_COUNT];
+};
+TAILQ_HEAD(superblocks, superblock);
+
+
+/*
+ * Description of the PF rule structure.
+ */
+enum {
+    BARRIER,	/* the presence of the field puts the rule in it's own block */
+    BREAK,	/* the field may not differ between rules in a superblock */
+    NOMERGE,	/* the field may not differ between rules when combined */
+    COMBINED,	/* the field may itself be combined with other rules */
+    DC,		/* we just don't care about the field */
+    NEVER};	/* we should never see this field set?!? */
+struct pf_rule_field {
+	const char	*prf_name;
+	int		 prf_type;
+	size_t		 prf_offset;
+	size_t		 prf_size;
+} pf_rule_desc[] = {
+#define PF_RULE_FIELD(field, ty)	\
+    {#field,				\
+    ty,					\
+    offsetof(struct pf_rule, field),	\
+    sizeof(((struct pf_rule *)0)->field)}
+
+
+    /*
+     * The presence of these fields in a rule put the rule in it's own
+     * superblock.  Thus it will not be optimized.  It also prevents the
+     * rule from being re-ordered at all.
+     */
+    PF_RULE_FIELD(label,		BARRIER),
+    PF_RULE_FIELD(prob,			BARRIER),
+    PF_RULE_FIELD(max_states,		BARRIER),
+    PF_RULE_FIELD(max_src_nodes,	BARRIER),
+    PF_RULE_FIELD(max_src_states,	BARRIER),
+    PF_RULE_FIELD(max_src_conn,		BARRIER),
+    PF_RULE_FIELD(max_src_conn_rate,	BARRIER),
+    PF_RULE_FIELD(anchor,		BARRIER),	/* for now */
+
+    /*
+     * These fields must be the same between all rules in the same superblock.
+     * These rules are allowed to be re-ordered but only among like rules.
+     * For instance we can re-order all 'tag "foo"' rules because they have the
+     * same tag.  But we can not re-order between a 'tag "foo"' and a
+     * 'tag "bar"' since that would change the meaning of the ruleset.
+     */
+    PF_RULE_FIELD(tagname,		BREAK),
+    PF_RULE_FIELD(keep_state,		BREAK),
+    PF_RULE_FIELD(qname,		BREAK),
+    PF_RULE_FIELD(pqname,		BREAK),
+    PF_RULE_FIELD(rt,			BREAK),
+    PF_RULE_FIELD(allow_opts,		BREAK),
+    PF_RULE_FIELD(rule_flag,		BREAK),
+    PF_RULE_FIELD(action,		BREAK),
+    PF_RULE_FIELD(log,			BREAK),
+    PF_RULE_FIELD(quick,		BREAK),
+    PF_RULE_FIELD(return_ttl,		BREAK),
+    PF_RULE_FIELD(overload_tblname,	BREAK),
+    PF_RULE_FIELD(flush,		BREAK),
+    PF_RULE_FIELD(rpool,		BREAK),
+    PF_RULE_FIELD(logif,		BREAK),
+
+    /*
+     * Any fields not listed in this structure act as BREAK fields
+     */
+
+
+    /*
+     * These fields must not differ when we merge two rules together but
+     * their difference isn't enough to put the rules in different superblocks.
+     * There are no problems re-ordering any rules with these fields.
+     */
+    PF_RULE_FIELD(af,			NOMERGE),
+    PF_RULE_FIELD(ifnot,		NOMERGE),
+    PF_RULE_FIELD(ifname,		NOMERGE),	/* hack for IF groups */
+    PF_RULE_FIELD(match_tag_not,	NOMERGE),
+    PF_RULE_FIELD(match_tagname,	NOMERGE),
+    PF_RULE_FIELD(os_fingerprint,	NOMERGE),
+    PF_RULE_FIELD(timeout,		NOMERGE),
+    PF_RULE_FIELD(return_icmp,		NOMERGE),
+    PF_RULE_FIELD(return_icmp6,		NOMERGE),
+    PF_RULE_FIELD(uid,			NOMERGE),
+    PF_RULE_FIELD(gid,			NOMERGE),
+    PF_RULE_FIELD(direction,		NOMERGE),
+    PF_RULE_FIELD(proto,		NOMERGE),
+    PF_RULE_FIELD(type,			NOMERGE),
+    PF_RULE_FIELD(code,			NOMERGE),
+    PF_RULE_FIELD(flags,		NOMERGE),
+    PF_RULE_FIELD(flagset,		NOMERGE),
+    PF_RULE_FIELD(tos,			NOMERGE),
+    PF_RULE_FIELD(src.port,		NOMERGE),
+    PF_RULE_FIELD(dst.port,		NOMERGE),
+    PF_RULE_FIELD(src.port_op,		NOMERGE),
+    PF_RULE_FIELD(dst.port_op,		NOMERGE),
+    PF_RULE_FIELD(src.neg,		NOMERGE),
+    PF_RULE_FIELD(dst.neg,		NOMERGE),
+
+    /* These fields can be merged */
+    PF_RULE_FIELD(src.addr,		COMBINED),
+    PF_RULE_FIELD(dst.addr,		COMBINED),
+
+    /* We just don't care about these fields.  They're set by the kernel */
+    PF_RULE_FIELD(skip,			DC),
+    PF_RULE_FIELD(evaluations,		DC),
+    PF_RULE_FIELD(packets,		DC),
+    PF_RULE_FIELD(bytes,		DC),
+    PF_RULE_FIELD(kif,			DC),
+    PF_RULE_FIELD(states_cur,		DC),
+    PF_RULE_FIELD(states_tot,		DC),
+    PF_RULE_FIELD(src_nodes,		DC),
+    PF_RULE_FIELD(nr,			DC),
+    PF_RULE_FIELD(entries,		DC),
+    PF_RULE_FIELD(qid,			DC),
+    PF_RULE_FIELD(pqid,			DC),
+    PF_RULE_FIELD(anchor_relative,	DC),
+    PF_RULE_FIELD(anchor_wildcard,	DC),
+    PF_RULE_FIELD(tag,			DC),
+    PF_RULE_FIELD(match_tag,		DC),
+    PF_RULE_FIELD(overload_tbl,		DC),
+
+    /* These fields should never be set in a PASS/BLOCK rule */
+    PF_RULE_FIELD(natpass,		NEVER),
+    PF_RULE_FIELD(max_mss,		NEVER),
+    PF_RULE_FIELD(min_ttl,		NEVER),
+    PF_RULE_FIELD(set_tos,		NEVER),
+};
+
+
+
+int	add_opt_table(struct pfctl *, struct pf_opt_tbl **, sa_family_t,
+	    struct pf_rule_addr *);
+int	addrs_combineable(struct pf_rule_addr *, struct pf_rule_addr *);
+int	addrs_equal(struct pf_rule_addr *, struct pf_rule_addr *);
+int	block_feedback(struct pfctl *, struct superblock *);
+int	combine_rules(struct pfctl *, struct superblock *);
+void	comparable_rule(struct pf_rule *, const struct pf_rule *, int);
+int	construct_superblocks(struct pfctl *, struct pf_opt_queue *,
+	    struct superblocks *);
+void	exclude_supersets(struct pf_rule *, struct pf_rule *);
+int	interface_group(const char *);
+int	load_feedback_profile(struct pfctl *, struct superblocks *);
+int	optimize_superblock(struct pfctl *, struct superblock *);
+int	pf_opt_create_table(struct pfctl *, struct pf_opt_tbl *);
+void	remove_from_skipsteps(struct skiplist *, struct superblock *,
+	    struct pf_opt_rule *, struct pf_skip_step *);
+int	remove_identical_rules(struct pfctl *, struct superblock *);
+int	reorder_rules(struct pfctl *, struct superblock *, int);
+int	rules_combineable(struct pf_rule *, struct pf_rule *);
+void	skip_append(struct superblock *, int, struct pf_skip_step *,
+	    struct pf_opt_rule *);
+int	skip_compare(int, struct pf_skip_step *, struct pf_opt_rule *);
+void	skip_init(void);
+int	skip_cmp_af(struct pf_rule *, struct pf_rule *);
+int	skip_cmp_dir(struct pf_rule *, struct pf_rule *);
+int	skip_cmp_dst_addr(struct pf_rule *, struct pf_rule *);
+int	skip_cmp_dst_port(struct pf_rule *, struct pf_rule *);
+int	skip_cmp_ifp(struct pf_rule *, struct pf_rule *);
+int	skip_cmp_proto(struct pf_rule *, struct pf_rule *);
+int	skip_cmp_src_addr(struct pf_rule *, struct pf_rule *);
+int	skip_cmp_src_port(struct pf_rule *, struct pf_rule *);
+int	superblock_inclusive(struct superblock *, struct pf_opt_rule *);
+void	superblock_free(struct pfctl *, struct superblock *);
+
+
+int (*skip_comparitors[PF_SKIP_COUNT])(struct pf_rule *, struct pf_rule *);
+const char *skip_comparitors_names[PF_SKIP_COUNT];
+#define PF_SKIP_COMPARITORS {				\
+    { "ifp", PF_SKIP_IFP, skip_cmp_ifp },		\
+    { "dir", PF_SKIP_DIR, skip_cmp_dir },		\
+    { "af", PF_SKIP_AF, skip_cmp_af },			\
+    { "proto", PF_SKIP_PROTO, skip_cmp_proto },		\
+    { "saddr", PF_SKIP_SRC_ADDR, skip_cmp_src_addr },	\
+    { "sport", PF_SKIP_SRC_PORT, skip_cmp_src_port },	\
+    { "daddr", PF_SKIP_DST_ADDR, skip_cmp_dst_addr },	\
+    { "dport", PF_SKIP_DST_PORT, skip_cmp_dst_port }	\
+}
+
+struct pfr_buffer table_buffer;
+int table_identifier;
+
+
+int
+pfctl_optimize_ruleset(struct pfctl *pf, struct pf_ruleset *rs)
+{
+	struct superblocks superblocks;
+	struct pf_opt_queue opt_queue;
+	struct superblock *block;
+	struct pf_opt_rule *por;
+	struct pf_rule *r;
+	struct pf_rulequeue *old_rules;
+
+	DEBUG("optimizing ruleset");
+	memset(&table_buffer, 0, sizeof(table_buffer));
+	skip_init();
+	TAILQ_INIT(&opt_queue);
+
+	old_rules = rs->rules[PF_RULESET_FILTER].active.ptr;
+	rs->rules[PF_RULESET_FILTER].active.ptr =
+	    rs->rules[PF_RULESET_FILTER].inactive.ptr;
+	rs->rules[PF_RULESET_FILTER].inactive.ptr = old_rules;
+
+	/*
+	 * XXX expanding the pf_opt_rule format throughout pfctl might allow
+	 * us to avoid all this copying.
+	 */
+	while ((r = TAILQ_FIRST(rs->rules[PF_RULESET_FILTER].inactive.ptr))
+	    != NULL) {
+		TAILQ_REMOVE(rs->rules[PF_RULESET_FILTER].inactive.ptr, r,
+		    entries);
+		if ((por = calloc(1, sizeof(*por))) == NULL)
+			err(1, "calloc");
+		memcpy(&por->por_rule, r, sizeof(*r));
+		if (TAILQ_FIRST(&r->rpool.list) != NULL) {
+			TAILQ_INIT(&por->por_rule.rpool.list);
+			pfctl_move_pool(&r->rpool, &por->por_rule.rpool);
+		} else
+			bzero(&por->por_rule.rpool,
+			    sizeof(por->por_rule.rpool));
+
+
+		TAILQ_INSERT_TAIL(&opt_queue, por, por_entry);
+	}
+
+	TAILQ_INIT(&superblocks);
+	if (construct_superblocks(pf, &opt_queue, &superblocks))
+		goto error;
+
+	if (pf->optimize & PF_OPTIMIZE_PROFILE) {
+		if (load_feedback_profile(pf, &superblocks))
+			goto error;
+	}
+
+	TAILQ_FOREACH(block, &superblocks, sb_entry) {
+		if (optimize_superblock(pf, block))
+			goto error;
+	}
+
+	rs->anchor->refcnt = 0;
+	while ((block = TAILQ_FIRST(&superblocks))) {
+		TAILQ_REMOVE(&superblocks, block, sb_entry);
+
+		while ((por = TAILQ_FIRST(&block->sb_rules))) {
+			TAILQ_REMOVE(&block->sb_rules, por, por_entry);
+			por->por_rule.nr = rs->anchor->refcnt++;
+			if ((r = calloc(1, sizeof(*r))) == NULL)
+				err(1, "calloc");
+			memcpy(r, &por->por_rule, sizeof(*r));
+			TAILQ_INIT(&r->rpool.list);
+			pfctl_move_pool(&por->por_rule.rpool, &r->rpool);
+			TAILQ_INSERT_TAIL(
+			    rs->rules[PF_RULESET_FILTER].active.ptr,
+			    r, entries);
+			free(por);
+		}
+		free(block);
+	}
+
+	return (0);
+
+error:
+	while ((por = TAILQ_FIRST(&opt_queue))) {
+		TAILQ_REMOVE(&opt_queue, por, por_entry);
+		if (por->por_src_tbl) {
+			pfr_buf_clear(por->por_src_tbl->pt_buf);
+			free(por->por_src_tbl->pt_buf);
+			free(por->por_src_tbl);
+		}
+		if (por->por_dst_tbl) {
+			pfr_buf_clear(por->por_dst_tbl->pt_buf);
+			free(por->por_dst_tbl->pt_buf);
+			free(por->por_dst_tbl);
+		}
+		free(por);
+	}
+	while ((block = TAILQ_FIRST(&superblocks))) {
+		TAILQ_REMOVE(&superblocks, block, sb_entry);
+		superblock_free(pf, block);
+	}
+	return (1);
+}
+
+
+/*
+ * Go ahead and optimize a superblock
+ */
+int
+optimize_superblock(struct pfctl *pf, struct superblock *block)
+{
+#ifdef OPT_DEBUG
+	struct pf_opt_rule *por;
+#endif /* OPT_DEBUG */
+
+	/* We have a few optimization passes:
+	 *   1) remove duplicate rules or rules that are a subset of other
+	 *      rules
+	 *   2) combine otherwise identical rules with different IP addresses
+	 *      into a single rule and put the addresses in a table.
+	 *   3) re-order the rules to improve kernel skip steps
+	 *   4) re-order the 'quick' rules based on feedback from the
+	 *      active ruleset statistics
+	 *
+	 * XXX combine_rules() doesn't combine v4 and v6 rules.  would just
+	 *     have to keep af in the table container, make af 'COMBINE' and
+	 *     twiddle the af on the merged rule
+	 * XXX maybe add a weighting to the metric on skipsteps when doing
+	 *     reordering.  sometimes two sequential tables will be better
+	 *     that four consecutive interfaces.
+	 * XXX need to adjust the skipstep count of everything after PROTO,
+	 *     since they aren't actually checked on a proto mismatch in
+	 *     pf_test_{tcp, udp, icmp}()
+	 * XXX should i treat proto=0, af=0 or dir=0 special in skepstep
+	 *     calculation since they are a DC?
+	 * XXX keep last skiplist of last superblock to influence this
+	 *     superblock.  '5 inet6 log' should make '3 inet6' come before '4
+	 *     inet' in the next superblock.
+	 * XXX would be useful to add tables for ports
+	 * XXX we can also re-order some mutually exclusive superblocks to
+	 *     try merging superblocks before any of these optimization passes.
+	 *     for instance a single 'log in' rule in the middle of non-logging
+	 *     out rules.
+	 */
+
+	/* shortcut.  there will be a lot of 1-rule superblocks */
+	if (!TAILQ_NEXT(TAILQ_FIRST(&block->sb_rules), por_entry))
+		return (0);
+
+#ifdef OPT_DEBUG
+	printf("--- Superblock ---\n");
+	TAILQ_FOREACH(por, &block->sb_rules, por_entry) {
+		printf("  ");
+		print_rule(&por->por_rule, por->por_rule.anchor ?
+		    por->por_rule.anchor->name : "", 1, 0);
+	}
+#endif /* OPT_DEBUG */
+
+
+	if (remove_identical_rules(pf, block))
+		return (1);
+	if (combine_rules(pf, block))
+		return (1);
+	if ((pf->optimize & PF_OPTIMIZE_PROFILE) &&
+	    TAILQ_FIRST(&block->sb_rules)->por_rule.quick &&
+	    block->sb_profiled_block) {
+		if (block_feedback(pf, block))
+			return (1);
+	} else if (reorder_rules(pf, block, 0)) {
+		return (1);
+	}
+
+	/*
+	 * Don't add any optimization passes below reorder_rules().  It will
+	 * have divided superblocks into smaller blocks for further refinement
+	 * and doesn't put them back together again.  What once was a true
+	 * superblock might have been split into multiple superblocks.
+	 */
+
+#ifdef OPT_DEBUG
+	printf("--- END Superblock ---\n");
+#endif /* OPT_DEBUG */
+	return (0);
+}
+
+
+/*
+ * Optimization pass #1: remove identical rules
+ */
+int
+remove_identical_rules(struct pfctl *pf, struct superblock *block)
+{
+	struct pf_opt_rule *por1, *por2, *por_next, *por2_next;
+	struct pf_rule a, a2, b, b2;
+
+	for (por1 = TAILQ_FIRST(&block->sb_rules); por1; por1 = por_next) {
+		por_next = TAILQ_NEXT(por1, por_entry);
+		for (por2 = por_next; por2; por2 = por2_next) {
+			por2_next = TAILQ_NEXT(por2, por_entry);
+			comparable_rule(&a, &por1->por_rule, DC);
+			comparable_rule(&b, &por2->por_rule, DC);
+			memcpy(&a2, &a, sizeof(a2));
+			memcpy(&b2, &b, sizeof(b2));
+
+			exclude_supersets(&a, &b);
+			exclude_supersets(&b2, &a2);
+			if (memcmp(&a, &b, sizeof(a)) == 0) {
+				DEBUG("removing identical rule  nr%d = *nr%d*",
+				    por1->por_rule.nr, por2->por_rule.nr);
+				TAILQ_REMOVE(&block->sb_rules, por2, por_entry);
+				if (por_next == por2)
+					por_next = TAILQ_NEXT(por1, por_entry);
+				free(por2);
+			} else if (memcmp(&a2, &b2, sizeof(a2)) == 0) {
+				DEBUG("removing identical rule  *nr%d* = nr%d",
+				    por1->por_rule.nr, por2->por_rule.nr);
+				TAILQ_REMOVE(&block->sb_rules, por1, por_entry);
+				free(por1);
+				break;
+			}
+		}
+	}
+
+	return (0);
+}
+
+
+/*
+ * Optimization pass #2: combine similar rules with different addresses
+ * into a single rule and a table
+ */
+int
+combine_rules(struct pfctl *pf, struct superblock *block)
+{
+	struct pf_opt_rule *p1, *p2, *por_next;
+	int src_eq, dst_eq;
+
+	if ((pf->loadopt & PFCTL_FLAG_TABLE) == 0) {
+		warnx("Must enable table loading for optimizations");
+		return (1);
+	}
+
+	/* First we make a pass to combine the rules.  O(n log n) */
+	TAILQ_FOREACH(p1, &block->sb_rules, por_entry) {
+		for (p2 = TAILQ_NEXT(p1, por_entry); p2; p2 = por_next) {
+			por_next = TAILQ_NEXT(p2, por_entry);
+
+			src_eq = addrs_equal(&p1->por_rule.src,
+			    &p2->por_rule.src);
+			dst_eq = addrs_equal(&p1->por_rule.dst,
+			    &p2->por_rule.dst);
+
+			if (src_eq && !dst_eq && p1->por_src_tbl == NULL &&
+			    p2->por_dst_tbl == NULL &&
+			    p2->por_src_tbl == NULL &&
+			    rules_combineable(&p1->por_rule, &p2->por_rule) &&
+			    addrs_combineable(&p1->por_rule.dst,
+			    &p2->por_rule.dst)) {
+				DEBUG("can combine rules  nr%d = nr%d",
+				    p1->por_rule.nr, p2->por_rule.nr);
+				if (p1->por_dst_tbl == NULL &&
+				    add_opt_table(pf, &p1->por_dst_tbl,
+				    p1->por_rule.af, &p1->por_rule.dst))
+					return (1);
+				if (add_opt_table(pf, &p1->por_dst_tbl,
+				    p1->por_rule.af, &p2->por_rule.dst))
+					return (1);
+				p2->por_dst_tbl = p1->por_dst_tbl;
+				if (p1->por_dst_tbl->pt_rulecount >=
+				    TABLE_THRESHOLD) {
+					TAILQ_REMOVE(&block->sb_rules, p2,
+					    por_entry);
+					free(p2);
+				}
+			} else if (!src_eq && dst_eq && p1->por_dst_tbl == NULL
+			    && p2->por_src_tbl == NULL &&
+			    p2->por_dst_tbl == NULL &&
+			    rules_combineable(&p1->por_rule, &p2->por_rule) &&
+			    addrs_combineable(&p1->por_rule.src,
+			    &p2->por_rule.src)) {
+				DEBUG("can combine rules  nr%d = nr%d",
+				    p1->por_rule.nr, p2->por_rule.nr);
+				if (p1->por_src_tbl == NULL &&
+				    add_opt_table(pf, &p1->por_src_tbl,
+				    p1->por_rule.af, &p1->por_rule.src))
+					return (1);
+				if (add_opt_table(pf, &p1->por_src_tbl,
+				    p1->por_rule.af, &p2->por_rule.src))
+					return (1);
+				p2->por_src_tbl = p1->por_src_tbl;
+				if (p1->por_src_tbl->pt_rulecount >=
+				    TABLE_THRESHOLD) {
+					TAILQ_REMOVE(&block->sb_rules, p2,
+					    por_entry);
+					free(p2);
+				}
+			}
+		}
+	}
+
+
+	/*
+	 * Then we make a final pass to create a valid table name and
+	 * insert the name into the rules.
+	 */
+	for (p1 = TAILQ_FIRST(&block->sb_rules); p1; p1 = por_next) {
+		por_next = TAILQ_NEXT(p1, por_entry);
+		assert(p1->por_src_tbl == NULL || p1->por_dst_tbl == NULL);
+
+		if (p1->por_src_tbl && p1->por_src_tbl->pt_rulecount >=
+		    TABLE_THRESHOLD) {
+			if (p1->por_src_tbl->pt_generated) {
+				/* This rule is included in a table */
+				TAILQ_REMOVE(&block->sb_rules, p1, por_entry);
+				free(p1);
+				continue;
+			}
+			p1->por_src_tbl->pt_generated = 1;
+
+			if ((pf->opts & PF_OPT_NOACTION) == 0 &&
+			    pf_opt_create_table(pf, p1->por_src_tbl))
+				return (1);
+
+			pf->tdirty = 1;
+
+			if (pf->opts & PF_OPT_VERBOSE)
+				print_tabledef(p1->por_src_tbl->pt_name,
+				    PFR_TFLAG_CONST, 1,
+				    &p1->por_src_tbl->pt_nodes);
+
+			memset(&p1->por_rule.src.addr, 0,
+			    sizeof(p1->por_rule.src.addr));
+			p1->por_rule.src.addr.type = PF_ADDR_TABLE;
+			strlcpy(p1->por_rule.src.addr.v.tblname,
+			    p1->por_src_tbl->pt_name,
+			    sizeof(p1->por_rule.src.addr.v.tblname));
+
+			pfr_buf_clear(p1->por_src_tbl->pt_buf);
+			free(p1->por_src_tbl->pt_buf);
+			p1->por_src_tbl->pt_buf = NULL;
+		}
+		if (p1->por_dst_tbl && p1->por_dst_tbl->pt_rulecount >=
+		    TABLE_THRESHOLD) {
+			if (p1->por_dst_tbl->pt_generated) {
+				/* This rule is included in a table */
+				TAILQ_REMOVE(&block->sb_rules, p1, por_entry);
+				free(p1);
+				continue;
+			}
+			p1->por_dst_tbl->pt_generated = 1;
+
+			if ((pf->opts & PF_OPT_NOACTION) == 0 &&
+			    pf_opt_create_table(pf, p1->por_dst_tbl))
+				return (1);
+			pf->tdirty = 1;
+
+			if (pf->opts & PF_OPT_VERBOSE)
+				print_tabledef(p1->por_dst_tbl->pt_name,
+				    PFR_TFLAG_CONST, 1,
+				    &p1->por_dst_tbl->pt_nodes);
+
+			memset(&p1->por_rule.dst.addr, 0,
+			    sizeof(p1->por_rule.dst.addr));
+			p1->por_rule.dst.addr.type = PF_ADDR_TABLE;
+			strlcpy(p1->por_rule.dst.addr.v.tblname,
+			    p1->por_dst_tbl->pt_name,
+			    sizeof(p1->por_rule.dst.addr.v.tblname));
+
+			pfr_buf_clear(p1->por_dst_tbl->pt_buf);
+			free(p1->por_dst_tbl->pt_buf);
+			p1->por_dst_tbl->pt_buf = NULL;
+		}
+	}
+
+	return (0);
+}
+
+
+/*
+ * Optimization pass #3: re-order rules to improve skip steps
+ */
+int
+reorder_rules(struct pfctl *pf, struct superblock *block, int depth)
+{
+	struct superblock *newblock;
+	struct pf_skip_step *skiplist;
+	struct pf_opt_rule *por;
+	int i, largest, largest_list, rule_count = 0;
+	TAILQ_HEAD( , pf_opt_rule) head;
+
+	/*
+	 * Calculate the best-case skip steps.  We put each rule in a list
+	 * of other rules with common fields
+	 */
+	for (i = 0; i < PF_SKIP_COUNT; i++) {
+		TAILQ_FOREACH(por, &block->sb_rules, por_entry) {
+			TAILQ_FOREACH(skiplist, &block->sb_skipsteps[i],
+			    ps_entry) {
+				if (skip_compare(i, skiplist, por) == 0)
+					break;
+			}
+			if (skiplist == NULL) {
+				if ((skiplist = calloc(1, sizeof(*skiplist))) ==
+				    NULL)
+					err(1, "calloc");
+				TAILQ_INIT(&skiplist->ps_rules);
+				TAILQ_INSERT_TAIL(&block->sb_skipsteps[i],
+				    skiplist, ps_entry);
+			}
+			skip_append(block, i, skiplist, por);
+		}
+	}
+
+	TAILQ_FOREACH(por, &block->sb_rules, por_entry)
+		rule_count++;
+
+	/*
+	 * Now we're going to ignore any fields that are identical between
+	 * all of the rules in the superblock and those fields which differ
+	 * between every rule in the superblock.
+	 */
+	largest = 0;
+	for (i = 0; i < PF_SKIP_COUNT; i++) {
+		skiplist = TAILQ_FIRST(&block->sb_skipsteps[i]);
+		if (skiplist->ps_count == rule_count) {
+			DEBUG("(%d) original skipstep '%s' is all rules",
+			    depth, skip_comparitors_names[i]);
+			skiplist->ps_count = 0;
+		} else if (skiplist->ps_count == 1) {
+			skiplist->ps_count = 0;
+		} else {
+			DEBUG("(%d) original skipstep '%s' largest jump is %d",
+			    depth, skip_comparitors_names[i],
+			    skiplist->ps_count);
+			if (skiplist->ps_count > largest)
+				largest = skiplist->ps_count;
+		}
+	}
+	if (largest == 0) {
+		/* Ugh.  There is NO commonality in the superblock on which
+		 * optimize the skipsteps optimization.
+		 */
+		goto done;
+	}
+
+	/*
+	 * Now we're going to empty the superblock rule list and re-create
+	 * it based on a more optimal skipstep order.
+	 */
+	TAILQ_INIT(&head);
+	while ((por = TAILQ_FIRST(&block->sb_rules))) {
+		TAILQ_REMOVE(&block->sb_rules, por, por_entry);
+		TAILQ_INSERT_TAIL(&head, por, por_entry);
+	}
+
+
+	while (!TAILQ_EMPTY(&head)) {
+		largest = 1;
+
+		/*
+		 * Find the most useful skip steps remaining
+		 */
+		for (i = 0; i < PF_SKIP_COUNT; i++) {
+			skiplist = TAILQ_FIRST(&block->sb_skipsteps[i]);
+			if (skiplist->ps_count > largest) {
+				largest = skiplist->ps_count;
+				largest_list = i;
+			}
+		}
+
+		if (largest <= 1) {
+			/*
+			 * Nothing useful left.  Leave remaining rules in order.
+			 */
+			DEBUG("(%d) no more commonality for skip steps", depth);
+			while ((por = TAILQ_FIRST(&head))) {
+				TAILQ_REMOVE(&head, por, por_entry);
+				TAILQ_INSERT_TAIL(&block->sb_rules, por,
+				    por_entry);
+			}
+		} else {
+			/*
+			 * There is commonality.  Extract those common rules
+			 * and place them in the ruleset adjacent to each
+			 * other.
+			 */
+			skiplist = TAILQ_FIRST(&block->sb_skipsteps[
+			    largest_list]);
+			DEBUG("(%d) skipstep '%s' largest jump is %d @ #%d",
+			    depth, skip_comparitors_names[largest_list],
+			    largest, TAILQ_FIRST(&TAILQ_FIRST(&block->
+			    sb_skipsteps [largest_list])->ps_rules)->
+			    por_rule.nr);
+			TAILQ_REMOVE(&block->sb_skipsteps[largest_list],
+			    skiplist, ps_entry);
+
+
+			/*
+			 * There may be further commonality inside these
+			 * rules.  So we'll split them off into they're own
+			 * superblock and pass it back into the optimizer.
+			 */
+			if (skiplist->ps_count > 2) {
+				if ((newblock = calloc(1, sizeof(*newblock)))
+				    == NULL) {
+					warn("calloc");
+					return (1);
+				}
+				TAILQ_INIT(&newblock->sb_rules);
+				for (i = 0; i < PF_SKIP_COUNT; i++)
+					TAILQ_INIT(&newblock->sb_skipsteps[i]);
+				TAILQ_INSERT_BEFORE(block, newblock, sb_entry);
+				DEBUG("(%d) splitting off %d rules from superblock @ #%d",
+				    depth, skiplist->ps_count,
+				    TAILQ_FIRST(&skiplist->ps_rules)->
+				    por_rule.nr);
+			} else {
+				newblock = block;
+			}
+
+			while ((por = TAILQ_FIRST(&skiplist->ps_rules))) {
+				TAILQ_REMOVE(&head, por, por_entry);
+				TAILQ_REMOVE(&skiplist->ps_rules, por,
+				    por_skip_entry[largest_list]);
+				TAILQ_INSERT_TAIL(&newblock->sb_rules, por,
+				    por_entry);
+
+				/* Remove this rule from all other skiplists */
+				remove_from_skipsteps(&block->sb_skipsteps[
+				    largest_list], block, por, skiplist);
+			}
+			free(skiplist);
+			if (newblock != block)
+				if (reorder_rules(pf, newblock, depth + 1))
+					return (1);
+		}
+	}
+
+done:
+	for (i = 0; i < PF_SKIP_COUNT; i++) {
+		while ((skiplist = TAILQ_FIRST(&block->sb_skipsteps[i]))) {
+			TAILQ_REMOVE(&block->sb_skipsteps[i], skiplist,
+			    ps_entry);
+			free(skiplist);
+		}
+	}
+
+	return (0);
+}
+
+
+/*
+ * Optimization pass #4: re-order 'quick' rules based on feedback from the
+ * currently running ruleset
+ */
+int
+block_feedback(struct pfctl *pf, struct superblock *block)
+{
+	TAILQ_HEAD( , pf_opt_rule) queue;
+	struct pf_opt_rule *por1, *por2;
+	u_int64_t total_count = 0;
+	struct pf_rule a, b;
+
+
+	/*
+	 * Walk through all of the profiled superblock's rules and copy
+	 * the counters onto our rules.
+	 */
+	TAILQ_FOREACH(por1, &block->sb_profiled_block->sb_rules, por_entry) {
+		comparable_rule(&a, &por1->por_rule, DC);
+		total_count += por1->por_rule.packets[0] +
+		    por1->por_rule.packets[1];
+		TAILQ_FOREACH(por2, &block->sb_rules, por_entry) {
+			if (por2->por_profile_count)
+				continue;
+			comparable_rule(&b, &por2->por_rule, DC);
+			if (memcmp(&a, &b, sizeof(a)) == 0) {
+				por2->por_profile_count =
+				    por1->por_rule.packets[0] +
+				    por1->por_rule.packets[1];
+				break;
+			}
+		}
+	}
+	superblock_free(pf, block->sb_profiled_block);
+	block->sb_profiled_block = NULL;
+
+	/*
+	 * Now we pull all of the rules off the superblock and re-insert them
+	 * in sorted order.
+	 */
+
+	TAILQ_INIT(&queue);
+	while ((por1 = TAILQ_FIRST(&block->sb_rules)) != NULL) {
+		TAILQ_REMOVE(&block->sb_rules, por1, por_entry);
+		TAILQ_INSERT_TAIL(&queue, por1, por_entry);
+	}
+
+	while ((por1 = TAILQ_FIRST(&queue)) != NULL) {
+		TAILQ_REMOVE(&queue, por1, por_entry);
+/* XXX I should sort all of the unused rules based on skip steps */
+		TAILQ_FOREACH(por2, &block->sb_rules, por_entry) {
+			if (por1->por_profile_count > por2->por_profile_count) {
+				TAILQ_INSERT_BEFORE(por2, por1, por_entry);
+				break;
+			}
+		}
+#ifdef __FreeBSD__
+		if (por2 == NULL)
+#else
+		if (por2 == TAILQ_END(&block->sb_rules))
+#endif
+			TAILQ_INSERT_TAIL(&block->sb_rules, por1, por_entry);
+	}
+
+	return (0);
+}
+
+
+/*
+ * Load the current ruleset from the kernel and try to associate them with
+ * the ruleset we're optimizing.
+ */
+int
+load_feedback_profile(struct pfctl *pf, struct superblocks *superblocks)
+{
+	struct superblock *block, *blockcur;
+	struct superblocks prof_superblocks;
+	struct pf_opt_rule *por;
+	struct pf_opt_queue queue;
+	struct pfioc_rule pr;
+	struct pf_rule a, b;
+	int nr, mnr;
+
+	TAILQ_INIT(&queue);
+	TAILQ_INIT(&prof_superblocks);
+
+	memset(&pr, 0, sizeof(pr));
+	pr.rule.action = PF_PASS;
+	if (ioctl(pf->dev, DIOCGETRULES, &pr)) {
+		warn("DIOCGETRULES");
+		return (1);
+	}
+	mnr = pr.nr;
+
+	DEBUG("Loading %d active rules for a feedback profile", mnr);
+	for (nr = 0; nr < mnr; ++nr) {
+		struct pf_ruleset *rs;
+		if ((por = calloc(1, sizeof(*por))) == NULL) {
+			warn("calloc");
+			return (1);
+		}
+		pr.nr = nr;
+		if (ioctl(pf->dev, DIOCGETRULE, &pr)) {
+			warn("DIOCGETRULES");
+			return (1);
+		}
+		memcpy(&por->por_rule, &pr.rule, sizeof(por->por_rule));
+		rs = pf_find_or_create_ruleset(pr.anchor_call);
+		por->por_rule.anchor = rs->anchor;
+		if (TAILQ_EMPTY(&por->por_rule.rpool.list))
+			memset(&por->por_rule.rpool, 0,
+			    sizeof(por->por_rule.rpool));
+		TAILQ_INSERT_TAIL(&queue, por, por_entry);
+
+		/* XXX pfctl_get_pool(pf->dev, &pr.rule.rpool, nr, pr.ticket,
+		 *         PF_PASS, pf->anchor) ???
+		 * ... pfctl_clear_pool(&pr.rule.rpool)
+		 */
+	}
+
+	if (construct_superblocks(pf, &queue, &prof_superblocks))
+		return (1);
+
+
+	/*
+	 * Now we try to associate the active ruleset's superblocks with
+	 * the superblocks we're compiling.
+	 */
+	block = TAILQ_FIRST(superblocks);
+	blockcur = TAILQ_FIRST(&prof_superblocks);
+	while (block && blockcur) {
+		comparable_rule(&a, &TAILQ_FIRST(&block->sb_rules)->por_rule,
+		    BREAK);
+		comparable_rule(&b, &TAILQ_FIRST(&blockcur->sb_rules)->por_rule,
+		    BREAK);
+		if (memcmp(&a, &b, sizeof(a)) == 0) {
+			/* The two superblocks lined up */
+			block->sb_profiled_block = blockcur;
+		} else {
+			DEBUG("superblocks don't line up between #%d and #%d",
+			    TAILQ_FIRST(&block->sb_rules)->por_rule.nr,
+			    TAILQ_FIRST(&blockcur->sb_rules)->por_rule.nr);
+			break;
+		}
+		block = TAILQ_NEXT(block, sb_entry);
+		blockcur = TAILQ_NEXT(blockcur, sb_entry);
+	}
+
+
+
+	/* Free any superblocks we couldn't link */
+	while (blockcur) {
+		block = TAILQ_NEXT(blockcur, sb_entry);
+		superblock_free(pf, blockcur);
+		blockcur = block;
+	}
+	return (0);
+}
+
+
+/*
+ * Compare a rule to a skiplist to see if the rule is a member
+ */
+int
+skip_compare(int skipnum, struct pf_skip_step *skiplist,
+    struct pf_opt_rule *por)
+{
+	struct pf_rule *a, *b;
+	if (skipnum >= PF_SKIP_COUNT || skipnum < 0)
+		errx(1, "skip_compare() out of bounds");
+	a = &por->por_rule;
+	b = &TAILQ_FIRST(&skiplist->ps_rules)->por_rule;
+
+	return ((skip_comparitors[skipnum])(a, b));
+}
+
+
+/*
+ * Add a rule to a skiplist
+ */
+void
+skip_append(struct superblock *superblock, int skipnum,
+    struct pf_skip_step *skiplist, struct pf_opt_rule *por)
+{
+	struct pf_skip_step *prev;
+
+	skiplist->ps_count++;
+	TAILQ_INSERT_TAIL(&skiplist->ps_rules, por, por_skip_entry[skipnum]);
+
+	/* Keep the list of skiplists sorted by whichever is larger */
+	while ((prev = TAILQ_PREV(skiplist, skiplist, ps_entry)) &&
+	    prev->ps_count < skiplist->ps_count) {
+		TAILQ_REMOVE(&superblock->sb_skipsteps[skipnum],
+		    skiplist, ps_entry);
+		TAILQ_INSERT_BEFORE(prev, skiplist, ps_entry);
+	}
+}
+
+
+/*
+ * Remove a rule from the other skiplist calculations.
+ */
+void
+remove_from_skipsteps(struct skiplist *head, struct superblock *block,
+    struct pf_opt_rule *por, struct pf_skip_step *active_list)
+{
+	struct pf_skip_step *sk, *next;
+	struct pf_opt_rule *p2;
+	int i, found;
+
+	for (i = 0; i < PF_SKIP_COUNT; i++) {
+		sk = TAILQ_FIRST(&block->sb_skipsteps[i]);
+		if (sk == NULL || sk == active_list || sk->ps_count <= 1)
+			continue;
+		found = 0;
+		do {
+			TAILQ_FOREACH(p2, &sk->ps_rules, por_skip_entry[i])
+				if (p2 == por) {
+					TAILQ_REMOVE(&sk->ps_rules, p2,
+					    por_skip_entry[i]);
+					found = 1;
+					sk->ps_count--;
+					break;
+				}
+		} while (!found && (sk = TAILQ_NEXT(sk, ps_entry)));
+		if (found && sk) {
+			/* Does this change the sorting order? */
+			while ((next = TAILQ_NEXT(sk, ps_entry)) &&
+			    next->ps_count > sk->ps_count) {
+				TAILQ_REMOVE(head, sk, ps_entry);
+				TAILQ_INSERT_AFTER(head, next, sk, ps_entry);
+			}
+#ifdef OPT_DEBUG
+			next = TAILQ_NEXT(sk, ps_entry);
+			assert(next == NULL || next->ps_count <= sk->ps_count);
+#endif /* OPT_DEBUG */
+		}
+	}
+}
+
+
+/* Compare two rules AF field for skiplist construction */
+int
+skip_cmp_af(struct pf_rule *a, struct pf_rule *b)
+{
+	if (a->af != b->af || a->af == 0)
+		return (1);
+	return (0);
+}
+
+/* Compare two rules DIRECTION field for skiplist construction */
+int
+skip_cmp_dir(struct pf_rule *a, struct pf_rule *b)
+{
+	if (a->direction == 0 || a->direction != b->direction)
+		return (1);
+	return (0);
+}
+
+/* Compare two rules DST Address field for skiplist construction */
+int
+skip_cmp_dst_addr(struct pf_rule *a, struct pf_rule *b)
+{
+	if (a->dst.neg != b->dst.neg ||
+	    a->dst.addr.type != b->dst.addr.type)
+		return (1);
+	/* XXX if (a->proto != b->proto && a->proto != 0 && b->proto != 0
+	 *    && (a->proto == IPPROTO_TCP || a->proto == IPPROTO_UDP ||
+	 *    a->proto == IPPROTO_ICMP
+	 *	return (1);
+	 */
+	switch (a->dst.addr.type) {
+	case PF_ADDR_ADDRMASK:
+		if (memcmp(&a->dst.addr.v.a.addr, &b->dst.addr.v.a.addr,
+		    sizeof(a->dst.addr.v.a.addr)) ||
+		    memcmp(&a->dst.addr.v.a.mask, &b->dst.addr.v.a.mask,
+		    sizeof(a->dst.addr.v.a.mask)) ||
+		    (a->dst.addr.v.a.addr.addr32[0] == 0 &&
+		    a->dst.addr.v.a.addr.addr32[1] == 0 &&
+		    a->dst.addr.v.a.addr.addr32[2] == 0 &&
+		    a->dst.addr.v.a.addr.addr32[3] == 0))
+			return (1);
+		return (0);
+	case PF_ADDR_DYNIFTL:
+		if (strcmp(a->dst.addr.v.ifname, b->dst.addr.v.ifname) != 0 ||
+		    a->dst.addr.iflags != a->dst.addr.iflags ||
+		    memcmp(&a->dst.addr.v.a.mask, &b->dst.addr.v.a.mask,
+		    sizeof(a->dst.addr.v.a.mask)))
+			return (1);
+		return (0);
+	case PF_ADDR_NOROUTE:
+	case PF_ADDR_URPFFAILED:
+		return (0);
+	case PF_ADDR_TABLE:
+		return (strcmp(a->dst.addr.v.tblname, b->dst.addr.v.tblname));
+	}
+	return (1);
+}
+
+/* Compare two rules DST port field for skiplist construction */
+int
+skip_cmp_dst_port(struct pf_rule *a, struct pf_rule *b)
+{
+	/* XXX if (a->proto != b->proto && a->proto != 0 && b->proto != 0
+	 *    && (a->proto == IPPROTO_TCP || a->proto == IPPROTO_UDP ||
+	 *    a->proto == IPPROTO_ICMP
+	 *	return (1);
+	 */
+	if (a->dst.port_op == PF_OP_NONE || a->dst.port_op != b->dst.port_op ||
+	    a->dst.port[0] != b->dst.port[0] ||
+	    a->dst.port[1] != b->dst.port[1])
+		return (1);
+	return (0);
+}
+
+/* Compare two rules IFP field for skiplist construction */
+int
+skip_cmp_ifp(struct pf_rule *a, struct pf_rule *b)
+{
+	if (strcmp(a->ifname, b->ifname) || a->ifname[0] == '\0')
+		return (1);
+	return (a->ifnot != b->ifnot);
+}
+
+/* Compare two rules PROTO field for skiplist construction */
+int
+skip_cmp_proto(struct pf_rule *a, struct pf_rule *b)
+{
+	return (a->proto != b->proto || a->proto == 0);
+}
+
+/* Compare two rules SRC addr field for skiplist construction */
+int
+skip_cmp_src_addr(struct pf_rule *a, struct pf_rule *b)
+{
+	if (a->src.neg != b->src.neg ||
+	    a->src.addr.type != b->src.addr.type)
+		return (1);
+	/* XXX if (a->proto != b->proto && a->proto != 0 && b->proto != 0
+	 *    && (a->proto == IPPROTO_TCP || a->proto == IPPROTO_UDP ||
+	 *    a->proto == IPPROTO_ICMP
+	 *	return (1);
+	 */
+	switch (a->src.addr.type) {
+	case PF_ADDR_ADDRMASK:
+		if (memcmp(&a->src.addr.v.a.addr, &b->src.addr.v.a.addr,
+		    sizeof(a->src.addr.v.a.addr)) ||
+		    memcmp(&a->src.addr.v.a.mask, &b->src.addr.v.a.mask,
+		    sizeof(a->src.addr.v.a.mask)) ||
+		    (a->src.addr.v.a.addr.addr32[0] == 0 &&
+		    a->src.addr.v.a.addr.addr32[1] == 0 &&
+		    a->src.addr.v.a.addr.addr32[2] == 0 &&
+		    a->src.addr.v.a.addr.addr32[3] == 0))
+			return (1);
+		return (0);
+	case PF_ADDR_DYNIFTL:
+		if (strcmp(a->src.addr.v.ifname, b->src.addr.v.ifname) != 0 ||
+		    a->src.addr.iflags != a->src.addr.iflags ||
+		    memcmp(&a->src.addr.v.a.mask, &b->src.addr.v.a.mask,
+		    sizeof(a->src.addr.v.a.mask)))
+			return (1);
+		return (0);
+	case PF_ADDR_NOROUTE:
+	case PF_ADDR_URPFFAILED:
+		return (0);
+	case PF_ADDR_TABLE:
+		return (strcmp(a->src.addr.v.tblname, b->src.addr.v.tblname));
+	}
+	return (1);
+}
+
+/* Compare two rules SRC port field for skiplist construction */
+int
+skip_cmp_src_port(struct pf_rule *a, struct pf_rule *b)
+{
+	if (a->src.port_op == PF_OP_NONE || a->src.port_op != b->src.port_op ||
+	    a->src.port[0] != b->src.port[0] ||
+	    a->src.port[1] != b->src.port[1])
+		return (1);
+	/* XXX if (a->proto != b->proto && a->proto != 0 && b->proto != 0
+	 *    && (a->proto == IPPROTO_TCP || a->proto == IPPROTO_UDP ||
+	 *    a->proto == IPPROTO_ICMP
+	 *	return (1);
+	 */
+	return (0);
+}
+
+
+void
+skip_init(void)
+{
+	struct {
+		char *name;
+		int skipnum;
+		int (*func)(struct pf_rule *, struct pf_rule *);
+	} comps[] = PF_SKIP_COMPARITORS;
+	int skipnum, i;
+
+	for (skipnum = 0; skipnum < PF_SKIP_COUNT; skipnum++) {
+		for (i = 0; i < sizeof(comps)/sizeof(*comps); i++)
+			if (comps[i].skipnum == skipnum) {
+				skip_comparitors[skipnum] = comps[i].func;
+				skip_comparitors_names[skipnum] = comps[i].name;
+			}
+	}
+	for (skipnum = 0; skipnum < PF_SKIP_COUNT; skipnum++)
+		if (skip_comparitors[skipnum] == NULL)
+			errx(1, "Need to add skip step comparitor to pfctl?!");
+}
+
+/*
+ * Add a host/netmask to a table
+ */
+int
+add_opt_table(struct pfctl *pf, struct pf_opt_tbl **tbl, sa_family_t af,
+    struct pf_rule_addr *addr)
+{
+#ifdef OPT_DEBUG
+	char buf[128];
+#endif /* OPT_DEBUG */
+	static int tablenum = 0;
+	struct node_host node_host;
+
+	if (*tbl == NULL) {
+		if ((*tbl = calloc(1, sizeof(**tbl))) == NULL ||
+		    ((*tbl)->pt_buf = calloc(1, sizeof(*(*tbl)->pt_buf))) ==
+		    NULL)
+			err(1, "calloc");
+		(*tbl)->pt_buf->pfrb_type = PFRB_ADDRS;
+		SIMPLEQ_INIT(&(*tbl)->pt_nodes);
+
+		/* This is just a temporary table name */
+		snprintf((*tbl)->pt_name, sizeof((*tbl)->pt_name), "%s%d",
+		    PF_OPT_TABLE_PREFIX, tablenum++);
+		DEBUG("creating table <%s>", (*tbl)->pt_name);
+	}
+
+	memset(&node_host, 0, sizeof(node_host));
+	node_host.af = af;
+	node_host.addr = addr->addr;
+
+#ifdef OPT_DEBUG
+	DEBUG("<%s> adding %s/%d", (*tbl)->pt_name, inet_ntop(af,
+	    &node_host.addr.v.a.addr, buf, sizeof(buf)),
+	    unmask(&node_host.addr.v.a.mask, af));
+#endif /* OPT_DEBUG */
+
+	if (append_addr_host((*tbl)->pt_buf, &node_host, 0, 0)) {
+		warn("failed to add host");
+		return (1);
+	}
+	if (pf->opts & PF_OPT_VERBOSE) {
+		struct node_tinit *ti;
+
+		if ((ti = calloc(1, sizeof(*ti))) == NULL)
+			err(1, "malloc");
+		if ((ti->host = malloc(sizeof(*ti->host))) == NULL)
+			err(1, "malloc");
+		memcpy(ti->host, &node_host, sizeof(*ti->host));
+		SIMPLEQ_INSERT_TAIL(&(*tbl)->pt_nodes, ti, entries);
+	}
+
+	(*tbl)->pt_rulecount++;
+	if ((*tbl)->pt_rulecount == TABLE_THRESHOLD)
+		DEBUG("table <%s> now faster than skip steps", (*tbl)->pt_name);
+
+	return (0);
+}
+
+
+/*
+ * Do the dirty work of choosing an unused table name and creating it.
+ * (be careful with the table name, it might already be used in another anchor)
+ */
+int
+pf_opt_create_table(struct pfctl *pf, struct pf_opt_tbl *tbl)
+{
+	static int tablenum;
+	struct pfr_table *t;
+
+	if (table_buffer.pfrb_type == 0) {
+		/* Initialize the list of tables */
+		table_buffer.pfrb_type = PFRB_TABLES;
+		for (;;) {
+			pfr_buf_grow(&table_buffer, table_buffer.pfrb_size);
+			table_buffer.pfrb_size = table_buffer.pfrb_msize;
+			if (pfr_get_tables(NULL, table_buffer.pfrb_caddr,
+			    &table_buffer.pfrb_size, PFR_FLAG_ALLRSETS))
+				err(1, "pfr_get_tables");
+			if (table_buffer.pfrb_size <= table_buffer.pfrb_msize)
+				break;
+		}
+		table_identifier = arc4random();
+	}
+
+	/* XXX would be *really* nice to avoid duplicating identical tables */
+
+	/* Now we have to pick a table name that isn't used */
+again:
+	DEBUG("translating temporary table <%s> to <%s%x_%d>", tbl->pt_name,
+	    PF_OPT_TABLE_PREFIX, table_identifier, tablenum);
+	snprintf(tbl->pt_name, sizeof(tbl->pt_name), "%s%x_%d",
+	    PF_OPT_TABLE_PREFIX, table_identifier, tablenum);
+	PFRB_FOREACH(t, &table_buffer) {
+		if (strcasecmp(t->pfrt_name, tbl->pt_name) == 0) {
+			/* Collision.  Try again */
+			DEBUG("wow, table <%s> in use.  trying again",
+			    tbl->pt_name);
+			table_identifier = arc4random();
+			goto again;
+		}
+	}
+	tablenum++;
+
+
+	if (pfctl_define_table(tbl->pt_name, PFR_TFLAG_CONST, 1,
+	    pf->astack[0]->name, tbl->pt_buf, pf->astack[0]->ruleset.tticket)) {
+		warn("failed to create table %s in %s",
+		    tbl->pt_name, pf->astack[0]->name);
+		return (1);
+	}
+	return (0);
+}
+
+/*
+ * Partition the flat ruleset into a list of distinct superblocks
+ */
+int
+construct_superblocks(struct pfctl *pf, struct pf_opt_queue *opt_queue,
+    struct superblocks *superblocks)
+{
+	struct superblock *block = NULL;
+	struct pf_opt_rule *por;
+	int i;
+
+	while (!TAILQ_EMPTY(opt_queue)) {
+		por = TAILQ_FIRST(opt_queue);
+		TAILQ_REMOVE(opt_queue, por, por_entry);
+		if (block == NULL || !superblock_inclusive(block, por)) {
+			if ((block = calloc(1, sizeof(*block))) == NULL) {
+				warn("calloc");
+				return (1);
+			}
+			TAILQ_INIT(&block->sb_rules);
+			for (i = 0; i < PF_SKIP_COUNT; i++)
+				TAILQ_INIT(&block->sb_skipsteps[i]);
+			TAILQ_INSERT_TAIL(superblocks, block, sb_entry);
+		}
+		TAILQ_INSERT_TAIL(&block->sb_rules, por, por_entry);
+	}
+
+	return (0);
+}
+
+
+/*
+ * Compare two rule addresses
+ */
+int
+addrs_equal(struct pf_rule_addr *a, struct pf_rule_addr *b)
+{
+	if (a->neg != b->neg)
+		return (0);
+	return (memcmp(&a->addr, &b->addr, sizeof(a->addr)) == 0);
+}
+
+
+/*
+ * The addresses are not equal, but can we combine them into one table?
+ */
+int
+addrs_combineable(struct pf_rule_addr *a, struct pf_rule_addr *b)
+{
+	if (a->addr.type != PF_ADDR_ADDRMASK ||
+	    b->addr.type != PF_ADDR_ADDRMASK)
+		return (0);
+	if (a->neg != b->neg || a->port_op != b->port_op ||
+	    a->port[0] != b->port[0] || a->port[1] != b->port[1])
+		return (0);
+	return (1);
+}
+
+
+/*
+ * Are we allowed to combine these two rules
+ */
+int
+rules_combineable(struct pf_rule *p1, struct pf_rule *p2)
+{
+	struct pf_rule a, b;
+
+	comparable_rule(&a, p1, COMBINED);
+	comparable_rule(&b, p2, COMBINED);
+	return (memcmp(&a, &b, sizeof(a)) == 0);
+}
+
+
+/*
+ * Can a rule be included inside a superblock
+ */
+int
+superblock_inclusive(struct superblock *block, struct pf_opt_rule *por)
+{
+	struct pf_rule a, b;
+	int i, j;
+
+	/* First check for hard breaks */
+	for (i = 0; i < sizeof(pf_rule_desc)/sizeof(*pf_rule_desc); i++) {
+		if (pf_rule_desc[i].prf_type == BARRIER) {
+			for (j = 0; j < pf_rule_desc[i].prf_size; j++)
+				if (((char *)&por->por_rule)[j +
+				    pf_rule_desc[i].prf_offset] != 0)
+					return (0);
+		}
+	}
+
+	/* per-rule src-track is also a hard break */
+	if (por->por_rule.rule_flag & PFRULE_RULESRCTRACK)
+		return (0);
+
+	/*
+	 * Have to handle interface groups separately.  Consider the following
+	 * rules:
+	 *	block on EXTIFS to any port 22
+	 *	pass  on em0 to any port 22
+	 * (where EXTIFS is an arbitrary interface group)
+	 * The optimizer may decide to re-order the pass rule in front of the
+	 * block rule.  But what if EXTIFS includes em0???  Such a reordering
+	 * would change the meaning of the ruleset.
+	 * We can't just lookup the EXTIFS group and check if em0 is a member
+	 * because the user is allowed to add interfaces to a group during
+	 * runtime.
+	 * Ergo interface groups become a defacto superblock break :-(
+	 */
+	if (interface_group(por->por_rule.ifname) ||
+	    interface_group(TAILQ_FIRST(&block->sb_rules)->por_rule.ifname)) {
+		if (strcasecmp(por->por_rule.ifname,
+		    TAILQ_FIRST(&block->sb_rules)->por_rule.ifname) != 0)
+			return (0);
+	}
+
+	comparable_rule(&a, &TAILQ_FIRST(&block->sb_rules)->por_rule, NOMERGE);
+	comparable_rule(&b, &por->por_rule, NOMERGE);
+	if (memcmp(&a, &b, sizeof(a)) == 0)
+		return (1);
+
+#ifdef OPT_DEBUG
+	for (i = 0; i < sizeof(por->por_rule); i++) {
+		int closest = -1;
+		if (((u_int8_t *)&a)[i] != ((u_int8_t *)&b)[i]) {
+			for (j = 0; j < sizeof(pf_rule_desc) /
+			    sizeof(*pf_rule_desc); j++) {
+				if (i >= pf_rule_desc[j].prf_offset &&
+				    i < pf_rule_desc[j].prf_offset +
+				    pf_rule_desc[j].prf_size) {
+					DEBUG("superblock break @ %d due to %s",
+					    por->por_rule.nr,
+					    pf_rule_desc[j].prf_name);
+					return (0);
+				}
+				if (i > pf_rule_desc[j].prf_offset) {
+					if (closest == -1 ||
+					    i-pf_rule_desc[j].prf_offset <
+					    i-pf_rule_desc[closest].prf_offset)
+						closest = j;
+				}
+			}
+
+			if (closest >= 0)
+				DEBUG("superblock break @ %d on %s+%xh",
+				    por->por_rule.nr,
+				    pf_rule_desc[closest].prf_name,
+				    i - pf_rule_desc[closest].prf_offset -
+				    pf_rule_desc[closest].prf_size);
+			else
+				DEBUG("superblock break @ %d on field @ %d",
+				    por->por_rule.nr, i);
+			return (0);
+		}
+	}
+#endif /* OPT_DEBUG */
+
+	return (0);
+}
+
+
+/*
+ * Figure out if an interface name is an actual interface or actually a
+ * group of interfaces.
+ */
+int
+interface_group(const char *ifname)
+{
+	if (ifname == NULL || !ifname[0])
+		return (0);
+
+	/* Real interfaces must end in a number, interface groups do not */
+	if (isdigit(ifname[strlen(ifname) - 1]))
+		return (0);
+	else
+		return (1);
+}
+
+
+/*
+ * Make a rule that can directly compared by memcmp()
+ */
+void
+comparable_rule(struct pf_rule *dst, const struct pf_rule *src, int type)
+{
+	int i;
+	/*
+	 * To simplify the comparison, we just zero out the fields that are
+	 * allowed to be different and then do a simple memcmp()
+	 */
+	memcpy(dst, src, sizeof(*dst));
+	for (i = 0; i < sizeof(pf_rule_desc)/sizeof(*pf_rule_desc); i++)
+		if (pf_rule_desc[i].prf_type >= type) {
+#ifdef OPT_DEBUG
+			assert(pf_rule_desc[i].prf_type != NEVER ||
+			    *(((char *)dst) + pf_rule_desc[i].prf_offset) == 0);
+#endif /* OPT_DEBUG */
+			memset(((char *)dst) + pf_rule_desc[i].prf_offset, 0,
+			    pf_rule_desc[i].prf_size);
+		}
+}
+
+
+/*
+ * Remove superset information from two rules so we can directly compare them
+ * with memcmp()
+ */
+void
+exclude_supersets(struct pf_rule *super, struct pf_rule *sub)
+{
+	if (super->ifname[0] == '\0')
+		memset(sub->ifname, 0, sizeof(sub->ifname));
+	if (super->direction == PF_INOUT)
+		sub->direction = PF_INOUT;
+	if ((super->proto == 0 || super->proto == sub->proto) &&
+	    super->flags == 0 && super->flagset == 0 && (sub->flags ||
+	    sub->flagset)) {
+		sub->flags = super->flags;
+		sub->flagset = super->flagset;
+	}
+	if (super->proto == 0)
+		sub->proto = 0;
+
+	if (super->src.port_op == 0) {
+		sub->src.port_op = 0;
+		sub->src.port[0] = 0;
+		sub->src.port[1] = 0;
+	}
+	if (super->dst.port_op == 0) {
+		sub->dst.port_op = 0;
+		sub->dst.port[0] = 0;
+		sub->dst.port[1] = 0;
+	}
+
+	if (super->src.addr.type == PF_ADDR_ADDRMASK && !super->src.neg &&
+	    !sub->src.neg && super->src.addr.v.a.mask.addr32[0] == 0 &&
+	    super->src.addr.v.a.mask.addr32[1] == 0 &&
+	    super->src.addr.v.a.mask.addr32[2] == 0 &&
+	    super->src.addr.v.a.mask.addr32[3] == 0)
+		memset(&sub->src.addr, 0, sizeof(sub->src.addr));
+	else if (super->src.addr.type == PF_ADDR_ADDRMASK &&
+	    sub->src.addr.type == PF_ADDR_ADDRMASK &&
+	    super->src.neg == sub->src.neg &&
+	    super->af == sub->af &&
+	    unmask(&super->src.addr.v.a.mask, super->af) <
+	    unmask(&sub->src.addr.v.a.mask, sub->af) &&
+	    super->src.addr.v.a.addr.addr32[0] ==
+	    (sub->src.addr.v.a.addr.addr32[0] &
+	    super->src.addr.v.a.mask.addr32[0]) &&
+	    super->src.addr.v.a.addr.addr32[1] ==
+	    (sub->src.addr.v.a.addr.addr32[1] &
+	    super->src.addr.v.a.mask.addr32[1]) &&
+	    super->src.addr.v.a.addr.addr32[2] ==
+	    (sub->src.addr.v.a.addr.addr32[2] &
+	    super->src.addr.v.a.mask.addr32[2]) &&
+	    super->src.addr.v.a.addr.addr32[3] ==
+	    (sub->src.addr.v.a.addr.addr32[3] &
+	    super->src.addr.v.a.mask.addr32[3])) {
+		/* sub->src.addr is a subset of super->src.addr/mask */
+		memcpy(&sub->src.addr, &super->src.addr, sizeof(sub->src.addr));
+	}
+
+	if (super->dst.addr.type == PF_ADDR_ADDRMASK && !super->dst.neg &&
+	    !sub->dst.neg && super->dst.addr.v.a.mask.addr32[0] == 0 &&
+	    super->dst.addr.v.a.mask.addr32[1] == 0 &&
+	    super->dst.addr.v.a.mask.addr32[2] == 0 &&
+	    super->dst.addr.v.a.mask.addr32[3] == 0)
+		memset(&sub->dst.addr, 0, sizeof(sub->dst.addr));
+	else if (super->dst.addr.type == PF_ADDR_ADDRMASK &&
+	    sub->dst.addr.type == PF_ADDR_ADDRMASK &&
+	    super->dst.neg == sub->dst.neg &&
+	    super->af == sub->af &&
+	    unmask(&super->dst.addr.v.a.mask, super->af) <
+	    unmask(&sub->dst.addr.v.a.mask, sub->af) &&
+	    super->dst.addr.v.a.addr.addr32[0] ==
+	    (sub->dst.addr.v.a.addr.addr32[0] &
+	    super->dst.addr.v.a.mask.addr32[0]) &&
+	    super->dst.addr.v.a.addr.addr32[1] ==
+	    (sub->dst.addr.v.a.addr.addr32[1] &
+	    super->dst.addr.v.a.mask.addr32[1]) &&
+	    super->dst.addr.v.a.addr.addr32[2] ==
+	    (sub->dst.addr.v.a.addr.addr32[2] &
+	    super->dst.addr.v.a.mask.addr32[2]) &&
+	    super->dst.addr.v.a.addr.addr32[3] ==
+	    (sub->dst.addr.v.a.addr.addr32[3] &
+	    super->dst.addr.v.a.mask.addr32[3])) {
+		/* sub->dst.addr is a subset of super->dst.addr/mask */
+		memcpy(&sub->dst.addr, &super->dst.addr, sizeof(sub->dst.addr));
+	}
+
+	if (super->af == 0)
+		sub->af = 0;
+}
+
+
+void
+superblock_free(struct pfctl *pf, struct superblock *block)
+{
+	struct pf_opt_rule *por;
+	while ((por = TAILQ_FIRST(&block->sb_rules))) {
+		TAILQ_REMOVE(&block->sb_rules, por, por_entry);
+		if (por->por_src_tbl) {
+			if (por->por_src_tbl->pt_buf) {
+				pfr_buf_clear(por->por_src_tbl->pt_buf);
+				free(por->por_src_tbl->pt_buf);
+			}
+			free(por->por_src_tbl);
+		}
+		if (por->por_dst_tbl) {
+			if (por->por_dst_tbl->pt_buf) {
+				pfr_buf_clear(por->por_dst_tbl->pt_buf);
+				free(por->por_dst_tbl->pt_buf);
+			}
+			free(por->por_dst_tbl);
+		}
+		free(por);
+	}
+	if (block->sb_profiled_block)
+		superblock_free(pf, block->sb_profiled_block);
+	free(block);
+}
+
diff --git a/freebsd/contrib/pf/pfctl/pfctl_osfp.c b/freebsd/contrib/pf/pfctl/pfctl_osfp.c
new file mode 100644
index 0000000..541ac3b
--- /dev/null
+++ b/freebsd/contrib/pf/pfctl/pfctl_osfp.c
@@ -0,0 +1,1110 @@
+#include <machine/rtems-bsd-user-space.h>
+
+/*	$OpenBSD: pfctl_osfp.c,v 1.14 2006/04/08 02:13:14 ray Exp $ */
+
+/*
+ * Copyright (c) 2003 Mike Frantzen <frantzen at openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+
+#include <net/if.h>
+#include <net/pfvar.h>
+
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/ip6.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "pfctl_parser.h"
+#include "pfctl.h"
+
+#ifndef MIN
+# define MIN(a,b)	(((a) < (b)) ? (a) : (b))
+#endif /* MIN */
+#ifndef MAX
+# define MAX(a,b)	(((a) > (b)) ? (a) : (b))
+#endif /* MAX */
+
+
+#if 0
+# define DEBUG(fp, str, v...) \
+	fprintf(stderr, "%s:%s:%s " str "\n", (fp)->fp_os.fp_class_nm, \
+	    (fp)->fp_os.fp_version_nm, (fp)->fp_os.fp_subtype_nm , ## v);
+#else
+# define DEBUG(fp, str, v...) ((void)0)
+#endif
+
+
+struct name_entry;
+LIST_HEAD(name_list, name_entry);
+struct name_entry {
+	LIST_ENTRY(name_entry)	nm_entry;
+	int			nm_num;
+	char			nm_name[PF_OSFP_LEN];
+
+	struct name_list	nm_sublist;
+	int			nm_sublist_num;
+};
+struct name_list classes = LIST_HEAD_INITIALIZER(&classes);
+int class_count;
+int fingerprint_count;
+
+void			 add_fingerprint(int, int, struct pf_osfp_ioctl *);
+struct name_entry	*fingerprint_name_entry(struct name_list *, char *);
+void			 pfctl_flush_my_fingerprints(struct name_list *);
+char			*get_field(char **, size_t *, int *);
+int			 get_int(char **, size_t *, int *, int *, const char *,
+			     int, int, const char *, int);
+int			 get_str(char **, size_t *, char **, const char *, int,
+			     const char *, int);
+int			 get_tcpopts(const char *, int, const char *,
+			    pf_tcpopts_t *, int *, int *, int *, int *, int *,
+			    int *);
+void			 import_fingerprint(struct pf_osfp_ioctl *);
+const char		*print_ioctl(struct pf_osfp_ioctl *);
+void			 print_name_list(int, struct name_list *, const char *);
+void			 sort_name_list(int, struct name_list *);
+struct name_entry	*lookup_name_list(struct name_list *, const char *);
+
+/* Load fingerprints from a file */
+int
+pfctl_file_fingerprints(int dev, int opts, const char *fp_filename)
+{
+	FILE *in;
+	char *line;
+	size_t len;
+	int i, lineno = 0;
+	int window, w_mod, ttl, df, psize, p_mod, mss, mss_mod, wscale,
+	    wscale_mod, optcnt, ts0;
+	pf_tcpopts_t packed_tcpopts;
+	char *class, *version, *subtype, *desc, *tcpopts;
+	struct pf_osfp_ioctl fp;
+
+	pfctl_flush_my_fingerprints(&classes);
+
+	if ((in = pfctl_fopen(fp_filename, "r")) == NULL) {
+		warn("%s", fp_filename);
+		return (1);
+	}
+	class = version = subtype = desc = tcpopts = NULL;
+
+	if ((opts & PF_OPT_NOACTION) == 0)
+		pfctl_clear_fingerprints(dev, opts);
+
+	while ((line = fgetln(in, &len)) != NULL) {
+		lineno++;
+		if (class)
+			free(class);
+		if (version)
+			free(version);
+		if (subtype)
+			free(subtype);
+		if (desc)
+			free(desc);
+		if (tcpopts)
+			free(tcpopts);
+		class = version = subtype = desc = tcpopts = NULL;
+		memset(&fp, 0, sizeof(fp));
+
+		/* Chop off comment */
+		for (i = 0; i < len; i++)
+			if (line[i] == '#') {
+				len = i;
+				break;
+			}
+		/* Chop off whitespace */
+		while (len > 0 && isspace(line[len - 1]))
+			len--;
+		while (len > 0 && isspace(line[0])) {
+			len--;
+			line++;
+		}
+		if (len == 0)
+			continue;
+
+#define T_DC	0x01	/* Allow don't care */
+#define T_MSS	0x02	/* Allow MSS multiple */
+#define T_MTU	0x04	/* Allow MTU multiple */
+#define T_MOD	0x08	/* Allow modulus */
+
+#define GET_INT(v, mod, n, ty, mx) \
+	get_int(&line, &len, &v, mod, n, ty, mx, fp_filename, lineno)
+#define GET_STR(v, n, mn) \
+	get_str(&line, &len, &v, n, mn, fp_filename, lineno)
+
+		if (GET_INT(window, &w_mod, "window size", T_DC|T_MSS|T_MTU|
+		    T_MOD, 0xffff) ||
+		    GET_INT(ttl, NULL, "ttl", 0, 0xff) ||
+		    GET_INT(df, NULL, "don't fragment frag", 0, 1) ||
+		    GET_INT(psize, &p_mod, "overall packet size", T_MOD|T_DC,
+		    8192) ||
+		    GET_STR(tcpopts, "TCP Options", 1) ||
+		    GET_STR(class, "OS class", 1) ||
+		    GET_STR(version, "OS version", 0) ||
+		    GET_STR(subtype, "OS subtype", 0) ||
+		    GET_STR(desc, "OS description", 2))
+			continue;
+		if (get_tcpopts(fp_filename, lineno, tcpopts, &packed_tcpopts,
+		    &optcnt, &mss, &mss_mod, &wscale, &wscale_mod, &ts0))
+			continue;
+		if (len != 0) {
+			fprintf(stderr, "%s:%d excess field\n", fp_filename,
+			    lineno);
+			continue;
+		}
+
+		fp.fp_ttl = ttl;
+		if (df)
+			fp.fp_flags |= PF_OSFP_DF;
+		switch (w_mod) {
+		case 0:
+			break;
+		case T_DC:
+			fp.fp_flags |= PF_OSFP_WSIZE_DC;
+			break;
+		case T_MSS:
+			fp.fp_flags |= PF_OSFP_WSIZE_MSS;
+			break;
+		case T_MTU:
+			fp.fp_flags |= PF_OSFP_WSIZE_MTU;
+			break;
+		case T_MOD:
+			fp.fp_flags |= PF_OSFP_WSIZE_MOD;
+			break;
+		}
+		fp.fp_wsize = window;
+
+		switch (p_mod) {
+		case T_DC:
+			fp.fp_flags |= PF_OSFP_PSIZE_DC;
+			break;
+		case T_MOD:
+			fp.fp_flags |= PF_OSFP_PSIZE_MOD;
+		}
+		fp.fp_psize = psize;
+
+
+		switch (wscale_mod) {
+		case T_DC:
+			fp.fp_flags |= PF_OSFP_WSCALE_DC;
+			break;
+		case T_MOD:
+			fp.fp_flags |= PF_OSFP_WSCALE_MOD;
+		}
+		fp.fp_wscale = wscale;
+
+		switch (mss_mod) {
+		case T_DC:
+			fp.fp_flags |= PF_OSFP_MSS_DC;
+			break;
+		case T_MOD:
+			fp.fp_flags |= PF_OSFP_MSS_MOD;
+			break;
+		}
+		fp.fp_mss = mss;
+
+		fp.fp_tcpopts = packed_tcpopts;
+		fp.fp_optcnt = optcnt;
+		if (ts0)
+			fp.fp_flags |= PF_OSFP_TS0;
+
+		if (class[0] == '@')
+			fp.fp_os.fp_enflags |= PF_OSFP_GENERIC;
+		if (class[0] == '*')
+			fp.fp_os.fp_enflags |= PF_OSFP_NODETAIL;
+
+		if (class[0] == '@' || class[0] == '*')
+			strlcpy(fp.fp_os.fp_class_nm, class + 1,
+			    sizeof(fp.fp_os.fp_class_nm));
+		else
+			strlcpy(fp.fp_os.fp_class_nm, class,
+			    sizeof(fp.fp_os.fp_class_nm));
+		strlcpy(fp.fp_os.fp_version_nm, version,
+		    sizeof(fp.fp_os.fp_version_nm));
+		strlcpy(fp.fp_os.fp_subtype_nm, subtype,
+		    sizeof(fp.fp_os.fp_subtype_nm));
+
+		add_fingerprint(dev, opts, &fp);
+
+		fp.fp_flags |= (PF_OSFP_DF | PF_OSFP_INET6);
+		fp.fp_psize += sizeof(struct ip6_hdr) - sizeof(struct ip);
+		add_fingerprint(dev, opts, &fp);
+	}
+
+	if (class)
+		free(class);
+	if (version)
+		free(version);
+	if (subtype)
+		free(subtype);
+	if (desc)
+		free(desc);
+	if (tcpopts)
+		free(tcpopts);
+
+	fclose(in);
+
+	if (opts & PF_OPT_VERBOSE2)
+		printf("Loaded %d passive OS fingerprints\n",
+		    fingerprint_count);
+	return (0);
+}
+
+/* flush the kernel's fingerprints */
+void
+pfctl_clear_fingerprints(int dev, int opts)
+{
+	if (ioctl(dev, DIOCOSFPFLUSH))
+		err(1, "DIOCOSFPFLUSH");
+}
+
+/* flush pfctl's view of the fingerprints */
+void
+pfctl_flush_my_fingerprints(struct name_list *list)
+{
+	struct name_entry *nm;
+
+	while ((nm = LIST_FIRST(list)) != NULL) {
+		LIST_REMOVE(nm, nm_entry);
+		pfctl_flush_my_fingerprints(&nm->nm_sublist);
+		free(nm);
+	}
+	fingerprint_count = 0;
+	class_count = 0;
+}
+
+/* Fetch the active fingerprints from the kernel */
+int
+pfctl_load_fingerprints(int dev, int opts)
+{
+	struct pf_osfp_ioctl io;
+	int i;
+
+	pfctl_flush_my_fingerprints(&classes);
+
+	for (i = 0; i >= 0; i++) {
+		memset(&io, 0, sizeof(io));
+		io.fp_getnum = i;
+		if (ioctl(dev, DIOCOSFPGET, &io)) {
+			if (errno == EBUSY)
+				break;
+			warn("DIOCOSFPGET");
+			return (1);
+		}
+		import_fingerprint(&io);
+	}
+	return (0);
+}
+
+/* List the fingerprints */
+void
+pfctl_show_fingerprints(int opts)
+{
+	if (LIST_FIRST(&classes) != NULL) {
+		if (opts & PF_OPT_SHOWALL) {
+			pfctl_print_title("OS FINGERPRINTS:");
+			printf("%u fingerprints loaded\n", fingerprint_count);
+		} else {
+			printf("Class\tVersion\tSubtype(subversion)\n");
+			printf("-----\t-------\t-------------------\n");
+			sort_name_list(opts, &classes);
+			print_name_list(opts, &classes, "");
+		}
+	}
+}
+
+/* Lookup a fingerprint */
+pf_osfp_t
+pfctl_get_fingerprint(const char *name)
+{
+	struct name_entry *nm, *class_nm, *version_nm, *subtype_nm;
+	pf_osfp_t ret = PF_OSFP_NOMATCH;
+	int class, version, subtype;
+	int unp_class, unp_version, unp_subtype;
+	int wr_len, version_len, subtype_len;
+	char *ptr, *wr_name;
+
+	if (strcasecmp(name, "unknown") == 0)
+		return (PF_OSFP_UNKNOWN);
+
+	/* Try most likely no version and no subtype */
+	if ((nm = lookup_name_list(&classes, name))) {
+		class = nm->nm_num;
+		version = PF_OSFP_ANY;
+		subtype = PF_OSFP_ANY;
+		goto found;
+	} else {
+
+		/* Chop it up into class/version/subtype */
+
+		if ((wr_name = strdup(name)) == NULL)
+			err(1, "malloc");
+		if ((ptr = strchr(wr_name, ' ')) == NULL) {
+			free(wr_name);
+			return (PF_OSFP_NOMATCH);
+		}
+		*ptr++ = '\0';
+
+		/* The class is easy to find since it is delimited by a space */
+		if ((class_nm = lookup_name_list(&classes, wr_name)) == NULL) {
+			free(wr_name);
+			return (PF_OSFP_NOMATCH);
+		}
+		class = class_nm->nm_num;
+
+		/* Try no subtype */
+		if ((version_nm = lookup_name_list(&class_nm->nm_sublist, ptr)))
+		{
+			version = version_nm->nm_num;
+			subtype = PF_OSFP_ANY;
+			free(wr_name);
+			goto found;
+		}
+
+
+		/*
+		 * There must be a version and a subtype.
+		 * We'll do some fuzzy matching to pick up things like:
+		 *   Linux 2.2.14 (version=2.2 subtype=14)
+		 *   FreeBSD 4.0-STABLE (version=4.0 subtype=STABLE)
+		 *   Windows 2000 SP2	(version=2000 subtype=SP2)
+		 */
+#define CONNECTOR(x)	((x) == '.' || (x) == ' ' || (x) == '\t' || (x) == '-')
+		wr_len = strlen(ptr);
+		LIST_FOREACH(version_nm, &class_nm->nm_sublist, nm_entry) {
+			version_len = strlen(version_nm->nm_name);
+			if (wr_len < version_len + 2 ||
+			    !CONNECTOR(ptr[version_len]))
+				continue;
+			/* first part of the string must be version */
+			if (strncasecmp(ptr, version_nm->nm_name,
+			    version_len))
+				continue;
+
+			LIST_FOREACH(subtype_nm, &version_nm->nm_sublist,
+			    nm_entry) {
+				subtype_len = strlen(subtype_nm->nm_name);
+				if (wr_len != version_len + subtype_len + 1)
+					continue;
+
+				/* last part of the string must be subtype */
+				if (strcasecmp(&ptr[version_len+1],
+				    subtype_nm->nm_name) != 0)
+					continue;
+
+				/* Found it!! */
+				version = version_nm->nm_num;
+				subtype = subtype_nm->nm_num;
+				free(wr_name);
+				goto found;
+			}
+		}
+
+		free(wr_name);
+		return (PF_OSFP_NOMATCH);
+	}
+
+found:
+	PF_OSFP_PACK(ret, class, version, subtype);
+	if (ret != PF_OSFP_NOMATCH) {
+		PF_OSFP_UNPACK(ret, unp_class, unp_version, unp_subtype);
+		if (class != unp_class) {
+			fprintf(stderr, "warning: fingerprint table overflowed "
+			    "classes\n");
+			return (PF_OSFP_NOMATCH);
+		}
+		if (version != unp_version) {
+			fprintf(stderr, "warning: fingerprint table overflowed "
+			    "versions\n");
+			return (PF_OSFP_NOMATCH);
+		}
+		if (subtype != unp_subtype) {
+			fprintf(stderr, "warning: fingerprint table overflowed "
+			    "subtypes\n");
+			return (PF_OSFP_NOMATCH);
+		}
+	}
+	if (ret == PF_OSFP_ANY) {
+		/* should never happen */
+		fprintf(stderr, "warning: fingerprint packed to 'any'\n");
+		return (PF_OSFP_NOMATCH);
+	}
+
+	return (ret);
+}
+
+/* Lookup a fingerprint name by ID */
+char *
+pfctl_lookup_fingerprint(pf_osfp_t fp, char *buf, size_t len)
+{
+	int class, version, subtype;
+	struct name_list *list;
+	struct name_entry *nm;
+
+	char *class_name, *version_name, *subtype_name;
+	class_name = version_name = subtype_name = NULL;
+
+	if (fp == PF_OSFP_UNKNOWN) {
+		strlcpy(buf, "unknown", len);
+		return (buf);
+	}
+	if (fp == PF_OSFP_ANY) {
+		strlcpy(buf, "any", len);
+		return (buf);
+	}
+
+	PF_OSFP_UNPACK(fp, class, version, subtype);
+	if (class >= (1 << _FP_CLASS_BITS) ||
+	    version >= (1 << _FP_VERSION_BITS) ||
+	    subtype >= (1 << _FP_SUBTYPE_BITS)) {
+		warnx("PF_OSFP_UNPACK(0x%x) failed!!", fp);
+		strlcpy(buf, "nomatch", len);
+		return (buf);
+	}
+
+	LIST_FOREACH(nm, &classes, nm_entry) {
+		if (nm->nm_num == class) {
+			class_name = nm->nm_name;
+			if (version == PF_OSFP_ANY)
+				goto found;
+			list = &nm->nm_sublist;
+			LIST_FOREACH(nm, list, nm_entry) {
+				if (nm->nm_num == version) {
+					version_name = nm->nm_name;
+					if (subtype == PF_OSFP_ANY)
+						goto found;
+					list = &nm->nm_sublist;
+					LIST_FOREACH(nm, list, nm_entry) {
+						if (nm->nm_num == subtype) {
+							subtype_name =
+							    nm->nm_name;
+							goto found;
+						}
+					} /* foreach subtype */
+					strlcpy(buf, "nomatch", len);
+					return (buf);
+				}
+			} /* foreach version */
+			strlcpy(buf, "nomatch", len);
+			return (buf);
+		}
+	} /* foreach class */
+
+	strlcpy(buf, "nomatch", len);
+	return (buf);
+
+found:
+	snprintf(buf, len, "%s", class_name);
+	if (version_name) {
+		strlcat(buf, " ", len);
+		strlcat(buf, version_name, len);
+		if (subtype_name) {
+			if (strchr(version_name, ' '))
+				strlcat(buf, " ", len);
+			else if (strchr(version_name, '.') &&
+			    isdigit(*subtype_name))
+				strlcat(buf, ".", len);
+			else
+				strlcat(buf, " ", len);
+			strlcat(buf, subtype_name, len);
+		}
+	}
+	return (buf);
+}
+
+/* lookup a name in a list */
+struct name_entry *
+lookup_name_list(struct name_list *list, const char *name)
+{
+	struct name_entry *nm;
+	LIST_FOREACH(nm, list, nm_entry)
+		if (strcasecmp(name, nm->nm_name) == 0)
+			return (nm);
+
+	return (NULL);
+}
+
+
+void
+add_fingerprint(int dev, int opts, struct pf_osfp_ioctl *fp)
+{
+	struct pf_osfp_ioctl fptmp;
+	struct name_entry *nm_class, *nm_version, *nm_subtype;
+	int class, version, subtype;
+
+/* We expand #-# or #.#-#.# version/subtypes into multiple fingerprints */
+#define EXPAND(field) do {						\
+	int _dot = -1, _start = -1, _end = -1, _i = 0;			\
+	/* pick major version out of #.# */				\
+	if (isdigit(fp->field[_i]) && fp->field[_i+1] == '.') {		\
+		_dot = fp->field[_i] - '0';				\
+		_i += 2;						\
+	}								\
+	if (isdigit(fp->field[_i]))					\
+		_start = fp->field[_i++] - '0';				\
+	else								\
+		break;							\
+	if (isdigit(fp->field[_i]))					\
+		_start = (_start * 10) + fp->field[_i++] - '0';		\
+	if (fp->field[_i++] != '-')					\
+		break;							\
+	if (isdigit(fp->field[_i]) && fp->field[_i+1] == '.' &&		\
+	    fp->field[_i] - '0' == _dot)				\
+		_i += 2;						\
+	else if (_dot != -1)						\
+		break;							\
+	if (isdigit(fp->field[_i]))					\
+		_end = fp->field[_i++] - '0';				\
+	else								\
+		break;							\
+	if (isdigit(fp->field[_i]))					\
+		_end = (_end * 10) + fp->field[_i++] - '0';		\
+	if (isdigit(fp->field[_i]))					\
+		_end = (_end * 10) + fp->field[_i++] - '0';		\
+	if (fp->field[_i] != '\0')					\
+		break;							\
+	memcpy(&fptmp, fp, sizeof(fptmp));				\
+	for (;_start <= _end; _start++) {				\
+		memset(fptmp.field, 0, sizeof(fptmp.field));		\
+		fptmp.fp_os.fp_enflags |= PF_OSFP_EXPANDED;		\
+		if (_dot == -1)						\
+			snprintf(fptmp.field, sizeof(fptmp.field),	\
+			    "%d", _start);				\
+		    else						\
+			snprintf(fptmp.field, sizeof(fptmp.field),	\
+			    "%d.%d", _dot, _start);			\
+		add_fingerprint(dev, opts, &fptmp);			\
+	}								\
+} while(0)
+
+	/* We allow "#-#" as a version or subtype and we'll expand it */
+	EXPAND(fp_os.fp_version_nm);
+	EXPAND(fp_os.fp_subtype_nm);
+
+	if (strcasecmp(fp->fp_os.fp_class_nm, "nomatch") == 0)
+		errx(1, "fingerprint class \"nomatch\" is reserved");
+
+	version = PF_OSFP_ANY;
+	subtype = PF_OSFP_ANY;
+
+	nm_class = fingerprint_name_entry(&classes, fp->fp_os.fp_class_nm);
+	if (nm_class->nm_num == 0)
+		nm_class->nm_num = ++class_count;
+	class = nm_class->nm_num;
+
+	nm_version = fingerprint_name_entry(&nm_class->nm_sublist,
+	    fp->fp_os.fp_version_nm);
+	if (nm_version) {
+		if (nm_version->nm_num == 0)
+			nm_version->nm_num = ++nm_class->nm_sublist_num;
+		version = nm_version->nm_num;
+		nm_subtype = fingerprint_name_entry(&nm_version->nm_sublist,
+		    fp->fp_os.fp_subtype_nm);
+		if (nm_subtype) {
+			if (nm_subtype->nm_num == 0)
+				nm_subtype->nm_num =
+				    ++nm_version->nm_sublist_num;
+			subtype = nm_subtype->nm_num;
+		}
+	}
+
+
+	DEBUG(fp, "\tsignature %d:%d:%d %s", class, version, subtype,
+	    print_ioctl(fp));
+
+	PF_OSFP_PACK(fp->fp_os.fp_os, class, version, subtype);
+	fingerprint_count++;
+
+#ifdef FAKE_PF_KERNEL
+	/* Linked to the sys/net/pf_osfp.c.  Call pf_osfp_add() */
+	if ((errno = pf_osfp_add(fp)))
+#else
+	if ((opts & PF_OPT_NOACTION) == 0 && ioctl(dev, DIOCOSFPADD, fp))
+#endif /* FAKE_PF_KERNEL */
+	{
+		if (errno == EEXIST) {
+			warn("Duplicate signature for %s %s %s",
+				fp->fp_os.fp_class_nm,
+				fp->fp_os.fp_version_nm,
+				fp->fp_os.fp_subtype_nm);
+
+		} else {
+			err(1, "DIOCOSFPADD");
+		}
+	}
+}
+
+/* import a fingerprint from the kernel */
+void
+import_fingerprint(struct pf_osfp_ioctl *fp)
+{
+	struct name_entry *nm_class, *nm_version, *nm_subtype;
+	int class, version, subtype;
+
+	PF_OSFP_UNPACK(fp->fp_os.fp_os, class, version, subtype);
+
+	nm_class = fingerprint_name_entry(&classes, fp->fp_os.fp_class_nm);
+	if (nm_class->nm_num == 0) {
+		nm_class->nm_num = class;
+		class_count = MAX(class_count, class);
+	}
+
+	nm_version = fingerprint_name_entry(&nm_class->nm_sublist,
+	    fp->fp_os.fp_version_nm);
+	if (nm_version) {
+		if (nm_version->nm_num == 0) {
+			nm_version->nm_num = version;
+			nm_class->nm_sublist_num = MAX(nm_class->nm_sublist_num,
+			    version);
+		}
+		nm_subtype = fingerprint_name_entry(&nm_version->nm_sublist,
+		    fp->fp_os.fp_subtype_nm);
+		if (nm_subtype) {
+			if (nm_subtype->nm_num == 0) {
+				nm_subtype->nm_num = subtype;
+				nm_version->nm_sublist_num =
+				    MAX(nm_version->nm_sublist_num, subtype);
+			}
+		}
+	}
+
+
+	fingerprint_count++;
+	DEBUG(fp, "import signature %d:%d:%d", class, version, subtype);
+}
+
+/* Find an entry for a fingerprints class/version/subtype */
+struct name_entry *
+fingerprint_name_entry(struct name_list *list, char *name)
+{
+	struct name_entry *nm_entry;
+
+	if (name == NULL || strlen(name) == 0)
+		return (NULL);
+
+	LIST_FOREACH(nm_entry, list, nm_entry) {
+		if (strcasecmp(nm_entry->nm_name, name) == 0) {
+			/* We'll move this to the front of the list later */
+			LIST_REMOVE(nm_entry, nm_entry);
+			break;
+		}
+	}
+	if (nm_entry == NULL) {
+		nm_entry = calloc(1, sizeof(*nm_entry));
+		if (nm_entry == NULL)
+			err(1, "calloc");
+		LIST_INIT(&nm_entry->nm_sublist);
+		strlcpy(nm_entry->nm_name, name, sizeof(nm_entry->nm_name));
+	}
+	LIST_INSERT_HEAD(list, nm_entry, nm_entry);
+	return (nm_entry);
+}
+
+
+void
+print_name_list(int opts, struct name_list *nml, const char *prefix)
+{
+	char newprefix[32];
+	struct name_entry *nm;
+
+	LIST_FOREACH(nm, nml, nm_entry) {
+		snprintf(newprefix, sizeof(newprefix), "%s%s\t", prefix,
+		    nm->nm_name);
+		printf("%s\n", newprefix);
+		print_name_list(opts, &nm->nm_sublist, newprefix);
+	}
+}
+
+void
+sort_name_list(int opts, struct name_list *nml)
+{
+	struct name_list new;
+	struct name_entry *nm, *nmsearch, *nmlast;
+
+	/* yes yes, it's a very slow sort.  so sue me */
+
+	LIST_INIT(&new);
+
+	while ((nm = LIST_FIRST(nml)) != NULL) {
+		LIST_REMOVE(nm, nm_entry);
+		nmlast = NULL;
+		LIST_FOREACH(nmsearch, &new, nm_entry) {
+			if (strcasecmp(nmsearch->nm_name, nm->nm_name) > 0) {
+				LIST_INSERT_BEFORE(nmsearch, nm, nm_entry);
+				break;
+			}
+			nmlast = nmsearch;
+		}
+		if (nmsearch == NULL) {
+			if (nmlast)
+				LIST_INSERT_AFTER(nmlast, nm, nm_entry);
+			else
+				LIST_INSERT_HEAD(&new, nm, nm_entry);
+		}
+
+		sort_name_list(opts, &nm->nm_sublist);
+	}
+	nmlast = NULL;
+	while ((nm = LIST_FIRST(&new)) != NULL) {
+		LIST_REMOVE(nm, nm_entry);
+		if (nmlast == NULL)
+			LIST_INSERT_HEAD(nml, nm, nm_entry);
+		else
+			LIST_INSERT_AFTER(nmlast, nm, nm_entry);
+		nmlast = nm;
+	}
+}
+
+/* parse the next integer in a formatted config file line */
+int
+get_int(char **line, size_t *len, int *var, int *mod,
+    const char *name, int flags, int max, const char *filename, int lineno)
+{
+	int fieldlen, i;
+	char *field;
+	long val = 0;
+
+	if (mod)
+		*mod = 0;
+	*var = 0;
+
+	field = get_field(line, len, &fieldlen);
+	if (field == NULL)
+		return (1);
+	if (fieldlen == 0) {
+		fprintf(stderr, "%s:%d empty %s\n", filename, lineno, name);
+		return (1);
+	}
+
+	i = 0;
+	if ((*field == '%' || *field == 'S' || *field == 'T' || *field == '*')
+	    && fieldlen >= 1) {
+		switch (*field) {
+		case 'S':
+			if (mod && (flags & T_MSS))
+				*mod = T_MSS;
+			if (fieldlen == 1)
+				return (0);
+			break;
+		case 'T':
+			if (mod && (flags & T_MTU))
+				*mod = T_MTU;
+			if (fieldlen == 1)
+				return (0);
+			break;
+		case '*':
+			if (fieldlen != 1) {
+				fprintf(stderr, "%s:%d long '%c' %s\n",
+				    filename, lineno, *field, name);
+				return (1);
+			}
+			if (mod && (flags & T_DC)) {
+				*mod = T_DC;
+				return (0);
+			}
+		case '%':
+			if (mod && (flags & T_MOD))
+				*mod = T_MOD;
+			if (fieldlen == 1) {
+				fprintf(stderr, "%s:%d modulus %s must have a "
+				    "value\n", filename, lineno, name);
+				return (1);
+			}
+			break;
+		}
+		if (mod == NULL || *mod == 0) {
+			fprintf(stderr, "%s:%d does not allow %c' %s\n",
+			    filename, lineno, *field, name);
+			return (1);
+		}
+		i++;
+	}
+
+	for (; i < fieldlen; i++) {
+		if (field[i] < '0' || field[i] > '9') {
+			fprintf(stderr, "%s:%d non-digit character in %s\n",
+			    filename, lineno, name);
+			return (1);
+		}
+		val = val * 10 + field[i] - '0';
+		if (val < 0) {
+			fprintf(stderr, "%s:%d %s overflowed\n", filename,
+			    lineno, name);
+			return (1);
+		}
+	}
+
+	if (val > max) {
+		fprintf(stderr, "%s:%d %s value %ld > %d\n", filename, lineno,
+		    name, val, max);
+		return (1);
+	}
+	*var = (int)val;
+
+	return (0);
+}
+
+/* parse the next string in a formatted config file line */
+int
+get_str(char **line, size_t *len, char **v, const char *name, int minlen,
+    const char *filename, int lineno)
+{
+	int fieldlen;
+	char *ptr;
+
+	ptr = get_field(line, len, &fieldlen);
+	if (ptr == NULL)
+		return (1);
+	if (fieldlen < minlen) {
+		fprintf(stderr, "%s:%d too short %s\n", filename, lineno, name);
+		return (1);
+	}
+	if ((*v = malloc(fieldlen + 1)) == NULL) {
+		perror("malloc()");
+		return (1);
+	}
+	memcpy(*v, ptr, fieldlen);
+	(*v)[fieldlen] = '\0';
+
+	return (0);
+}
+
+/* Parse out the TCP opts */
+int
+get_tcpopts(const char *filename, int lineno, const char *tcpopts,
+    pf_tcpopts_t *packed, int *optcnt, int *mss, int *mss_mod, int *wscale,
+    int *wscale_mod, int *ts0)
+{
+	int i, opt;
+
+	*packed = 0;
+	*optcnt = 0;
+	*wscale = 0;
+	*wscale_mod = T_DC;
+	*mss = 0;
+	*mss_mod = T_DC;
+	*ts0 = 0;
+	if (strcmp(tcpopts, ".") == 0)
+		return (0);
+
+	for (i = 0; tcpopts[i] && *optcnt < PF_OSFP_MAX_OPTS;) {
+		switch ((opt = toupper(tcpopts[i++]))) {
+		case 'N':	/* FALLTHROUGH */
+		case 'S':
+			*packed = (*packed << PF_OSFP_TCPOPT_BITS) |
+			    (opt == 'N' ? PF_OSFP_TCPOPT_NOP :
+			    PF_OSFP_TCPOPT_SACK);
+			break;
+		case 'W':	/* FALLTHROUGH */
+		case 'M': {
+			int *this_mod, *this;
+
+			if (opt == 'W') {
+				this = wscale;
+				this_mod = wscale_mod;
+			} else {
+				this = mss;
+				this_mod = mss_mod;
+			}
+			*this = 0;
+			*this_mod = 0;
+
+			*packed = (*packed << PF_OSFP_TCPOPT_BITS) |
+			    (opt == 'W' ? PF_OSFP_TCPOPT_WSCALE :
+			    PF_OSFP_TCPOPT_MSS);
+			if (tcpopts[i] == '*' && (tcpopts[i + 1] == '\0' ||
+			    tcpopts[i + 1] == ',')) {
+				*this_mod = T_DC;
+				i++;
+				break;
+			}
+
+			if (tcpopts[i] == '%') {
+				*this_mod = T_MOD;
+				i++;
+			}
+			do {
+				if (!isdigit(tcpopts[i])) {
+					fprintf(stderr, "%s:%d unknown "
+					    "character '%c' in %c TCP opt\n",
+					    filename, lineno, tcpopts[i], opt);
+					return (1);
+				}
+				*this = (*this * 10) + tcpopts[i++] - '0';
+			} while(tcpopts[i] != ',' && tcpopts[i] != '\0');
+			break;
+		}
+		case 'T':
+			if (tcpopts[i] == '0') {
+				*ts0 = 1;
+				i++;
+			}
+			*packed = (*packed << PF_OSFP_TCPOPT_BITS) |
+			    PF_OSFP_TCPOPT_TS;
+			break;
+		}
+		(*optcnt) ++;
+		if (tcpopts[i] == '\0')
+			break;
+		if (tcpopts[i] != ',') {
+			fprintf(stderr, "%s:%d unknown option to %c TCP opt\n",
+			    filename, lineno, opt);
+			return (1);
+		}
+		i++;
+	}
+
+	return (0);
+}
+
+/* rip the next field ouf of a formatted config file line */
+char *
+get_field(char **line, size_t *len, int *fieldlen)
+{
+	char *ret, *ptr = *line;
+	size_t plen = *len;
+
+
+	while (plen && isspace(*ptr)) {
+		plen--;
+		ptr++;
+	}
+	ret = ptr;
+	*fieldlen = 0;
+
+	for (; plen > 0 && *ptr != ':'; plen--, ptr++)
+		(*fieldlen)++;
+	if (plen) {
+		*line = ptr + 1;
+		*len = plen - 1;
+	} else {
+		*len = 0;
+	}
+	while (*fieldlen && isspace(ret[*fieldlen - 1]))
+		(*fieldlen)--;
+	return (ret);
+}
+
+
+const char *
+print_ioctl(struct pf_osfp_ioctl *fp)
+{
+	static char buf[1024];
+	char tmp[32];
+	int i, opt;
+
+	*buf = '\0';
+	if (fp->fp_flags & PF_OSFP_WSIZE_DC)
+		strlcat(buf, "*", sizeof(buf));
+	else if (fp->fp_flags & PF_OSFP_WSIZE_MSS)
+		strlcat(buf, "S", sizeof(buf));
+	else if (fp->fp_flags & PF_OSFP_WSIZE_MTU)
+		strlcat(buf, "T", sizeof(buf));
+	else {
+		if (fp->fp_flags & PF_OSFP_WSIZE_MOD)
+			strlcat(buf, "%", sizeof(buf));
+		snprintf(tmp, sizeof(tmp), "%d", fp->fp_wsize);
+		strlcat(buf, tmp, sizeof(buf));
+	}
+	strlcat(buf, ":", sizeof(buf));
+
+	snprintf(tmp, sizeof(tmp), "%d", fp->fp_ttl);
+	strlcat(buf, tmp, sizeof(buf));
+	strlcat(buf, ":", sizeof(buf));
+
+	if (fp->fp_flags & PF_OSFP_DF)
+		strlcat(buf, "1", sizeof(buf));
+	else
+		strlcat(buf, "0", sizeof(buf));
+	strlcat(buf, ":", sizeof(buf));
+
+	if (fp->fp_flags & PF_OSFP_PSIZE_DC)
+		strlcat(buf, "*", sizeof(buf));
+	else {
+		if (fp->fp_flags & PF_OSFP_PSIZE_MOD)
+			strlcat(buf, "%", sizeof(buf));
+		snprintf(tmp, sizeof(tmp), "%d", fp->fp_psize);
+		strlcat(buf, tmp, sizeof(buf));
+	}
+	strlcat(buf, ":", sizeof(buf));
+
+	if (fp->fp_optcnt == 0)
+		strlcat(buf, ".", sizeof(buf));
+	for (i = fp->fp_optcnt - 1; i >= 0; i--) {
+		opt = fp->fp_tcpopts >> (i * PF_OSFP_TCPOPT_BITS);
+		opt &= (1 << PF_OSFP_TCPOPT_BITS) - 1;
+		switch (opt) {
+		case PF_OSFP_TCPOPT_NOP:
+			strlcat(buf, "N", sizeof(buf));
+			break;
+		case PF_OSFP_TCPOPT_SACK:
+			strlcat(buf, "S", sizeof(buf));
+			break;
+		case PF_OSFP_TCPOPT_TS:
+			strlcat(buf, "T", sizeof(buf));
+			if (fp->fp_flags & PF_OSFP_TS0)
+				strlcat(buf, "0", sizeof(buf));
+			break;
+		case PF_OSFP_TCPOPT_MSS:
+			strlcat(buf, "M", sizeof(buf));
+			if (fp->fp_flags & PF_OSFP_MSS_DC)
+				strlcat(buf, "*", sizeof(buf));
+			else {
+				if (fp->fp_flags & PF_OSFP_MSS_MOD)
+					strlcat(buf, "%", sizeof(buf));
+				snprintf(tmp, sizeof(tmp), "%d", fp->fp_mss);
+				strlcat(buf, tmp, sizeof(buf));
+			}
+			break;
+		case PF_OSFP_TCPOPT_WSCALE:
+			strlcat(buf, "W", sizeof(buf));
+			if (fp->fp_flags & PF_OSFP_WSCALE_DC)
+				strlcat(buf, "*", sizeof(buf));
+			else {
+				if (fp->fp_flags & PF_OSFP_WSCALE_MOD)
+					strlcat(buf, "%", sizeof(buf));
+				snprintf(tmp, sizeof(tmp), "%d", fp->fp_wscale);
+				strlcat(buf, tmp, sizeof(buf));
+			}
+			break;
+		}
+
+		if (i != 0)
+			strlcat(buf, ",", sizeof(buf));
+	}
+	strlcat(buf, ":", sizeof(buf));
+
+	strlcat(buf, fp->fp_os.fp_class_nm, sizeof(buf));
+	strlcat(buf, ":", sizeof(buf));
+	strlcat(buf, fp->fp_os.fp_version_nm, sizeof(buf));
+	strlcat(buf, ":", sizeof(buf));
+	strlcat(buf, fp->fp_os.fp_subtype_nm, sizeof(buf));
+	strlcat(buf, ":", sizeof(buf));
+
+	snprintf(tmp, sizeof(tmp), "TcpOpts %d 0x%llx", fp->fp_optcnt,
+	    (long long int)fp->fp_tcpopts);
+	strlcat(buf, tmp, sizeof(buf));
+
+	return (buf);
+}
diff --git a/freebsd/contrib/pf/pfctl/pfctl_parser.c b/freebsd/contrib/pf/pfctl/pfctl_parser.c
new file mode 100644
index 0000000..0e6a5a1
--- /dev/null
+++ b/freebsd/contrib/pf/pfctl/pfctl_parser.c
@@ -0,0 +1,1754 @@
+#include <machine/rtems-bsd-user-space.h>
+
+/*	$OpenBSD: pfctl_parser.c,v 1.240 2008/06/10 20:55:02 mcbride Exp $ */
+
+/*
+ * Copyright (c) 2001 Daniel Hartmeier
+ * Copyright (c) 2002,2003 Henning Brauer
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *    - Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *    - 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 COPYRIGHT HOLDERS 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
+ * COPYRIGHT HOLDERS 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>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <rtems/bsd/sys/param.h>
+#include <sys/proc.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/ip_icmp.h>
+#include <netinet/icmp6.h>
+#include <net/pfvar.h>
+#include <arpa/inet.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <netdb.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <err.h>
+#include <ifaddrs.h>
+#include <unistd.h>
+
+#include "pfctl_parser.h"
+#include "pfctl.h"
+
+void		 print_op (u_int8_t, const char *, const char *);
+void		 print_port (u_int8_t, u_int16_t, u_int16_t, const char *, int);
+void		 print_ugid (u_int8_t, unsigned, unsigned, const char *, unsigned);
+void		 print_flags (u_int8_t);
+void		 print_fromto(struct pf_rule_addr *, pf_osfp_t,
+		    struct pf_rule_addr *, u_int8_t, u_int8_t, int, int);
+int		 ifa_skip_if(const char *filter, struct node_host *p);
+
+struct node_host	*ifa_grouplookup(const char *, int);
+struct node_host	*host_if(const char *, int);
+struct node_host	*host_v4(const char *, int);
+struct node_host	*host_v6(const char *, int);
+struct node_host	*host_dns(const char *, int, int);
+
+const char *tcpflags = "FSRPAUEW";
+
+static const struct icmptypeent icmp_type[] = {
+	{ "echoreq",	ICMP_ECHO },
+	{ "echorep",	ICMP_ECHOREPLY },
+	{ "unreach",	ICMP_UNREACH },
+	{ "squench",	ICMP_SOURCEQUENCH },
+	{ "redir",	ICMP_REDIRECT },
+	{ "althost",	ICMP_ALTHOSTADDR },
+	{ "routeradv",	ICMP_ROUTERADVERT },
+	{ "routersol",	ICMP_ROUTERSOLICIT },
+	{ "timex",	ICMP_TIMXCEED },
+	{ "paramprob",	ICMP_PARAMPROB },
+	{ "timereq",	ICMP_TSTAMP },
+	{ "timerep",	ICMP_TSTAMPREPLY },
+	{ "inforeq",	ICMP_IREQ },
+	{ "inforep",	ICMP_IREQREPLY },
+	{ "maskreq",	ICMP_MASKREQ },
+	{ "maskrep",	ICMP_MASKREPLY },
+	{ "trace",	ICMP_TRACEROUTE },
+	{ "dataconv",	ICMP_DATACONVERR },
+	{ "mobredir",	ICMP_MOBILE_REDIRECT },
+	{ "ipv6-where",	ICMP_IPV6_WHEREAREYOU },
+	{ "ipv6-here",	ICMP_IPV6_IAMHERE },
+	{ "mobregreq",	ICMP_MOBILE_REGREQUEST },
+	{ "mobregrep",	ICMP_MOBILE_REGREPLY },
+	{ "skip",	ICMP_SKIP },
+	{ "photuris",	ICMP_PHOTURIS }
+};
+
+static const struct icmptypeent icmp6_type[] = {
+	{ "unreach",	ICMP6_DST_UNREACH },
+	{ "toobig",	ICMP6_PACKET_TOO_BIG },
+	{ "timex",	ICMP6_TIME_EXCEEDED },
+	{ "paramprob",	ICMP6_PARAM_PROB },
+	{ "echoreq",	ICMP6_ECHO_REQUEST },
+	{ "echorep",	ICMP6_ECHO_REPLY },
+	{ "groupqry",	ICMP6_MEMBERSHIP_QUERY },
+	{ "listqry",	MLD_LISTENER_QUERY },
+	{ "grouprep",	ICMP6_MEMBERSHIP_REPORT },
+	{ "listenrep",	MLD_LISTENER_REPORT },
+	{ "groupterm",	ICMP6_MEMBERSHIP_REDUCTION },
+	{ "listendone", MLD_LISTENER_DONE },
+	{ "routersol",	ND_ROUTER_SOLICIT },
+	{ "routeradv",	ND_ROUTER_ADVERT },
+	{ "neighbrsol", ND_NEIGHBOR_SOLICIT },
+	{ "neighbradv", ND_NEIGHBOR_ADVERT },
+	{ "redir",	ND_REDIRECT },
+	{ "routrrenum", ICMP6_ROUTER_RENUMBERING },
+	{ "wrureq",	ICMP6_WRUREQUEST },
+	{ "wrurep",	ICMP6_WRUREPLY },
+	{ "fqdnreq",	ICMP6_FQDN_QUERY },
+	{ "fqdnrep",	ICMP6_FQDN_REPLY },
+	{ "niqry",	ICMP6_NI_QUERY },
+	{ "nirep",	ICMP6_NI_REPLY },
+	{ "mtraceresp",	MLD_MTRACE_RESP },
+	{ "mtrace",	MLD_MTRACE }
+};
+
+static const struct icmpcodeent icmp_code[] = {
+	{ "net-unr",		ICMP_UNREACH,	ICMP_UNREACH_NET },
+	{ "host-unr",		ICMP_UNREACH,	ICMP_UNREACH_HOST },
+	{ "proto-unr",		ICMP_UNREACH,	ICMP_UNREACH_PROTOCOL },
+	{ "port-unr",		ICMP_UNREACH,	ICMP_UNREACH_PORT },
+	{ "needfrag",		ICMP_UNREACH,	ICMP_UNREACH_NEEDFRAG },
+	{ "srcfail",		ICMP_UNREACH,	ICMP_UNREACH_SRCFAIL },
+	{ "net-unk",		ICMP_UNREACH,	ICMP_UNREACH_NET_UNKNOWN },
+	{ "host-unk",		ICMP_UNREACH,	ICMP_UNREACH_HOST_UNKNOWN },
+	{ "isolate",		ICMP_UNREACH,	ICMP_UNREACH_ISOLATED },
+	{ "net-prohib",		ICMP_UNREACH,	ICMP_UNREACH_NET_PROHIB },
+	{ "host-prohib",	ICMP_UNREACH,	ICMP_UNREACH_HOST_PROHIB },
+	{ "net-tos",		ICMP_UNREACH,	ICMP_UNREACH_TOSNET },
+	{ "host-tos",		ICMP_UNREACH,	ICMP_UNREACH_TOSHOST },
+	{ "filter-prohib",	ICMP_UNREACH,	ICMP_UNREACH_FILTER_PROHIB },
+	{ "host-preced",	ICMP_UNREACH,	ICMP_UNREACH_HOST_PRECEDENCE },
+	{ "cutoff-preced",	ICMP_UNREACH,	ICMP_UNREACH_PRECEDENCE_CUTOFF },
+	{ "redir-net",		ICMP_REDIRECT,	ICMP_REDIRECT_NET },
+	{ "redir-host",		ICMP_REDIRECT,	ICMP_REDIRECT_HOST },
+	{ "redir-tos-net",	ICMP_REDIRECT,	ICMP_REDIRECT_TOSNET },
+	{ "redir-tos-host",	ICMP_REDIRECT,	ICMP_REDIRECT_TOSHOST },
+	{ "normal-adv",		ICMP_ROUTERADVERT, ICMP_ROUTERADVERT_NORMAL },
+	{ "common-adv",		ICMP_ROUTERADVERT, ICMP_ROUTERADVERT_NOROUTE_COMMON },
+	{ "transit",		ICMP_TIMXCEED,	ICMP_TIMXCEED_INTRANS },
+	{ "reassemb",		ICMP_TIMXCEED,	ICMP_TIMXCEED_REASS },
+	{ "badhead",		ICMP_PARAMPROB,	ICMP_PARAMPROB_ERRATPTR },
+	{ "optmiss",		ICMP_PARAMPROB,	ICMP_PARAMPROB_OPTABSENT },
+	{ "badlen",		ICMP_PARAMPROB,	ICMP_PARAMPROB_LENGTH },
+	{ "unknown-ind",	ICMP_PHOTURIS,	ICMP_PHOTURIS_UNKNOWN_INDEX },
+	{ "auth-fail",		ICMP_PHOTURIS,	ICMP_PHOTURIS_AUTH_FAILED },
+	{ "decrypt-fail",	ICMP_PHOTURIS,	ICMP_PHOTURIS_DECRYPT_FAILED }
+};
+
+static const struct icmpcodeent icmp6_code[] = {
+	{ "admin-unr", ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_ADMIN },
+	{ "noroute-unr", ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_NOROUTE },
+	{ "notnbr-unr",	ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_NOTNEIGHBOR },
+	{ "beyond-unr", ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_BEYONDSCOPE },
+	{ "addr-unr", ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_ADDR },
+	{ "port-unr", ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_NOPORT },
+	{ "transit", ICMP6_TIME_EXCEEDED, ICMP6_TIME_EXCEED_TRANSIT },
+	{ "reassemb", ICMP6_TIME_EXCEEDED, ICMP6_TIME_EXCEED_REASSEMBLY },
+	{ "badhead", ICMP6_PARAM_PROB, ICMP6_PARAMPROB_HEADER },
+	{ "nxthdr", ICMP6_PARAM_PROB, ICMP6_PARAMPROB_NEXTHEADER },
+	{ "redironlink", ND_REDIRECT, ND_REDIRECT_ONLINK },
+	{ "redirrouter", ND_REDIRECT, ND_REDIRECT_ROUTER }
+};
+
+const struct pf_timeout pf_timeouts[] = {
+	{ "tcp.first",		PFTM_TCP_FIRST_PACKET },
+	{ "tcp.opening",	PFTM_TCP_OPENING },
+	{ "tcp.established",	PFTM_TCP_ESTABLISHED },
+	{ "tcp.closing",	PFTM_TCP_CLOSING },
+	{ "tcp.finwait",	PFTM_TCP_FIN_WAIT },
+	{ "tcp.closed",		PFTM_TCP_CLOSED },
+	{ "tcp.tsdiff",		PFTM_TS_DIFF },
+	{ "udp.first",		PFTM_UDP_FIRST_PACKET },
+	{ "udp.single",		PFTM_UDP_SINGLE },
+	{ "udp.multiple",	PFTM_UDP_MULTIPLE },
+	{ "icmp.first",		PFTM_ICMP_FIRST_PACKET },
+	{ "icmp.error",		PFTM_ICMP_ERROR_REPLY },
+	{ "other.first",	PFTM_OTHER_FIRST_PACKET },
+	{ "other.single",	PFTM_OTHER_SINGLE },
+	{ "other.multiple",	PFTM_OTHER_MULTIPLE },
+	{ "frag",		PFTM_FRAG },
+	{ "interval",		PFTM_INTERVAL },
+	{ "adaptive.start",	PFTM_ADAPTIVE_START },
+	{ "adaptive.end",	PFTM_ADAPTIVE_END },
+	{ "src.track",		PFTM_SRC_NODE },
+	{ NULL,			0 }
+};
+
+const struct icmptypeent *
+geticmptypebynumber(u_int8_t type, sa_family_t af)
+{
+	unsigned int	i;
+
+	if (af != AF_INET6) {
+		for (i=0; i < (sizeof (icmp_type) / sizeof(icmp_type[0]));
+		    i++) {
+			if (type == icmp_type[i].type)
+				return (&icmp_type[i]);
+		}
+	} else {
+		for (i=0; i < (sizeof (icmp6_type) /
+		    sizeof(icmp6_type[0])); i++) {
+			if (type == icmp6_type[i].type)
+				 return (&icmp6_type[i]);
+		}
+	}
+	return (NULL);
+}
+
+const struct icmptypeent *
+geticmptypebyname(char *w, sa_family_t af)
+{
+	unsigned int	i;
+
+	if (af != AF_INET6) {
+		for (i=0; i < (sizeof (icmp_type) / sizeof(icmp_type[0]));
+		    i++) {
+			if (!strcmp(w, icmp_type[i].name))
+				return (&icmp_type[i]);
+		}
+	} else {
+		for (i=0; i < (sizeof (icmp6_type) /
+		    sizeof(icmp6_type[0])); i++) {
+			if (!strcmp(w, icmp6_type[i].name))
+				return (&icmp6_type[i]);
+		}
+	}
+	return (NULL);
+}
+
+const struct icmpcodeent *
+geticmpcodebynumber(u_int8_t type, u_int8_t code, sa_family_t af)
+{
+	unsigned int	i;
+
+	if (af != AF_INET6) {
+		for (i=0; i < (sizeof (icmp_code) / sizeof(icmp_code[0]));
+		    i++) {
+			if (type == icmp_code[i].type &&
+			    code == icmp_code[i].code)
+				return (&icmp_code[i]);
+		}
+	} else {
+		for (i=0; i < (sizeof (icmp6_code) /
+		    sizeof(icmp6_code[0])); i++) {
+			if (type == icmp6_code[i].type &&
+			    code == icmp6_code[i].code)
+				return (&icmp6_code[i]);
+		}
+	}
+	return (NULL);
+}
+
+const struct icmpcodeent *
+geticmpcodebyname(u_long type, char *w, sa_family_t af)
+{
+	unsigned int	i;
+
+	if (af != AF_INET6) {
+		for (i=0; i < (sizeof (icmp_code) / sizeof(icmp_code[0]));
+		    i++) {
+			if (type == icmp_code[i].type &&
+			    !strcmp(w, icmp_code[i].name))
+				return (&icmp_code[i]);
+		}
+	} else {
+		for (i=0; i < (sizeof (icmp6_code) /
+		    sizeof(icmp6_code[0])); i++) {
+			if (type == icmp6_code[i].type &&
+			    !strcmp(w, icmp6_code[i].name))
+				return (&icmp6_code[i]);
+		}
+	}
+	return (NULL);
+}
+
+void
+print_op(u_int8_t op, const char *a1, const char *a2)
+{
+	if (op == PF_OP_IRG)
+		printf(" %s >< %s", a1, a2);
+	else if (op == PF_OP_XRG)
+		printf(" %s <> %s", a1, a2);
+	else if (op == PF_OP_EQ)
+		printf(" = %s", a1);
+	else if (op == PF_OP_NE)
+		printf(" != %s", a1);
+	else if (op == PF_OP_LT)
+		printf(" < %s", a1);
+	else if (op == PF_OP_LE)
+		printf(" <= %s", a1);
+	else if (op == PF_OP_GT)
+		printf(" > %s", a1);
+	else if (op == PF_OP_GE)
+		printf(" >= %s", a1);
+	else if (op == PF_OP_RRG)
+		printf(" %s:%s", a1, a2);
+}
+
+void
+print_port(u_int8_t op, u_int16_t p1, u_int16_t p2, const char *proto, int numeric)
+{
+	char		 a1[6], a2[6];
+	struct servent	*s;
+
+	if (!numeric)
+		s = getservbyport(p1, proto);
+	else
+		s = NULL;
+	p1 = ntohs(p1);
+	p2 = ntohs(p2);
+	snprintf(a1, sizeof(a1), "%u", p1);
+	snprintf(a2, sizeof(a2), "%u", p2);
+	printf(" port");
+	if (s != NULL && (op == PF_OP_EQ || op == PF_OP_NE))
+		print_op(op, s->s_name, a2);
+	else
+		print_op(op, a1, a2);
+}
+
+void
+print_ugid(u_int8_t op, unsigned u1, unsigned u2, const char *t, unsigned umax)
+{
+	char	a1[11], a2[11];
+
+	snprintf(a1, sizeof(a1), "%u", u1);
+	snprintf(a2, sizeof(a2), "%u", u2);
+	printf(" %s", t);
+	if (u1 == umax && (op == PF_OP_EQ || op == PF_OP_NE))
+		print_op(op, "unknown", a2);
+	else
+		print_op(op, a1, a2);
+}
+
+void
+print_flags(u_int8_t f)
+{
+	int	i;
+
+	for (i = 0; tcpflags[i]; ++i)
+		if (f & (1 << i))
+			printf("%c", tcpflags[i]);
+}
+
+void
+print_fromto(struct pf_rule_addr *src, pf_osfp_t osfp, struct pf_rule_addr *dst,
+    sa_family_t af, u_int8_t proto, int verbose, int numeric)
+{
+	char buf[PF_OSFP_LEN*3];
+	if (src->addr.type == PF_ADDR_ADDRMASK &&
+	    dst->addr.type == PF_ADDR_ADDRMASK &&
+	    PF_AZERO(&src->addr.v.a.addr, AF_INET6) &&
+	    PF_AZERO(&src->addr.v.a.mask, AF_INET6) &&
+	    PF_AZERO(&dst->addr.v.a.addr, AF_INET6) &&
+	    PF_AZERO(&dst->addr.v.a.mask, AF_INET6) &&
+	    !src->neg && !dst->neg &&
+	    !src->port_op && !dst->port_op &&
+	    osfp == PF_OSFP_ANY)
+		printf(" all");
+	else {
+		printf(" from ");
+		if (src->neg)
+			printf("! ");
+		print_addr(&src->addr, af, verbose);
+		if (src->port_op)
+			print_port(src->port_op, src->port[0],
+			    src->port[1],
+			    proto == IPPROTO_TCP ? "tcp" : "udp",
+			    numeric);
+		if (osfp != PF_OSFP_ANY)
+			printf(" os \"%s\"", pfctl_lookup_fingerprint(osfp, buf,
+			    sizeof(buf)));
+
+		printf(" to ");
+		if (dst->neg)
+			printf("! ");
+		print_addr(&dst->addr, af, verbose);
+		if (dst->port_op)
+			print_port(dst->port_op, dst->port[0],
+			    dst->port[1],
+			    proto == IPPROTO_TCP ? "tcp" : "udp",
+			    numeric);
+	}
+}
+
+void
+print_pool(struct pf_pool *pool, u_int16_t p1, u_int16_t p2,
+    sa_family_t af, int id)
+{
+	struct pf_pooladdr	*pooladdr;
+
+	if ((TAILQ_FIRST(&pool->list) != NULL) &&
+	    TAILQ_NEXT(TAILQ_FIRST(&pool->list), entries) != NULL)
+		printf("{ ");
+	TAILQ_FOREACH(pooladdr, &pool->list, entries){
+		switch (id) {
+		case PF_NAT:
+		case PF_RDR:
+		case PF_BINAT:
+			print_addr(&pooladdr->addr, af, 0);
+			break;
+		case PF_PASS:
+			if (PF_AZERO(&pooladdr->addr.v.a.addr, af))
+				printf("%s", pooladdr->ifname);
+			else {
+				printf("(%s ", pooladdr->ifname);
+				print_addr(&pooladdr->addr, af, 0);
+				printf(")");
+			}
+			break;
+		default:
+			break;
+		}
+		if (TAILQ_NEXT(pooladdr, entries) != NULL)
+			printf(", ");
+		else if (TAILQ_NEXT(TAILQ_FIRST(&pool->list), entries) != NULL)
+			printf(" }");
+	}
+	switch (id) {
+	case PF_NAT:
+		if ((p1 != PF_NAT_PROXY_PORT_LOW ||
+		    p2 != PF_NAT_PROXY_PORT_HIGH) && (p1 != 0 || p2 != 0)) {
+			if (p1 == p2)
+				printf(" port %u", p1);
+			else
+				printf(" port %u:%u", p1, p2);
+		}
+		break;
+	case PF_RDR:
+		if (p1) {
+			printf(" port %u", p1);
+			if (p2 && (p2 != p1))
+				printf(":%u", p2);
+		}
+		break;
+	default:
+		break;
+	}
+	switch (pool->opts & PF_POOL_TYPEMASK) {
+	case PF_POOL_NONE:
+		break;
+	case PF_POOL_BITMASK:
+		printf(" bitmask");
+		break;
+	case PF_POOL_RANDOM:
+		printf(" random");
+		break;
+	case PF_POOL_SRCHASH:
+		printf(" source-hash 0x%08x%08x%08x%08x",
+		    pool->key.key32[0], pool->key.key32[1],
+		    pool->key.key32[2], pool->key.key32[3]);
+		break;
+	case PF_POOL_ROUNDROBIN:
+		printf(" round-robin");
+		break;
+	}
+	if (pool->opts & PF_POOL_STICKYADDR)
+		printf(" sticky-address");
+	if (id == PF_NAT && p1 == 0 && p2 == 0)
+		printf(" static-port");
+}
+
+const char	*pf_reasons[PFRES_MAX+1] = PFRES_NAMES;
+const char	*pf_lcounters[LCNT_MAX+1] = LCNT_NAMES;
+const char	*pf_fcounters[FCNT_MAX+1] = FCNT_NAMES;
+const char	*pf_scounters[FCNT_MAX+1] = FCNT_NAMES;
+
+void
+print_status(struct pf_status *s, int opts)
+{
+	char			statline[80], *running;
+	time_t			runtime;
+	int			i;
+	char			buf[PF_MD5_DIGEST_LENGTH * 2 + 1];
+	static const char	hex[] = "0123456789abcdef";
+
+	runtime = time(NULL) - s->since;
+	running = s->running ? "Enabled" : "Disabled";
+
+	if (s->since) {
+		unsigned int	sec, min, hrs, day = runtime;
+
+		sec = day % 60;
+		day /= 60;
+		min = day % 60;
+		day /= 60;
+		hrs = day % 24;
+		day /= 24;
+		snprintf(statline, sizeof(statline),
+		    "Status: %s for %u days %.2u:%.2u:%.2u",
+		    running, day, hrs, min, sec);
+	} else
+		snprintf(statline, sizeof(statline), "Status: %s", running);
+	printf("%-44s", statline);
+	switch (s->debug) {
+	case PF_DEBUG_NONE:
+		printf("%15s\n\n", "Debug: None");
+		break;
+	case PF_DEBUG_URGENT:
+		printf("%15s\n\n", "Debug: Urgent");
+		break;
+	case PF_DEBUG_MISC:
+		printf("%15s\n\n", "Debug: Misc");
+		break;
+	case PF_DEBUG_NOISY:
+		printf("%15s\n\n", "Debug: Loud");
+		break;
+	}
+
+	if (opts & PF_OPT_VERBOSE) {
+		printf("Hostid:   0x%08x\n", ntohl(s->hostid));
+
+		for (i = 0; i < PF_MD5_DIGEST_LENGTH; i++) {
+			buf[i + i] = hex[s->pf_chksum[i] >> 4];
+			buf[i + i + 1] = hex[s->pf_chksum[i] & 0x0f];
+		}
+		buf[i + i] = '\0';
+		printf("Checksum: 0x%s\n\n", buf);
+	}
+
+	if (s->ifname[0] != 0) {
+		printf("Interface Stats for %-16s %5s %16s\n",
+		    s->ifname, "IPv4", "IPv6");
+		printf("  %-25s %14llu %16llu\n", "Bytes In",
+		    (unsigned long long)s->bcounters[0][0],
+		    (unsigned long long)s->bcounters[1][0]);
+		printf("  %-25s %14llu %16llu\n", "Bytes Out",
+		    (unsigned long long)s->bcounters[0][1],
+		    (unsigned long long)s->bcounters[1][1]);
+		printf("  Packets In\n");
+		printf("    %-23s %14llu %16llu\n", "Passed",
+		    (unsigned long long)s->pcounters[0][0][PF_PASS],
+		    (unsigned long long)s->pcounters[1][0][PF_PASS]);
+		printf("    %-23s %14llu %16llu\n", "Blocked",
+		    (unsigned long long)s->pcounters[0][0][PF_DROP],
+		    (unsigned long long)s->pcounters[1][0][PF_DROP]);
+		printf("  Packets Out\n");
+		printf("    %-23s %14llu %16llu\n", "Passed",
+		    (unsigned long long)s->pcounters[0][1][PF_PASS],
+		    (unsigned long long)s->pcounters[1][1][PF_PASS]);
+		printf("    %-23s %14llu %16llu\n\n", "Blocked",
+		    (unsigned long long)s->pcounters[0][1][PF_DROP],
+		    (unsigned long long)s->pcounters[1][1][PF_DROP]);
+	}
+	printf("%-27s %14s %16s\n", "State Table", "Total", "Rate");
+	printf("  %-25s %14u %14s\n", "current entries", s->states, "");
+	for (i = 0; i < FCNT_MAX; i++) {
+		printf("  %-25s %14llu ", pf_fcounters[i],
+			    (unsigned long long)s->fcounters[i]);
+		if (runtime > 0)
+			printf("%14.1f/s\n",
+			    (double)s->fcounters[i] / (double)runtime);
+		else
+			printf("%14s\n", "");
+	}
+	if (opts & PF_OPT_VERBOSE) {
+		printf("Source Tracking Table\n");
+		printf("  %-25s %14u %14s\n", "current entries",
+		    s->src_nodes, "");
+		for (i = 0; i < SCNT_MAX; i++) {
+			printf("  %-25s %14lld ", pf_scounters[i],
+#ifdef __FreeBSD__
+				    (long long)s->scounters[i]);
+#else
+				    s->scounters[i]);
+#endif
+			if (runtime > 0)
+				printf("%14.1f/s\n",
+				    (double)s->scounters[i] / (double)runtime);
+			else
+				printf("%14s\n", "");
+		}
+	}
+	printf("Counters\n");
+	for (i = 0; i < PFRES_MAX; i++) {
+		printf("  %-25s %14llu ", pf_reasons[i],
+		    (unsigned long long)s->counters[i]);
+		if (runtime > 0)
+			printf("%14.1f/s\n",
+			    (double)s->counters[i] / (double)runtime);
+		else
+			printf("%14s\n", "");
+	}
+	if (opts & PF_OPT_VERBOSE) {
+		printf("Limit Counters\n");
+		for (i = 0; i < LCNT_MAX; i++) {
+			printf("  %-25s %14lld ", pf_lcounters[i],
+#ifdef __FreeBSD__
+				    (unsigned long long)s->lcounters[i]);
+#else
+				    s->lcounters[i]);
+#endif
+			if (runtime > 0)
+				printf("%14.1f/s\n",
+				    (double)s->lcounters[i] / (double)runtime);
+			else
+				printf("%14s\n", "");
+		}
+	}
+}
+
+void
+print_src_node(struct pf_src_node *sn, int opts)
+{
+	struct pf_addr_wrap aw;
+	int min, sec;
+
+	memset(&aw, 0, sizeof(aw));
+	if (sn->af == AF_INET)
+		aw.v.a.mask.addr32[0] = 0xffffffff;
+	else
+		memset(&aw.v.a.mask, 0xff, sizeof(aw.v.a.mask));
+
+	aw.v.a.addr = sn->addr;
+	print_addr(&aw, sn->af, opts & PF_OPT_VERBOSE2);
+	printf(" -> ");
+	aw.v.a.addr = sn->raddr;
+	print_addr(&aw, sn->af, opts & PF_OPT_VERBOSE2);
+	printf(" ( states %u, connections %u, rate %u.%u/%us )\n", sn->states,
+	    sn->conn, sn->conn_rate.count / 1000,
+	    (sn->conn_rate.count % 1000) / 100, sn->conn_rate.seconds);
+	if (opts & PF_OPT_VERBOSE) {
+		sec = sn->creation % 60;
+		sn->creation /= 60;
+		min = sn->creation % 60;
+		sn->creation /= 60;
+		printf("   age %.2u:%.2u:%.2u", sn->creation, min, sec);
+		if (sn->states == 0) {
+			sec = sn->expire % 60;
+			sn->expire /= 60;
+			min = sn->expire % 60;
+			sn->expire /= 60;
+			printf(", expires in %.2u:%.2u:%.2u",
+			    sn->expire, min, sec);
+		}
+		printf(", %llu pkts, %llu bytes",
+#ifdef __FreeBSD__
+		    (unsigned long long)(sn->packets[0] + sn->packets[1]),
+		    (unsigned long long)(sn->bytes[0] + sn->bytes[1]));
+#else
+		    sn->packets[0] + sn->packets[1],
+		    sn->bytes[0] + sn->bytes[1]);
+#endif
+		switch (sn->ruletype) {
+		case PF_NAT:
+			if (sn->rule.nr != -1)
+				printf(", nat rule %u", sn->rule.nr);
+			break;
+		case PF_RDR:
+			if (sn->rule.nr != -1)
+				printf(", rdr rule %u", sn->rule.nr);
+			break;
+		case PF_PASS:
+			if (sn->rule.nr != -1)
+				printf(", filter rule %u", sn->rule.nr);
+			break;
+		}
+		printf("\n");
+	}
+}
+
+void
+print_rule(struct pf_rule *r, const char *anchor_call, int verbose, int numeric)
+{
+	static const char *actiontypes[] = { "pass", "block", "scrub",
+	    "no scrub", "nat", "no nat", "binat", "no binat", "rdr", "no rdr" };
+	static const char *anchortypes[] = { "anchor", "anchor", "anchor",
+	    "anchor", "nat-anchor", "nat-anchor", "binat-anchor",
+	    "binat-anchor", "rdr-anchor", "rdr-anchor" };
+	int	i, opts;
+
+	if (verbose)
+		printf("@%d ", r->nr);
+	if (r->action > PF_NORDR)
+		printf("action(%d)", r->action);
+	else if (anchor_call[0]) {
+		if (anchor_call[0] == '_') {
+			printf("%s", anchortypes[r->action]);
+		} else
+			printf("%s \"%s\"", anchortypes[r->action],
+			    anchor_call);
+	} else {
+		printf("%s", actiontypes[r->action]);
+		if (r->natpass)
+			printf(" pass");
+	}
+	if (r->action == PF_DROP) {
+		if (r->rule_flag & PFRULE_RETURN)
+			printf(" return");
+		else if (r->rule_flag & PFRULE_RETURNRST) {
+			if (!r->return_ttl)
+				printf(" return-rst");
+			else
+				printf(" return-rst(ttl %d)", r->return_ttl);
+		} else if (r->rule_flag & PFRULE_RETURNICMP) {
+			const struct icmpcodeent	*ic, *ic6;
+
+			ic = geticmpcodebynumber(r->return_icmp >> 8,
+			    r->return_icmp & 255, AF_INET);
+			ic6 = geticmpcodebynumber(r->return_icmp6 >> 8,
+			    r->return_icmp6 & 255, AF_INET6);
+
+			switch (r->af) {
+			case AF_INET:
+				printf(" return-icmp");
+				if (ic == NULL)
+					printf("(%u)", r->return_icmp & 255);
+				else
+					printf("(%s)", ic->name);
+				break;
+			case AF_INET6:
+				printf(" return-icmp6");
+				if (ic6 == NULL)
+					printf("(%u)", r->return_icmp6 & 255);
+				else
+					printf("(%s)", ic6->name);
+				break;
+			default:
+				printf(" return-icmp");
+				if (ic == NULL)
+					printf("(%u, ", r->return_icmp & 255);
+				else
+					printf("(%s, ", ic->name);
+				if (ic6 == NULL)
+					printf("%u)", r->return_icmp6 & 255);
+				else
+					printf("%s)", ic6->name);
+				break;
+			}
+		} else
+			printf(" drop");
+	}
+	if (r->direction == PF_IN)
+		printf(" in");
+	else if (r->direction == PF_OUT)
+		printf(" out");
+	if (r->log) {
+		printf(" log");
+		if (r->log & ~PF_LOG || r->logif) {
+			int count = 0;
+
+			printf(" (");
+			if (r->log & PF_LOG_ALL)
+				printf("%sall", count++ ? ", " : "");
+			if (r->log & PF_LOG_SOCKET_LOOKUP)
+				printf("%suser", count++ ? ", " : "");
+			if (r->logif)
+				printf("%sto pflog%u", count++ ? ", " : "",
+				    r->logif);
+			printf(")");
+		}
+	}
+	if (r->quick)
+		printf(" quick");
+	if (r->ifname[0]) {
+		if (r->ifnot)
+			printf(" on ! %s", r->ifname);
+		else
+			printf(" on %s", r->ifname);
+	}
+	if (r->rt) {
+		if (r->rt == PF_ROUTETO)
+			printf(" route-to");
+		else if (r->rt == PF_REPLYTO)
+			printf(" reply-to");
+		else if (r->rt == PF_DUPTO)
+			printf(" dup-to");
+		else if (r->rt == PF_FASTROUTE)
+			printf(" fastroute");
+		if (r->rt != PF_FASTROUTE) {
+			printf(" ");
+			print_pool(&r->rpool, 0, 0, r->af, PF_PASS);
+		}
+	}
+	if (r->af) {
+		if (r->af == AF_INET)
+			printf(" inet");
+		else
+			printf(" inet6");
+	}
+	if (r->proto) {
+		struct protoent	*p;
+
+		if ((p = getprotobynumber(r->proto)) != NULL)
+			printf(" proto %s", p->p_name);
+		else
+			printf(" proto %u", r->proto);
+	}
+	print_fromto(&r->src, r->os_fingerprint, &r->dst, r->af, r->proto,
+	    verbose, numeric);
+	if (r->uid.op)
+		print_ugid(r->uid.op, r->uid.uid[0], r->uid.uid[1], "user",
+		    UID_MAX);
+	if (r->gid.op)
+		print_ugid(r->gid.op, r->gid.gid[0], r->gid.gid[1], "group",
+		    GID_MAX);
+	if (r->flags || r->flagset) {
+		printf(" flags ");
+		print_flags(r->flags);
+		printf("/");
+		print_flags(r->flagset);
+	} else if (r->action == PF_PASS &&
+	    (!r->proto || r->proto == IPPROTO_TCP) &&
+	    !(r->rule_flag & PFRULE_FRAGMENT) &&
+	    !anchor_call[0] && r->keep_state)
+		printf(" flags any");
+	if (r->type) {
+		const struct icmptypeent	*it;
+
+		it = geticmptypebynumber(r->type-1, r->af);
+		if (r->af != AF_INET6)
+			printf(" icmp-type");
+		else
+			printf(" icmp6-type");
+		if (it != NULL)
+			printf(" %s", it->name);
+		else
+			printf(" %u", r->type-1);
+		if (r->code) {
+			const struct icmpcodeent	*ic;
+
+			ic = geticmpcodebynumber(r->type-1, r->code-1, r->af);
+			if (ic != NULL)
+				printf(" code %s", ic->name);
+			else
+				printf(" code %u", r->code-1);
+		}
+	}
+	if (r->tos)
+		printf(" tos 0x%2.2x", r->tos);
+	if (!r->keep_state && r->action == PF_PASS && !anchor_call[0])
+		printf(" no state");
+	else if (r->keep_state == PF_STATE_NORMAL)
+		printf(" keep state");
+	else if (r->keep_state == PF_STATE_MODULATE)
+		printf(" modulate state");
+	else if (r->keep_state == PF_STATE_SYNPROXY)
+		printf(" synproxy state");
+	if (r->prob) {
+		char	buf[20];
+
+		snprintf(buf, sizeof(buf), "%f", r->prob*100.0/(UINT_MAX+1.0));
+		for (i = strlen(buf)-1; i > 0; i--) {
+			if (buf[i] == '0')
+				buf[i] = '\0';
+			else {
+				if (buf[i] == '.')
+					buf[i] = '\0';
+				break;
+			}
+		}
+		printf(" probability %s%%", buf);
+	}
+	opts = 0;
+	if (r->max_states || r->max_src_nodes || r->max_src_states)
+		opts = 1;
+	if (r->rule_flag & PFRULE_NOSYNC)
+		opts = 1;
+	if (r->rule_flag & PFRULE_SRCTRACK)
+		opts = 1;
+	if (r->rule_flag & PFRULE_IFBOUND)
+		opts = 1;
+	if (r->rule_flag & PFRULE_STATESLOPPY)
+		opts = 1;
+	for (i = 0; !opts && i < PFTM_MAX; ++i)
+		if (r->timeout[i])
+			opts = 1;
+	if (opts) {
+		printf(" (");
+		if (r->max_states) {
+			printf("max %u", r->max_states);
+			opts = 0;
+		}
+		if (r->rule_flag & PFRULE_NOSYNC) {
+			if (!opts)
+				printf(", ");
+			printf("no-sync");
+			opts = 0;
+		}
+		if (r->rule_flag & PFRULE_SRCTRACK) {
+			if (!opts)
+				printf(", ");
+			printf("source-track");
+			if (r->rule_flag & PFRULE_RULESRCTRACK)
+				printf(" rule");
+			else
+				printf(" global");
+			opts = 0;
+		}
+		if (r->max_src_states) {
+			if (!opts)
+				printf(", ");
+			printf("max-src-states %u", r->max_src_states);
+			opts = 0;
+		}
+		if (r->max_src_conn) {
+			if (!opts)
+				printf(", ");
+			printf("max-src-conn %u", r->max_src_conn);
+			opts = 0;
+		}
+		if (r->max_src_conn_rate.limit) {
+			if (!opts)
+				printf(", ");
+			printf("max-src-conn-rate %u/%u",
+			    r->max_src_conn_rate.limit,
+			    r->max_src_conn_rate.seconds);
+			opts = 0;
+		}
+		if (r->max_src_nodes) {
+			if (!opts)
+				printf(", ");
+			printf("max-src-nodes %u", r->max_src_nodes);
+			opts = 0;
+		}
+		if (r->overload_tblname[0]) {
+			if (!opts)
+				printf(", ");
+			printf("overload <%s>", r->overload_tblname);
+			if (r->flush)
+				printf(" flush");
+			if (r->flush & PF_FLUSH_GLOBAL)
+				printf(" global");
+		}
+		if (r->rule_flag & PFRULE_IFBOUND) {
+			if (!opts)
+				printf(", ");
+			printf("if-bound");
+			opts = 0;
+		}
+		if (r->rule_flag & PFRULE_STATESLOPPY) {
+			if (!opts)
+				printf(", ");
+			printf("sloppy");
+			opts = 0;
+		}
+		if (r->rule_flag & PFRULE_PFLOW) {
+			if (!opts)
+				printf(", ");
+			printf("pflow");
+			opts = 0;
+		}
+		for (i = 0; i < PFTM_MAX; ++i)
+			if (r->timeout[i]) {
+				int j;
+
+				if (!opts)
+					printf(", ");
+				opts = 0;
+				for (j = 0; pf_timeouts[j].name != NULL;
+				    ++j)
+					if (pf_timeouts[j].timeout == i)
+						break;
+				printf("%s %u", pf_timeouts[j].name == NULL ?
+				    "inv.timeout" : pf_timeouts[j].name,
+				    r->timeout[i]);
+			}
+		printf(")");
+	}
+	if (r->rule_flag & PFRULE_FRAGMENT)
+		printf(" fragment");
+	if (r->rule_flag & PFRULE_NODF)
+		printf(" no-df");
+	if (r->rule_flag & PFRULE_RANDOMID)
+		printf(" random-id");
+	if (r->min_ttl)
+		printf(" min-ttl %d", r->min_ttl);
+	if (r->max_mss)
+		printf(" max-mss %d", r->max_mss);
+	if (r->rule_flag & PFRULE_SET_TOS)
+		printf(" set-tos 0x%2.2x", r->set_tos);
+	if (r->allow_opts)
+		printf(" allow-opts");
+	if (r->action == PF_SCRUB) {
+		if (r->rule_flag & PFRULE_REASSEMBLE_TCP)
+			printf(" reassemble tcp");
+
+		if (r->rule_flag & PFRULE_FRAGDROP)
+			printf(" fragment drop-ovl");
+		else if (r->rule_flag & PFRULE_FRAGCROP)
+			printf(" fragment crop");
+		else
+			printf(" fragment reassemble");
+	}
+	if (r->label[0])
+		printf(" label \"%s\"", r->label);
+	if (r->qname[0] && r->pqname[0])
+		printf(" queue(%s, %s)", r->qname, r->pqname);
+	else if (r->qname[0])
+		printf(" queue %s", r->qname);
+	if (r->tagname[0])
+		printf(" tag %s", r->tagname);
+	if (r->match_tagname[0]) {
+		if (r->match_tag_not)
+			printf(" !");
+		printf(" tagged %s", r->match_tagname);
+	}
+	if (r->rtableid != -1)
+		printf(" rtable %u", r->rtableid);
+	if (r->divert.port) {
+#ifdef __FreeBSD__
+		printf(" divert-to %u", ntohs(r->divert.port));
+#else
+		if (PF_AZERO(&r->divert.addr, r->af)) {
+			printf(" divert-reply");
+		} else {
+			/* XXX cut&paste from print_addr */
+			char buf[48];
+
+			printf(" divert-to ");
+			if (inet_ntop(r->af, &r->divert.addr, buf,
+			    sizeof(buf)) == NULL)
+				printf("?");
+			else
+				printf("%s", buf);
+			printf(" port %u", ntohs(r->divert.port));
+		}
+#endif
+	}
+	if (!anchor_call[0] && (r->action == PF_NAT ||
+	    r->action == PF_BINAT || r->action == PF_RDR)) {
+		printf(" -> ");
+		print_pool(&r->rpool, r->rpool.proxy_port[0],
+		    r->rpool.proxy_port[1], r->af, r->action);
+	}
+}
+
+void
+print_tabledef(const char *name, int flags, int addrs,
+    struct node_tinithead *nodes)
+{
+	struct node_tinit	*ti, *nti;
+	struct node_host	*h;
+
+	printf("table <%s>", name);
+	if (flags & PFR_TFLAG_CONST)
+		printf(" const");
+	if (flags & PFR_TFLAG_PERSIST)
+		printf(" persist");
+	if (flags & PFR_TFLAG_COUNTERS)
+		printf(" counters");
+	SIMPLEQ_FOREACH(ti, nodes, entries) {
+		if (ti->file) {
+			printf(" file \"%s\"", ti->file);
+			continue;
+		}
+		printf(" {");
+		for (;;) {
+			for (h = ti->host; h != NULL; h = h->next) {
+				printf(h->not ? " !" : " ");
+				print_addr(&h->addr, h->af, 0);
+			}
+			nti = SIMPLEQ_NEXT(ti, entries);
+			if (nti != NULL && nti->file == NULL)
+				ti = nti;	/* merge lists */
+			else
+				break;
+		}
+		printf(" }");
+	}
+	if (addrs && SIMPLEQ_EMPTY(nodes))
+		printf(" { }");
+	printf("\n");
+}
+
+int
+parse_flags(char *s)
+{
+	char		*p, *q;
+	u_int8_t	 f = 0;
+
+	for (p = s; *p; p++) {
+		if ((q = strchr(tcpflags, *p)) == NULL)
+			return -1;
+		else
+			f |= 1 << (q - tcpflags);
+	}
+	return (f ? f : PF_TH_ALL);
+}
+
+void
+set_ipmask(struct node_host *h, u_int8_t b)
+{
+	struct pf_addr	*m, *n;
+	int		 i, j = 0;
+
+	m = &h->addr.v.a.mask;
+	memset(m, 0, sizeof(*m));
+
+	while (b >= 32) {
+		m->addr32[j++] = 0xffffffff;
+		b -= 32;
+	}
+	for (i = 31; i > 31-b; --i)
+		m->addr32[j] |= (1 << i);
+	if (b)
+		m->addr32[j] = htonl(m->addr32[j]);
+
+	/* Mask off bits of the address that will never be used. */
+	n = &h->addr.v.a.addr;
+	if (h->addr.type == PF_ADDR_ADDRMASK)
+		for (i = 0; i < 4; i++)
+			n->addr32[i] = n->addr32[i] & m->addr32[i];
+}
+
+int
+check_netmask(struct node_host *h, sa_family_t af)
+{
+	struct node_host	*n = NULL;
+	struct pf_addr	*m;
+
+	for (n = h; n != NULL; n = n->next) {
+		if (h->addr.type == PF_ADDR_TABLE)
+			continue;
+		m = &h->addr.v.a.mask;
+		/* fix up netmask for dynaddr */
+		if (af == AF_INET && h->addr.type == PF_ADDR_DYNIFTL &&
+		    unmask(m, AF_INET6) > 32)
+			set_ipmask(n, 32);
+		/* netmasks > 32 bit are invalid on v4 */
+		if (af == AF_INET &&
+		    (m->addr32[1] || m->addr32[2] || m->addr32[3])) {
+			fprintf(stderr, "netmask %u invalid for IPv4 address\n",
+			    unmask(m, AF_INET6));
+			return (1);
+		}
+	}
+	return (0);
+}
+
+/* interface lookup routines */
+
+struct node_host	*iftab;
+
+void
+ifa_load(void)
+{
+	struct ifaddrs		*ifap, *ifa;
+	struct node_host	*n = NULL, *h = NULL;
+
+	if (getifaddrs(&ifap) < 0)
+		err(1, "getifaddrs");
+
+	for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
+		if (!(ifa->ifa_addr->sa_family == AF_INET ||
+		    ifa->ifa_addr->sa_family == AF_INET6 ||
+		    ifa->ifa_addr->sa_family == AF_LINK))
+				continue;
+		n = calloc(1, sizeof(struct node_host));
+		if (n == NULL)
+			err(1, "address: calloc");
+		n->af = ifa->ifa_addr->sa_family;
+		n->ifa_flags = ifa->ifa_flags;
+#ifdef __KAME__
+		if (n->af == AF_INET6 &&
+		    IN6_IS_ADDR_LINKLOCAL(&((struct sockaddr_in6 *)
+		    ifa->ifa_addr)->sin6_addr) &&
+		    ((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_scope_id ==
+		    0) {
+			struct sockaddr_in6	*sin6;
+
+			sin6 = (struct sockaddr_in6 *)ifa->ifa_addr;
+			sin6->sin6_scope_id = sin6->sin6_addr.s6_addr[2] << 8 |
+			    sin6->sin6_addr.s6_addr[3];
+			sin6->sin6_addr.s6_addr[2] = 0;
+			sin6->sin6_addr.s6_addr[3] = 0;
+		}
+#endif
+		n->ifindex = 0;
+		if (n->af == AF_INET) {
+			memcpy(&n->addr.v.a.addr, &((struct sockaddr_in *)
+			    ifa->ifa_addr)->sin_addr.s_addr,
+			    sizeof(struct in_addr));
+			memcpy(&n->addr.v.a.mask, &((struct sockaddr_in *)
+			    ifa->ifa_netmask)->sin_addr.s_addr,
+			    sizeof(struct in_addr));
+			if (ifa->ifa_broadaddr != NULL)
+				memcpy(&n->bcast, &((struct sockaddr_in *)
+				    ifa->ifa_broadaddr)->sin_addr.s_addr,
+				    sizeof(struct in_addr));
+			if (ifa->ifa_dstaddr != NULL)
+				memcpy(&n->peer, &((struct sockaddr_in *)
+				    ifa->ifa_dstaddr)->sin_addr.s_addr,
+				    sizeof(struct in_addr));
+		} else if (n->af == AF_INET6) {
+			memcpy(&n->addr.v.a.addr, &((struct sockaddr_in6 *)
+			    ifa->ifa_addr)->sin6_addr.s6_addr,
+			    sizeof(struct in6_addr));
+			memcpy(&n->addr.v.a.mask, &((struct sockaddr_in6 *)
+			    ifa->ifa_netmask)->sin6_addr.s6_addr,
+			    sizeof(struct in6_addr));
+			if (ifa->ifa_broadaddr != NULL)
+				memcpy(&n->bcast, &((struct sockaddr_in6 *)
+				    ifa->ifa_broadaddr)->sin6_addr.s6_addr,
+				    sizeof(struct in6_addr));
+			if (ifa->ifa_dstaddr != NULL)
+				 memcpy(&n->peer, &((struct sockaddr_in6 *)
+				    ifa->ifa_dstaddr)->sin6_addr.s6_addr,
+				    sizeof(struct in6_addr));
+			n->ifindex = ((struct sockaddr_in6 *)
+			    ifa->ifa_addr)->sin6_scope_id;
+		}
+		if ((n->ifname = strdup(ifa->ifa_name)) == NULL)
+			err(1, "ifa_load: strdup");
+		n->next = NULL;
+		n->tail = n;
+		if (h == NULL)
+			h = n;
+		else {
+			h->tail->next = n;
+			h->tail = n;
+		}
+	}
+
+	iftab = h;
+	freeifaddrs(ifap);
+}
+
+struct node_host *
+ifa_exists(const char *ifa_name)
+{
+	struct node_host	*n;
+	struct ifgroupreq	ifgr;
+	int			s;
+
+	if (iftab == NULL)
+		ifa_load();
+
+	/* check wether this is a group */
+	if ((s = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
+		err(1, "socket");
+	bzero(&ifgr, sizeof(ifgr));
+	strlcpy(ifgr.ifgr_name, ifa_name, sizeof(ifgr.ifgr_name));
+	if (ioctl(s, SIOCGIFGMEMB, (caddr_t)&ifgr) == 0) {
+		/* fake a node_host */
+		if ((n = calloc(1, sizeof(*n))) == NULL)
+			err(1, "calloc");
+		if ((n->ifname = strdup(ifa_name)) == NULL)
+			err(1, "strdup");
+		close(s);
+		return (n);
+	}
+	close(s);
+
+	for (n = iftab; n; n = n->next) {
+		if (n->af == AF_LINK && !strncmp(n->ifname, ifa_name, IFNAMSIZ))
+			return (n);
+	}
+
+	return (NULL);
+}
+
+struct node_host *
+ifa_grouplookup(const char *ifa_name, int flags)
+{
+	struct ifg_req		*ifg;
+	struct ifgroupreq	 ifgr;
+	int			 s, len;
+	struct node_host	*n, *h = NULL;
+
+	if ((s = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
+		err(1, "socket");
+	bzero(&ifgr, sizeof(ifgr));
+	strlcpy(ifgr.ifgr_name, ifa_name, sizeof(ifgr.ifgr_name));
+	if (ioctl(s, SIOCGIFGMEMB, (caddr_t)&ifgr) == -1) {
+		close(s);
+		return (NULL);
+	}
+
+	len = ifgr.ifgr_len;
+	if ((ifgr.ifgr_groups = calloc(1, len)) == NULL)
+		err(1, "calloc");
+	if (ioctl(s, SIOCGIFGMEMB, (caddr_t)&ifgr) == -1)
+		err(1, "SIOCGIFGMEMB");
+
+	for (ifg = ifgr.ifgr_groups; ifg && len >= sizeof(struct ifg_req);
+	    ifg++) {
+		len -= sizeof(struct ifg_req);
+		if ((n = ifa_lookup(ifg->ifgrq_member, flags)) == NULL)
+			continue;
+		if (h == NULL)
+			h = n;
+		else {
+			h->tail->next = n;
+			h->tail = n->tail;
+		}
+	}
+	free(ifgr.ifgr_groups);
+	close(s);
+
+	return (h);
+}
+
+struct node_host *
+ifa_lookup(const char *ifa_name, int flags)
+{
+	struct node_host	*p = NULL, *h = NULL, *n = NULL;
+	int			 got4 = 0, got6 = 0;
+	const char		 *last_if = NULL;
+
+	if ((h = ifa_grouplookup(ifa_name, flags)) != NULL)
+		return (h);
+
+	if (!strncmp(ifa_name, "self", IFNAMSIZ))
+		ifa_name = NULL;
+
+	if (iftab == NULL)
+		ifa_load();
+
+	for (p = iftab; p; p = p->next) {
+		if (ifa_skip_if(ifa_name, p))
+			continue;
+		if ((flags & PFI_AFLAG_BROADCAST) && p->af != AF_INET)
+			continue;
+		if ((flags & PFI_AFLAG_BROADCAST) &&
+		    !(p->ifa_flags & IFF_BROADCAST))
+			continue;
+		if ((flags & PFI_AFLAG_PEER) &&
+		    !(p->ifa_flags & IFF_POINTOPOINT))
+			continue;
+		if ((flags & PFI_AFLAG_NETWORK) && p->ifindex > 0)
+			continue;
+		if (last_if == NULL || strcmp(last_if, p->ifname))
+			got4 = got6 = 0;
+		last_if = p->ifname;
+		if ((flags & PFI_AFLAG_NOALIAS) && p->af == AF_INET && got4)
+			continue;
+		if ((flags & PFI_AFLAG_NOALIAS) && p->af == AF_INET6 && got6)
+			continue;
+		if (p->af == AF_INET)
+			got4 = 1;
+		else
+			got6 = 1;
+		n = calloc(1, sizeof(struct node_host));
+		if (n == NULL)
+			err(1, "address: calloc");
+		n->af = p->af;
+		if (flags & PFI_AFLAG_BROADCAST)
+			memcpy(&n->addr.v.a.addr, &p->bcast,
+			    sizeof(struct pf_addr));
+		else if (flags & PFI_AFLAG_PEER)
+			memcpy(&n->addr.v.a.addr, &p->peer,
+			    sizeof(struct pf_addr));
+		else
+			memcpy(&n->addr.v.a.addr, &p->addr.v.a.addr,
+			    sizeof(struct pf_addr));
+		if (flags & PFI_AFLAG_NETWORK)
+			set_ipmask(n, unmask(&p->addr.v.a.mask, n->af));
+		else {
+			if (n->af == AF_INET) {
+				if (p->ifa_flags & IFF_LOOPBACK &&
+				    p->ifa_flags & IFF_LINK1)
+					memcpy(&n->addr.v.a.mask,
+					    &p->addr.v.a.mask,
+					    sizeof(struct pf_addr));
+				else
+					set_ipmask(n, 32);
+			} else
+				set_ipmask(n, 128);
+		}
+		n->ifindex = p->ifindex;
+
+		n->next = NULL;
+		n->tail = n;
+		if (h == NULL)
+			h = n;
+		else {
+			h->tail->next = n;
+			h->tail = n;
+		}
+	}
+	return (h);
+}
+
+int
+ifa_skip_if(const char *filter, struct node_host *p)
+{
+	int	n;
+
+	if (p->af != AF_INET && p->af != AF_INET6)
+		return (1);
+	if (filter == NULL || !*filter)
+		return (0);
+	if (!strcmp(p->ifname, filter))
+		return (0);	/* exact match */
+	n = strlen(filter);
+	if (n < 1 || n >= IFNAMSIZ)
+		return (1);	/* sanity check */
+	if (filter[n-1] >= '0' && filter[n-1] <= '9')
+		return (1);	/* only do exact match in that case */
+	if (strncmp(p->ifname, filter, n))
+		return (1);	/* prefix doesn't match */
+	return (p->ifname[n] < '0' || p->ifname[n] > '9');
+}
+
+
+struct node_host *
+host(const char *s)
+{
+	struct node_host	*h = NULL;
+	int			 mask, v4mask, v6mask, cont = 1;
+	char			*p, *q, *ps;
+
+	if ((p = strrchr(s, '/')) != NULL) {
+		mask = strtol(p+1, &q, 0);
+		if (!q || *q || mask > 128 || q == (p+1)) {
+			fprintf(stderr, "invalid netmask '%s'\n", p);
+			return (NULL);
+		}
+		if ((ps = malloc(strlen(s) - strlen(p) + 1)) == NULL)
+			err(1, "host: malloc");
+		strlcpy(ps, s, strlen(s) - strlen(p) + 1);
+		v4mask = v6mask = mask;
+	} else {
+		if ((ps = strdup(s)) == NULL)
+			err(1, "host: strdup");
+		v4mask = 32;
+		v6mask = 128;
+		mask = -1;
+	}
+
+	/* interface with this name exists? */
+	if (cont && (h = host_if(ps, mask)) != NULL)
+		cont = 0;
+
+	/* IPv4 address? */
+	if (cont && (h = host_v4(s, mask)) != NULL)
+		cont = 0;
+
+	/* IPv6 address? */
+	if (cont && (h = host_v6(ps, v6mask)) != NULL)
+		cont = 0;
+
+	/* dns lookup */
+	if (cont && (h = host_dns(ps, v4mask, v6mask)) != NULL)
+		cont = 0;
+	free(ps);
+
+	if (h == NULL || cont == 1) {
+		fprintf(stderr, "no IP address found for %s\n", s);
+		return (NULL);
+	}
+	return (h);
+}
+
+struct node_host *
+host_if(const char *s, int mask)
+{
+	struct node_host	*n, *h = NULL;
+	char			*p, *ps;
+	int			 flags = 0;
+
+	if ((ps = strdup(s)) == NULL)
+		err(1, "host_if: strdup");
+	while ((p = strrchr(ps, ':')) != NULL) {
+		if (!strcmp(p+1, "network"))
+			flags |= PFI_AFLAG_NETWORK;
+		else if (!strcmp(p+1, "broadcast"))
+			flags |= PFI_AFLAG_BROADCAST;
+		else if (!strcmp(p+1, "peer"))
+			flags |= PFI_AFLAG_PEER;
+		else if (!strcmp(p+1, "0"))
+			flags |= PFI_AFLAG_NOALIAS;
+		else {
+			free(ps);
+			return (NULL);
+		}
+		*p = '\0';
+	}
+	if (flags & (flags - 1) & PFI_AFLAG_MODEMASK) { /* Yep! */
+		fprintf(stderr, "illegal combination of interface modifiers\n");
+		free(ps);
+		return (NULL);
+	}
+	if ((flags & (PFI_AFLAG_NETWORK|PFI_AFLAG_BROADCAST)) && mask > -1) {
+		fprintf(stderr, "network or broadcast lookup, but "
+		    "extra netmask given\n");
+		free(ps);
+		return (NULL);
+	}
+	if (ifa_exists(ps) || !strncmp(ps, "self", IFNAMSIZ)) {
+		/* interface with this name exists */
+		h = ifa_lookup(ps, flags);
+		for (n = h; n != NULL && mask > -1; n = n->next)
+			set_ipmask(n, mask);
+	}
+
+	free(ps);
+	return (h);
+}
+
+struct node_host *
+host_v4(const char *s, int mask)
+{
+	struct node_host	*h = NULL;
+	struct in_addr		 ina;
+	int			 bits = 32;
+
+	memset(&ina, 0, sizeof(struct in_addr));
+	if (strrchr(s, '/') != NULL) {
+		if ((bits = inet_net_pton(AF_INET, s, &ina, sizeof(ina))) == -1)
+			return (NULL);
+	} else {
+		if (inet_pton(AF_INET, s, &ina) != 1)
+			return (NULL);
+	}
+
+	h = calloc(1, sizeof(struct node_host));
+	if (h == NULL)
+		err(1, "address: calloc");
+	h->ifname = NULL;
+	h->af = AF_INET;
+	h->addr.v.a.addr.addr32[0] = ina.s_addr;
+	set_ipmask(h, bits);
+	h->next = NULL;
+	h->tail = h;
+
+	return (h);
+}
+
+struct node_host *
+host_v6(const char *s, int mask)
+{
+	struct addrinfo		 hints, *res;
+	struct node_host	*h = NULL;
+
+	memset(&hints, 0, sizeof(hints));
+	hints.ai_family = AF_INET6;
+	hints.ai_socktype = SOCK_DGRAM; /*dummy*/
+	hints.ai_flags = AI_NUMERICHOST;
+	if (getaddrinfo(s, "0", &hints, &res) == 0) {
+		h = calloc(1, sizeof(struct node_host));
+		if (h == NULL)
+			err(1, "address: calloc");
+		h->ifname = NULL;
+		h->af = AF_INET6;
+		memcpy(&h->addr.v.a.addr,
+		    &((struct sockaddr_in6 *)res->ai_addr)->sin6_addr,
+		    sizeof(h->addr.v.a.addr));
+		h->ifindex =
+		    ((struct sockaddr_in6 *)res->ai_addr)->sin6_scope_id;
+		set_ipmask(h, mask);
+		freeaddrinfo(res);
+		h->next = NULL;
+		h->tail = h;
+	}
+
+	return (h);
+}
+
+struct node_host *
+host_dns(const char *s, int v4mask, int v6mask)
+{
+	struct addrinfo		 hints, *res0, *res;
+	struct node_host	*n, *h = NULL;
+	int			 error, noalias = 0;
+	int			 got4 = 0, got6 = 0;
+	char			*p, *ps;
+
+	if ((ps = strdup(s)) == NULL)
+		err(1, "host_dns: strdup");
+	if ((p = strrchr(ps, ':')) != NULL && !strcmp(p, ":0")) {
+		noalias = 1;
+		*p = '\0';
+	}
+	memset(&hints, 0, sizeof(hints));
+	hints.ai_family = PF_UNSPEC;
+	hints.ai_socktype = SOCK_STREAM; /* DUMMY */
+	error = getaddrinfo(ps, NULL, &hints, &res0);
+	if (error) {
+		free(ps);
+		return (h);
+	}
+
+	for (res = res0; res; res = res->ai_next) {
+		if (res->ai_family != AF_INET &&
+		    res->ai_family != AF_INET6)
+			continue;
+		if (noalias) {
+			if (res->ai_family == AF_INET) {
+				if (got4)
+					continue;
+				got4 = 1;
+			} else {
+				if (got6)
+					continue;
+				got6 = 1;
+			}
+		}
+		n = calloc(1, sizeof(struct node_host));
+		if (n == NULL)
+			err(1, "host_dns: calloc");
+		n->ifname = NULL;
+		n->af = res->ai_family;
+		if (res->ai_family == AF_INET) {
+			memcpy(&n->addr.v.a.addr,
+			    &((struct sockaddr_in *)
+			    res->ai_addr)->sin_addr.s_addr,
+			    sizeof(struct in_addr));
+			set_ipmask(n, v4mask);
+		} else {
+			memcpy(&n->addr.v.a.addr,
+			    &((struct sockaddr_in6 *)
+			    res->ai_addr)->sin6_addr.s6_addr,
+			    sizeof(struct in6_addr));
+			n->ifindex =
+			    ((struct sockaddr_in6 *)
+			    res->ai_addr)->sin6_scope_id;
+			set_ipmask(n, v6mask);
+		}
+		n->next = NULL;
+		n->tail = n;
+		if (h == NULL)
+			h = n;
+		else {
+			h->tail->next = n;
+			h->tail = n;
+		}
+	}
+	freeaddrinfo(res0);
+	free(ps);
+
+	return (h);
+}
+
+/*
+ * convert a hostname to a list of addresses and put them in the given buffer.
+ * test:
+ *	if set to 1, only simple addresses are accepted (no netblock, no "!").
+ */
+int
+append_addr(struct pfr_buffer *b, char *s, int test)
+{
+	char			 *r;
+	struct node_host	*h, *n;
+	int			 rv, not = 0;
+
+	for (r = s; *r == '!'; r++)
+		not = !not;
+	if ((n = host(r)) == NULL) {
+		errno = 0;
+		return (-1);
+	}
+	rv = append_addr_host(b, n, test, not);
+	do {
+		h = n;
+		n = n->next;
+		free(h);
+	} while (n != NULL);
+	return (rv);
+}
+
+/*
+ * same as previous function, but with a pre-parsed input and the ability
+ * to "negate" the result. Does not free the node_host list.
+ * not:
+ *      setting it to 1 is equivalent to adding "!" in front of parameter s.
+ */
+int
+append_addr_host(struct pfr_buffer *b, struct node_host *n, int test, int not)
+{
+	int			 bits;
+	struct pfr_addr		 addr;
+
+	do {
+		bzero(&addr, sizeof(addr));
+		addr.pfra_not = n->not ^ not;
+		addr.pfra_af = n->af;
+		addr.pfra_net = unmask(&n->addr.v.a.mask, n->af);
+		switch (n->af) {
+		case AF_INET:
+			addr.pfra_ip4addr.s_addr = n->addr.v.a.addr.addr32[0];
+			bits = 32;
+			break;
+		case AF_INET6:
+			memcpy(&addr.pfra_ip6addr, &n->addr.v.a.addr.v6,
+			    sizeof(struct in6_addr));
+			bits = 128;
+			break;
+		default:
+			errno = EINVAL;
+			return (-1);
+		}
+		if ((test && (not || addr.pfra_net != bits)) ||
+		    addr.pfra_net > bits) {
+			errno = EINVAL;
+			return (-1);
+		}
+		if (pfr_buf_add(b, &addr))
+			return (-1);
+	} while ((n = n->next) != NULL);
+
+	return (0);
+}
+
+int
+pfctl_add_trans(struct pfr_buffer *buf, int rs_num, const char *anchor)
+{
+	struct pfioc_trans_e trans;
+
+	bzero(&trans, sizeof(trans));
+	trans.rs_num = rs_num;
+	if (strlcpy(trans.anchor, anchor,
+	    sizeof(trans.anchor)) >= sizeof(trans.anchor))
+		errx(1, "pfctl_add_trans: strlcpy");
+
+	return pfr_buf_add(buf, &trans);
+}
+
+u_int32_t
+pfctl_get_ticket(struct pfr_buffer *buf, int rs_num, const char *anchor)
+{
+	struct pfioc_trans_e *p;
+
+	PFRB_FOREACH(p, buf)
+		if (rs_num == p->rs_num && !strcmp(anchor, p->anchor))
+			return (p->ticket);
+	errx(1, "pfctl_get_ticket: assertion failed");
+}
+
+int
+pfctl_trans(int dev, struct pfr_buffer *buf, u_long cmd, int from)
+{
+	struct pfioc_trans trans;
+
+	bzero(&trans, sizeof(trans));
+	trans.size = buf->pfrb_size - from;
+	trans.esize = sizeof(struct pfioc_trans_e);
+	trans.array = ((struct pfioc_trans_e *)buf->pfrb_caddr) + from;
+	return ioctl(dev, cmd, &trans);
+}
diff --git a/freebsd/contrib/pf/pfctl/pfctl_parser.h b/freebsd/contrib/pf/pfctl/pfctl_parser.h
new file mode 100644
index 0000000..4560d66
--- /dev/null
+++ b/freebsd/contrib/pf/pfctl/pfctl_parser.h
@@ -0,0 +1,305 @@
+/*	$OpenBSD: pfctl_parser.h,v 1.86 2006/10/31 23:46:25 mcbride Exp $ */
+
+/*
+ * Copyright (c) 2001 Daniel Hartmeier
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *    - Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *    - 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 COPYRIGHT HOLDERS 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
+ * COPYRIGHT HOLDERS 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.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _PFCTL_PARSER_H_
+#define _PFCTL_PARSER_H_
+
+#define PF_OSFP_FILE		"/etc/pf.os"
+
+#define PF_OPT_DISABLE		0x0001
+#define PF_OPT_ENABLE		0x0002
+#define PF_OPT_VERBOSE		0x0004
+#define PF_OPT_NOACTION		0x0008
+#define PF_OPT_QUIET		0x0010
+#define PF_OPT_CLRRULECTRS	0x0020
+#define PF_OPT_USEDNS		0x0040
+#define PF_OPT_VERBOSE2		0x0080
+#define PF_OPT_DUMMYACTION	0x0100
+#define PF_OPT_DEBUG		0x0200
+#define PF_OPT_SHOWALL		0x0400
+#define PF_OPT_OPTIMIZE		0x0800
+#define PF_OPT_NUMERIC		0x1000
+#define PF_OPT_MERGE		0x2000
+#define PF_OPT_RECURSE		0x4000
+
+#define PF_TH_ALL		0xFF
+
+#define PF_NAT_PROXY_PORT_LOW	50001
+#define PF_NAT_PROXY_PORT_HIGH	65535
+
+#define PF_OPTIMIZE_BASIC	0x0001
+#define PF_OPTIMIZE_PROFILE	0x0002
+
+#define FCNT_NAMES { \
+	"searches", \
+	"inserts", \
+	"removals", \
+	NULL \
+}
+
+struct pfr_buffer;	/* forward definition */
+
+
+struct pfctl {
+	int dev;
+	int opts;
+	int optimize;
+	int loadopt;
+	int asd;			/* anchor stack depth */
+	int bn;				/* brace number */
+	int brace;
+	int tdirty;			/* kernel dirty */
+#define PFCTL_ANCHOR_STACK_DEPTH 64
+	struct pf_anchor *astack[PFCTL_ANCHOR_STACK_DEPTH];
+	struct pfioc_pooladdr paddr;
+	struct pfioc_altq *paltq;
+	struct pfioc_queue *pqueue;
+	struct pfr_buffer *trans;
+	struct pf_anchor *anchor, *alast;
+	const char *ruleset;
+
+	/* 'set foo' options */
+	u_int32_t	 timeout[PFTM_MAX];
+	u_int32_t	 limit[PF_LIMIT_MAX];
+	u_int32_t	 debug;
+	u_int32_t	 hostid;
+	char		*ifname;
+
+	u_int8_t	 timeout_set[PFTM_MAX];
+	u_int8_t	 limit_set[PF_LIMIT_MAX];
+	u_int8_t	 debug_set;
+	u_int8_t	 hostid_set;
+	u_int8_t	 ifname_set;
+};
+
+struct node_if {
+	char			 ifname[IFNAMSIZ];
+	u_int8_t		 not;
+	u_int8_t		 dynamic; /* antispoof */
+	u_int			 ifa_flags;
+	struct node_if		*next;
+	struct node_if		*tail;
+};
+
+struct node_host {
+	struct pf_addr_wrap	 addr;
+	struct pf_addr		 bcast;
+	struct pf_addr		 peer;
+	sa_family_t		 af;
+	u_int8_t		 not;
+	u_int32_t		 ifindex;	/* link-local IPv6 addrs */
+	char			*ifname;
+	u_int			 ifa_flags;
+	struct node_host	*next;
+	struct node_host	*tail;
+};
+
+struct node_os {
+	char			*os;
+	pf_osfp_t		 fingerprint;
+	struct node_os		*next;
+	struct node_os		*tail;
+};
+
+struct node_queue_bw {
+	u_int32_t	bw_absolute;
+	u_int16_t	bw_percent;
+};
+
+struct node_hfsc_sc {
+	struct node_queue_bw	m1;	/* slope of 1st segment; bps */
+	u_int			d;	/* x-projection of m1; msec */
+	struct node_queue_bw	m2;	/* slope of 2nd segment; bps */
+	u_int8_t		used;
+};
+
+struct node_hfsc_opts {
+	struct node_hfsc_sc	realtime;
+	struct node_hfsc_sc	linkshare;
+	struct node_hfsc_sc	upperlimit;
+	int			flags;
+};
+
+struct node_queue_opt {
+	int			 qtype;
+	union {
+		struct cbq_opts		cbq_opts;
+		struct priq_opts	priq_opts;
+		struct node_hfsc_opts	hfsc_opts;
+	}			 data;
+};
+
+#ifdef __FreeBSD__
+/*
+ * XXX
+ * Absolutely this is not correct location to define this.
+ * Should we use an another sperate header file?
+ */
+#define	SIMPLEQ_HEAD			STAILQ_HEAD
+#define	SIMPLEQ_HEAD_INITIALIZER	STAILQ_HEAD_INITIALIZER
+#define	SIMPLEQ_ENTRY			STAILQ_ENTRY
+#define	SIMPLEQ_FIRST			STAILQ_FIRST
+#define	SIMPLEQ_END(head)		NULL
+#define	SIMPLEQ_EMPTY			STAILQ_EMPTY
+#define	SIMPLEQ_NEXT			STAILQ_NEXT
+/*#define	SIMPLEQ_FOREACH			STAILQ_FOREACH*/
+#define	SIMPLEQ_FOREACH(var, head, field)		\
+    for((var) = SIMPLEQ_FIRST(head);			\
+	(var) != SIMPLEQ_END(head);			\
+	(var) = SIMPLEQ_NEXT(var, field))
+#define	SIMPLEQ_INIT			STAILQ_INIT
+#define	SIMPLEQ_INSERT_HEAD		STAILQ_INSERT_HEAD
+#define	SIMPLEQ_INSERT_TAIL		STAILQ_INSERT_TAIL
+#define	SIMPLEQ_INSERT_AFTER		STAILQ_INSERT_AFTER
+#define	SIMPLEQ_REMOVE_HEAD		STAILQ_REMOVE_HEAD
+#endif
+SIMPLEQ_HEAD(node_tinithead, node_tinit);
+struct node_tinit {	/* table initializer */
+	SIMPLEQ_ENTRY(node_tinit)	 entries;
+	struct node_host		*host;
+	char				*file;
+};
+
+
+/* optimizer created tables */
+struct pf_opt_tbl {
+	char			 pt_name[PF_TABLE_NAME_SIZE];
+	int			 pt_rulecount;
+	int			 pt_generated;
+	struct node_tinithead	 pt_nodes;
+	struct pfr_buffer	*pt_buf;
+};
+#define PF_OPT_TABLE_PREFIX	"__automatic_"
+
+/* optimizer pf_rule container */
+struct pf_opt_rule {
+	struct pf_rule		 por_rule;
+	struct pf_opt_tbl	*por_src_tbl;
+	struct pf_opt_tbl	*por_dst_tbl;
+	u_int64_t		 por_profile_count;
+	TAILQ_ENTRY(pf_opt_rule) por_entry;
+	TAILQ_ENTRY(pf_opt_rule) por_skip_entry[PF_SKIP_COUNT];
+};
+
+TAILQ_HEAD(pf_opt_queue, pf_opt_rule);
+
+int	pfctl_rules(int, char *, int, int, char *, struct pfr_buffer *);
+int	pfctl_optimize_ruleset(struct pfctl *, struct pf_ruleset *);
+
+int	pfctl_add_rule(struct pfctl *, struct pf_rule *, const char *);
+int	pfctl_add_altq(struct pfctl *, struct pf_altq *);
+int	pfctl_add_pool(struct pfctl *, struct pf_pool *, sa_family_t);
+void	pfctl_move_pool(struct pf_pool *, struct pf_pool *);
+void	pfctl_clear_pool(struct pf_pool *);
+
+int	pfctl_set_timeout(struct pfctl *, const char *, int, int);
+int	pfctl_set_optimization(struct pfctl *, const char *);
+int	pfctl_set_limit(struct pfctl *, const char *, unsigned int);
+int	pfctl_set_logif(struct pfctl *, char *);
+int	pfctl_set_hostid(struct pfctl *, u_int32_t);
+int	pfctl_set_debug(struct pfctl *, char *);
+int	pfctl_set_interface_flags(struct pfctl *, char *, int, int);
+
+int	parse_config(char *, struct pfctl *);
+int	parse_flags(char *);
+int	pfctl_load_anchors(int, struct pfctl *, struct pfr_buffer *);
+
+void	print_pool(struct pf_pool *, u_int16_t, u_int16_t, sa_family_t, int);
+void	print_src_node(struct pf_src_node *, int);
+void	print_rule(struct pf_rule *, const char *, int, int);
+void	print_tabledef(const char *, int, int, struct node_tinithead *);
+void	print_status(struct pf_status *, int);
+
+int	eval_pfaltq(struct pfctl *, struct pf_altq *, struct node_queue_bw *,
+	    struct node_queue_opt *);
+int	eval_pfqueue(struct pfctl *, struct pf_altq *, struct node_queue_bw *,
+	    struct node_queue_opt *);
+
+void	 print_altq(const struct pf_altq *, unsigned, struct node_queue_bw *,
+	    struct node_queue_opt *);
+void	 print_queue(const struct pf_altq *, unsigned, struct node_queue_bw *,
+	    int, struct node_queue_opt *);
+
+int	pfctl_define_table(char *, int, int, const char *, struct pfr_buffer *,
+	    u_int32_t);
+
+void		 pfctl_clear_fingerprints(int, int);
+int		 pfctl_file_fingerprints(int, int, const char *);
+pf_osfp_t	 pfctl_get_fingerprint(const char *);
+int		 pfctl_load_fingerprints(int, int);
+char		*pfctl_lookup_fingerprint(pf_osfp_t, char *, size_t);
+void		 pfctl_show_fingerprints(int);
+
+
+struct icmptypeent {
+	const char *name;
+	u_int8_t type;
+};
+
+struct icmpcodeent {
+	const char *name;
+	u_int8_t type;
+	u_int8_t code;
+};
+
+const struct icmptypeent *geticmptypebynumber(u_int8_t, u_int8_t);
+const struct icmptypeent *geticmptypebyname(char *, u_int8_t);
+const struct icmpcodeent *geticmpcodebynumber(u_int8_t, u_int8_t, u_int8_t);
+const struct icmpcodeent *geticmpcodebyname(u_long, char *, u_int8_t);
+
+struct pf_timeout {
+	const char	*name;
+	int		 timeout;
+};
+
+#define PFCTL_FLAG_FILTER	0x02
+#define PFCTL_FLAG_NAT		0x04
+#define PFCTL_FLAG_OPTION	0x08
+#define PFCTL_FLAG_ALTQ		0x10
+#define PFCTL_FLAG_TABLE	0x20
+
+extern const struct pf_timeout pf_timeouts[];
+
+void			 set_ipmask(struct node_host *, u_int8_t);
+int			 check_netmask(struct node_host *, sa_family_t);
+int			 unmask(struct pf_addr *, sa_family_t);
+void			 ifa_load(void);
+struct node_host	*ifa_exists(const char *);
+struct node_host	*ifa_lookup(const char *, int);
+struct node_host	*host(const char *);
+
+int			 append_addr(struct pfr_buffer *, char *, int);
+int			 append_addr_host(struct pfr_buffer *,
+			    struct node_host *, int, int);
+
+#endif /* _PFCTL_PARSER_H_ */
diff --git a/freebsd/contrib/pf/pfctl/pfctl_qstats.c b/freebsd/contrib/pf/pfctl/pfctl_qstats.c
new file mode 100644
index 0000000..a86702d
--- /dev/null
+++ b/freebsd/contrib/pf/pfctl/pfctl_qstats.c
@@ -0,0 +1,451 @@
+#include <machine/rtems-bsd-user-space.h>
+
+/*	$OpenBSD: pfctl_qstats.c,v 1.30 2004/04/27 21:47:32 kjc Exp $ */
+
+/*
+ * Copyright (c) Henning Brauer <henning at openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+
+#include <net/if.h>
+#include <netinet/in.h>
+#include <net/pfvar.h>
+#include <arpa/inet.h>
+
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <altq/altq.h>
+#include <altq/altq_cbq.h>
+#include <altq/altq_priq.h>
+#include <altq/altq_hfsc.h>
+
+#include "pfctl.h"
+#include "pfctl_parser.h"
+
+union class_stats {
+	class_stats_t		cbq_stats;
+	struct priq_classstats	priq_stats;
+	struct hfsc_classstats	hfsc_stats;
+};
+
+#define AVGN_MAX	8
+#define STAT_INTERVAL	5
+
+struct queue_stats {
+	union class_stats	 data;
+	int			 avgn;
+	double			 avg_bytes;
+	double			 avg_packets;
+	u_int64_t		 prev_bytes;
+	u_int64_t		 prev_packets;
+};
+
+struct pf_altq_node {
+	struct pf_altq		 altq;
+	struct pf_altq_node	*next;
+	struct pf_altq_node	*children;
+	struct queue_stats	 qstats;
+};
+
+int			 pfctl_update_qstats(int, struct pf_altq_node **);
+void			 pfctl_insert_altq_node(struct pf_altq_node **,
+			    const struct pf_altq, const struct queue_stats);
+struct pf_altq_node	*pfctl_find_altq_node(struct pf_altq_node *,
+			    const char *, const char *);
+void			 pfctl_print_altq_node(int, const struct pf_altq_node *,
+			    unsigned, int);
+void			 print_cbqstats(struct queue_stats);
+void			 print_priqstats(struct queue_stats);
+void			 print_hfscstats(struct queue_stats);
+void			 pfctl_free_altq_node(struct pf_altq_node *);
+void			 pfctl_print_altq_nodestat(int,
+			    const struct pf_altq_node *);
+
+void			 update_avg(struct pf_altq_node *);
+
+int
+pfctl_show_altq(int dev, const char *iface, int opts, int verbose2)
+{
+	struct pf_altq_node	*root = NULL, *node;
+	int			 nodes, dotitle = (opts & PF_OPT_SHOWALL);
+
+#ifdef __FreeBSD__
+	if (!altqsupport)
+		return (-1);
+#endif
+
+	if ((nodes = pfctl_update_qstats(dev, &root)) < 0)
+		return (-1);
+
+	if (nodes == 0)
+		printf("No queue in use\n");
+	for (node = root; node != NULL; node = node->next) {
+		if (iface != NULL && strcmp(node->altq.ifname, iface))
+			continue;
+		if (dotitle) {
+			pfctl_print_title("ALTQ:");
+			dotitle = 0;
+		}
+		pfctl_print_altq_node(dev, node, 0, opts);
+	}
+
+	while (verbose2 && nodes > 0) {
+		printf("\n");
+		fflush(stdout);
+		sleep(STAT_INTERVAL);
+		if ((nodes = pfctl_update_qstats(dev, &root)) == -1)
+			return (-1);
+		for (node = root; node != NULL; node = node->next) {
+			if (iface != NULL && strcmp(node->altq.ifname, iface))
+				continue;
+#ifdef __FreeBSD__
+			if (node->altq.local_flags & PFALTQ_FLAG_IF_REMOVED)
+				continue;
+#endif
+			pfctl_print_altq_node(dev, node, 0, opts);
+		}
+	}
+	pfctl_free_altq_node(root);
+	return (0);
+}
+
+int
+pfctl_update_qstats(int dev, struct pf_altq_node **root)
+{
+	struct pf_altq_node	*node;
+	struct pfioc_altq	 pa;
+	struct pfioc_qstats	 pq;
+	u_int32_t		 mnr, nr;
+	struct queue_stats	 qstats;
+	static	u_int32_t	 last_ticket;
+
+	memset(&pa, 0, sizeof(pa));
+	memset(&pq, 0, sizeof(pq));
+	memset(&qstats, 0, sizeof(qstats));
+	if (ioctl(dev, DIOCGETALTQS, &pa)) {
+		warn("DIOCGETALTQS");
+		return (-1);
+	}
+
+	/* if a new set is found, start over */
+	if (pa.ticket != last_ticket && *root != NULL) {
+		pfctl_free_altq_node(*root);
+		*root = NULL;
+	}
+	last_ticket = pa.ticket;
+
+	mnr = pa.nr;
+	for (nr = 0; nr < mnr; ++nr) {
+		pa.nr = nr;
+		if (ioctl(dev, DIOCGETALTQ, &pa)) {
+			warn("DIOCGETALTQ");
+			return (-1);
+		}
+#ifdef __FreeBSD__
+		if (pa.altq.qid > 0 &&
+		    !(pa.altq.local_flags & PFALTQ_FLAG_IF_REMOVED)) {
+#else
+		if (pa.altq.qid > 0) {
+#endif
+			pq.nr = nr;
+			pq.ticket = pa.ticket;
+			pq.buf = &qstats.data;
+			pq.nbytes = sizeof(qstats.data);
+			if (ioctl(dev, DIOCGETQSTATS, &pq)) {
+				warn("DIOCGETQSTATS");
+				return (-1);
+			}
+			if ((node = pfctl_find_altq_node(*root, pa.altq.qname,
+			    pa.altq.ifname)) != NULL) {
+				memcpy(&node->qstats.data, &qstats.data,
+				    sizeof(qstats.data));
+				update_avg(node);
+			} else {
+				pfctl_insert_altq_node(root, pa.altq, qstats);
+			}
+		}
+#ifdef __FreeBSD__
+		else if (pa.altq.local_flags & PFALTQ_FLAG_IF_REMOVED) {
+			memset(&qstats.data, 0, sizeof(qstats.data));
+			if ((node = pfctl_find_altq_node(*root, pa.altq.qname,
+			    pa.altq.ifname)) != NULL) {
+				memcpy(&node->qstats.data, &qstats.data,
+				    sizeof(qstats.data));
+				update_avg(node);
+			} else {
+				pfctl_insert_altq_node(root, pa.altq, qstats);
+			}
+		}
+#endif
+	}
+	return (mnr);
+}
+
+void
+pfctl_insert_altq_node(struct pf_altq_node **root,
+    const struct pf_altq altq, const struct queue_stats qstats)
+{
+	struct pf_altq_node	*node;
+
+	node = calloc(1, sizeof(struct pf_altq_node));
+	if (node == NULL)
+		err(1, "pfctl_insert_altq_node: calloc");
+	memcpy(&node->altq, &altq, sizeof(struct pf_altq));
+	memcpy(&node->qstats, &qstats, sizeof(qstats));
+	node->next = node->children = NULL;
+
+	if (*root == NULL)
+		*root = node;
+	else if (!altq.parent[0]) {
+		struct pf_altq_node	*prev = *root;
+
+		while (prev->next != NULL)
+			prev = prev->next;
+		prev->next = node;
+	} else {
+		struct pf_altq_node	*parent;
+
+		parent = pfctl_find_altq_node(*root, altq.parent, altq.ifname);
+		if (parent == NULL)
+			errx(1, "parent %s not found", altq.parent);
+		if (parent->children == NULL)
+			parent->children = node;
+		else {
+			struct pf_altq_node *prev = parent->children;
+
+			while (prev->next != NULL)
+				prev = prev->next;
+			prev->next = node;
+		}
+	}
+	update_avg(node);
+}
+
+struct pf_altq_node *
+pfctl_find_altq_node(struct pf_altq_node *root, const char *qname,
+    const char *ifname)
+{
+	struct pf_altq_node	*node, *child;
+
+	for (node = root; node != NULL; node = node->next) {
+		if (!strcmp(node->altq.qname, qname)
+		    && !(strcmp(node->altq.ifname, ifname)))
+			return (node);
+		if (node->children != NULL) {
+			child = pfctl_find_altq_node(node->children, qname,
+			    ifname);
+			if (child != NULL)
+				return (child);
+		}
+	}
+	return (NULL);
+}
+
+void
+pfctl_print_altq_node(int dev, const struct pf_altq_node *node,
+    unsigned int level, int opts)
+{
+	const struct pf_altq_node	*child;
+
+	if (node == NULL)
+		return;
+
+	print_altq(&node->altq, level, NULL, NULL);
+
+	if (node->children != NULL) {
+		printf("{");
+		for (child = node->children; child != NULL;
+		    child = child->next) {
+			printf("%s", child->altq.qname);
+			if (child->next != NULL)
+				printf(", ");
+		}
+		printf("}");
+	}
+	printf("\n");
+
+	if (opts & PF_OPT_VERBOSE)
+		pfctl_print_altq_nodestat(dev, node);
+
+	if (opts & PF_OPT_DEBUG)
+		printf("  [ qid=%u ifname=%s ifbandwidth=%s ]\n",
+		    node->altq.qid, node->altq.ifname,
+		    rate2str((double)(node->altq.ifbandwidth)));
+
+	for (child = node->children; child != NULL;
+	    child = child->next)
+		pfctl_print_altq_node(dev, child, level + 1, opts);
+}
+
+void
+pfctl_print_altq_nodestat(int dev, const struct pf_altq_node *a)
+{
+	if (a->altq.qid == 0)
+		return;
+
+#ifdef __FreeBSD__
+	if (a->altq.local_flags & PFALTQ_FLAG_IF_REMOVED)
+		return;
+#endif
+	switch (a->altq.scheduler) {
+	case ALTQT_CBQ:
+		print_cbqstats(a->qstats);
+		break;
+	case ALTQT_PRIQ:
+		print_priqstats(a->qstats);
+		break;
+	case ALTQT_HFSC:
+		print_hfscstats(a->qstats);
+		break;
+	}
+}
+
+void
+print_cbqstats(struct queue_stats cur)
+{
+	printf("  [ pkts: %10llu  bytes: %10llu  "
+	    "dropped pkts: %6llu bytes: %6llu ]\n",
+	    (unsigned long long)cur.data.cbq_stats.xmit_cnt.packets,
+	    (unsigned long long)cur.data.cbq_stats.xmit_cnt.bytes,
+	    (unsigned long long)cur.data.cbq_stats.drop_cnt.packets,
+	    (unsigned long long)cur.data.cbq_stats.drop_cnt.bytes);
+	printf("  [ qlength: %3d/%3d  borrows: %6u  suspends: %6u ]\n",
+	    cur.data.cbq_stats.qcnt, cur.data.cbq_stats.qmax,
+	    cur.data.cbq_stats.borrows, cur.data.cbq_stats.delays);
+
+	if (cur.avgn < 2)
+		return;
+
+	printf("  [ measured: %7.1f packets/s, %s/s ]\n",
+	    cur.avg_packets / STAT_INTERVAL,
+	    rate2str((8 * cur.avg_bytes) / STAT_INTERVAL));
+}
+
+void
+print_priqstats(struct queue_stats cur)
+{
+	printf("  [ pkts: %10llu  bytes: %10llu  "
+	    "dropped pkts: %6llu bytes: %6llu ]\n",
+	    (unsigned long long)cur.data.priq_stats.xmitcnt.packets,
+	    (unsigned long long)cur.data.priq_stats.xmitcnt.bytes,
+	    (unsigned long long)cur.data.priq_stats.dropcnt.packets,
+	    (unsigned long long)cur.data.priq_stats.dropcnt.bytes);
+	printf("  [ qlength: %3d/%3d ]\n",
+	    cur.data.priq_stats.qlength, cur.data.priq_stats.qlimit);
+
+	if (cur.avgn < 2)
+		return;
+
+	printf("  [ measured: %7.1f packets/s, %s/s ]\n",
+	    cur.avg_packets / STAT_INTERVAL,
+	    rate2str((8 * cur.avg_bytes) / STAT_INTERVAL));
+}
+
+void
+print_hfscstats(struct queue_stats cur)
+{
+	printf("  [ pkts: %10llu  bytes: %10llu  "
+	    "dropped pkts: %6llu bytes: %6llu ]\n",
+	    (unsigned long long)cur.data.hfsc_stats.xmit_cnt.packets,
+	    (unsigned long long)cur.data.hfsc_stats.xmit_cnt.bytes,
+	    (unsigned long long)cur.data.hfsc_stats.drop_cnt.packets,
+	    (unsigned long long)cur.data.hfsc_stats.drop_cnt.bytes);
+	printf("  [ qlength: %3d/%3d ]\n",
+	    cur.data.hfsc_stats.qlength, cur.data.hfsc_stats.qlimit);
+
+	if (cur.avgn < 2)
+		return;
+
+	printf("  [ measured: %7.1f packets/s, %s/s ]\n",
+	    cur.avg_packets / STAT_INTERVAL,
+	    rate2str((8 * cur.avg_bytes) / STAT_INTERVAL));
+}
+
+void
+pfctl_free_altq_node(struct pf_altq_node *node)
+{
+	while (node != NULL) {
+		struct pf_altq_node	*prev;
+
+		if (node->children != NULL)
+			pfctl_free_altq_node(node->children);
+		prev = node;
+		node = node->next;
+		free(prev);
+	}
+}
+
+void
+update_avg(struct pf_altq_node *a)
+{
+	struct queue_stats	*qs;
+	u_int64_t		 b, p;
+	int			 n;
+
+	if (a->altq.qid == 0)
+		return;
+
+	qs = &a->qstats;
+	n = qs->avgn;
+
+	switch (a->altq.scheduler) {
+	case ALTQT_CBQ:
+		b = qs->data.cbq_stats.xmit_cnt.bytes;
+		p = qs->data.cbq_stats.xmit_cnt.packets;
+		break;
+	case ALTQT_PRIQ:
+		b = qs->data.priq_stats.xmitcnt.bytes;
+		p = qs->data.priq_stats.xmitcnt.packets;
+		break;
+	case ALTQT_HFSC:
+		b = qs->data.hfsc_stats.xmit_cnt.bytes;
+		p = qs->data.hfsc_stats.xmit_cnt.packets;
+		break;
+	default:
+		b = 0;
+		p = 0;
+		break;
+	}
+
+	if (n == 0) {
+		qs->prev_bytes = b;
+		qs->prev_packets = p;
+		qs->avgn++;
+		return;
+	}
+
+	if (b >= qs->prev_bytes)
+		qs->avg_bytes = ((qs->avg_bytes * (n - 1)) +
+		    (b - qs->prev_bytes)) / n;
+
+	if (p >= qs->prev_packets)
+		qs->avg_packets = ((qs->avg_packets * (n - 1)) +
+		    (p - qs->prev_packets)) / n;
+
+	qs->prev_bytes = b;
+	qs->prev_packets = p;
+	if (n < AVGN_MAX)
+		qs->avgn++;
+}
diff --git a/freebsd/contrib/pf/pfctl/pfctl_radix.c b/freebsd/contrib/pf/pfctl/pfctl_radix.c
new file mode 100644
index 0000000..f074414
--- /dev/null
+++ b/freebsd/contrib/pf/pfctl/pfctl_radix.c
@@ -0,0 +1,587 @@
+#include <machine/rtems-bsd-user-space.h>
+
+/*	$OpenBSD: pfctl_radix.c,v 1.27 2005/05/21 21:03:58 henning Exp $ */
+
+/*
+ * Copyright (c) 2002 Cedric Berger
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *    - Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *    - 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 COPYRIGHT HOLDERS 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
+ * COPYRIGHT HOLDERS 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>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+
+#include <net/if.h>
+#include <net/pfvar.h>
+
+#include <errno.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <err.h>
+
+#include "pfctl.h"
+
+#define BUF_SIZE 256
+
+extern int dev;
+
+static int	 pfr_next_token(char buf[], FILE *);
+
+
+int
+pfr_clr_tables(struct pfr_table *filter, int *ndel, int flags)
+{
+	struct pfioc_table io;
+
+	bzero(&io, sizeof io);
+	io.pfrio_flags = flags;
+	if (filter != NULL)
+		io.pfrio_table = *filter;
+	if (ioctl(dev, DIOCRCLRTABLES, &io))
+		return (-1);
+	if (ndel != NULL)
+		*ndel = io.pfrio_ndel;
+	return (0);
+}
+
+int
+pfr_add_tables(struct pfr_table *tbl, int size, int *nadd, int flags)
+{
+	struct pfioc_table io;
+
+	if (size < 0 || (size && tbl == NULL)) {
+		errno = EINVAL;
+		return (-1);
+	}
+	bzero(&io, sizeof io);
+	io.pfrio_flags = flags;
+	io.pfrio_buffer = tbl;
+	io.pfrio_esize = sizeof(*tbl);
+	io.pfrio_size = size;
+	if (ioctl(dev, DIOCRADDTABLES, &io))
+		return (-1);
+	if (nadd != NULL)
+		*nadd = io.pfrio_nadd;
+	return (0);
+}
+
+int
+pfr_del_tables(struct pfr_table *tbl, int size, int *ndel, int flags)
+{
+	struct pfioc_table io;
+
+	if (size < 0 || (size && tbl == NULL)) {
+		errno = EINVAL;
+		return (-1);
+	}
+	bzero(&io, sizeof io);
+	io.pfrio_flags = flags;
+	io.pfrio_buffer = tbl;
+	io.pfrio_esize = sizeof(*tbl);
+	io.pfrio_size = size;
+	if (ioctl(dev, DIOCRDELTABLES, &io))
+		return (-1);
+	if (ndel != NULL)
+		*ndel = io.pfrio_ndel;
+	return (0);
+}
+
+int
+pfr_get_tables(struct pfr_table *filter, struct pfr_table *tbl, int *size,
+	int flags)
+{
+	struct pfioc_table io;
+
+	if (size == NULL || *size < 0 || (*size && tbl == NULL)) {
+		errno = EINVAL;
+		return (-1);
+	}
+	bzero(&io, sizeof io);
+	io.pfrio_flags = flags;
+	if (filter != NULL)
+		io.pfrio_table = *filter;
+	io.pfrio_buffer = tbl;
+	io.pfrio_esize = sizeof(*tbl);
+	io.pfrio_size = *size;
+	if (ioctl(dev, DIOCRGETTABLES, &io))
+		return (-1);
+	*size = io.pfrio_size;
+	return (0);
+}
+
+int
+pfr_get_tstats(struct pfr_table *filter, struct pfr_tstats *tbl, int *size,
+	int flags)
+{
+	struct pfioc_table io;
+
+	if (size == NULL || *size < 0 || (*size && tbl == NULL)) {
+		errno = EINVAL;
+		return (-1);
+	}
+	bzero(&io, sizeof io);
+	io.pfrio_flags = flags;
+	if (filter != NULL)
+		io.pfrio_table = *filter;
+	io.pfrio_buffer = tbl;
+	io.pfrio_esize = sizeof(*tbl);
+	io.pfrio_size = *size;
+	if (ioctl(dev, DIOCRGETTSTATS, &io))
+		return (-1);
+	*size = io.pfrio_size;
+	return (0);
+}
+
+int
+pfr_clr_addrs(struct pfr_table *tbl, int *ndel, int flags)
+{
+	struct pfioc_table io;
+
+	if (tbl == NULL) {
+		errno = EINVAL;
+		return (-1);
+	}
+	bzero(&io, sizeof io);
+	io.pfrio_flags = flags;
+	io.pfrio_table = *tbl;
+	if (ioctl(dev, DIOCRCLRADDRS, &io))
+		return (-1);
+	if (ndel != NULL)
+		*ndel = io.pfrio_ndel;
+	return (0);
+}
+
+int
+pfr_add_addrs(struct pfr_table *tbl, struct pfr_addr *addr, int size,
+    int *nadd, int flags)
+{
+	struct pfioc_table io;
+
+	if (tbl == NULL || size < 0 || (size && addr == NULL)) {
+		errno = EINVAL;
+		return (-1);
+	}
+	bzero(&io, sizeof io);
+	io.pfrio_flags = flags;
+	io.pfrio_table = *tbl;
+	io.pfrio_buffer = addr;
+	io.pfrio_esize = sizeof(*addr);
+	io.pfrio_size = size;
+	if (ioctl(dev, DIOCRADDADDRS, &io))
+		return (-1);
+	if (nadd != NULL)
+		*nadd = io.pfrio_nadd;
+	return (0);
+}
+
+int
+pfr_del_addrs(struct pfr_table *tbl, struct pfr_addr *addr, int size,
+    int *ndel, int flags)
+{
+	struct pfioc_table io;
+
+	if (tbl == NULL || size < 0 || (size && addr == NULL)) {
+		errno = EINVAL;
+		return (-1);
+	}
+	bzero(&io, sizeof io);
+	io.pfrio_flags = flags;
+	io.pfrio_table = *tbl;
+	io.pfrio_buffer = addr;
+	io.pfrio_esize = sizeof(*addr);
+	io.pfrio_size = size;
+	if (ioctl(dev, DIOCRDELADDRS, &io))
+		return (-1);
+	if (ndel != NULL)
+		*ndel = io.pfrio_ndel;
+	return (0);
+}
+
+int
+pfr_set_addrs(struct pfr_table *tbl, struct pfr_addr *addr, int size,
+    int *size2, int *nadd, int *ndel, int *nchange, int flags)
+{
+	struct pfioc_table io;
+
+	if (tbl == NULL || size < 0 || (size && addr == NULL)) {
+		errno = EINVAL;
+		return (-1);
+	}
+	bzero(&io, sizeof io);
+	io.pfrio_flags = flags;
+	io.pfrio_table = *tbl;
+	io.pfrio_buffer = addr;
+	io.pfrio_esize = sizeof(*addr);
+	io.pfrio_size = size;
+	io.pfrio_size2 = (size2 != NULL) ? *size2 : 0;
+	if (ioctl(dev, DIOCRSETADDRS, &io))
+		return (-1);
+	if (nadd != NULL)
+		*nadd = io.pfrio_nadd;
+	if (ndel != NULL)
+		*ndel = io.pfrio_ndel;
+	if (nchange != NULL)
+		*nchange = io.pfrio_nchange;
+	if (size2 != NULL)
+		*size2 = io.pfrio_size2;
+	return (0);
+}
+
+int
+pfr_get_addrs(struct pfr_table *tbl, struct pfr_addr *addr, int *size,
+    int flags)
+{
+	struct pfioc_table io;
+
+	if (tbl == NULL || size == NULL || *size < 0 ||
+	    (*size && addr == NULL)) {
+		errno = EINVAL;
+		return (-1);
+	}
+	bzero(&io, sizeof io);
+	io.pfrio_flags = flags;
+	io.pfrio_table = *tbl;
+	io.pfrio_buffer = addr;
+	io.pfrio_esize = sizeof(*addr);
+	io.pfrio_size = *size;
+	if (ioctl(dev, DIOCRGETADDRS, &io))
+		return (-1);
+	*size = io.pfrio_size;
+	return (0);
+}
+
+int
+pfr_get_astats(struct pfr_table *tbl, struct pfr_astats *addr, int *size,
+    int flags)
+{
+	struct pfioc_table io;
+
+	if (tbl == NULL || size == NULL || *size < 0 ||
+	    (*size && addr == NULL)) {
+		errno = EINVAL;
+		return (-1);
+	}
+	bzero(&io, sizeof io);
+	io.pfrio_flags = flags;
+	io.pfrio_table = *tbl;
+	io.pfrio_buffer = addr;
+	io.pfrio_esize = sizeof(*addr);
+	io.pfrio_size = *size;
+	if (ioctl(dev, DIOCRGETASTATS, &io))
+		return (-1);
+	*size = io.pfrio_size;
+	return (0);
+}
+
+int
+pfr_clr_tstats(struct pfr_table *tbl, int size, int *nzero, int flags)
+{
+	struct pfioc_table io;
+
+	if (size < 0 || (size && !tbl)) {
+		errno = EINVAL;
+		return (-1);
+	}
+	bzero(&io, sizeof io);
+	io.pfrio_flags = flags;
+	io.pfrio_buffer = tbl;
+	io.pfrio_esize = sizeof(*tbl);
+	io.pfrio_size = size;
+	if (ioctl(dev, DIOCRCLRTSTATS, &io))
+		return (-1);
+	if (nzero)
+		*nzero = io.pfrio_nzero;
+	return (0);
+}
+
+int
+pfr_tst_addrs(struct pfr_table *tbl, struct pfr_addr *addr, int size,
+    int *nmatch, int flags)
+{
+	struct pfioc_table io;
+
+	if (tbl == NULL || size < 0 || (size && addr == NULL)) {
+		errno = EINVAL;
+		return (-1);
+	}
+	bzero(&io, sizeof io);
+	io.pfrio_flags = flags;
+	io.pfrio_table = *tbl;
+	io.pfrio_buffer = addr;
+	io.pfrio_esize = sizeof(*addr);
+	io.pfrio_size = size;
+	if (ioctl(dev, DIOCRTSTADDRS, &io))
+		return (-1);
+	if (nmatch)
+		*nmatch = io.pfrio_nmatch;
+	return (0);
+}
+
+int
+pfr_ina_define(struct pfr_table *tbl, struct pfr_addr *addr, int size,
+    int *nadd, int *naddr, int ticket, int flags)
+{
+	struct pfioc_table io;
+
+	if (tbl == NULL || size < 0 || (size && addr == NULL)) {
+		errno = EINVAL;
+		return (-1);
+	}
+	bzero(&io, sizeof io);
+	io.pfrio_flags = flags;
+	io.pfrio_table = *tbl;
+	io.pfrio_buffer = addr;
+	io.pfrio_esize = sizeof(*addr);
+	io.pfrio_size = size;
+	io.pfrio_ticket = ticket;
+	if (ioctl(dev, DIOCRINADEFINE, &io))
+		return (-1);
+	if (nadd != NULL)
+		*nadd = io.pfrio_nadd;
+	if (naddr != NULL)
+		*naddr = io.pfrio_naddr;
+	return (0);
+}
+
+/* interface management code */
+
+int
+pfi_get_ifaces(const char *filter, struct pfi_kif *buf, int *size)
+{
+	struct pfioc_iface io;
+
+	if (size == NULL || *size < 0 || (*size && buf == NULL)) {
+		errno = EINVAL;
+		return (-1);
+	}
+	bzero(&io, sizeof io);
+	if (filter != NULL)
+		if (strlcpy(io.pfiio_name, filter, sizeof(io.pfiio_name)) >=
+		    sizeof(io.pfiio_name)) {
+			errno = EINVAL;
+			return (-1);
+		}
+	io.pfiio_buffer = buf;
+	io.pfiio_esize = sizeof(*buf);
+	io.pfiio_size = *size;
+	if (ioctl(dev, DIOCIGETIFACES, &io))
+		return (-1);
+	*size = io.pfiio_size;
+	return (0);
+}
+
+/* buffer management code */
+
+size_t buf_esize[PFRB_MAX] = { 0,
+	sizeof(struct pfr_table), sizeof(struct pfr_tstats),
+	sizeof(struct pfr_addr), sizeof(struct pfr_astats),
+	sizeof(struct pfi_kif), sizeof(struct pfioc_trans_e)
+};
+
+/*
+ * add one element to the buffer
+ */
+int
+pfr_buf_add(struct pfr_buffer *b, const void *e)
+{
+	size_t bs;
+
+	if (b == NULL || b->pfrb_type <= 0 || b->pfrb_type >= PFRB_MAX ||
+	    e == NULL) {
+		errno = EINVAL;
+		return (-1);
+	}
+	bs = buf_esize[b->pfrb_type];
+	if (b->pfrb_size == b->pfrb_msize)
+		if (pfr_buf_grow(b, 0))
+			return (-1);
+	memcpy(((caddr_t)b->pfrb_caddr) + bs * b->pfrb_size, e, bs);
+	b->pfrb_size++;
+	return (0);
+}
+
+/*
+ * return next element of the buffer (or first one if prev is NULL)
+ * see PFRB_FOREACH macro
+ */
+void *
+pfr_buf_next(struct pfr_buffer *b, const void *prev)
+{
+	size_t bs;
+
+	if (b == NULL || b->pfrb_type <= 0 || b->pfrb_type >= PFRB_MAX)
+		return (NULL);
+	if (b->pfrb_size == 0)
+		return (NULL);
+	if (prev == NULL)
+		return (b->pfrb_caddr);
+	bs = buf_esize[b->pfrb_type];
+	if ((((caddr_t)prev)-((caddr_t)b->pfrb_caddr)) / bs >= b->pfrb_size-1)
+		return (NULL);
+	return (((caddr_t)prev) + bs);
+}
+
+/*
+ * minsize:
+ *    0: make the buffer somewhat bigger
+ *    n: make room for "n" entries in the buffer
+ */
+int
+pfr_buf_grow(struct pfr_buffer *b, int minsize)
+{
+	caddr_t p;
+	size_t bs;
+
+	if (b == NULL || b->pfrb_type <= 0 || b->pfrb_type >= PFRB_MAX) {
+		errno = EINVAL;
+		return (-1);
+	}
+	if (minsize != 0 && minsize <= b->pfrb_msize)
+		return (0);
+	bs = buf_esize[b->pfrb_type];
+	if (!b->pfrb_msize) {
+		if (minsize < 64)
+			minsize = 64;
+		b->pfrb_caddr = calloc(bs, minsize);
+		if (b->pfrb_caddr == NULL)
+			return (-1);
+		b->pfrb_msize = minsize;
+	} else {
+		if (minsize == 0)
+			minsize = b->pfrb_msize * 2;
+		if (minsize < 0 || minsize >= SIZE_T_MAX / bs) {
+			/* msize overflow */
+			errno = ENOMEM;
+			return (-1);
+		}
+		p = realloc(b->pfrb_caddr, minsize * bs);
+		if (p == NULL)
+			return (-1);
+		bzero(p + b->pfrb_msize * bs, (minsize - b->pfrb_msize) * bs);
+		b->pfrb_caddr = p;
+		b->pfrb_msize = minsize;
+	}
+	return (0);
+}
+
+/*
+ * reset buffer and free memory.
+ */
+void
+pfr_buf_clear(struct pfr_buffer *b)
+{
+	if (b == NULL)
+		return;
+	if (b->pfrb_caddr != NULL)
+		free(b->pfrb_caddr);
+	b->pfrb_caddr = NULL;
+	b->pfrb_size = b->pfrb_msize = 0;
+}
+
+int
+pfr_buf_load(struct pfr_buffer *b, char *file, int nonetwork,
+    int (*append_addr)(struct pfr_buffer *, char *, int))
+{
+	FILE	*fp;
+	char	 buf[BUF_SIZE];
+	int	 rv;
+
+	if (file == NULL)
+		return (0);
+	if (!strcmp(file, "-"))
+		fp = stdin;
+	else {
+		fp = pfctl_fopen(file, "r");
+		if (fp == NULL)
+			return (-1);
+	}
+	while ((rv = pfr_next_token(buf, fp)) == 1)
+		if (append_addr(b, buf, nonetwork)) {
+			rv = -1;
+			break;
+		}
+	if (fp != stdin)
+		fclose(fp);
+	return (rv);
+}
+
+int
+pfr_next_token(char buf[BUF_SIZE], FILE *fp)
+{
+	static char	next_ch = ' ';
+	int		i = 0;
+
+	for (;;) {
+		/* skip spaces */
+		while (isspace(next_ch) && !feof(fp))
+			next_ch = fgetc(fp);
+		/* remove from '#' until end of line */
+		if (next_ch == '#')
+			while (!feof(fp)) {
+				next_ch = fgetc(fp);
+				if (next_ch == '\n')
+					break;
+			}
+		else
+			break;
+	}
+	if (feof(fp)) {
+		next_ch = ' ';
+		return (0);
+	}
+	do {
+		if (i < BUF_SIZE)
+			buf[i++] = next_ch;
+		next_ch = fgetc(fp);
+	} while (!feof(fp) && !isspace(next_ch));
+	if (i >= BUF_SIZE) {
+		errno = EINVAL;
+		return (-1);
+	}
+	buf[i] = '\0';
+	return (1);
+}
+
+char *
+pfr_strerror(int errnum)
+{
+	switch (errnum) {
+	case ESRCH:
+		return "Table does not exist";
+	case ENOENT:
+		return "Anchor or Ruleset does not exist";
+	default:
+		return strerror(errnum);
+	}
+}
diff --git a/freebsd/contrib/pf/pfctl/pfctl_table.c b/freebsd/contrib/pf/pfctl/pfctl_table.c
new file mode 100644
index 0000000..590697c
--- /dev/null
+++ b/freebsd/contrib/pf/pfctl/pfctl_table.c
@@ -0,0 +1,637 @@
+#include <machine/rtems-bsd-user-space.h>
+
+/*	$OpenBSD: pfctl_table.c,v 1.67 2008/06/10 20:55:02 mcbride Exp $ */
+
+/*
+ * Copyright (c) 2002 Cedric Berger
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *    - Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *    - 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 COPYRIGHT HOLDERS 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
+ * COPYRIGHT HOLDERS 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>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+
+#include <net/if.h>
+#include <net/pfvar.h>
+#include <arpa/inet.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <netdb.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#include "pfctl_parser.h"
+#include "pfctl.h"
+
+extern void	usage(void);
+static int	pfctl_table(int, char *[], char *, const char *, char *,
+		    const char *, int);
+static void	print_table(struct pfr_table *, int, int);
+static void	print_tstats(struct pfr_tstats *, int);
+static int	load_addr(struct pfr_buffer *, int, char *[], char *, int);
+static void	print_addrx(struct pfr_addr *, struct pfr_addr *, int);
+static void	print_astats(struct pfr_astats *, int);
+static void	radix_perror(void);
+static void	xprintf(int, const char *, ...);
+static void	print_iface(struct pfi_kif *, int);
+
+static const char	*stats_text[PFR_DIR_MAX][PFR_OP_TABLE_MAX] = {
+	{ "In/Block:",	"In/Pass:",	"In/XPass:" },
+	{ "Out/Block:",	"Out/Pass:",	"Out/XPass:" }
+};
+
+static const char	*istats_text[2][2][2] = {
+	{ { "In4/Pass:", "In4/Block:" }, { "Out4/Pass:", "Out4/Block:" } },
+	{ { "In6/Pass:", "In6/Block:" }, { "Out6/Pass:", "Out6/Block:" } }
+};
+
+#define RVTEST(fct) do {				\
+		if ((!(opts & PF_OPT_NOACTION) ||	\
+		    (opts & PF_OPT_DUMMYACTION)) &&	\
+		    (fct)) {				\
+			radix_perror();			\
+			goto _error;			\
+		}					\
+	} while (0)
+
+#define CREATE_TABLE do {						\
+		table.pfrt_flags |= PFR_TFLAG_PERSIST;			\
+		if ((!(opts & PF_OPT_NOACTION) ||			\
+		    (opts & PF_OPT_DUMMYACTION)) &&			\
+		    (pfr_add_tables(&table, 1, &nadd, flags)) &&	\
+		    (errno != EPERM)) {					\
+			radix_perror();					\
+			goto _error;					\
+		}							\
+		if (nadd) {						\
+			warn_namespace_collision(table.pfrt_name);	\
+			xprintf(opts, "%d table created", nadd);	\
+			if (opts & PF_OPT_NOACTION)			\
+				return (0);				\
+		}							\
+		table.pfrt_flags &= ~PFR_TFLAG_PERSIST;			\
+	} while(0)
+
+int
+pfctl_clear_tables(const char *anchor, int opts)
+{
+	return pfctl_table(0, NULL, NULL, "-F", NULL, anchor, opts);
+}
+
+int
+pfctl_show_tables(const char *anchor, int opts)
+{
+	return pfctl_table(0, NULL, NULL, "-s", NULL, anchor, opts);
+}
+
+int
+pfctl_command_tables(int argc, char *argv[], char *tname,
+    const char *command, char *file, const char *anchor, int opts)
+{
+	if (tname == NULL || command == NULL)
+		usage();
+	return pfctl_table(argc, argv, tname, command, file, anchor, opts);
+}
+
+int
+pfctl_table(int argc, char *argv[], char *tname, const char *command,
+    char *file, const char *anchor, int opts)
+{
+	struct pfr_table	 table;
+	struct pfr_buffer	 b, b2;
+	struct pfr_addr		*a, *a2;
+	int			 nadd = 0, ndel = 0, nchange = 0, nzero = 0;
+	int			 rv = 0, flags = 0, nmatch = 0;
+	void			*p;
+
+	if (command == NULL)
+		usage();
+	if (opts & PF_OPT_NOACTION)
+		flags |= PFR_FLAG_DUMMY;
+
+	bzero(&b, sizeof(b));
+	bzero(&b2, sizeof(b2));
+	bzero(&table, sizeof(table));
+	if (tname != NULL) {
+		if (strlen(tname) >= PF_TABLE_NAME_SIZE)
+			usage();
+		if (strlcpy(table.pfrt_name, tname,
+		    sizeof(table.pfrt_name)) >= sizeof(table.pfrt_name))
+			errx(1, "pfctl_table: strlcpy");
+	}
+	if (strlcpy(table.pfrt_anchor, anchor,
+	    sizeof(table.pfrt_anchor)) >= sizeof(table.pfrt_anchor))
+		errx(1, "pfctl_table: strlcpy");
+
+	if (!strcmp(command, "-F")) {
+		if (argc || file != NULL)
+			usage();
+		RVTEST(pfr_clr_tables(&table, &ndel, flags));
+		xprintf(opts, "%d tables deleted", ndel);
+	} else if (!strcmp(command, "-s")) {
+		b.pfrb_type = (opts & PF_OPT_VERBOSE2) ?
+		    PFRB_TSTATS : PFRB_TABLES;
+		if (argc || file != NULL)
+			usage();
+		for (;;) {
+			pfr_buf_grow(&b, b.pfrb_size);
+			b.pfrb_size = b.pfrb_msize;
+			if (opts & PF_OPT_VERBOSE2)
+				RVTEST(pfr_get_tstats(&table,
+				    b.pfrb_caddr, &b.pfrb_size, flags));
+			else
+				RVTEST(pfr_get_tables(&table,
+				    b.pfrb_caddr, &b.pfrb_size, flags));
+			if (b.pfrb_size <= b.pfrb_msize)
+				break;
+		}
+
+		if ((opts & PF_OPT_SHOWALL) && b.pfrb_size > 0)
+			pfctl_print_title("TABLES:");
+
+		PFRB_FOREACH(p, &b)
+			if (opts & PF_OPT_VERBOSE2)
+				print_tstats(p, opts & PF_OPT_DEBUG);
+			else
+				print_table(p, opts & PF_OPT_VERBOSE,
+				    opts & PF_OPT_DEBUG);
+	} else if (!strcmp(command, "kill")) {
+		if (argc || file != NULL)
+			usage();
+		RVTEST(pfr_del_tables(&table, 1, &ndel, flags));
+		xprintf(opts, "%d table deleted", ndel);
+	} else if (!strcmp(command, "flush")) {
+		if (argc || file != NULL)
+			usage();
+		RVTEST(pfr_clr_addrs(&table, &ndel, flags));
+		xprintf(opts, "%d addresses deleted", ndel);
+	} else if (!strcmp(command, "add")) {
+		b.pfrb_type = PFRB_ADDRS;
+		if (load_addr(&b, argc, argv, file, 0))
+			goto _error;
+		CREATE_TABLE;
+		if (opts & PF_OPT_VERBOSE)
+			flags |= PFR_FLAG_FEEDBACK;
+		RVTEST(pfr_add_addrs(&table, b.pfrb_caddr, b.pfrb_size,
+		    &nadd, flags));
+		xprintf(opts, "%d/%d addresses added", nadd, b.pfrb_size);
+		if (opts & PF_OPT_VERBOSE)
+			PFRB_FOREACH(a, &b)
+				if ((opts & PF_OPT_VERBOSE2) || a->pfra_fback)
+					print_addrx(a, NULL,
+					    opts & PF_OPT_USEDNS);
+	} else if (!strcmp(command, "delete")) {
+		b.pfrb_type = PFRB_ADDRS;
+		if (load_addr(&b, argc, argv, file, 0))
+			goto _error;
+		if (opts & PF_OPT_VERBOSE)
+			flags |= PFR_FLAG_FEEDBACK;
+		RVTEST(pfr_del_addrs(&table, b.pfrb_caddr, b.pfrb_size,
+		    &ndel, flags));
+		xprintf(opts, "%d/%d addresses deleted", ndel, b.pfrb_size);
+		if (opts & PF_OPT_VERBOSE)
+			PFRB_FOREACH(a, &b)
+				if ((opts & PF_OPT_VERBOSE2) || a->pfra_fback)
+					print_addrx(a, NULL,
+					    opts & PF_OPT_USEDNS);
+	} else if (!strcmp(command, "replace")) {
+		b.pfrb_type = PFRB_ADDRS;
+		if (load_addr(&b, argc, argv, file, 0))
+			goto _error;
+		CREATE_TABLE;
+		if (opts & PF_OPT_VERBOSE)
+			flags |= PFR_FLAG_FEEDBACK;
+		for (;;) {
+			int sz2 = b.pfrb_msize;
+
+			RVTEST(pfr_set_addrs(&table, b.pfrb_caddr, b.pfrb_size,
+			    &sz2, &nadd, &ndel, &nchange, flags));
+			if (sz2 <= b.pfrb_msize) {
+				b.pfrb_size = sz2;
+				break;
+			} else
+				pfr_buf_grow(&b, sz2);
+		}
+		if (nadd)
+			xprintf(opts, "%d addresses added", nadd);
+		if (ndel)
+			xprintf(opts, "%d addresses deleted", ndel);
+		if (nchange)
+			xprintf(opts, "%d addresses changed", nchange);
+		if (!nadd && !ndel && !nchange)
+			xprintf(opts, "no changes");
+		if (opts & PF_OPT_VERBOSE)
+			PFRB_FOREACH(a, &b)
+				if ((opts & PF_OPT_VERBOSE2) || a->pfra_fback)
+					print_addrx(a, NULL,
+					    opts & PF_OPT_USEDNS);
+	} else if (!strcmp(command, "expire")) {
+		const char		*errstr;
+		u_int			 lifetime;
+
+		b.pfrb_type = PFRB_ASTATS;
+		b2.pfrb_type = PFRB_ADDRS;
+		if (argc != 1 || file != NULL)
+			usage();
+		lifetime = strtonum(*argv, 0, UINT_MAX, &errstr);
+		if (errstr)
+			errx(1, "expiry time: %s", errstr);
+		for (;;) {
+			pfr_buf_grow(&b, b.pfrb_size);
+			b.pfrb_size = b.pfrb_msize;
+			RVTEST(pfr_get_astats(&table, b.pfrb_caddr,
+			    &b.pfrb_size, flags));
+			if (b.pfrb_size <= b.pfrb_msize)
+				break;
+		}
+		PFRB_FOREACH(p, &b) {
+			((struct pfr_astats *)p)->pfras_a.pfra_fback = 0;
+			if (time(NULL) - ((struct pfr_astats *)p)->pfras_tzero >
+			    lifetime)
+				if (pfr_buf_add(&b2,
+				    &((struct pfr_astats *)p)->pfras_a))
+					err(1, "duplicate buffer");
+		}
+
+		if (opts & PF_OPT_VERBOSE)
+			flags |= PFR_FLAG_FEEDBACK;
+		RVTEST(pfr_del_addrs(&table, b2.pfrb_caddr, b2.pfrb_size,
+		    &ndel, flags));
+		xprintf(opts, "%d/%d addresses expired", ndel, b2.pfrb_size);
+		if (opts & PF_OPT_VERBOSE)
+			PFRB_FOREACH(a, &b2)
+				if ((opts & PF_OPT_VERBOSE2) || a->pfra_fback)
+					print_addrx(a, NULL,
+					    opts & PF_OPT_USEDNS);
+	} else if (!strcmp(command, "show")) {
+		b.pfrb_type = (opts & PF_OPT_VERBOSE) ?
+			PFRB_ASTATS : PFRB_ADDRS;
+		if (argc || file != NULL)
+			usage();
+		for (;;) {
+			pfr_buf_grow(&b, b.pfrb_size);
+			b.pfrb_size = b.pfrb_msize;
+			if (opts & PF_OPT_VERBOSE)
+				RVTEST(pfr_get_astats(&table, b.pfrb_caddr,
+				    &b.pfrb_size, flags));
+			else
+				RVTEST(pfr_get_addrs(&table, b.pfrb_caddr,
+				    &b.pfrb_size, flags));
+			if (b.pfrb_size <= b.pfrb_msize)
+				break;
+		}
+		PFRB_FOREACH(p, &b)
+			if (opts & PF_OPT_VERBOSE)
+				print_astats(p, opts & PF_OPT_USEDNS);
+			else
+				print_addrx(p, NULL, opts & PF_OPT_USEDNS);
+	} else if (!strcmp(command, "test")) {
+		b.pfrb_type = PFRB_ADDRS;
+		b2.pfrb_type = PFRB_ADDRS;
+
+		if (load_addr(&b, argc, argv, file, 1))
+			goto _error;
+		if (opts & PF_OPT_VERBOSE2) {
+			flags |= PFR_FLAG_REPLACE;
+			PFRB_FOREACH(a, &b)
+				if (pfr_buf_add(&b2, a))
+					err(1, "duplicate buffer");
+		}
+		RVTEST(pfr_tst_addrs(&table, b.pfrb_caddr, b.pfrb_size,
+		    &nmatch, flags));
+		xprintf(opts, "%d/%d addresses match", nmatch, b.pfrb_size);
+		if ((opts & PF_OPT_VERBOSE) && !(opts & PF_OPT_VERBOSE2))
+			PFRB_FOREACH(a, &b)
+				if (a->pfra_fback == PFR_FB_MATCH)
+					print_addrx(a, NULL,
+					    opts & PF_OPT_USEDNS);
+		if (opts & PF_OPT_VERBOSE2) {
+			a2 = NULL;
+			PFRB_FOREACH(a, &b) {
+				a2 = pfr_buf_next(&b2, a2);
+				print_addrx(a2, a, opts & PF_OPT_USEDNS);
+			}
+		}
+		if (nmatch < b.pfrb_size)
+			rv = 2;
+	} else if (!strcmp(command, "zero")) {
+		if (argc || file != NULL)
+			usage();
+		flags |= PFR_FLAG_ADDRSTOO;
+		RVTEST(pfr_clr_tstats(&table, 1, &nzero, flags));
+		xprintf(opts, "%d table/stats cleared", nzero);
+	} else
+		warnx("pfctl_table: unknown command '%s'", command);
+	goto _cleanup;
+
+_error:
+	rv = -1;
+_cleanup:
+	pfr_buf_clear(&b);
+	pfr_buf_clear(&b2);
+	return (rv);
+}
+
+void
+print_table(struct pfr_table *ta, int verbose, int debug)
+{
+	if (!debug && !(ta->pfrt_flags & PFR_TFLAG_ACTIVE))
+		return;
+	if (verbose) {
+		printf("%c%c%c%c%c%c%c\t%s",
+		    (ta->pfrt_flags & PFR_TFLAG_CONST) ? 'c' : '-',
+		    (ta->pfrt_flags & PFR_TFLAG_PERSIST) ? 'p' : '-',
+		    (ta->pfrt_flags & PFR_TFLAG_ACTIVE) ? 'a' : '-',
+		    (ta->pfrt_flags & PFR_TFLAG_INACTIVE) ? 'i' : '-',
+		    (ta->pfrt_flags & PFR_TFLAG_REFERENCED) ? 'r' : '-',
+		    (ta->pfrt_flags & PFR_TFLAG_REFDANCHOR) ? 'h' : '-',
+		    (ta->pfrt_flags & PFR_TFLAG_COUNTERS) ? 'C' : '-',
+		    ta->pfrt_name);
+		if (ta->pfrt_anchor[0])
+			printf("\t%s", ta->pfrt_anchor);
+		puts("");
+	} else
+		puts(ta->pfrt_name);
+}
+
+void
+print_tstats(struct pfr_tstats *ts, int debug)
+{
+	time_t	time = ts->pfrts_tzero;
+	int	dir, op;
+
+	if (!debug && !(ts->pfrts_flags & PFR_TFLAG_ACTIVE))
+		return;
+	print_table(&ts->pfrts_t, 1, debug);
+	printf("\tAddresses:   %d\n", ts->pfrts_cnt);
+	printf("\tCleared:     %s", ctime(&time));
+	printf("\tReferences:  [ Anchors: %-18d Rules: %-18d ]\n",
+	    ts->pfrts_refcnt[PFR_REFCNT_ANCHOR],
+	    ts->pfrts_refcnt[PFR_REFCNT_RULE]);
+	printf("\tEvaluations: [ NoMatch: %-18llu Match: %-18llu ]\n",
+	    (unsigned long long)ts->pfrts_nomatch,
+	    (unsigned long long)ts->pfrts_match);
+	for (dir = 0; dir < PFR_DIR_MAX; dir++)
+		for (op = 0; op < PFR_OP_TABLE_MAX; op++)
+			printf("\t%-12s [ Packets: %-18llu Bytes: %-18llu ]\n",
+			    stats_text[dir][op],
+			    (unsigned long long)ts->pfrts_packets[dir][op],
+			    (unsigned long long)ts->pfrts_bytes[dir][op]);
+}
+
+int
+load_addr(struct pfr_buffer *b, int argc, char *argv[], char *file,
+    int nonetwork)
+{
+	while (argc--)
+		if (append_addr(b, *argv++, nonetwork)) {
+			if (errno)
+				warn("cannot decode %s", argv[-1]);
+			return (-1);
+		}
+	if (pfr_buf_load(b, file, nonetwork, append_addr)) {
+		warn("cannot load %s", file);
+		return (-1);
+	}
+	return (0);
+}
+
+void
+print_addrx(struct pfr_addr *ad, struct pfr_addr *rad, int dns)
+{
+	char		ch, buf[256] = "{error}";
+	char		fb[] = { ' ', 'M', 'A', 'D', 'C', 'Z', 'X', ' ', 'Y', ' ' };
+	unsigned int	fback, hostnet;
+
+	fback = (rad != NULL) ? rad->pfra_fback : ad->pfra_fback;
+	ch = (fback < sizeof(fb)/sizeof(*fb)) ? fb[fback] : '?';
+	hostnet = (ad->pfra_af == AF_INET6) ? 128 : 32;
+	inet_ntop(ad->pfra_af, &ad->pfra_u, buf, sizeof(buf));
+	printf("%c %c%s", ch, (ad->pfra_not?'!':' '), buf);
+	if (ad->pfra_net < hostnet)
+		printf("/%d", ad->pfra_net);
+	if (rad != NULL && fback != PFR_FB_NONE) {
+		if (strlcpy(buf, "{error}", sizeof(buf)) >= sizeof(buf))
+			errx(1, "print_addrx: strlcpy");
+		inet_ntop(rad->pfra_af, &rad->pfra_u, buf, sizeof(buf));
+		printf("\t%c%s", (rad->pfra_not?'!':' '), buf);
+		if (rad->pfra_net < hostnet)
+			printf("/%d", rad->pfra_net);
+	}
+	if (rad != NULL && fback == PFR_FB_NONE)
+		printf("\t nomatch");
+	if (dns && ad->pfra_net == hostnet) {
+		char host[NI_MAXHOST];
+		union sockaddr_union sa;
+
+		strlcpy(host, "?", sizeof(host));
+		bzero(&sa, sizeof(sa));
+		sa.sa.sa_family = ad->pfra_af;
+		if (sa.sa.sa_family == AF_INET) {
+			sa.sa.sa_len = sizeof(sa.sin);
+			sa.sin.sin_addr = ad->pfra_ip4addr;
+		} else {
+			sa.sa.sa_len = sizeof(sa.sin6);
+			sa.sin6.sin6_addr = ad->pfra_ip6addr;
+		}
+		if (getnameinfo(&sa.sa, sa.sa.sa_len, host, sizeof(host),
+		    NULL, 0, NI_NAMEREQD) == 0)
+			printf("\t(%s)", host);
+	}
+	printf("\n");
+}
+
+void
+print_astats(struct pfr_astats *as, int dns)
+{
+	time_t	time = as->pfras_tzero;
+	int	dir, op;
+
+	print_addrx(&as->pfras_a, NULL, dns);
+	printf("\tCleared:     %s", ctime(&time));
+ 	if (as->pfras_a.pfra_fback == PFR_FB_NOCOUNT)
+		return;
+	for (dir = 0; dir < PFR_DIR_MAX; dir++)
+		for (op = 0; op < PFR_OP_ADDR_MAX; op++)
+			printf("\t%-12s [ Packets: %-18llu Bytes: %-18llu ]\n",
+			    stats_text[dir][op],
+			    (unsigned long long)as->pfras_packets[dir][op],
+			    (unsigned long long)as->pfras_bytes[dir][op]);
+}
+
+void
+radix_perror(void)
+{
+	extern char *__progname;
+	fprintf(stderr, "%s: %s.\n", __progname, pfr_strerror(errno));
+}
+
+int
+pfctl_define_table(char *name, int flags, int addrs, const char *anchor,
+    struct pfr_buffer *ab, u_int32_t ticket)
+{
+	struct pfr_table tbl;
+
+	bzero(&tbl, sizeof(tbl));
+	if (strlcpy(tbl.pfrt_name, name, sizeof(tbl.pfrt_name)) >=
+	    sizeof(tbl.pfrt_name) || strlcpy(tbl.pfrt_anchor, anchor,
+	    sizeof(tbl.pfrt_anchor)) >= sizeof(tbl.pfrt_anchor))
+		errx(1, "pfctl_define_table: strlcpy");
+	tbl.pfrt_flags = flags;
+
+	return pfr_ina_define(&tbl, ab->pfrb_caddr, ab->pfrb_size, NULL,
+	    NULL, ticket, addrs ? PFR_FLAG_ADDRSTOO : 0);
+}
+
+void
+warn_namespace_collision(const char *filter)
+{
+	struct pfr_buffer b;
+	struct pfr_table *t;
+	const char *name = NULL, *lastcoll;
+	int coll = 0;
+
+	bzero(&b, sizeof(b));
+	b.pfrb_type = PFRB_TABLES;
+	for (;;) {
+		pfr_buf_grow(&b, b.pfrb_size);
+		b.pfrb_size = b.pfrb_msize;
+		if (pfr_get_tables(NULL, b.pfrb_caddr,
+		    &b.pfrb_size, PFR_FLAG_ALLRSETS))
+			err(1, "pfr_get_tables");
+		if (b.pfrb_size <= b.pfrb_msize)
+			break;
+	}
+	PFRB_FOREACH(t, &b) {
+		if (!(t->pfrt_flags & PFR_TFLAG_ACTIVE))
+			continue;
+		if (filter != NULL && strcmp(filter, t->pfrt_name))
+			continue;
+		if (!t->pfrt_anchor[0])
+			name = t->pfrt_name;
+		else if (name != NULL && !strcmp(name, t->pfrt_name)) {
+			coll++;
+			lastcoll = name;
+			name = NULL;
+		}
+	}
+	if (coll == 1)
+		warnx("warning: namespace collision with <%s> global table.",
+		    lastcoll);
+	else if (coll > 1)
+		warnx("warning: namespace collisions with %d global tables.",
+		    coll);
+	pfr_buf_clear(&b);
+}
+
+void
+xprintf(int opts, const char *fmt, ...)
+{
+	va_list args;
+
+	if (opts & PF_OPT_QUIET)
+		return;
+
+	va_start(args, fmt);
+	vfprintf(stderr, fmt, args);
+	va_end(args);
+
+	if (opts & PF_OPT_DUMMYACTION)
+		fprintf(stderr, " (dummy).\n");
+	else if (opts & PF_OPT_NOACTION)
+		fprintf(stderr, " (syntax only).\n");
+	else
+		fprintf(stderr, ".\n");
+}
+
+
+/* interface stuff */
+
+int
+pfctl_show_ifaces(const char *filter, int opts)
+{
+	struct pfr_buffer	 b;
+	struct pfi_kif		*p;
+	int			 i = 0;
+
+	bzero(&b, sizeof(b));
+	b.pfrb_type = PFRB_IFACES;
+	for (;;) {
+		pfr_buf_grow(&b, b.pfrb_size);
+		b.pfrb_size = b.pfrb_msize;
+		if (pfi_get_ifaces(filter, b.pfrb_caddr, &b.pfrb_size)) {
+			radix_perror();
+			return (1);
+		}
+		if (b.pfrb_size <= b.pfrb_msize)
+			break;
+		i++;
+	}
+	if (opts & PF_OPT_SHOWALL)
+		pfctl_print_title("INTERFACES:");
+	PFRB_FOREACH(p, &b)
+		print_iface(p, opts);
+	return (0);
+}
+
+void
+print_iface(struct pfi_kif *p, int opts)
+{
+	time_t	tzero = p->pfik_tzero;
+	int	i, af, dir, act;
+
+	printf("%s", p->pfik_name);
+	if (opts & PF_OPT_VERBOSE) {
+		if (p->pfik_flags & PFI_IFLAG_SKIP)
+			printf(" (skip)");
+	}
+	printf("\n");
+
+	if (!(opts & PF_OPT_VERBOSE2))
+		return;
+	printf("\tCleared:     %s", ctime(&tzero));
+	printf("\tReferences:  [ States:  %-18d Rules: %-18d ]\n",
+	    p->pfik_states, p->pfik_rules);
+	for (i = 0; i < 8; i++) {
+		af = (i>>2) & 1;
+		dir = (i>>1) &1;
+		act = i & 1;
+		printf("\t%-12s [ Packets: %-18llu Bytes: %-18llu ]\n",
+		    istats_text[af][dir][act],
+		    (unsigned long long)p->pfik_packets[af][dir][act],
+		    (unsigned long long)p->pfik_bytes[af][dir][act]);
+	}
+}



More information about the vc mailing list