? usr.sbin/pf/pfs/.gdbinit
? usr.sbin/pf/pfs/.new.pfs.c
Index: distrib/sets/lists/base/mi
===================================================================
RCS file: /cvsroot/src/distrib/sets/lists/base/mi,v
retrieving revision 1.860
diff -u -r1.860 mi
--- distrib/sets/lists/base/mi	8 Mar 2010 06:40:06 -0000	1.860
+++ distrib/sets/lists/base/mi	6 Apr 2010 17:28:06 -0000
@@ -271,6 +271,7 @@
 ./sbin/nologin					base-sysutil-root
 ./sbin/pfctl					base-pf-root		pf
 ./sbin/pflogd					base-pf-root		pf
+./sbin/pfs						base-pf-root		pf
 ./sbin/ping					base-netutil-root
 ./sbin/ping6					base-netutil-root	use_inet6
 ./sbin/poweroff					base-sysutil-root
Index: distrib/sets/lists/man/mi
===================================================================
RCS file: /cvsroot/src/distrib/sets/lists/man/mi,v
retrieving revision 1.1198
diff -u -r1.1198 mi
--- distrib/sets/lists/man/mi	11 Mar 2010 10:38:36 -0000	1.1198
+++ distrib/sets/lists/man/mi	6 Apr 2010 17:28:12 -0000
@@ -2442,6 +2442,7 @@
 ./usr/share/man/cat8/peace.0			man-sys-catman		.cat
 ./usr/share/man/cat8/pfctl.0			man-pf-catman		pf,.cat
 ./usr/share/man/cat8/pflogd.0			man-pf-catman		pf,.cat
+./usr/share/man/cat8/pfs.0				man-pf-catman		pf,.cat
 ./usr/share/man/cat8/pfspamd-setup.0		man-obsolete		obsolete
 ./usr/share/man/cat8/pfspamd.0			man-obsolete		obsolete
 ./usr/share/man/cat8/pfspamdb.0			man-obsolete		obsolete
@@ -4865,6 +4866,7 @@
 ./usr/share/man/html8/peace.html		man-sys-htmlman		html
 ./usr/share/man/html8/pfctl.html		man-pf-htmlman		pf,html
 ./usr/share/man/html8/pflogd.html		man-pf-htmlman		pf,html
+./usr/share/man/html8/pfs.html			man-pf-htmlman		pf,html
 ./usr/share/man/html8/pickup.html		man-postfix-htmlman	postfix,html
 ./usr/share/man/html8/ping.html			man-netutil-htmlman	html
 ./usr/share/man/html8/ping6.html		man-netutil-htmlman	use_inet6,html
@@ -7522,6 +7524,7 @@
 ./usr/share/man/man8/peace.8			man-sys-man		.man
 ./usr/share/man/man8/pfctl.8			man-pf-man		pf,.man
 ./usr/share/man/man8/pflogd.8			man-pf-man		pf,.man
+./usr/share/man/man8/pfs.8				man-pf-man		pf,.man
 ./usr/share/man/man8/pfspamd-setup.8		man-obsolete		obsolete
 ./usr/share/man/man8/pfspamd.8			man-obsolete		obsolete
 ./usr/share/man/man8/pfspamdb.8			man-obsolete		obsolete
Index: usr.sbin/pf/Makefile
===================================================================
RCS file: /cvsroot/src/usr.sbin/pf/Makefile,v
retrieving revision 1.8
diff -u -r1.8 Makefile
--- usr.sbin/pf/Makefile	18 Jun 2008 09:06:28 -0000	1.8
+++ usr.sbin/pf/Makefile	6 Apr 2010 17:28:15 -0000
@@ -6,6 +6,7 @@
 SUBDIR+=	ftp-proxy
 SUBDIR+=	pfctl
 SUBDIR+=	pflogd
+SUBDIR+=	pfs
 SUBDIR+=	tftp-proxy
 
 SUBDIR+=	man
