Index: usr.sbin/pfctl/parse.y
===================================================================
RCS file: /cvs/src/usr.sbin/pfctl/parse.y,v
retrieving revision 1.4
diff -u -p -r1.4 parse.y
--- usr.sbin/pfctl/parse.y	6 Apr 2008 21:12:40 -0000	1.4
+++ usr.sbin/pfctl/parse.y	7 Apr 2008 16:50:49 -0000
@@ -121,7 +121,8 @@ };
 
 enum	{ PF_STATE_OPT_MAX, PF_STATE_OPT_NOSYNC, PF_STATE_OPT_SRCTRACK,
 	    PF_STATE_OPT_MAX_SRC_STATES, PF_STATE_OPT_MAX_SRC_NODES,
-	    PF_STATE_OPT_STATELOCK, PF_STATE_OPT_TIMEOUT };
+	    PF_STATE_OPT_STATELOCK, PF_STATE_OPT_TIMEOUT,
+	    PF_STATE_OPT_PICKUPS };
 
 enum	{ PF_SRCTRACK_NONE, PF_SRCTRACK, PF_SRCTRACK_GLOBAL, PF_SRCTRACK_RULE };
 
@@ -132,6 +133,7 @@ 		u_int32_t	 max_states;
 		u_int32_t	 max_src_states;
 		u_int32_t	 max_src_nodes;
 		u_int8_t	 src_track;
+		u_int8_t	 pickup_mode;
 		u_int32_t	 statelock;
 		struct {
 			int		number;
@@ -410,6 +412,7 @@ %token	BITMASK RANDOM SOURCEHASH ROUNDRO
 %token	ALTQ CBQ PRIQ HFSC FAIRQ BANDWIDTH TBRSIZE LINKSHARE REALTIME UPPERLIMIT
 %token	QUEUE PRIORITY QLIMIT HOGS BUCKETS
 %token	LOAD
+%token  PICKUPS NOPICKUPS HASHONLY
 %token	STICKYADDRESS MAXSRCSTATES MAXSRCNODES SOURCETRACK GLOBAL RULE
 %token	TAGGED TAG IFBOUND GRBOUND FLOATING STATEPOLICY
 %token	<v.string>		STRING
@@ -1681,10 +1684,38 @@ 						YYERROR;
 					}
 					r.timeout[o->data.timeout.number] =
 					    o->data.timeout.seconds;
+					break;
+				case PF_STATE_OPT_PICKUPS:
+					r.pickup_mode = o->data.pickup_mode;
+					break;
 				}
 				o = o->next;
 				free(p);
 			}
