? 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)