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, ©back)) + *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, ©back)) + *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, + ©back) == 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>