+
+			/*
+			 * 'flags S/SA' by default on stateful rules if
+			 * the pickup mode is unspecified or disabled.
+			 *
+			 * If the pickup mode is enabled or hashonly we
+			 * want to create state regardless of the flags.
+			 */
+			if (!r.action && !r.flags && !r.flagset &&
+			    !$9.fragment && !($9.marker & FOM_FLAGS) &&
+			    r.keep_state) {
+				switch(r.pickup_mode) {
+				case PF_PICKUPS_UNSPECIFIED:
+				case PF_PICKUPS_DISABLED:
+					r.flags = parse_flags("S");
+					r.flagset = parse_flags("SA");
+					break;
+				case PF_PICKUPS_HASHONLY:
+				case PF_PICKUPS_ENABLED:
+					/* no flag restrictions */
+					break;
+				}
+			}
+
 			if (srctrack) {
 				if (srctrack == PF_SRCTRACK_GLOBAL &&
 				    r.max_src_nodes) {
@@ -2828,6 +2859,33 @@ 			$$->data.max_src_nodes = $2;
 			$$->next = NULL;
 			$$->tail = $$;
 		}
+		| PICKUPS 				{
+			$$ = calloc(1, sizeof(struct node_state_opt));
+			if ($$ == NULL)
+				err(1, "state_opt_item: calloc");
+			$$->type = PF_STATE_OPT_PICKUPS;
+			$$->data.pickup_mode = PF_PICKUPS_ENABLED;
+			$$->next = NULL;
+			$$->tail = $$;
+		}
+		| NOPICKUPS 				{
+			$$ = calloc(1, sizeof(struct node_state_opt));
+			if ($$ == NULL)
+				err(1, "state_opt_item: calloc");
+			$$->type = PF_STATE_OPT_PICKUPS;
+			$$->data.pickup_mode = PF_PICKUPS_DISABLED;
+			$$->next = NULL;
+			$$->tail = $$;
+		}
+		| HASHONLY 				{
+			$$ = calloc(1, sizeof(struct node_state_opt));
+			if ($$ == NULL)
+				err(1, "state_opt_item: calloc");
+			$$->type = PF_STATE_OPT_PICKUPS;
+			$$->data.pickup_mode = PF_PICKUPS_HASHONLY;
+			$$->next = NULL;
+			$$->tail = $$;
+		}
 		| sourcetrack {
 			$$ = calloc(1, sizeof(struct node_state_opt));
 			if ($$ == NULL)
@@ -4425,6 +4483,7 @@ 		{ "from",		FROM},
 		{ "global",		GLOBAL},
 		{ "group",		GROUP},
 		{ "group-bound",	GRBOUND},
+		{ "hash-only",		HASHONLY},
 		{ "hfsc",		HFSC},
 		{ "hogs",		HOGS},
 		{ "hostid",		HOSTID},
@@ -4452,6 +4511,7 @@ 		{ "nat",		NAT},
 		{ "nat-anchor",		NATANCHOR},
 		{ "no",			NO},
 		{ "no-df",		NODF},
+		{ "no-pickups",		NOPICKUPS},
 		{ "no-route",		NOROUTE},
 		{ "no-sync",		NOSYNC},
 		{ "on",			ON},
@@ -4459,6 +4519,7 @@ 		{ "optimization",	OPTIMIZATION},
 		{ "os",			OS},
 		{ "out",		OUT},
 		{ "pass",		PASS},
+		{ "pickups",		PICKUPS},
 		{ "port",		PORT},
 		{ "priority",		PRIORITY},
 		{ "priq",		PRIQ},
Index: usr.sbin/pfctl/pf.conf.5
===================================================================
RCS file: /cvs/src/usr.sbin/pfctl/pf.conf.5,v
retrieving revision 1.12
diff -u -p -r1.12 pf.conf.5
--- usr.sbin/pfctl/pf.conf.5	6 Apr 2008 21:12:40 -0000	1.12
+++ usr.sbin/pfctl/pf.conf.5	7 Apr 2008 17:33:53 -0000
@@ -718,6 +718,11 @@ .Ar fairq
 to function properly,
 .Ar keep state
 must be enabled on most of the rule sets that route packets to the queue.
+Any rules for which keep state is not enabled are added to the end of the
+queue.  If you do not wish keep state to do TCP sequence space checks use
+.Ar keep state ( no-pickups )
+or
+.Ar keep state ( hash-only ) .
 .Pp
 Packet selection operates as follows:
 The queues are scanned from highest priority to lowest priority.
@@ -1966,7 +1971,35 @@ have state table entries.
 .It Ar max-src-states
 Limits the maximum number of simultaneous state entries that a single
 source address can create with this rule.
+.It Ar pickups
+Specify that mid-stream pickups are to be allowed.  The default
+is to NOT allow mid-stream pickups and implies flags S/SA for TCP
+connections.   If pickups are enabled, flags S/SA are not implied
+for TCP connections and state can be created for any packet.
+.Pp
+The implied flags parameters need not be specified in either case
+unless you explicitly wish to override them, which also allows
+you to roll-up several protocols into a single rule.
+.Pp
+Certain validations are disabled when mid-stream pickups occur.
+For example, the window scaling options are not known for
+TCP pickups and sequence space comparisons must be disabled.
+.Pp
+This does not effect state representing fully quantified
+connections (for which the SYN/SYN-ACK passed through the routing
+engine).  Those connections continue to be fully validated.
+.It Ar hash-only
+Specify that mid-stream pickups are to be allowed, but unconditionally
+disables sequence space checks even if full state is available.
+.It Ar no-pickups
+Specify that mid-stream pickups are not to be allowed.  This is the
+default and this keyword does not normally need to be specified.
+However, if you are concerned about rule set portability then 
+specifying this keyword will at least result in an error from pfctl
+if it doesn't understand the feature.   TCP flags of S/SA are implied
+and do not need to explicitly specified.
 .El
+.Pp
 For a list of all valid timeout names, see
 .Sx OPTIONS
 above.
@@ -2501,6 +2534,24 @@ # a viral worm. Alternately we could lim
 block in on $ext_if proto tcp from any os {"Windows 95", "Windows 98"} \e
       to any port smtp
 
+# Using the pickup options to keep/modulate/synproxy state
+#
+# no-pickups	(default) Do not allow connections to be picked up in the
+#		middle.  Implies flags S/SA (the 'no-pickups' option need
+#		not be specified, it is the default).
+#
+# pickups	Allow connections to be picked up in the middle, even if
+#		no window scaling information is known.  Such connections
+#		will disable sequence space checks.  Implies no flag
+#		restrictions.
+#		
+# hash-only	Do not fail packets on sequence space checks.  Implies no
+#		flag restrictions.
+
+pass in on $ext_if proto tcp ... keep state (no-pickups)
+pass in on $ext_if proto tcp ... keep state (pickups)
+pass in on $ext_if proto tcp ... keep state (hash-only)
+
 # Packet Tagging
 
 # three interfaces: $int_if, $ext_if, and $wifi_if (wireless). NAT is
@@ -2678,7 +2729,8 @@ state-opts     = state-opt [ [ "," ] sta
 state-opt      = ( "max" number | "no-sync" | timeout |
                  "source-track" [ ( "rule" | "global" ) ] |
 		 "max-src-nodes" number | "max-src-states" number |
-                 "if-bound" | "group-bound" | "floating" )
+                 "if-bound" | "group-bound" | "floating" |
+		 "pickups" | "no-pickups" | "hash-only" )
 
 fragmentation  = [ "fragment reassemble" | "fragment crop" |
                  "fragment drop-ovl" ]
Index: usr.sbin/pfctl/pf_print_state.c
===================================================================
RCS file: /cvs/src/usr.sbin/pfctl/pf_print_state.c,v
retrieving revision 1.2
diff -u -p -r1.2 pf_print_state.c
--- usr.sbin/pfctl/pf_print_state.c	11 Feb 2005 22:31:45 -0000	1.2
+++ usr.sbin/pfctl/pf_print_state.c	7 Apr 2008 21:07:02 -0000
@@ -286,8 +286,19 @@ 			printf(", sticky-address");
 		printf("\n");
 	}
 	if (opts & PF_OPT_VERBOSE2) {
-		printf("   id: %016llx creatorid: %08x\n",
+		printf("   id: %016llx creatorid: %08x",
 		    be64toh(s->id), ntohl(s->creatorid));
+
+		printf(" synchronization: ");
+		if ((s->sync_flags & PFSTATE_GOT_SYN_MASK) ==
+		    PFSTATE_GOT_SYN_MASK) {
+			printf("good");
+		} else if (s->sync_flags & PFSTATE_GOT_SYN_MASK) {
+			printf("incomplete");
+		} else {
+			printf("indeterminate");
+		}
+		printf("\n");
 	}
 }
 
Index: usr.sbin/pfctl/pfctl_parser.c
===================================================================
RCS file: /cvs/src/usr.sbin/pfctl/pfctl_parser.c,v
retrieving revision 1.2
diff -u -p -r1.2 pfctl_parser.c
--- usr.sbin/pfctl/pfctl_parser.c	6 Apr 2008 21:12:40 -0000	1.2
+++ usr.sbin/pfctl/pfctl_parser.c	7 Apr 2008 15:12:08 -0000
@@ -946,9 +946,9 @@ 	printf("\n");
 }
 
 int