Index: usr.sbin/pf/pfs/Makefile
===================================================================
RCS file: usr.sbin/pf/pfs/Makefile
diff -N usr.sbin/pf/pfs/Makefile
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ usr.sbin/pf/pfs/Makefile	6 Apr 2010 17:28:15 -0000
@@ -0,0 +1,18 @@
+
+SRCS=	pfs.c token.l parse.y
+PROG=	pfs
+CPPFLAGS+=-I${NETBSDSRCDIR}/sys/dist/pf
+CPPFLAGS+=-I${.CURDIR}
+WARNS=   4
+
+YHEADER=parse.h
+
+LDADD+= -ll -ly
+DPADD+= ${LIBL} ${LIBY}
+
+BINDIR=/sbin
+
+MAN= pfs.8
+
+
+.include <bsd.prog.mk>
Index: usr.sbin/pf/pfs/parse.y
===================================================================
RCS file: usr.sbin/pf/pfs/parse.y
diff -N usr.sbin/pf/pfs/parse.y
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ usr.sbin/pf/pfs/parse.y	6 Apr 2010 17:28:15 -0000
@@ -0,0 +1,503 @@
+/* $NetBSD: parse.y$ */
+
+/*-
+ * Copyright (c) 2010 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+%{
+#include <sys/cdefs.h>
+
+#ifndef lint
+__RCSID("$NetBSD: parse.y$");
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <inttypes.h>
+#include <errno.h>
+
+#include <net/if.h>
+#include <netinet/in.h>
+#include <net/pfvar.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <netinet/tcp_fsm.h>
+
+#include "parser.h"
+
+// XXX it is really correct ?
+extern const char * const tcpstates[];
+
+
+struct pfsync_state global_state;
+struct pfsync_state_peer *src_peer, *dst_peer;
+struct pfsync_state_peer current_peer;
+
+static void parse_init(void);
+static void add_state(void);
+static bool get_pfsync_host(const char*, struct pfsync_state_host*, sa_family_t*);
+static uint8_t retrieve_peer_state(const char*, int);
+static bool retrieve_seq(const char*, struct pfsync_state_peer*);
+static bool strtou32(const char*, uint32_t*);
+
+%}
+
+%union {
+	uintmax_t num;
+	char* str;
+}
+
+%token STATE 
+%token IN OUT
+%token ON PROTO
+%token FROM TO USING
+%token ID CID EXPIRE TIMEOUT
+%token SRC DST
+%token SEQ  MAX_WIN WSCALE MSS
+%token NOSCRUB SCRUB FLAGS TTL MODE
+%token NUMBER STRING
+
+%type <str> STRING
+%type <num> NUMBER
+%%
+
+states
+	: /* NOTHING */
+	| state states  { parse_init(); }
+	;
+
+state
+	: STATE direction iface proto addrs id cid expire timeout src_peer dst_peer { 
+			add_state();
+		}
+	;
+
+direction
+	: IN {
+		   global_state.direction = PF_IN;
+		   src_peer = &global_state.dst;
+		   dst_peer = &global_state.src;
+		}
+	| OUT {
+			 global_state.direction = PF_OUT; 
+			 src_peer = &global_state.src;
+			 dst_peer = &global_state.dst;
+		}
+	;
+
+iface
+	: ON STRING { 
+			strlcpy(global_state.ifname, $2, sizeof(global_state.ifname));
+			free($2);
+		}
+	;
+
+proto
+	: PROTO STRING { 
+			struct protoent *p;
+			p = getprotobyname($2);
+			if (p == NULL) 
+				yyfatal("Invalid protocol name");
+			global_state.proto = p->p_proto;
+			free($2);
+			}
+	| PROTO NUMBER { 
+			// check that the number may be valid proto ?
+			global_state.proto = $2;
+			}
+	;
+
+addrs
+	: FROM STRING TO STRING { 
+		get_pfsync_host($2, &global_state.lan, &global_state.af);
+		get_pfsync_host($4, &global_state.ext, &global_state.af);
+		memcpy(&global_state.gwy, &global_state.lan, sizeof(struct pfsync_state_host));
+		free($2);
+		free($4);
+		}
+	| FROM STRING TO STRING USING STRING {
+		get_pfsync_host($2, &global_state.lan, &global_state.af);
+		get_pfsync_host($4, &global_state.ext, &global_state.af);
+		get_pfsync_host($6, &global_state.gwy, &global_state.af);
+		free($2);
+		free($4);
+		free($6);
+		}
+	;
+
+id	
+	: ID NUMBER { 
+			if ( $2 > UINT64_MAX) 
+				yyfatal("id is too big");
+			uint64_t value = (uint64_t)$2;
+			memcpy(global_state.id, &value, sizeof(global_state.id));
+		}
+	;
+
+cid 
+	: CID NUMBER { 
+			if ( $2 > UINT32_MAX)
+				yyfatal("creator id is too big");
+			global_state.creatorid = (uint32_t)$2;
+		}
+	;
+
+expire 
+	: EXPIRE NUMBER { 
+			if ( $2 > UINT32_MAX)
+				yyfatal("expire time is too big");
+			global_state.expire = (uint32_t) $2;
+		}
+	;
+
+timeout
+	: TIMEOUT NUMBER {
+			if ($2 > UINT8_MAX)
+				yyfatal("timeout time is too big");
+			global_state.timeout = (uint8_t) $2;
+		}
+	;
+
+src_peer
+	: SRC peer {
+			memcpy(src_peer, &current_peer, sizeof(current_peer));
+		}
+	;
+
+dst_peer
+	: DST peer { 
+			memcpy(dst_peer, &current_peer, sizeof(current_peer));
+		}
+	;
+
+peer
+	: peer_state scrub 
+	| peer_state tcp_options scrub 
+	;
+
+peer_state
+	: STATE STRING {
+			current_peer.state = retrieve_peer_state($2, global_state.proto);
+			free($2);
+		}
+	| STATE	NUMBER { 
+		if ( $2 > UINT8_MAX)
+			yyfatal("peer state is too big");
+		current_peer.state = $2;
+		}
+	;
+
+tcp_options
+	: SEQ seqs MAX_WIN NUMBER WSCALE NUMBER {
+			if ($4 > UINT16_MAX)
+				yyfatal("max_win is too big");
+			current_peer.max_win = $4;
+			
+			if ($6 > UINT8_MAX)
+				yyfatal("wscale is too big");
+			current_peer.wscale = $6;
+		}
+	| SEQ seqs MAX_WIN NUMBER WSCALE NUMBER MSS NUMBER {
+			if ($4 > UINT16_MAX)
+				yyfatal("max_win is too big");
+			current_peer.max_win = $4;
+			
+			if ($6 > UINT8_MAX)
+				yyfatal("wscale is too big");
+			current_peer.wscale = $6;
+
+			if ($8 > UINT16_MAX)
+				yyfatal("mss is too big");
+			current_peer.mss = $8;
+		}
+	;
+
+seqs 
+	: STRING {
+		if (!retrieve_seq($1, &current_peer))
+			yyfatal("invalid seq number");
+
+		free($1);
+		}
+	;
+
+scrub
+	: NOSCRUB { current_peer.scrub.scrub_flag= 0;}
+	| SCRUB FLAGS NUMBER MODE NUMBER TTL NUMBER {
+			current_peer.scrub.scrub_flag= PFSYNC_SCRUB_FLAG_VALID;
+			if ($3 > UINT16_MAX)
+				yyfatal("scrub flags is too big");
+			current_peer.scrub.pfss_flags = $3;
+
+			if ($5 > UINT32_MAX)
+				yyfatal("scrub mode is too big");
+			current_peer.scrub.pfss_ts_mod = $5;
+
+			if ($7 > UINT8_MAX)
+				yyfatal("scrub ttl is too big");
+			current_peer.scrub.pfss_ttl = $7;
+		}
+	;
+
+
+%%
+
+static void
+parse_init(void)
+{
+	memset(&global_state, 0, sizeof(global_state));
+	memset(&current_peer, 0, sizeof(current_peer));
+	src_peer = NULL;
+	dst_peer = NULL;
+}
+
+static bool
+get_pfsync_host(const char* str, struct pfsync_state_host* host, sa_family_t* af)
+{
+	size_t count_colon, addr_len, port_len;
+	const char* p, *last_colon, *first_bracket, *last_bracket;
+	char buf[48];
+	char buf_port[6];
+
+	if (str == NULL || *str == '\0')
+		return false;
+
+	p = str;
+	last_colon = NULL;
+	count_colon = 0;
+
+	while (*p != '\0') {
+		if (*p == ':') {
+			count_colon++;
+			last_colon = p;
+		}
+		p++;
+	}
+
+	/*
+	 * If no colon, it is not an expected addr
+	 * If there are more than one colon, we guess that af = AF_INET6
+	 */
+
+	if (count_colon == 0)
+		return false;
+
+	if (count_colon == 1)
+		*af = AF_INET;
+	else
+		*af = AF_INET6;
+
+	/*
+	 * First bracket must be next character after last colon
+	 * Last bracket must be the last character
+	 * distance between both must be <= 7
+	 */ 
+	
+	if (*(last_colon+1) == '[')
+		first_bracket = last_colon + 1;
+	else
+		return false;
+
+	last_bracket = str + (strlen(str) - 1);
+	if (*last_bracket != ']')
+		return false;
+	
+	port_len = last_bracket - first_bracket;
+	if (last_bracket - first_bracket > 7)
+		return false;
+
+	memcpy(buf_port, first_bracket +1, port_len - 1);
+	buf_port[port_len-1]= '\0';
+	
+	addr_len = last_colon - str;
+	if (addr_len >= sizeof(buf))
+		return false;
+	memcpy(buf, str, addr_len);
+	buf[addr_len] = '\0';
+
+	if (inet_pton(*af, buf, &host->addr) != 1)
+		return false;
+
+	host->port = htons(atoi(buf_port));
+
+	return true;
+}
+
+static uint8_t
+retrieve_peer_state(const char* str, int proto)
+{
+	uint8_t i;
+
+	if (proto == IPPROTO_TCP) {
+		i = 0;
+		while (i < TCP_NSTATES) {
+			if (strcmp(str, tcpstates[i]) == 0)
+				return i;
+			i++;
+		}
+		yyfatal("Invalid peer state");
+
+	} else {
+		if (proto == IPPROTO_UDP) {
+			const char* mystates[] = PFUDPS_NAMES;
+			i = 0;
+
+			while (i < PFUDPS_NSTATES) {
+				if (strcmp(str, mystates[i]) == 0)
+					return i;
+				i++;
+			}
+
+			yyfatal("Invalid peer state");
+		} else {
+			const char *mystates[] = PFOTHERS_NAMES;
+			i = 0;
+
+			while (i < PFOTHERS_NSTATES) {
+				if (strcmp(str, mystates[i]) == 0)
+					return i;
+				i++;
+			}
+
+			yyfatal("Invalid peer state");
+		}
+	}
+     /*NOTREACHED*/
+	return 0;
+}
+
+static bool
+strtou32(const char* str, uint32_t* res)
+{
+	uintmax_t u;
+	errno = 0;
+	u = strtoumax(str, NULL, 10);
+	if (errno == ERANGE && u == UINTMAX_MAX)
+		return false;
+	if (u > UINT32_MAX)
+		return false;
+	*res = (uint32_t) u;
+	return true;
+}
+
+static bool
+retrieve_seq(const char* str, struct pfsync_state_peer* peer)
+{
+	const char* p, *p_colon, *p_comma;
+	char buf[100];
+	size_t size;
+
+	if (str == NULL || *str == '\0')
+		return false;
+
+	if (*str != '[' || *(str+(strlen(str) -1)) != ']')
+		return false;
+
+	p = str;
+	p_colon = NULL;
+	p_comma = NULL;
+	while (*p != '\0') {
+		if (*p == ':') {
+			if (p_colon !=NULL) 
+				return false;
+			else 
+				p_colon = p;
+		}
+
+		if (*p == ',') {
+			if (p_comma != NULL) 
+				return false;
+			else
+				p_comma = p;
+		}
+		p++;
+	}
+
+	size = p_colon - str;
+	if (size > sizeof(buf))
+		return false;
+	memcpy(buf, str+1, size-1);
+	buf[size-1] = '\0';
+
+	if (!strtou32(buf, &peer->seqlo))
+		return false;
+
+
+	if (p_comma == NULL) 
+		size = str + strlen(str) - 1 - p_colon;
+	else 
+		size = p_comma - p_colon;
+		
+	if (size > sizeof(buf))
+		return false;
+	memcpy(buf, p_colon+1, size -1);
+	buf[size-1] = '\0';
+
+	if (!strtou32(buf, &peer->seqhi))
+		return false;
+
+	if (p_comma == NULL) {
+		peer->seqdiff = 0;
+	} else {
+		size = str + strlen(str) - 1 - p_comma;
+		if (size > sizeof(buf))
+			return false;
+		memcpy(buf, p_comma +1, size -1);
+		buf[size-1] = '\0';
+
+		if (!strtou32(buf, &peer->seqdiff))
+			return false;
+	}
+		
+	return true;
+}
+
+static void
+add_state(void)
+{
+	int idx;
+
+	if (allocated == 0) {
+		allocated = 5;
+		states->ps_buf = malloc(allocated * sizeof(struct pfsync_state));
+		if (states->ps_buf == NULL)
+			yyfatal("Not enougth memory");
+	}
+
+	if (allocated == (states->ps_len / sizeof(struct pfsync_state))) {
+		void *buf;
+		allocated = allocated * 2 + 1; 
+		buf = realloc(states->ps_buf, allocated * sizeof(struct pfsync_state));
+		if (buf == NULL) {
+			free(states->ps_buf);
+			yyfatal("Not enougth memory");
+		}
+		states->ps_buf = buf;
+	}
+
+	idx = states->ps_len / sizeof(struct pfsync_state);
+	memcpy(&states->ps_states[idx], &global_state, sizeof(struct pfsync_state));
+	states->ps_len += sizeof(struct pfsync_state);
+}
+		
+
+
Index: usr.sbin/pf/pfs/parser.h
===================================================================
RCS file: usr.sbin/pf/pfs/parser.h
diff -N usr.sbin/pf/pfs/parser.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ usr.sbin/pf/pfs/parser.h	6 Apr 2010 17:28:15 -0000
@@ -0,0 +1,42 @@
+/* $NetBSD: parser.h$ */
+
+/*-
+ * Copyright (c) 2010 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+#ifndef _PARSER_H_
+#define _PARSER_H_
+
+int yylex(void);
+void yyerror(const char*);
+void yyfatal(const char*);
+int parse(FILE*, struct pfioc_states*);
+int yyparse(void);
+
+int lineno;
+
+struct pfioc_states* states;
+size_t allocated;
+
+#endif /* _PARSER_H_*/
Index: usr.sbin/pf/pfs/pfs.8
===================================================================
RCS file: usr.sbin/pf/pfs/pfs.8
diff -N usr.sbin/pf/pfs/pfs.8
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ usr.sbin/pf/pfs/pfs.8	6 Apr 2010 17:28:15 -0000
@@ -0,0 +1,75 @@
+.Dd July 21, 2009
+.Dt PFS 8
+.Os
+.Sh NAME
+.Nm pfs 
+.Nd saves and restores information for NAT and state tables.
+.Sh SYNOPSIS
+.Nm
+.Op Fl v
+.Fl l
+.Nm
+.Op Fl v
+.Fl u
+.Nm
+.Op Fl v
+.Op Fl b
+.Fl w
+.Ar filename
+.Nm
+.Op Fl v
+.Op Fl b
+.Fl r
+.Ar filename
+.Nm
+.Op Fl v
+.Op Fl b
+.Fl R
+.Ar filename
+.Nm
+.Op Fl v
+.Op Fl b
+.Fl W
+.Ar filename
+.Sh DESCRIPTION
+The
+.Nm
+command allows state information created for NAT entries and rules using
+.Pa keep state
+to be locked (modification prevented) and then saved to disk,
+allowing for the system to experience a reboot, followed by the restoration
+of that information, resulting in connections not being interrupted.
+.Sh OPTIONS
+.Bl -tag -width indent
+.It Fl b
+The information are read or stored using binary format. The default format is
+a readable ascii format, similar to 
+.Pa pfctl.conf 
+syntax.
+.It Fl v
+Provides a verbose description of what's being done.
+.It Fl u
+Unlock state tables in the kernel.
+.It Fl l
+Lock state tables in the kernel.
+.It Fl r
+Read information in from the specified file and load it into the
+kernel.  This requires the state tables to have already been locked
+and does not change the lock once complete.
+.It Fl w
+Write information out to the specified file and from the kernel.
+This requires the state tables to have already been locked
+and does not change the lock once complete.
+.It Fl R
+Restores  information in from the specified file and load it into the
+kernel.  The state tables are locked at the beginning of this operation and
+unlocked once complete.
+.It Fl W
+Write information out to the specified file and from the kernel.  The state
+tables are locked at the beginning of this operation and unlocked once
+complete.
+.El
+.Sh FILES
+/dev/pf
+.Sh SEE ALSO
+.Xr pf 4
Index: usr.sbin/pf/pfs/pfs.c
===================================================================
RCS file: usr.sbin/pf/pfs/pfs.c
diff -N usr.sbin/pf/pfs/pfs.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ usr.sbin/pf/pfs/pfs.c	6 Apr 2010 17:28:15 -0000
@@ -0,0 +1,575 @@
+/* $NetBSD: pfs.c$ */
+
+/*-
+ * Copyright (c) 2010 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+
+#ifndef lint
+__RCSID("$NetBSD: pfs.c$");
+#endif
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+
+#include <net/if.h>
+#include <netinet/in.h>
+#define TCPSTATES
+#include <netinet/tcp_fsm.h>
+#include <net/pfvar.h>
+#include <arpa/inet.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 <stdbool.h>
+#include <unistd.h>
+
+#include "parser.h"
+
+__dead static void usage(void);
+static int setlock(int, int, int);
+static int get_states(int, int, struct pfioc_states*);
+static int dump_states_binary(int, int, const char*);
+static int restore_states_binary(int, int, const char*);
+static int dump_states_ascii(int, int, const char*);
+static int restore_states_ascii(int, int, const char*);
+static char* print_host(const struct pfsync_state_host *h, sa_family_t, char*, size_t);
+static void print_peer(const struct pfsync_state_peer *peer, uint8_t, FILE*);
+static int print_states(int, int, FILE*);
+static void display_states(const struct pfioc_states*, int, FILE*);
+static int test_ascii_dump(int, const char*, const char*);
+
+static char pf_device[] = "/dev/pf";
+
+__dead static void
+usage(void) 
+{
+	fprintf(stderr, 
+			"usage : %s [-v] [-u | -l | -w <filename> | -r <filename> |\n"
+			"			[ -W <filename> | -R <filename> ]\n",
+			getprogname());
+	exit(EXIT_FAILURE);
+}
+
+/*
+ * The state table must be locked before calling this function
+ * Return the number of state in case of success, -1 in case of failure
+ * ps::ps_buf must be freed by user after use (in case of success)
+ */
+static int
+get_states(int fd, int verbose __unused, struct pfioc_states* ps)
+{
+	memset(ps, 0, sizeof(*ps));
+	ps->ps_len = 0;
+	char* inbuf;
+
+	// ask the kernel how much memory we need to allocate
+	if (ioctl(fd, DIOCGETSTATES, ps) == -1) {
+		err(EXIT_FAILURE, "DIOCGETSTATES");
+	}	
+
+	/* no state */
+	if (ps->ps_len == 0)
+		return 0;
+
+	inbuf = malloc(ps->ps_len);
+	if (inbuf == NULL)
+		err(EXIT_FAILURE, NULL);
+
+	ps->ps_buf = inbuf;
+
+	// really retrieve the different states
+	if (ioctl(fd, DIOCGETSTATES, ps) == -1) { 
+		free(ps->ps_buf);
+		err(EXIT_FAILURE, "DIOCGETSTATES");
+	}
+
+	return (ps->ps_len / sizeof(struct pfsync_state));
+}
+
+static int
+dump_states_binary(int fd, int verbose, const char* filename)
+{
+	int wfd; 
+	struct pfioc_states ps;
+	struct pfsync_state *p = NULL;
+	int nb_states;
+	int i;
+	int error = 0;
+	int errno_saved = 0;
+
+	wfd = open(filename, O_WRONLY|O_TRUNC|O_CREAT, 0600);
+	if (wfd == -1)
+		err(EXIT_FAILURE, "Cannot open %s", filename);
+
+	nb_states = get_states(fd, verbose, &ps);
+	if (nb_states <= 0) {
+		close(wfd);
+		return nb_states;
+	}
+
+	/*
+	 * In the file, write the number of states, then store the different states
+	 * When we will switch to text format, we probably don't care any more about the len
+	 */
+	if (write(wfd, &nb_states, sizeof(nb_states)) != sizeof(nb_states)) {
+		error = EXIT_FAILURE;
+		errno_saved = errno;
+		goto done;
+	}
+
+	p = ps.ps_states;
+	for (i = 0; i < nb_states; i++) {
+		if (write(wfd, &p[i], sizeof(*p)) != sizeof(*p)) {
+			error = EXIT_FAILURE; 
+			errno_saved = errno;
+			goto done;
+		}
+	}
+
+done:
+	free(p);
+	close(wfd);
+	// close can't modify errno
+	if (error) {
+		errno = errno_saved;
+		err(error, NULL);
+	}
+
+	return 0;
+}
+
+static int
+restore_states_binary(int fd, int verbose __unused, const char* filename)
+{
+	int rfd;
+	struct pfioc_states ps;
+	struct pfsync_state *p;
+	int nb_states;
+	int errno_saved = 0;
+	int i;
+
+	rfd = open(filename, O_RDONLY, 0600);
+	if (rfd == -1)
+		err(EXIT_FAILURE, "Cannot open %s", filename);
+
+	if (read(rfd, &nb_states, sizeof(nb_states)) != sizeof(nb_states)) {
+		errno_saved = errno;
+		close(rfd);
+		errno = errno_saved;
+		err(EXIT_FAILURE, NULL);
+	}
+
+	ps.ps_len = nb_states * sizeof(struct pfsync_state);
+	ps.ps_states = malloc(ps.ps_len);
+	if (ps.ps_states == NULL) {
+		errno_saved = errno;
+		close(rfd);
+		errno = errno_saved;
+		err(EXIT_FAILURE, NULL);
+	}
+
+	p = ps.ps_states;
+
+	for (i = 0; i < nb_states; i++) {
+		if (read(rfd, &p[i], sizeof(*p)) != sizeof(*p)) {
+			errno_saved = errno;
+			close(rfd);
+			free(ps.ps_states);
+			errno = errno_saved;
+			err(EXIT_FAILURE, NULL);
+		}
+	}
+
+	if (ioctl(fd, DIOCADDSTATES, &ps) == -1) {
+		errno_saved = errno;
+		close(rfd);
+		free(ps.ps_states);
+		errno = errno_saved;
+		err(EXIT_FAILURE, "DIOCADDSTATES");
+	}
+
+	free(ps.ps_states);
+	close(rfd);
+	return 0;
+}
+
+static char*
+print_host(const struct pfsync_state_host *h, sa_family_t af, char* buf, 
+		size_t size_buf)
+{
+	uint16_t port;
+	char	buf_addr[48];
+
+	port = ntohs(h->port);
+	if (inet_ntop(af, &(h->addr) , buf_addr, sizeof(buf_addr)) == NULL) {
+		strcpy(buf_addr, "?");
+	} 
+
+	snprintf(buf, size_buf, "%s:[%d]", buf_addr, port);
+	return buf;
+}
+
+static void
+print_peer(const struct pfsync_state_peer* peer, uint8_t proto, FILE* f)
+{
+	if (proto == IPPROTO_TCP) { 
+		if (peer->state < TCP_NSTATES)
+			fprintf(f, "state %s", tcpstates[peer->state]);
+
+		if (peer->seqdiff != 0) 
+			fprintf(f, " seq [%" PRIu32 ":%" PRIu32 ",%" PRIu32"]",
+					peer->seqlo, peer->seqhi, peer->seqdiff);
+		else 
+			fprintf(f, " seq [%" PRIu32 ":%" PRIu32 "]",
+					peer->seqlo, peer->seqhi);
+
+		if (peer->mss != 0) 
+			fprintf(f, " max_win %" PRIu16 " mss %" PRIu16 " wscale %" PRIu8, 
+					peer->max_win, peer->mss, peer->wscale);
+		else
+			fprintf(f, " max_win %" PRIu16 " wscale %" PRIu8, peer->max_win, 
+					peer->wscale);
+				
+	} else {
+		if (proto == IPPROTO_UDP) {
+			const char *mystates[] = PFUDPS_NAMES;
+			if (peer->state < PFUDPS_NSTATES)
+				fprintf(f, "state %s", mystates[peer->state]);
+		} else if (proto == IPPROTO_ICMP || proto == IPPROTO_ICMPV6) { 
+			fprintf(f, " state %" PRIu8, peer->state);
+		} else {
+			const char *mystates[] = PFOTHERS_NAMES;
+			if (peer->state < PFOTHERS_NSTATES)
+				fprintf(f, " state %s", mystates[peer->state]);
+		}
+	}
+
+	if (peer->scrub.scrub_flag == PFSYNC_SCRUB_FLAG_VALID) {
+		fprintf(f, " scrub flags %" PRIu16 "ttl %" PRIu8 "mod %"PRIu32,
+				peer->scrub.pfss_flags, peer->scrub.pfss_ttl, peer->scrub.pfss_ts_mod);
+	} else {
+		fprintf(f, " no-scrub");
+	}
+}
+
+static void 
+display_states(const struct pfioc_states *ps, int verbose __unused, FILE* f)
+{
+	struct pfsync_state *p = NULL;
+	struct pfsync_state_peer *src, *dst;
+	struct protoent *proto;
+	int nb_states;
+	int i;
+	uint64_t id;
+
+	p = ps->ps_states;
+	nb_states = ps->ps_len / sizeof(struct pfsync_state);
+
+	for (i = 0; i < nb_states; i++, p++) {
+		fprintf(f, "state %s ", p->direction == PF_OUT ? "out" : "in");
+		fprintf(f, "on %s ", p->ifname);
+
+		if ((proto = getprotobynumber(p->proto)) != NULL)
+			fprintf(f, "proto %s ", proto->p_name);
+		else
+			fprintf(f, "proto %u ", p->proto);
+
+
+		if (PF_ANEQ(&p->lan.addr, &p->gwy.addr, p->af) || 
+				(p->lan.port != p->gwy.port)) {
+					
+			char buf1[64], buf2[64], buf3[64];
+			fprintf(f, "from %s to %s using %s", 
+					print_host(&p->lan, p->af, buf1, sizeof(buf1)),
+					print_host(&p->ext, p->af, buf2, sizeof(buf2)),
+					print_host(&p->gwy, p->af, buf3, sizeof(buf3)));
+		} else {
+			char buf1[64], buf2[64];
+			fprintf(f, "from %s to %s", 
+					print_host(&p->lan, p->af, buf1, sizeof(buf1)),
+					print_host(&p->ext, p->af, buf2, sizeof(buf2)));
+		}
+
+		memcpy(&id, p->id, sizeof(p->id));
+		fprintf(f, " id %" PRIu64 " cid %" PRIu32 " expire %" PRIu32 " timeout %" PRIu8,
+				id , p->creatorid, p->expire, p->timeout);
+
+		if (p->direction == PF_OUT) {
+			src = &p->src;
+			dst = &p->dst;
+		} else {
+			src = &p->dst;
+			dst = &p->src;
+		}
+
+		fprintf(f, " src "); 
+		print_peer(src, p->proto, f);
+		fprintf(f, " dst ");
+		print_peer(dst, p->proto, f);
+
+		fprintf(f, "\n");
+	}
+}
+
+static int
+print_states(int fd, int verbose, FILE* f)
+{
+	struct pfioc_states ps;
+	int nb_states;
+
+	nb_states = get_states(fd, verbose, &ps);
+	if (nb_states <= 0) {
+		return nb_states;
+	}
+
+	display_states(&ps, verbose, f);
+					
+	free(ps.ps_states);
+	return 0;
+}
+
+static int
+dump_states_ascii(int fd, int verbose, const char* filename)
+{
+	FILE *f;
+
+	if (strcmp(filename, "-") == 0) {
+		f = stdout;
+	} else {
+		f = fopen(filename, "w");
+		if (f == NULL) 
+			err(EXIT_FAILURE, "Can't open %s\n", filename);
+	}
+
+	print_states(fd, verbose, f);
+
+	if (f != stdout)
+		fclose(f);
+
+	return 0;
+}
+
+static int
+restore_states_ascii(int fd, int verbose __unused, const char* filename)
+{
+	FILE *f;
+	struct pfioc_states ps;
+	int errno_saved;
+
+	f = fopen(filename, "r");
+	if (f == NULL)
+		err(EXIT_FAILURE, "Can't open %s\n", filename);
+
+	parse(f, &ps);
+
+	if (ioctl(fd, DIOCADDSTATES, &ps) == -1) {
+		errno_saved = errno;
+		fclose(f);
+		free(ps.ps_states);
+		errno = errno_saved;
+		err(EXIT_FAILURE, "DIOCADDSTATES");
+	}
+
+	free(ps.ps_states);
+	fclose(f);
+	return 0;
+}
+			
+static int
+setlock(int fd, int verbose, int lock)
+{
+	if (verbose)
+		printf("Turning lock %s\n", lock ? "on" : "off");
+
+	if (ioctl(fd, DIOCSETLCK, &lock) == -1) 
+		err(EXIT_FAILURE, "DIOCSETLCK");
+
+	return 0;
+}
+
+static int
+test_ascii_dump(int verbose, const char* file1, const char *file2)
+{
+	FILE *f1, *f2;
+	struct pfioc_states ps;
+	int errno_saved;
+	
+	f1 = fopen(file1, "r");
+	if (f1 == NULL)
+		err(EXIT_FAILURE, "Can't open %s\n", file1);
+
+
+	f2 = fopen(file2, "w");
+	if (f2 == NULL) {
+		errno_saved = errno;
+		fclose(f2);
+		errno = errno_saved;
+		err(EXIT_FAILURE, "Can't open %s\n", file2);
+	}
+
+	parse(f1, &ps);
+	display_states(&ps, verbose, f2);
+
+	free(ps.ps_states);
+	fclose(f1);
+	fclose(f2);
+
+	return 0;
+}
+
+int main(int argc, char *argv[])
+{
+	setprogname(argv[0]);
+
+	int lock = 0;
+	int set = 0;
+	int dump = 0;
+	int restore = 0;
+	int verbose = 0;
+	int test = 0;
+	bool binary = false;
+	char* filename = NULL;
+	char* filename2 = NULL;
+	int error = 0;
+	int fd;
+	int c;
+
+	while ((c = getopt(argc, argv, "ulvw:r:R:W:bt:o:")) != -1) 
+		switch (c) {
+		case 'u' : 
+			lock = 0;
+			set = 1;
+			break;
+
+		case 'l' :
+			lock = 1;
+			set = 1;
+			break;
+
+		case 'b':
+			binary = true;
+			break;
+
+		case 'r':
+			restore = 1;
+			filename = optarg;
+			break;
+
+		case 'v':
+			verbose=1;
+			break;
+
+		case 'w':
+			dump=1;
+			filename=optarg;
+			break;
+
+		case 'R':
+			restore = 1;
+			set = 1;
+			filename = optarg;
+			break;
+
+		case 'W':
+			dump = 1;
+			set = 1;
+			filename = optarg;
+			break;
+
+		case 't':
+			test=1;
+			filename = optarg;
+			break;
+
+		case 'o':
+			filename2 = optarg; 
+			break;
+
+		case '?' :
+		default:
+			usage();
+		}
+
+	if (set == 0 && dump == 0 && restore == 0 && test == 0) 
+		usage();
+
+	if (dump == 1 && restore == 1)
+		usage();
+
+	if (test == 1) {
+		if (filename2 == NULL) {
+			fprintf(stderr, "-o <file> is required when using -t\n");
+			err(EXIT_FAILURE, NULL);
+		}
+		error = test_ascii_dump(verbose, filename, filename2);
+	} else {
+		fd = open(pf_device, O_RDWR);
+		if (fd == -1) 
+			err(EXIT_FAILURE, "Cannot open %s", pf_device);
+
+		if (set != 0 && dump == 0 && restore == 0)
+			error = setlock(fd, verbose, lock);
+
+		if (dump) {
+			if (set) 
+				error = setlock(fd, verbose, 1);
+
+			if (binary) 
+				error = dump_states_binary(fd, verbose, filename);
+			else
+				error = dump_states_ascii(fd, verbose, filename);
+
+			if (set)
+				error = setlock(fd, verbose, 0);
+		}
+
+		if (restore) {
+			if (set) 
+				error = setlock(fd, verbose, 1);
+
+			if (binary) 
+				error = restore_states_binary(fd, verbose, filename);
+			else 
+				error = restore_states_ascii(fd, verbose, filename);
+
+			if (set)
+				error = setlock(fd, verbose, 0);
+		}
+
+		close(fd);
+	}
+
+	return error;
+}
Index: usr.sbin/pf/pfs/token.l
===================================================================
RCS file: usr.sbin/pf/pfs/token.l
diff -N usr.sbin/pf/pfs/token.l
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ usr.sbin/pf/pfs/token.l	6 Apr 2010 17:28:15 -0000
@@ -0,0 +1,130 @@
+/* $NetBSD: token.l$ */
+
+/*-
+ * Copyright (c) 2010 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+%{
+#include <sys/cdefs.h>
+
+#ifndef lint
+__RCSID("$NetBSD: token.l$");
+#endif
+
+#include <stdlib.h>
+#include <limits.h>
+#include <inttypes.h>
+#include <string.h>
+#include <errno.h>
+
+#include <net/if.h>
+#include <netinet/in.h>
+#include <net/pfvar.h>
+
+#include "parse.h"
+#include "parser.h"
+
+%}
+
+%option nounput 
+
+%%
+
+state		{ return STATE;}
+on			{ return ON;}
+out			{ return OUT;}
+in			{ return IN;}
+proto   	{ return PROTO;}
+from		{ return FROM;}
+to			{ return TO;}
+using   	{ return USING;}
+id			{ return ID;}
+cid			{ return CID;}
+expire  	{ return EXPIRE;}
+timeout     { return TIMEOUT;}
+src			{ return SRC;}
+dst			{ return DST;}
+seq			{ return SEQ;}
+max_win 	{ return MAX_WIN;}
+wscale		{ return WSCALE;}
+mss			{ return MSS;}
+no-scrub	{ return NOSCRUB;}
+scrub		{ return SCRUB;}
+flags		{ return FLAGS;}
+ttl			{ return TTL;}
+mode		{ return MODE;}
+[0-9]+		{ char *ep;
+			  errno = 0;
+			  yylval.num = strtoumax(yytext, &ep, 10);
+			  if (errno == ERANGE && yylval.num == UINTMAX_MAX)
+					yyfatal("Number out of range");
+			  return NUMBER;
+			}
+
+[A-Za-z0-9:\[][A-Za-z0-9\[\]_:%\.-]* { yylval.str = strdup(yytext);
+							    if (yylval.str == NULL)
+									yyfatal("Not enough memory");
+								return STRING;
+							  }
+
+
+\n			{ lineno ++; }
+
+%%
+
+
+void
+yyfatal(const char *s)
+{
+	yyerror(s);
+	exit(EXIT_FAILURE);
+}
+
+void
+yyerror(const char *s)
+{
+	printf("line %d: %s at [%s]\n", lineno, s, yytext);
+}
+
+
+int
+parse(FILE *fp, struct pfioc_states* s)
+{
+	yyin = fp;
+
+	lineno = 1;
+
+	states = s;
+	allocated = 0;
+	memset(s, 0, sizeof(*s));
+
+	if (yyparse()) {
+		printf("parse failed, line %d.\n", lineno);
+		return(-1);
+	}
+
+	return(0);
+}
Index: sys/dist/pf/net/pf.c
===================================================================
RCS file: /cvsroot/src/sys/dist/pf/net/pf.c,v
retrieving revision 1.61
diff -u -r1.61 pf.c
--- sys/dist/pf/net/pf.c	19 Jan 2010 22:08:00 -0000	1.61
+++ sys/dist/pf/net/pf.c	6 Apr 2010 17:28:17 -0000
@@ -257,6 +257,8 @@
 extern struct pool pfr_ktable_pl;
 extern struct pool pfr_kentry_pl;
 
+extern int pf_state_lock;
+
 struct pf_pool_limit pf_pool_limits[PF_LIMIT_MAX] = {
 	{ &pf_state_pl, PFSTATE_HIWAT },
 	{ &pf_src_tree_pl, PFSNODE_HIWAT },
@@ -267,6 +269,10 @@
 
 #define STATE_LOOKUP()							\
 	do {								\
+		if (pf_state_lock) {		    \
+			*state = NULL;				\
+			return (PF_DROP);			\
+		}								\
 		if (direction == PF_IN)					\
 			*state = pf_find_state(kif, &key, PF_EXT_GWY);	\
 		else							\
@@ -928,8 +934,9 @@
 		s = splsoftnet();
 
 		/* process a fraction of the state table every second */
