Skip site navigation (1)Skip section navigation (2)
Date:      Thu, 20 Aug 2009 00:39:27 -0700
From:      Xin LI <delphij@delphij.net>
To:        "freebsd-net@freebsd.org" <freebsd-net@freebsd.org>
Subject:   (just for fun) port of OpenBSD pf's sloppy mode
Message-ID:  <4A8CFDAF.1000309@delphij.net>

next in thread | raw e-mail | index | archive | help
This is a multi-part message in MIME format.
--------------000402020406030203090005
Content-Type: text/plain; charset=ISO-8859-1
Content-Transfer-Encoding: 7bit

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Hi,

Since there is effort undergoing to port a newer pf version to FreeBSD,
I think this work would not be useful for inclusion in -CURRENT.
However, I'd like to share it here as someone may find it useful before
the new pf code hits the tree.  The patch can also be downloaded from my
website:

	http://www.delphij.net/pf-sloppy.diff

About this patch:

When pf(4) is operating in a manner that not all packet would went
through it, specifically, when being used in a DSR ("Direct Server
Return") network, the strict TCP state tracking would prevent some
packets from being able to pass through.  This can exhibit as, when you
upload files, the connection would stall at ~60KB (may differ if you
have special TCP setting), or stalled connections.

With this change, pf.conf would support a new syntax, i.e. "(sloppy)" as
state flag, e.g.:

pass in quick on em0 route-to { (em1 $server1), (em1 $server2) }
round-robin proto tcp from any to $ext_ip port 80 keep state (sloppy)

When enabled, the "sloppy" TCP FSM would be activated, which loosens the
state check.  When using this option, the backend server has to use its
own mechanism to prevent ICMP teardown attack and/or insertion attacks,
so please use caution and limit the use in cases where pf(4) won't see
some packets in the connection.

Cheers,
- --
Xin LI <delphij@delphij.net>	http://www.delphij.net/
FreeBSD - The Power to Serve!
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v2.0.12 (FreeBSD)

iEYEARECAAYFAkqM/a8ACgkQi+vbBBjt66BRSACfQaOY3gHdEhjhGO5bz1zYhdud
NFMAmgLaVnzbBdA4ofj5helYkDtdqTds
=N0nJ
-----END PGP SIGNATURE-----

--------------000402020406030203090005
Content-Type: text/plain;
 name="pf-sloppy.diff"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
 filename="pf-sloppy.diff"

Index: sys/contrib/pf/net/if_pfsync.c
===================================================================
--- sys/contrib/pf/net/if_pfsync.c	(revision 196398)
+++ sys/contrib/pf/net/if_pfsync.c	(working copy)
@@ -465,7 +465,7 @@ pfsync_insert_net_state(struct pfsync_state *sp, u
 	st->direction = sp->direction;
 	st->log = sp->log;
 	st->timeout = sp->timeout;
-	st->allow_opts = sp->allow_opts;
+	st->state_flags = sp->state_flags;
 
 	bcopy(sp->id, &st->id, sizeof(st->id));
 	st->creatorid = sp->creatorid;
@@ -1578,7 +1578,7 @@ pfsync_pack_state(u_int8_t action, struct pf_state
 		sp->proto = st->proto;
 		sp->direction = st->direction;
 		sp->log = st->log;
-		sp->allow_opts = st->allow_opts;
+		sp->state_flags = st->state_flags;
 		sp->timeout = st->timeout;
 
 		if (flags & PFSYNC_FLAG_STALE)
Index: sys/contrib/pf/net/pfvar.h
===================================================================
--- sys/contrib/pf/net/pfvar.h	(revision 196398)
+++ sys/contrib/pf/net/pfvar.h	(working copy)
@@ -700,6 +700,7 @@ struct pf_rule {
 
 /* rule flags again */
 #define PFRULE_IFBOUND		0x00010000	/* if-bound */
+#define PFRULE_STATESLOPPY	0x00020000	/* sloppy state tracking */
 
 #define PFSTATE_HIWAT		10000	/* default state table size */
 #define PFSTATE_ADAPT_START	6000	/* default adaptive timeout start */
@@ -800,7 +801,9 @@ struct pf_state {
 	u_int8_t	 pad;
 #endif
 	u_int8_t	 log;
-	u_int8_t	 allow_opts;
+	u_int8_t	 state_flags;
+#define	PFSTATE_ALLOWOPTS	0x01
+#define	PFSTATE_SLOPPY		0x02
 	u_int8_t	 timeout;
 	u_int8_t	 sync_flags;
 #define	PFSTATE_NOSYNC	 0x01
Index: sys/contrib/pf/net/if_pfsync.h
===================================================================
--- sys/contrib/pf/net/if_pfsync.h	(revision 196398)
+++ sys/contrib/pf/net/if_pfsync.h	(working copy)
@@ -80,7 +80,7 @@ struct pfsync_state {
 	u_int8_t	 proto;
 	u_int8_t	 direction;
 	u_int8_t	 log;
-	u_int8_t	 allow_opts;
+	u_int8_t	 state_flags;
 	u_int8_t	 timeout;
 	u_int8_t	 sync_flags;
 	u_int8_t	 updates;
Index: sys/contrib/pf/net/pf.c
===================================================================
--- sys/contrib/pf/net/pf.c	(revision 196398)
+++ sys/contrib/pf/net/pf.c	(working copy)
@@ -253,6 +253,13 @@ int			 pf_test_fragment(struct pf_rule **, int,
 			    struct pfi_kif *, struct mbuf *, void *,
 			    struct pf_pdesc *, struct pf_rule **,
 			    struct pf_ruleset **);
+int			 pf_tcp_track_full(struct pf_state_peer *,
+			    struct pf_state_peer *, struct pf_state **,
+			    struct pfi_kif *, struct mbuf *, int,
+			    struct pf_pdesc *, u_short *, int *);
+int			pf_tcp_track_sloppy(struct pf_state_peer *,
+			    struct pf_state_peer *, struct pf_state **,
+			    struct pf_pdesc *, u_short *);
 int			 pf_test_state_tcp(struct pf_state **, int,
 			    struct pfi_kif *, struct mbuf *, int,
 			    void *, struct pf_pdesc *, u_short *);
@@ -3528,7 +3535,10 @@ cleanup:
 		s->nat_rule.ptr = nr;
 		s->anchor.ptr = a;
 		STATE_INC_COUNTERS(s);
-		s->allow_opts = r->allow_opts;
+		if (r->allow_opts)
+			s->state_flags |= PFSTATE_ALLOWOPTS;
+		if (r->rule_flag & PFRULE_STATESLOPPY)
+			s->state_flags |= PFSTATE_SLOPPY;
 		s->log = r->log & PF_LOG_ALL;
 		if (nr != NULL)
 			s->log |= nr->log & PF_LOG_ALL;
@@ -3925,7 +3935,10 @@ cleanup:
 		s->nat_rule.ptr = nr;
 		s->anchor.ptr = a;
 		STATE_INC_COUNTERS(s);
-		s->allow_opts = r->allow_opts;
+		if (r->allow_opts)
+			s->state_flags |= PFSTATE_ALLOWOPTS;
+ 		if (r->rule_flag & PFRULE_STATESLOPPY)
+			s->state_flags |= PFSTATE_SLOPPY;
 		s->log = r->log & PF_LOG_ALL;
 		if (nr != NULL)
 			s->log |= nr->log & PF_LOG_ALL;
@@ -4238,7 +4251,10 @@ cleanup:
 		s->nat_rule.ptr = nr;
 		s->anchor.ptr = a;
 		STATE_INC_COUNTERS(s);
-		s->allow_opts = r->allow_opts;
+		if (r->allow_opts)
+			s->state_flags |= PFSTATE_ALLOWOPTS;
+ 		if (r->rule_flag & PFRULE_STATESLOPPY)
+			s->state_flags |= PFSTATE_SLOPPY;
 		s->log = r->log & PF_LOG_ALL;
 		if (nr != NULL)
 			s->log |= nr->log & PF_LOG_ALL;
@@ -4525,7 +4541,10 @@ cleanup:
 		s->nat_rule.ptr = nr;
 		s->anchor.ptr = a;
 		STATE_INC_COUNTERS(s);
-		s->allow_opts = r->allow_opts;
+		if (r->allow_opts)
+			s->state_flags |= PFSTATE_ALLOWOPTS;
+ 		if (r->rule_flag & PFRULE_STATESLOPPY)
+			s->state_flags |= PFSTATE_SLOPPY;
 		s->log = r->log & PF_LOG_ALL;
 		if (nr != NULL)
 			s->log |= nr->log & PF_LOG_ALL;
@@ -4666,166 +4685,16 @@ pf_test_fragment(struct pf_rule **rm, int directio
 }
 
 int
-pf_test_state_tcp(struct pf_state **state, int direction, struct pfi_kif *kif,
-    struct mbuf *m, int off, void *h, struct pf_pdesc *pd,
-    u_short *reason)
+pf_tcp_track_full(struct pf_state_peer *src, struct pf_state_peer *dst,
+	struct pf_state **state, struct pfi_kif *kif, struct mbuf *m, int off,
+	struct pf_pdesc *pd, u_short *reason, int *copyback)
 {
-	struct pf_state_cmp	 key;
-	struct tcphdr		*th = pd->hdr.tcp;
-	u_int16_t		 win = ntohs(th->th_win);
-	u_int32_t		 ack, end, seq, orig_seq;
-	u_int8_t		 sws, dws;
-	int			 ackskew;
-	int			 copyback = 0;
-	struct pf_state_peer	*src, *dst;
+ 	struct tcphdr		*th = pd->hdr.tcp;
+ 	u_int16_t		 win = ntohs(th->th_win);
+ 	u_int32_t		 ack, end, seq, orig_seq;
+ 	u_int8_t		 sws, dws;
+ 	int			 ackskew;
 
-	key.af = pd->af;
-	key.proto = IPPROTO_TCP;
-	if (direction == PF_IN)	{
-		PF_ACPY(&key.ext.addr, pd->src, key.af);
-		PF_ACPY(&key.gwy.addr, pd->dst, key.af);
-		key.ext.port = th->th_sport;
-		key.gwy.port = th->th_dport;
-	} else {
-		PF_ACPY(&key.lan.addr, pd->src, key.af);
-		PF_ACPY(&key.ext.addr, pd->dst, key.af);
-		key.lan.port = th->th_sport;
-		key.ext.port = th->th_dport;
-	}
-
-	STATE_LOOKUP();
-
-	if (direction == (*state)->direction) {
-		src = &(*state)->src;
-		dst = &(*state)->dst;
-	} else {
-		src = &(*state)->dst;
-		dst = &(*state)->src;
-	}
-
-	if ((*state)->src.state == PF_TCPS_PROXY_SRC) {
-		if (direction != (*state)->direction) {
-			REASON_SET(reason, PFRES_SYNPROXY);
-			return (PF_SYNPROXY_DROP);
-		}
-		if (th->th_flags & TH_SYN) {
-			if (ntohl(th->th_seq) != (*state)->src.seqlo) {
-				REASON_SET(reason, PFRES_SYNPROXY);
-				return (PF_DROP);
-			}
-#ifdef __FreeBSD__
-			pf_send_tcp(NULL, (*state)->rule.ptr, pd->af, pd->dst,
-#else
-			pf_send_tcp((*state)->rule.ptr, pd->af, pd->dst,
-#endif
-			    pd->src, th->th_dport, th->th_sport,
-			    (*state)->src.seqhi, ntohl(th->th_seq) + 1,
-			    TH_SYN|TH_ACK, 0, (*state)->src.mss, 0, 1,
-			    0, NULL, NULL);
-			REASON_SET(reason, PFRES_SYNPROXY);
-			return (PF_SYNPROXY_DROP);
-		} else if (!(th->th_flags & TH_ACK) ||
-		    (ntohl(th->th_ack) != (*state)->src.seqhi + 1) ||
-		    (ntohl(th->th_seq) != (*state)->src.seqlo + 1)) {
-			REASON_SET(reason, PFRES_SYNPROXY);
-			return (PF_DROP);
-		} else if ((*state)->src_node != NULL &&
-		    pf_src_connlimit(state)) {
-			REASON_SET(reason, PFRES_SRCLIMIT);
-			return (PF_DROP);
-		} else
-			(*state)->src.state = PF_TCPS_PROXY_DST;
-	}
-	if ((*state)->src.state == PF_TCPS_PROXY_DST) {
-		struct pf_state_host *src, *dst;
-
-		if (direction == PF_OUT) {
-			src = &(*state)->gwy;
-			dst = &(*state)->ext;
-		} else {
-			src = &(*state)->ext;
-			dst = &(*state)->lan;
-		}
-		if (direction == (*state)->direction) {
-			if (((th->th_flags & (TH_SYN|TH_ACK)) != TH_ACK) ||
-			    (ntohl(th->th_ack) != (*state)->src.seqhi + 1) ||
-			    (ntohl(th->th_seq) != (*state)->src.seqlo + 1)) {
-				REASON_SET(reason, PFRES_SYNPROXY);
-				return (PF_DROP);
-			}
-			(*state)->src.max_win = MAX(ntohs(th->th_win), 1);
-			if ((*state)->dst.seqhi == 1)
-				(*state)->dst.seqhi = htonl(arc4random());
-#ifdef __FreeBSD__
-			pf_send_tcp(NULL, (*state)->rule.ptr, pd->af,
-			    &src->addr,
-#else
-			pf_send_tcp((*state)->rule.ptr, pd->af, &src->addr,
-#endif
-			    &dst->addr, src->port, dst->port,
-			    (*state)->dst.seqhi, 0, TH_SYN, 0,
-			    (*state)->src.mss, 0, 0, (*state)->tag, NULL, NULL);
-			REASON_SET(reason, PFRES_SYNPROXY);
-			return (PF_SYNPROXY_DROP);
-		} else if (((th->th_flags & (TH_SYN|TH_ACK)) !=
-		    (TH_SYN|TH_ACK)) ||
-		    (ntohl(th->th_ack) != (*state)->dst.seqhi + 1)) {
-			REASON_SET(reason, PFRES_SYNPROXY);
-			return (PF_DROP);
-		} else {
-			(*state)->dst.max_win = MAX(ntohs(th->th_win), 1);
-			(*state)->dst.seqlo = ntohl(th->th_seq);
-#ifdef __FreeBSD__
-			pf_send_tcp(NULL, (*state)->rule.ptr, pd->af, pd->dst,
-#else
-			pf_send_tcp((*state)->rule.ptr, pd->af, pd->dst,
-#endif
-			    pd->src, th->th_dport, th->th_sport,
-			    ntohl(th->th_ack), ntohl(th->th_seq) + 1,
-			    TH_ACK, (*state)->src.max_win, 0, 0, 0,
-			    (*state)->tag, NULL, NULL);
-#ifdef __FreeBSD__
-			pf_send_tcp(NULL, (*state)->rule.ptr, pd->af,
-			    &src->addr,
-#else
-			pf_send_tcp((*state)->rule.ptr, pd->af, &src->addr,
-#endif
-			    &dst->addr, src->port, dst->port,
-			    (*state)->src.seqhi + 1, (*state)->src.seqlo + 1,
-			    TH_ACK, (*state)->dst.max_win, 0, 0, 1,
-			    0, NULL, NULL);
-			(*state)->src.seqdiff = (*state)->dst.seqhi -
-			    (*state)->src.seqlo;
-			(*state)->dst.seqdiff = (*state)->src.seqhi -
-			    (*state)->dst.seqlo;
-			(*state)->src.seqhi = (*state)->src.seqlo +
-			    (*state)->dst.max_win;
-			(*state)->dst.seqhi = (*state)->dst.seqlo +
-			    (*state)->src.max_win;
-			(*state)->src.wscale = (*state)->dst.wscale = 0;
-			(*state)->src.state = (*state)->dst.state =
-			    TCPS_ESTABLISHED;
-			REASON_SET(reason, PFRES_SYNPROXY);
-			return (PF_SYNPROXY_DROP);
-		}
-	}
-
-	if (((th->th_flags & (TH_SYN|TH_ACK)) == TH_SYN) &&
-	    dst->state >= TCPS_FIN_WAIT_2 &&
-	    src->state >= TCPS_FIN_WAIT_2) {
-		if (pf_status.debug >= PF_DEBUG_MISC) {
-			printf("pf: state reuse ");
-			pf_print_state(*state);
-			pf_print_flags(th->th_flags);
-			printf("\n");
-		}
-		/* XXX make sure it's the same direction ?? */
-		(*state)->src.state = (*state)->dst.state = TCPS_CLOSED;
-		pf_unlink_state(*state);
-		*state = NULL;
-		return (PF_DROP);
-	}
-
 	if (src->wscale && dst->wscale && !(th->th_flags & TH_SYN)) {
 		sws = src->wscale & PF_WSCALE_MASK;
 		dws = dst->wscale & PF_WSCALE_MASK;
@@ -4863,7 +4732,7 @@ int
 			pf_change_a(&th->th_seq, &th->th_sum, htonl(seq +
 			    src->seqdiff), 0);
 			pf_change_a(&th->th_ack, &th->th_sum, htonl(ack), 0);
-			copyback = 1;
+			*copyback = 1;
 		} else {
 			ack = ntohl(th->th_ack);
 		}
@@ -4915,7 +4784,7 @@ int
 			pf_change_a(&th->th_seq, &th->th_sum, htonl(seq +
 			    src->seqdiff), 0);
 			pf_change_a(&th->th_ack, &th->th_sum, htonl(ack), 0);
-			copyback = 1;
+			*copyback = 1;
 		}
 		end = seq + pd->p_len;
 		if (th->th_flags & TH_SYN)
@@ -4961,7 +4830,7 @@ int
 	 */
 	if (dst->seqdiff && (th->th_off << 2) > sizeof(struct tcphdr)) {
 		if (pf_modulate_sack(m, off, pd, th, dst))
-			copyback = 1;
+			*copyback = 1;
 	}
 
 
@@ -4980,7 +4849,7 @@ int
 
 		if (dst->scrub || src->scrub) {
 			if (pf_normalize_tcp_stateful(m, off, pd, reason, th,
-			    *state, src, dst, &copyback))
+			    *state, src, dst, copyback))
 				return (PF_DROP);
 		}
 
@@ -5082,7 +4951,7 @@ int
 
 		if (dst->scrub || src->scrub) {
 			if (pf_normalize_tcp_stateful(m, off, pd, reason, th,
-			    *state, src, dst, &copyback))
+			    *state, src, dst, copyback))
 				return (PF_DROP);
 		}
 
@@ -5128,6 +4997,7 @@ int
 			src->seqhi = 1;
 			src->max_win = 1;
 		} else if (pf_status.debug >= PF_DEBUG_MISC) {
+#if 0
 			printf("pf: BAD state: ");
 			pf_print_state(*state);
 			pf_print_flags(th->th_flags);
@@ -5140,8 +5010,8 @@ int
 #else
 			    (*state)->packets[0], (*state)->packets[1],
 #endif
-			    direction == PF_IN ? "in" : "out",
-			    direction == (*state)->direction ? "fwd" : "rev");
+			    pd->dir == PF_IN ? "in" : "out",
+			    pd->dir == (*state)->direction ? "fwd" : "rev");
 			printf("pf: State failure on: %c %c %c %c | %c %c\n",
 			    SEQ_GEQ(src->seqhi, end) ? ' ' : '1',
 			    SEQ_GEQ(seq, src->seqlo - (dst->max_win << dws)) ?
@@ -5150,13 +5020,255 @@ int
 			    (ackskew <= (MAXACKWINDOW << sws)) ? ' ' : '4',
 			    SEQ_GEQ(src->seqhi + MAXACKWINDOW, end) ?' ' :'5',
 			    SEQ_GEQ(seq, src->seqlo - MAXACKWINDOW) ?' ' :'6');
+#endif
 		}
 		REASON_SET(reason, PFRES_BADSTATE);
 		return (PF_DROP);
 	}
 
 	/* Any packets which have gotten here are to be passed */
+	return (PF_PASS);
+}
 
+int
+pf_tcp_track_sloppy(struct pf_state_peer *src, struct pf_state_peer *dst,
+	struct pf_state **state, struct pf_pdesc *pd, u_short *reason)
+{
+	struct tcphdr		*th = pd->hdr.tcp;
+
+	if (th->th_flags & TH_SYN)
+		if (src->state < TCPS_SYN_SENT)
+			src->state = TCPS_SYN_SENT;
+	if (th->th_flags & TH_FIN)
+		if (src->state < TCPS_CLOSING)
+			src->state = TCPS_CLOSING;
+	if (th->th_flags & TH_ACK) {
+		if (dst->state == TCPS_SYN_SENT) {
+			dst->state = TCPS_ESTABLISHED;
+			if (src->state == TCPS_ESTABLISHED &&
+			    (*state)->src_node != NULL &&
+			    pf_src_connlimit(state)) {
+				REASON_SET(reason, PFRES_SRCLIMIT);
+				return (PF_DROP);
+			}
+		} else if (dst->state == TCPS_CLOSING) {
+			dst->state = TCPS_FIN_WAIT_2;
+		} else if (src->state == TCPS_SYN_SENT &&
+		    dst->state < TCPS_SYN_SENT) {
+			/*
+			 * Handle a special sloppy case where we only see one
+			 * half of the connection. If there is a ACK after
+			 * the initial SYN without ever seeing a packet from
+			 * the destination, set the connection to established.
+			 */
+			dst->state = src->state = TCPS_ESTABLISHED;
+			if ((*state)->src_node != NULL &&
+			    pf_src_connlimit(state)) {
+				REASON_SET(reason, PFRES_SRCLIMIT);
+				return (PF_DROP);
+			}
+		} else if (src->state == TCPS_CLOSING &&
+		    dst->state == TCPS_ESTABLISHED &&
+		    dst->seqlo == 0) {
+			/*
+			 * Handle the closing of half connections where we
+			 * don't see the full bidirectional FIN/ACK+ACK
+			 * handshake.
+			 */
+			dst->state = TCPS_CLOSING;
+		}
+	}
+	if (th->th_flags & TH_RST)
+		src->state = dst->state = TCPS_TIME_WAIT;
+
+	/* update expire time */
+	(*state)->expire = time_second;
+	if (src->state >= TCPS_FIN_WAIT_2 &&
+	    dst->state >= TCPS_FIN_WAIT_2)
+		(*state)->timeout = PFTM_TCP_CLOSED;
+	else if (src->state >= TCPS_CLOSING &&
+	    dst->state >= TCPS_CLOSING)
+		(*state)->timeout = PFTM_TCP_FIN_WAIT;
+	else if (src->state < TCPS_ESTABLISHED ||
+	    dst->state < TCPS_ESTABLISHED)
+		(*state)->timeout = PFTM_TCP_OPENING;
+	else if (src->state >= TCPS_CLOSING ||
+	    dst->state >= TCPS_CLOSING)
+		(*state)->timeout = PFTM_TCP_CLOSING;
+	else
+		(*state)->timeout = PFTM_TCP_ESTABLISHED;
+
+	return (PF_PASS);
+}
+
+
+/* XXXXX */
+int
+pf_test_state_tcp(struct pf_state **state, int direction, struct pfi_kif *kif,
+    struct mbuf *m, int off, void *h, struct pf_pdesc *pd,
+    u_short *reason)
+{
+	struct pf_state_cmp	 key;
+	struct tcphdr		*th = pd->hdr.tcp;
+	int			 copyback = 0;
+	struct pf_state_peer	*src, *dst;
+
+	key.af = pd->af;
+	key.proto = IPPROTO_TCP;
+	if (direction == PF_IN)	{
+		PF_ACPY(&key.ext.addr, pd->src, key.af);
+		PF_ACPY(&key.gwy.addr, pd->dst, key.af);
+		key.ext.port = th->th_sport;
+		key.gwy.port = th->th_dport;
+	} else {
+		PF_ACPY(&key.lan.addr, pd->src, key.af);
+		PF_ACPY(&key.ext.addr, pd->dst, key.af);
+		key.lan.port = th->th_sport;
+		key.ext.port = th->th_dport;
+	}
+
+	STATE_LOOKUP();
+
+	if (direction == (*state)->direction) {
+		src = &(*state)->src;
+		dst = &(*state)->dst;
+	} else {
+		src = &(*state)->dst;
+		dst = &(*state)->src;
+	}
+
+	if ((*state)->src.state == PF_TCPS_PROXY_SRC) {
+		if (direction != (*state)->direction) {
+			REASON_SET(reason, PFRES_SYNPROXY);
+			return (PF_SYNPROXY_DROP);
+		}
+		if (th->th_flags & TH_SYN) {
+			if (ntohl(th->th_seq) != (*state)->src.seqlo) {
+				REASON_SET(reason, PFRES_SYNPROXY);
+				return (PF_DROP);
+			}
+#ifdef __FreeBSD__
+			pf_send_tcp(NULL, (*state)->rule.ptr, pd->af, pd->dst,
+#else
+			pf_send_tcp((*state)->rule.ptr, pd->af, pd->dst,
+#endif
+			    pd->src, th->th_dport, th->th_sport,
+			    (*state)->src.seqhi, ntohl(th->th_seq) + 1,
+			    TH_SYN|TH_ACK, 0, (*state)->src.mss, 0, 1,
+			    0, NULL, NULL);
+			REASON_SET(reason, PFRES_SYNPROXY);
+			return (PF_SYNPROXY_DROP);
+		} else if (!(th->th_flags & TH_ACK) ||
+		    (ntohl(th->th_ack) != (*state)->src.seqhi + 1) ||
+		    (ntohl(th->th_seq) != (*state)->src.seqlo + 1)) {
+			REASON_SET(reason, PFRES_SYNPROXY);
+			return (PF_DROP);
+		} else if ((*state)->src_node != NULL &&
+		    pf_src_connlimit(state)) {
+			REASON_SET(reason, PFRES_SRCLIMIT);
+			return (PF_DROP);
+		} else
+			(*state)->src.state = PF_TCPS_PROXY_DST;
+	}
+	if ((*state)->src.state == PF_TCPS_PROXY_DST) {
+		struct pf_state_host *src, *dst;
+
+		if (direction == PF_OUT) {
+			src = &(*state)->gwy;
+			dst = &(*state)->ext;
+		} else {
+			src = &(*state)->ext;
+			dst = &(*state)->lan;
+		}
+		if (direction == (*state)->direction) {
+			if (((th->th_flags & (TH_SYN|TH_ACK)) != TH_ACK) ||
+			    (ntohl(th->th_ack) != (*state)->src.seqhi + 1) ||
+			    (ntohl(th->th_seq) != (*state)->src.seqlo + 1)) {
+				REASON_SET(reason, PFRES_SYNPROXY);
+				return (PF_DROP);
+			}
+			(*state)->src.max_win = MAX(ntohs(th->th_win), 1);
+			if ((*state)->dst.seqhi == 1)
+				(*state)->dst.seqhi = htonl(arc4random());
+#ifdef __FreeBSD__
+			pf_send_tcp(NULL, (*state)->rule.ptr, pd->af,
+			    &src->addr,
+#else
+			pf_send_tcp((*state)->rule.ptr, pd->af, &src->addr,
+#endif
+			    &dst->addr, src->port, dst->port,
+			    (*state)->dst.seqhi, 0, TH_SYN, 0,
+			    (*state)->src.mss, 0, 0, (*state)->tag, NULL, NULL);
+			REASON_SET(reason, PFRES_SYNPROXY);
+			return (PF_SYNPROXY_DROP);
+		} else if (((th->th_flags & (TH_SYN|TH_ACK)) !=
+		    (TH_SYN|TH_ACK)) ||
+		    (ntohl(th->th_ack) != (*state)->dst.seqhi + 1)) {
+			REASON_SET(reason, PFRES_SYNPROXY);
+			return (PF_DROP);
+		} else {
+			(*state)->dst.max_win = MAX(ntohs(th->th_win), 1);
+			(*state)->dst.seqlo = ntohl(th->th_seq);
+#ifdef __FreeBSD__
+			pf_send_tcp(NULL, (*state)->rule.ptr, pd->af, pd->dst,
+#else
+			pf_send_tcp((*state)->rule.ptr, pd->af, pd->dst,
+#endif
+			    pd->src, th->th_dport, th->th_sport,
+			    ntohl(th->th_ack), ntohl(th->th_seq) + 1,
+			    TH_ACK, (*state)->src.max_win, 0, 0, 0,
+			    (*state)->tag, NULL, NULL);
+#ifdef __FreeBSD__
+			pf_send_tcp(NULL, (*state)->rule.ptr, pd->af,
+			    &src->addr,
+#else
+			pf_send_tcp((*state)->rule.ptr, pd->af, &src->addr,
+#endif
+			    &dst->addr, src->port, dst->port,
+			    (*state)->src.seqhi + 1, (*state)->src.seqlo + 1,
+			    TH_ACK, (*state)->dst.max_win, 0, 0, 1,
+			    0, NULL, NULL);
+			(*state)->src.seqdiff = (*state)->dst.seqhi -
+			    (*state)->src.seqlo;
+			(*state)->dst.seqdiff = (*state)->src.seqhi -
+			    (*state)->dst.seqlo;
+			(*state)->src.seqhi = (*state)->src.seqlo +
+			    (*state)->dst.max_win;
+			(*state)->dst.seqhi = (*state)->dst.seqlo +
+			    (*state)->src.max_win;
+			(*state)->src.wscale = (*state)->dst.wscale = 0;
+			(*state)->src.state = (*state)->dst.state =
+			    TCPS_ESTABLISHED;
+			REASON_SET(reason, PFRES_SYNPROXY);
+			return (PF_SYNPROXY_DROP);
+		}
+	}
+
+	if (((th->th_flags & (TH_SYN|TH_ACK)) == TH_SYN) &&
+	    dst->state >= TCPS_FIN_WAIT_2 &&
+	    src->state >= TCPS_FIN_WAIT_2) {
+		if (pf_status.debug >= PF_DEBUG_MISC) {
+			printf("pf: state reuse ");
+			pf_print_state(*state);
+			pf_print_flags(th->th_flags);
+			printf("\n");
+		}
+		/* XXX make sure it's the same direction ?? */
+		(*state)->src.state = (*state)->dst.state = TCPS_CLOSED;
+		pf_unlink_state(*state);
+		*state = NULL;
+		return (PF_DROP);
+	}
+
+	if ((*state)->state_flags & PFSTATE_SLOPPY) {
+		if (pf_tcp_track_sloppy(src, dst, state, pd, reason) == PF_DROP)
+			return (PF_DROP);
+	} else {
+		if (pf_tcp_track_full(src, dst, state, kif, m, off, pd, reason,
+		    &copyback) == PF_DROP)
+			return (PF_DROP);
+	}
+
 	/* translate source/destination address, if necessary */
 	if (STATE_TRANSLATE(*state)) {
 		if (direction == PF_OUT)
@@ -5533,8 +5645,9 @@ pf_test_state_icmp(struct pf_state **state, int di
 				copyback = 1;
 			}
 
-			if (!SEQ_GEQ(src->seqhi, seq) ||
-			    !SEQ_GEQ(seq, src->seqlo - (dst->max_win << dws))) {
+			if (!((*state)->state_flags & PFSTATE_SLOPPY) &&
+			    (!SEQ_GEQ(src->seqhi, seq) ||
+			    !SEQ_GEQ(seq, src->seqlo - (dst->max_win << dws)))) {
 				if (pf_status.debug >= PF_DEBUG_MISC) {
 					printf("pf: BAD ICMP %d:%d ",
 					    icmptype, pd->hdr.icmp->icmp_code);
@@ -7052,7 +7165,7 @@ pf_test(int dir, struct ifnet *ifp, struct mbuf **
 
 done:
 	if (action == PF_PASS && h->ip_hl > 5 &&
-	    !((s && s->allow_opts) || r->allow_opts)) {
+	    !((s && s->state_flags & PFSTATE_ALLOWOPTS) || r->allow_opts)) {
 		action = PF_DROP;
 		REASON_SET(&reason, PFRES_IPOPTIONS);
 		log = 1;
@@ -7513,7 +7626,7 @@ pf_test6(int dir, struct ifnet *ifp, struct mbuf *
 done:
 	/* handle dangerous IPv6 extension headers. */
 	if (action == PF_PASS && rh_cnt &&
-	    !((s && s->allow_opts) || r->allow_opts)) {
+	    !((s && s->state_flags & PFSTATE_ALLOWOPTS) || r->allow_opts)) {
 		action = PF_DROP;
 		REASON_SET(&reason, PFRES_IPOPTIONS);
 		log = 1;
Index: contrib/pf/pfctl/parse.y
===================================================================
--- contrib/pf/pfctl/parse.y	(revision 196387)
+++ contrib/pf/pfctl/parse.y	(working copy)
@@ -128,7 +128,7 @@ enum	{ PF_STATE_OPT_MAX, PF_STATE_OPT_NOSYNC, PF_S
 	    PF_STATE_OPT_MAX_SRC_STATES, PF_STATE_OPT_MAX_SRC_CONN,
 	    PF_STATE_OPT_MAX_SRC_CONN_RATE, PF_STATE_OPT_MAX_SRC_NODES,
 	    PF_STATE_OPT_OVERLOAD, PF_STATE_OPT_STATELOCK,
-	    PF_STATE_OPT_TIMEOUT };
+	    PF_STATE_OPT_TIMEOUT, PF_STATE_OPT_SLOPPY };
 
 enum	{ PF_SRCTRACK_NONE, PF_SRCTRACK, PF_SRCTRACK_GLOBAL, PF_SRCTRACK_RULE };
 
@@ -423,7 +423,7 @@ typedef struct {
 %token	QUEUE PRIORITY QLIMIT RTABLE
 %token	LOAD RULESET_OPTIMIZATION
 %token	STICKYADDRESS MAXSRCSTATES MAXSRCNODES SOURCETRACK GLOBAL RULE
-%token	MAXSRCCONN MAXSRCCONNRATE OVERLOAD FLUSH
+%token	MAXSRCCONN MAXSRCCONNRATE OVERLOAD FLUSH SLOPPY
 %token	TAGGED TAG IFBOUND FLOATING STATEPOLICY ROUTE
 %token	<v.string>		STRING
 %token	<v.i>			PORTBINARY
@@ -1891,6 +1891,14 @@ pfrule		: action dir logquick interface route af p
 					statelock = 1;
 					r.rule_flag |= o->data.statelock;
 					break;
+				case PF_STATE_OPT_SLOPPY:
+					if (r.rule_flag & PFRULE_STATESLOPPY) {
+						yyerror("state sloppy option: "
+						    "multiple definitions");
+						YYERROR;
+					}
+					r.rule_flag |= PFRULE_STATESLOPPY;
+					break;
 				case PF_STATE_OPT_TIMEOUT:
 					if (o->data.timeout.number ==
 					    PFTM_ADAPTIVE_START ||
@@ -3216,6 +3224,14 @@ state_opt_item	: MAXIMUM number		{
 			$$->next = NULL;
 			$$->tail = $$;
 		}
+		| SLOPPY {
+			$$ = calloc(1, sizeof(struct node_state_opt));
+			if ($$ == NULL)
+				err(1, "state_opt_item: calloc");
+			$$->type = PF_STATE_OPT_SLOPPY;
+			$$->next = NULL;
+			$$->tail = $$;
+		}
 		| STRING number			{
 			int	i;
 
@@ -4101,6 +4117,13 @@ filter_consistent(struct pf_rule *r, int anchor_ca
 		yyerror("keep state on block rules doesn't make sense");
 		problems++;
 	}
+	if (r->rule_flag & PFRULE_STATESLOPPY &&
+	    (r->keep_state == PF_STATE_MODULATE ||
+	    r->keep_state == PF_STATE_SYNPROXY)) {
+		yyerror("sloppy state matching cannot be used with "
+		    "synproxy state or modulate state");
+		problems++;
+	}
 	return (-problems);
 }
 
@@ -4969,6 +4992,7 @@ lookup(char *s)
 		{ "scrub",		SCRUB},
 		{ "set",		SET},
 		{ "skip",		SKIP},
+		{ "sloppy",		SLOPPY},
 		{ "source-hash",	SOURCEHASH},
 		{ "source-track",	SOURCETRACK},
 		{ "state",		STATE},
Index: contrib/pf/pfctl/pf_print_state.c
===================================================================
--- contrib/pf/pfctl/pf_print_state.c	(revision 196387)
+++ contrib/pf/pfctl/pf_print_state.c	(working copy)
@@ -294,6 +294,8 @@ print_state(struct pf_state *s, int opts)
 			printf(", anchor %u", s->anchor.nr);
 		if (s->rule.nr != -1)
 			printf(", rule %u", s->rule.nr);
+		if (s->state_flags & PFSTATE_SLOPPY)
+			printf(", sloppy");
 		if (s->src_node != NULL)
 			printf(", source-track");
 		if (s->nat_src_node != NULL)
Index: contrib/pf/pfctl/pfctl_parser.c
===================================================================
--- contrib/pf/pfctl/pfctl_parser.c	(revision 196387)
+++ contrib/pf/pfctl/pfctl_parser.c	(working copy)
@@ -873,6 +873,8 @@ print_rule(struct pf_rule *r, const char *anchor_c
 		opts = 1;
 	if (r->rule_flag & PFRULE_IFBOUND)
 		opts = 1;
+	if (r->rule_flag & PFRULE_STATESLOPPY)
+		opts = 1;
 	for (i = 0; !opts && i < PFTM_MAX; ++i)
 		if (r->timeout[i])
 			opts = 1;
@@ -939,6 +941,12 @@ print_rule(struct pf_rule *r, const char *anchor_c
 			printf("if-bound");
 			opts = 0;
 		}
+		if (r->rule_flag & PFRULE_STATESLOPPY) {
+			if (!opts)
+				printf(", ");
+			printf("sloppy");
+			opts = 0;
+		}
 		for (i = 0; i < PFTM_MAX; ++i)
 			if (r->timeout[i]) {
 				int j;

--------------000402020406030203090005--



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?4A8CFDAF.1000309>