-parse_flags(char *s)
+parse_flags(const char *s)
 {
-	char		*p, *q;
+	const char	*p, *q;
 	u_int8_t	 f = 0;
 
 	for (p = s; *p; p++) {
Index: usr.sbin/pfctl/pfctl_parser.h
===================================================================
RCS file: /cvs/src/usr.sbin/pfctl/pfctl_parser.h,v
retrieving revision 1.2
diff -u -p -r1.2 pfctl_parser.h
--- usr.sbin/pfctl/pfctl_parser.h	6 Apr 2008 18:58:14 -0000	1.2
+++ usr.sbin/pfctl/pfctl_parser.h	7 Apr 2008 15:11:53 -0000
@@ -204,7 +204,7 @@ int	pfctl_set_hostid(struct pfctl *, u_i
 int	pfctl_set_debug(struct pfctl *, char *);
 
 int	parse_rules(FILE *, struct pfctl *);
-int	parse_flags(char *);
+int	parse_flags(const char *);
 int	pfctl_load_anchors(int, int, struct pfr_buffer *);
 
 void	print_pool(struct pf_pool *, u_int16_t, u_int16_t, sa_family_t, int);
Index: sys/net/pf/if_pfsync.c
===================================================================
RCS file: /cvs/src/sys/net/pf/if_pfsync.c,v
retrieving revision 1.6
diff -u -p -r1.6 if_pfsync.c
--- sys/net/pf/if_pfsync.c	22 Dec 2006 23:44:57 -0000	1.6
+++ sys/net/pf/if_pfsync.c	7 Apr 2008 00:45:50 -0000
@@ -225,6 +225,7 @@ 	pf_state_peer_ntoh(&sp->src, &st->src);
 	pf_state_peer_ntoh(&sp->dst, &st->dst);
 
 	bcopy(&sp->rt_addr, &st->rt_addr, sizeof(st->rt_addr));
+	st->hash = pf_state_hash(st);
 	st->creation = ntohl(sp->creation) + time_second;
 	st->expire = ntohl(sp->expire) + time_second;
 
@@ -932,6 +933,7 @@ 		pf_state_host_hton(&st->ext, &sp->ext)
 
 		bcopy(&st->rt_addr, &sp->rt_addr, sizeof(sp->rt_addr));
 
+		sp->hash = pf_state_hash(sp);
 		sp->creation = htonl(secs - st->creation);
 		sp->packets[0] = htonl(st->packets[0]);
 		sp->packets[1] = htonl(st->packets[1]);
Index: sys/net/pf/pf.c
===================================================================
RCS file: /cvs/src/sys/net/pf/pf.c,v
retrieving revision 1.17
diff -u -p -r1.17 pf.c
--- sys/net/pf/pf.c	7 Apr 2008 00:43:44 -0000	1.17
+++ sys/net/pf/pf.c	7 Apr 2008 21:24:27 -0000
@@ -322,15 +322,16 @@ 	}
 	return (0);
 }
 
-static
-int
+u_int32_t
 pf_state_hash(struct pf_state *s)
 {
-	int hv = (intptr_t)s / sizeof(*s);
+	u_int32_t hv = (intptr_t)s / sizeof(*s);
 
 	hv ^= crc32(&s->lan, sizeof(s->lan));
 	hv ^= crc32(&s->gwy, sizeof(s->gwy));
 	hv ^= crc32(&s->ext, sizeof(s->ext));
+	if (hv == 0)	/* disallow 0 */
+		hv = 1;
 	return(hv);
 }
 
@@ -2724,8 +2725,11 @@ 				s->gwy.port = s->lan.port;
 			}
 		}
 