-		pf_purge_expired_states(1 + (pf_status.states
-		    / pf_default_rule.timeout[PFTM_INTERVAL]));
+		if (! pf_state_lock)
+			pf_purge_expired_states(1 + (pf_status.states
+						/ pf_default_rule.timeout[PFTM_INTERVAL]));
 
 		/* purge other expired types every PFTM_INTERVAL seconds */
 		if (++nloops >= pf_default_rule.timeout[PFTM_INTERVAL]) {
@@ -3323,6 +3330,11 @@
 		    a, ruleset, pd);
 	}
 
+	if (r->keep_state && pf_state_lock) {
+		REASON_SET(&reason, PFRES_STATELOCKED);
+		return PF_DROP;
+	}
+
 	if ((r->action == PF_DROP) &&
 	    ((r->rule_flag & PFRULE_RETURNRST) ||
 	    (r->rule_flag & PFRULE_RETURNICMP) ||
Index: sys/dist/pf/net/pf_ioctl.c
===================================================================
RCS file: /cvsroot/src/sys/dist/pf/net/pf_ioctl.c,v
retrieving revision 1.37
diff -u -r1.37 pf_ioctl.c
--- sys/dist/pf/net/pf_ioctl.c	3 Oct 2009 00:37:02 -0000	1.37
+++ sys/dist/pf/net/pf_ioctl.c	6 Apr 2010 17:28:18 -0000
@@ -133,6 +133,8 @@
 void			 pf_state_import(struct pfsync_state *,
 			    struct pf_state_key *, struct pf_state *);
 
+static	int		pf_state_add(struct pfsync_state*);
+
 struct pf_rule		 pf_default_rule;
 #ifdef __NetBSD__
 krwlock_t		 pf_consistency_lock;
@@ -143,6 +145,8 @@
 static int		 pf_altq_running;
 #endif
 
+int		pf_state_lock = 0;
+
 #define	TAGID_MAX	 50000
 TAILQ_HEAD(pf_tags, pf_tagname)	pf_tags = TAILQ_HEAD_INITIALIZER(pf_tags),
 				pf_qids = TAILQ_HEAD_INITIALIZER(pf_qids);
@@ -1016,21 +1020,62 @@
 	/* copy to state */
 	memcpy(&s->id, &sp->id, sizeof(sp->id));
 	s->creatorid = sp->creatorid;
-	strlcpy(sp->ifname, s->kif->pfik_name, sizeof(sp->ifname));
 	pf_state_peer_from_pfsync(&sp->src, &s->src);
 	pf_state_peer_from_pfsync(&sp->dst, &s->dst);
 
 	s->rule.ptr = &pf_default_rule;
+	s->rule.ptr->states++;
 	s->nat_rule.ptr = NULL;
 	s->anchor.ptr = NULL;
 	s->rt_kif = NULL;
 	s->creation = time_second;
+	s->expire = time_second;
+	s->timeout = sp->timeout;
+	if (sp->expire > 0)
+		s->expire -= pf_default_rule.timeout[sp->timeout] - sp->expire;
 	s->pfsync_time = 0;
 	s->packets[0] = s->packets[1] = 0;
 	s->bytes[0] = s->bytes[1] = 0;
 }
 
 int
+pf_state_add(struct pfsync_state* sp)
+{
+	struct pf_state		*s;
+	struct pf_state_key	*sk;
+	struct pfi_kif		*kif;
+
+	if (sp->timeout >= PFTM_MAX &&
+			sp->timeout != PFTM_UNTIL_PACKET) {
+		return EINVAL;
+	}
+	s = pool_get(&pf_state_pl, PR_NOWAIT);
+	if (s == NULL) {
+		return ENOMEM;
+	}
+	bzero(s, sizeof(struct pf_state));
+	if ((sk = pf_alloc_state_key(s)) == NULL) {
+		pool_put(&pf_state_pl, s);
+		return ENOMEM;
+	}
+	pf_state_import(sp, sk, s);
+	kif = pfi_kif_get(sp->ifname);
+	if (kif == NULL) {
+		pool_put(&pf_state_pl, s);
+		pool_put(&pf_state_key_pl, sk);
+		return ENOENT;
+	}
+	if (pf_insert_state(kif, s)) {
+		pfi_kif_unref(kif, PFI_KIF_REF_NONE);
+		pool_put(&pf_state_pl, s);
+		return ENOMEM;
+	}
+
+	return 0;
+}
+
+
+int
 pf_setup_pfsync_matching(struct pf_ruleset *rs)
 {
 	MD5_CTX			 ctx;
@@ -1118,6 +1163,8 @@
 		case DIOCIGETIFACES:
 		case DIOCSETIFFLAG:
 		case DIOCCLRIFFLAG:
+		case DIOCSETLCK:
+		case DIOCADDSTATES:
 			break;
 		case DIOCRCLRTABLES:
 		case DIOCRADDTABLES:
@@ -1155,6 +1202,7 @@
 		case DIOCOSFPGET:
 		case DIOCGETSRCNODES:
 		case DIOCIGETIFACES:
+		case DIOCSETLCK:
 			break;
 		case DIOCRCLRTABLES:
 		case DIOCRADDTABLES:
@@ -1165,6 +1213,7 @@
 		case DIOCRDELADDRS:
 		case DIOCRSETADDRS:
 		case DIOCRSETTFLAGS:
+		case DIOCADDSTATES:
 			if (((struct pfioc_table *)addr)->pfrio_flags &
 			    PFR_FLAG_DUMMY) {
 				flags |= FWRITE; /* need write lock for dummy */
@@ -1763,42 +1812,39 @@
 	case DIOCADDSTATE: {
 		struct pfioc_state	*ps = (struct pfioc_state *)addr;
 		struct pfsync_state 	*sp = (struct pfsync_state *)ps->state;
-		struct pf_state		*s;
-		struct pf_state_key	*sk;
-		struct pfi_kif		*kif;
 
-		if (sp->timeout >= PFTM_MAX &&
-		    sp->timeout != PFTM_UNTIL_PACKET) {
-			error = EINVAL;
-			break;
-		}
-		s = pool_get(&pf_state_pl, PR_NOWAIT);
-		if (s == NULL) {
-			error = ENOMEM;
-			break;
-		}
-		bzero(s, sizeof(struct pf_state));
-		if ((sk = pf_alloc_state_key(s)) == NULL) {
-			error = ENOMEM;
-			break;
-		}
-		pf_state_import(sp, sk, s);
-		kif = pfi_kif_get(sp->ifname);
-		if (kif == NULL) {
-			pool_put(&pf_state_pl, s);
-			pool_put(&pf_state_key_pl, sk);
-			error = ENOENT;
-			break;
-		}
-		if (pf_insert_state(kif, s)) {
-			pfi_kif_unref(kif, PFI_KIF_REF_NONE);
-			pool_put(&pf_state_pl, s);
-			pool_put(&pf_state_key_pl, sk);
-			error = ENOMEM;
+		error = pf_state_add(sp);
+		break;
+	}
+
+	case DIOCADDSTATES: {
+		struct pfioc_states	*ps = (struct pfioc_states *)addr;
+		struct pfsync_state	*p = (struct pfsync_state *) ps->ps_states;
+		struct pfsync_state *pk;
+		int size = ps->ps_len;
+		int i = 0;
+		error = 0;
+
+		pk = malloc(sizeof(*pk), M_TEMP,M_WAITOK);
+
+		while (error == 0 && i < size) 
+		{
+			if (copyin(p, pk, sizeof(struct pfsync_state))) 
+			{
+				error = EFAULT;
+				free(pk, M_TEMP);
+			} else {
+				error = pf_state_add(pk);
+				i += sizeof(*p);
+				p++;
+			}
 		}
+
+		free(pk, M_TEMP);
 		break;
 	}
 
+
 	case DIOCGETSTATE: {
 		struct pfioc_state	*ps = (struct pfioc_state *)addr;
 		struct pf_state		*s;
@@ -3069,6 +3115,11 @@
 		break;
 	}
 
+	case DIOCSETLCK: {
+		pf_state_lock = *(uint32_t*)addr;
+		break;
+	}
+
 	default:
 		error = ENODEV;
 		break;
Index: sys/dist/pf/net/pfvar.h
===================================================================
RCS file: /cvsroot/src/sys/dist/pf/net/pfvar.h,v
retrieving revision 1.17
diff -u -r1.17 pfvar.h
--- sys/dist/pf/net/pfvar.h	28 Jul 2009 18:15:26 -0000	1.17
+++ sys/dist/pf/net/pfvar.h	6 Apr 2010 17:28:21 -0000
@@ -1123,7 +1123,8 @@
 #define PFRES_MAXSTATES	12		/* State limit */
 #define PFRES_SRCLIMIT	13		/* Source node/conn limit */
 #define PFRES_SYNPROXY	14		/* SYN proxy */
-#define PFRES_MAX	15		/* total+1 */
+#define PFRES_STATELOCKED 15    /* state table locked */
+#define PFRES_MAX	16		/* total+1 */
 
 #define PFRES_NAMES { \
 	"match", \
@@ -1141,6 +1142,7 @@
 	"state-limit", \
 	"src-limit", \
 	"synproxy", \
+	"state-locked", \
 	NULL \
 }
 
@@ -1493,7 +1495,8 @@
 #define DIOCADDRULE	_IOWR('D',  4, struct pfioc_rule)
 #define DIOCGETRULES	_IOWR('D',  6, struct pfioc_rule)
 #define DIOCGETRULE	_IOWR('D',  7, struct pfioc_rule)
-/* XXX cut 8 - 17 */
+#define DIOCSETLCK  _IOWR('D', 8, uint32_t)
+/* XXX cut 9 - 17 */
 #define DIOCCLRSTATES	_IOWR('D', 18, struct pfioc_state_kill)
 #define DIOCGETSTATE	_IOWR('D', 19, struct pfioc_state)
 #define DIOCSETSTATUSIF _IOWR('D', 20, struct pfioc_if)
@@ -1523,7 +1526,8 @@
 #define DIOCGETADDRS	_IOWR('D', 53, struct pfioc_pooladdr)
 #define DIOCGETADDR	_IOWR('D', 54, struct pfioc_pooladdr)
 #define DIOCCHANGEADDR	_IOWR('D', 55, struct pfioc_pooladdr)
-/* XXX cut 55 - 57 */
+#define DIOCADDSTATES   _IOWR('D', 56, struct pfioc_states)
+/* XXX cut 57 - 57 */
 #define	DIOCGETRULESETS	_IOWR('D', 58, struct pfioc_ruleset)
 #define	DIOCGETRULESET	_IOWR('D', 59, struct pfioc_ruleset)
 #define	DIOCRCLRTABLES	_IOWR('D', 60, struct pfioc_table)
