-----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 <delp...@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-----
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;
_______________________________________________
freebsd-net@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/freebsd-net
To unsubscribe, send any mail to "freebsd-net-unsubscr...@freebsd.org"

Reply via email to