aboutsummaryrefslogtreecommitdiff
path: root/sys/contrib/pf
diff options
context:
space:
mode:
authorXin LI <delphij@FreeBSD.org>2009-12-24 00:43:44 +0000
committerXin LI <delphij@FreeBSD.org>2009-12-24 00:43:44 +0000
commitdcc2b1ff467a89c005babf3948557776c63fc7e5 (patch)
tree8b8e26c41e778334e32a9f2cec1e86d46a2b4619 /sys/contrib/pf
parentbbb8e19daf2c41b99ff2776fec487011bf93e638 (diff)
downloadsrc-dcc2b1ff467a89c005babf3948557776c63fc7e5.tar.gz
src-dcc2b1ff467a89c005babf3948557776c63fc7e5.zip
Adapt OpenBSD pf's "sloopy" TCP state machine which is useful for Direct
Server Return mode, where not all packets would be visible to the load balancer or gateway. This commit should be reverted when we merge future pf versions. The benefit it would provide is that this version does not break any existing public interface and thus won't be a problem if we want to MFC it to earlier FreeBSD releases. Discussed with: mlaier Obtained from: OpenBSD Sponsored by: iXsystems, Inc. MFC after: 1 month
Notes
Notes: svn path=/head/; revision=200930
Diffstat (limited to 'sys/contrib/pf')
-rw-r--r--sys/contrib/pf/net/if_pfsync.c4
-rw-r--r--sys/contrib/pf/net/if_pfsync.h2
-rw-r--r--sys/contrib/pf/net/pf.c460
-rw-r--r--sys/contrib/pf/net/pfvar.h5
4 files changed, 296 insertions, 175 deletions
diff --git a/sys/contrib/pf/net/if_pfsync.c b/sys/contrib/pf/net/if_pfsync.c
index 0925edb094b9..946611859175 100644
--- a/sys/contrib/pf/net/if_pfsync.c
+++ b/sys/contrib/pf/net/if_pfsync.c
@@ -465,7 +465,7 @@ pfsync_insert_net_state(struct pfsync_state *sp, u_int8_t chksum_flag)
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 *st, int flags)
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)
diff --git a/sys/contrib/pf/net/if_pfsync.h b/sys/contrib/pf/net/if_pfsync.h
index f7edca3e4003..f306610fb3d5 100644
--- a/sys/contrib/pf/net/if_pfsync.h
+++ b/sys/contrib/pf/net/if_pfsync.h
@@ -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;
diff --git a/sys/contrib/pf/net/pf.c b/sys/contrib/pf/net/pf.c
index e8031ac7dfec..ebe73c2e9320 100644
--- a/sys/contrib/pf/net/pf.c
+++ b/sys/contrib/pf/net/pf.c
@@ -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,165 +4685,15 @@ pf_test_fragment(struct pf_rule **rm, int direction, struct pfi_kif *kif,
}
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;
-
- 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);
- }
+ 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;
if (src->wscale && dst->wscale && !(th->th_flags & TH_SYN)) {
sws = src->wscale & PF_WSCALE_MASK;
@@ -4863,7 +4732,7 @@ pf_test_state_tcp(struct pf_state **state, int direction, struct pfi_kif *kif,
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 @@ pf_test_state_tcp(struct pf_state **state, int direction, struct pfi_kif *kif,
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 @@ pf_test_state_tcp(struct pf_state **state, int direction, struct pfi_kif *kif,
*/
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 @@ pf_test_state_tcp(struct pf_state **state, int direction, struct pfi_kif *kif,
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 @@ pf_test_state_tcp(struct pf_state **state, int direction, struct pfi_kif *kif,
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);
}
@@ -5132,7 +5001,11 @@ pf_test_state_tcp(struct pf_state **state, int direction, struct pfi_kif *kif,
pf_print_state(*state);
pf_print_flags(th->th_flags);
printf(" seq=%u (%u) ack=%u len=%u ackskew=%d "
+#ifdef notyet
"pkts=%llu:%llu dir=%s,%s\n",
+#else
+ "pkts=%llu:%llu%s\n",
+#endif
seq, orig_seq, ack, pd->p_len, ackskew,
#ifdef __FreeBSD__
(unsigned long long)(*state)->packets[0],
@@ -5140,8 +5013,12 @@ pf_test_state_tcp(struct pf_state **state, int direction, struct pfi_kif *kif,
#else
(*state)->packets[0], (*state)->packets[1],
#endif
+#ifdef notyet
direction == PF_IN ? "in" : "out",
direction == (*state)->direction ? "fwd" : "rev");
+#else
+ "");
+#endif
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)) ?
@@ -5156,6 +5033,246 @@ pf_test_state_tcp(struct pf_state **state, int direction, struct pfi_kif *kif,
}
/* 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);
+}
+
+
+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)) {
@@ -5533,8 +5650,9 @@ pf_test_state_icmp(struct pf_state **state, int direction, struct pfi_kif *kif,
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 +7170,7 @@ pf_test(int dir, struct ifnet *ifp, struct mbuf **m0,
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 +7631,7 @@ pf_test6(int dir, struct ifnet *ifp, struct mbuf **m0,
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;
diff --git a/sys/contrib/pf/net/pfvar.h b/sys/contrib/pf/net/pfvar.h
index 32e721c5733f..de175b1a0213 100644
--- a/sys/contrib/pf/net/pfvar.h
+++ b/sys/contrib/pf/net/pfvar.h
@@ -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