+		s->hash = pf_state_hash(s);
 		s->src.seqlo = ntohl(th->th_seq);
 		s->src.seqhi = s->src.seqlo + len + 1;
+		s->pickup_mode = r->pickup_mode;
+
 		if ((th->th_flags & (TH_SYN|TH_ACK)) == TH_SYN &&
 		    r->keep_state == PF_STATE_MODULATE) {
 			/* Generate sequence number modulator */
@@ -2740,6 +2744,7 @@ 		s->src.max_win = MAX(ntohs(th->th_win)
 		if (th->th_flags & TH_SYN) {
 			s->src.seqhi++;
 			s->src.wscale = pf_get_wscale(m, off, th->th_off, af);
+			s->sync_flags |= PFSTATE_GOT_SYN1;
 		} else if (s->src.wscale & PF_WSCALE_MASK) {
 			/* Remove scale factor from initial window */
 			u_int win = s->src.max_win;
@@ -3053,6 +3058,7 @@ 				PF_ACPY(&s->gwy.addr, &s->lan.addr, 
 				s->gwy.port = s->lan.port;
 			}
 		}
+		s->hash = pf_state_hash(s);
 		s->src.state = PFUDPS_SINGLE;
 		s->dst.state = PFUDPS_NO_TRAFFIC;
 		s->creation = time_second;
@@ -3328,6 +3334,7 @@ 			else
 				PF_ACPY(&s->gwy.addr, &s->lan.addr, af);
 			s->gwy.port = icmpid;
 		}
+		s->hash = pf_state_hash(s);
 		s->creation = time_second;
 		s->expire = time_second;
 		s->timeout = PFTM_ICMP_FIRST_PACKET;
@@ -3582,6 +3589,7 @@ 				PF_ACPY(&s->gwy.addr, &pd->baddr, af
 			else
 				PF_ACPY(&s->gwy.addr, &s->lan.addr, af);
 		}
+		s->hash = pf_state_hash(s);
 		s->src.state = PFOTHERS_SINGLE;
 		s->dst.state = PFOTHERS_NO_TRAFFIC;
 		s->creation = time_second;
@@ -3638,9 +3646,17 @@ 		else if (PF_MISMATCHAW(&r->dst.addr, p
 			r = r->skip[PF_SKIP_DST_ADDR].ptr;
 		else if (r->tos && !(r->tos & pd->tos))
 			r = TAILQ_NEXT(r, entries);
-		else if (r->src.port_op || r->dst.port_op ||
-		    r->flagset || r->type || r->code ||
-		    r->os_fingerprint != PF_OSFP_ANY)
+		else if (r->os_fingerprint != PF_OSFP_ANY)
+			r = TAILQ_NEXT(r, entries);
+		else if (pd->proto == IPPROTO_UDP &&
+		    (r->src.port_op || r->dst.port_op))
+			r = TAILQ_NEXT(r, entries);
+		else if (pd->proto == IPPROTO_TCP &&
+		    (r->src.port_op || r->dst.port_op || r->flagset))
+			r = TAILQ_NEXT(r, entries);
+		else if ((pd->proto == IPPROTO_ICMP ||
+		    pd->proto == IPPROTO_ICMPV6) &&
+		    (r->type || r->code))
 			r = TAILQ_NEXT(r, entries);
 		else if (r->prob && r->prob <= karc4random())
 			r = TAILQ_NEXT(r, entries);
@@ -3804,8 +3820,10 @@ 	 */
 
 	seq = ntohl(th->th_seq);
 	if (src->seqlo == 0) {
-		/* First packet from this end. Set its state */
-
+		/*
+		 * First packet from this end.  The other end has already set
+		 * the seqlo field.  Set its state.
+		 */
 		if ((pd->flags & PFDESC_TCP_NORM || dst->scrub) &&
 		    src->scrub == NULL) {
 			if (pf_normalize_tcp_init(m, off, pd, th, src, dst)) {
@@ -3830,6 +3848,7 @@ 
 		end = seq + pd->p_len;
 		if (th->th_flags & TH_SYN) {
 			end++;
+			(*state)->sync_flags |= PFSTATE_GOT_SYN2;
 			if (dst->wscale & PF_WSCALE_FLAG) {
 				src->wscale = pf_get_wscale(m, off, th->th_off,
 				    pd->af);
@@ -3906,6 +3925,7 @@ 
 	ackskew = dst->seqlo - ack;
 
 #define MAXACKWINDOW (0xffff + 1500)	/* 1500 is an arbitrary fudge factor */
+
 	if (SEQ_GEQ(src->seqhi, end) &&
 	    /* Last octet inside other's window space */
 	    SEQ_GEQ(seq, src->seqlo - (dst->max_win << dws)) &&
@@ -4022,7 +4042,25 @@ 			src->state = dst->state = TCPS_TIME_W
 
 		/* Fall through to PASS packet */
 
-	} else {
+	} else if ((*state)->pickup_mode == PF_PICKUPS_HASHONLY ||
+		    ((*state)->pickup_mode == PF_PICKUPS_ENABLED &&
+		     ((*state)->sync_flags & PFSTATE_GOT_SYN_MASK) !=
+		      PFSTATE_GOT_SYN_MASK)) {
+		/*
+		 * If pickup mode is hash only, do not fail on sequence checks.
+		 *
+		 * If pickup mode is enabled and we did not see the SYN in
+		 * both direction, do not fail on sequence checks because
+		 * we do not have complete information on window scale.
+		 *
+		 * Adjust expiration and fall through to PASS packet.
+		 * XXX Add a FIN check to reduce timeout?
+		 */
+		(*state)->expire = time_second;
+	} else  {
+		/*
+		 * Failure processing
+		 */
 		if ((*state)->dst.state == TCPS_SYN_SENT &&
 		    (*state)->src.state == TCPS_SYN_SENT) {
 			/* Send RST for state mismatches during handshake */
@@ -5483,8 +5521,9 @@ 			m->m_pkthdr.altq_qid = r->pqid;
 		else
 			m->m_pkthdr.altq_qid = r->qid;
 		if (s) {
+			KKASSERT(s->hash != 0);
 			m->m_pkthdr.fw_flags |= ALTQ_MBUF_STATE_HASHED;
-			m->m_pkthdr.altq_state_hash = pf_state_hash(s);
+			m->m_pkthdr.altq_state_hash = s->hash;
 		}
 		m->m_pkthdr.ecn_af = AF_INET;
 		m->m_pkthdr.header = h;
@@ -5797,8 +5836,9 @@ 			m->m_pkthdr.altq_qid = r->pqid;
 		else
 			m->m_pkthdr.altq_qid = r->qid;
 		if (s) {
+			KKASSERT(s->hash != 0);
 			m->m_pkthdr.fw_flags |= ALTQ_MBUF_STATE_HASHED;
-			m->m_pkthdr.altq_state_hash = pf_state_hash(s);
+			m->m_pkthdr.altq_state_hash = s->hash;
 		}
 		m->m_pkthdr.ecn_af = AF_INET6;
 		m->m_pkthdr.header = h;
Index: sys/net/pf/pf_ioctl.c
===================================================================
RCS file: /cvs/src/sys/net/pf/pf_ioctl.c,v
retrieving revision 1.13
diff -u -p -r1.13 pf_ioctl.c
--- sys/net/pf/pf_ioctl.c	5 Jan 2008 14:02:38 -0000	1.13
+++ sys/net/pf/pf_ioctl.c	7 Apr 2008 00:50:15 -0000
@@ -1494,6 +1494,7 @@ 		state->creation = time_second;
 		state->pfsync_time = 0;
 		state->packets[0] = state->packets[1] = 0;
 		state->bytes[0] = state->bytes[1] = 0;
+		state->hash = pf_state_hash(state);
 
 		if (pf_insert_state(kif, state)) {
 			pfi_maybe_destroy(kif);
Index: sys/net/pf/pfvar.h
===================================================================
RCS file: /cvs/src/sys/net/pf/pfvar.h,v
retrieving revision 1.7
diff -u -p -r1.7 pfvar.h
--- sys/net/pf/pfvar.h	6 Apr 2008 21:12:42 -0000	1.7
+++ sys/net/pf/pfvar.h	7 Apr 2008 21:02:53 -0000
@@ -585,6 +585,12 @@ 	u_int8_t		 allow_opts;
 	u_int8_t		 rt;
 	u_int8_t		 return_ttl;
 	u_int8_t		 tos;
+#define PF_PICKUPS_UNSPECIFIED	0
+#define PF_PICKUPS_DISABLED	1
+#define PF_PICKUPS_HASHONLY	2
+#define PF_PICKUPS_ENABLED	3
+	u_int8_t		 pickup_mode;
+	u_int8_t		 unused01;	/* available for use */
 };
 
 /* rule flags */
@@ -678,6 +684,7 @@ 	struct pf_addr	 rt_addr;
 	struct pfi_kif	*rt_kif;
 	struct pf_src_node	*src_node;
 	struct pf_src_node	*nat_src_node;
+	u_int32_t	 hash;
 	u_int32_t	 creation;
 	u_int32_t	 expire;
 	u_int32_t	 pfsync_time;
@@ -693,9 +700,14 @@ 	u_int8_t	 timeout;
 	u_int8_t	 sync_flags;
 #define	PFSTATE_NOSYNC	 0x01
 #define	PFSTATE_FROMSYNC 0x02
+#define PFSTATE_GOT_SYN1 0x04	/* got SYN in one direction */
+#define PFSTATE_GOT_SYN2 0x08	/* got SYN in the other direction */
+	u_int8_t	 pickup_mode;
 	u_int8_t	 pad;
 };
 
+#define PFSTATE_GOT_SYN_MASK	(PFSTATE_GOT_SYN1|PFSTATE_GOT_SYN2)
+
 TAILQ_HEAD(pf_rulequeue, pf_rule);
 
 struct pf_anchor;
@@ -1398,6 +1410,7 @@ extern int			 pf_insert_src_node(struct 
 				    struct pf_rule *, struct pf_addr *,
 				    sa_family_t);
 void				 pf_src_tree_remove_state(struct pf_state *);
+u_int32_t			 pf_state_hash(struct pf_state *s);
 extern struct pf_state		*pf_find_state_byid(struct pf_state *);
 extern struct pf_state		*pf_find_state_all(struct pf_state *key,
 				    u_int8_t tree, int *more);
