svn commit: r202852 - in stable/8: contrib/pf/man contrib/pf/pfctl
sys/contrib/pf/net
Xin LI
delphij at FreeBSD.org
Sat Jan 23 00:32:20 UTC 2010
Author: delphij
Date: Sat Jan 23 00:32:19 2010
New Revision: 202852
URL: http://svn.freebsd.org/changeset/base/202852
Log:
MFC r200930:
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.
Modified:
stable/8/contrib/pf/man/pf.conf.5
stable/8/contrib/pf/pfctl/parse.y
stable/8/contrib/pf/pfctl/pf_print_state.c
stable/8/contrib/pf/pfctl/pfctl_parser.c
stable/8/sys/contrib/pf/net/if_pfsync.c
stable/8/sys/contrib/pf/net/if_pfsync.h
stable/8/sys/contrib/pf/net/pf.c
stable/8/sys/contrib/pf/net/pfvar.h
Directory Properties:
stable/8/contrib/pf/ (props changed)
stable/8/sys/ (props changed)
stable/8/sys/amd64/include/xen/ (props changed)
stable/8/sys/cddl/contrib/opensolaris/ (props changed)
stable/8/sys/contrib/dev/acpica/ (props changed)
stable/8/sys/contrib/pf/ (props changed)
stable/8/sys/dev/xen/xenpci/ (props changed)
Modified: stable/8/contrib/pf/man/pf.conf.5
==============================================================================
--- stable/8/contrib/pf/man/pf.conf.5 Sat Jan 23 00:30:17 2010 (r202851)
+++ stable/8/contrib/pf/man/pf.conf.5 Sat Jan 23 00:32:19 2010 (r202852)
@@ -28,7 +28,7 @@
.\" ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
.\" POSSIBILITY OF SUCH DAMAGE.
.\"
-.Dd October 30, 2006
+.Dd June 10, 2008
.Dt PF.CONF 5
.Os
.Sh NAME
@@ -2059,6 +2059,13 @@ Changes the timeout values used for stat
For a list of all valid timeout names, see
.Sx OPTIONS
above.
+.It Ar sloppy
+Uses a sloppy TCP connection tracker that does not check sequence
+numbers at all, which makes insertion and ICMP teardown attacks way
+easier.
+This is intended to be used in situations where one does not see all
+packets of a connection, i.e. in asymmetric routing situations.
+Cannot be used with modulate or synproxy state.
.El
.Pp
Multiple options can be specified, separated by commas:
@@ -2923,7 +2930,7 @@ tos = "tos" ( "lowdelay" | "t
[ "0x" ] number )
state-opts = state-opt [ [ "," ] state-opts ]
-state-opt = ( "max" number | "no-sync" | timeout |
+state-opt = ( "max" number | "no-sync" | timeout | sloppy |
"source-track" [ ( "rule" | "global" ) ] |
"max-src-nodes" number | "max-src-states" number |
"max-src-conn" number |
Modified: stable/8/contrib/pf/pfctl/parse.y
==============================================================================
--- stable/8/contrib/pf/pfctl/parse.y Sat Jan 23 00:30:17 2010 (r202851)
+++ stable/8/contrib/pf/pfctl/parse.y Sat Jan 23 00:32:19 2010 (r202852)
@@ -128,7 +128,7 @@ enum { PF_STATE_OPT_MAX, PF_STATE_OPT_NO
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
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
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},
Modified: stable/8/contrib/pf/pfctl/pf_print_state.c
==============================================================================
--- stable/8/contrib/pf/pfctl/pf_print_state.c Sat Jan 23 00:30:17 2010 (r202851)
+++ stable/8/contrib/pf/pfctl/pf_print_state.c Sat Jan 23 00:32:19 2010 (r202852)
@@ -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)
Modified: stable/8/contrib/pf/pfctl/pfctl_parser.c
==============================================================================
--- stable/8/contrib/pf/pfctl/pfctl_parser.c Sat Jan 23 00:30:17 2010 (r202851)
+++ stable/8/contrib/pf/pfctl/pfctl_parser.c Sat Jan 23 00:32:19 2010 (r202852)
@@ -873,6 +873,8 @@ print_rule(struct pf_rule *r, const char
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
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;
Modified: stable/8/sys/contrib/pf/net/if_pfsync.c
==============================================================================
--- stable/8/sys/contrib/pf/net/if_pfsync.c Sat Jan 23 00:30:17 2010 (r202851)
+++ stable/8/sys/contrib/pf/net/if_pfsync.c Sat Jan 23 00:32:19 2010 (r202852)
@@ -465,7 +465,7 @@ pfsync_insert_net_state(struct pfsync_st
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, struc
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)
Modified: stable/8/sys/contrib/pf/net/if_pfsync.h
==============================================================================
--- stable/8/sys/contrib/pf/net/if_pfsync.h Sat Jan 23 00:30:17 2010 (r202851)
+++ stable/8/sys/contrib/pf/net/if_pfsync.h Sat Jan 23 00:32:19 2010 (r202852)
@@ -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;
Modified: stable/8/sys/contrib/pf/net/pf.c
==============================================================================
--- stable/8/sys/contrib/pf/net/pf.c Sat Jan 23 00:30:17 2010 (r202851)
+++ stable/8/sys/contrib/pf/net/pf.c Sat Jan 23 00:32:19 2010 (r202852)
@@ -253,6 +253,13 @@ int pf_test_fragment(struct pf_rule *
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, in
}
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;
- 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);
- }
+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 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 **stat
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 **stat
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 **stat
*/
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 **stat
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 @@ pf_test_state_tcp(struct pf_state **stat
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);
}
@@ -5132,7 +5001,11 @@ pf_test_state_tcp(struct pf_state **stat
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 **stat
#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 **stat
}
/* 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,
+ ©back) == 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 **sta
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, stru
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, str
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;
Modified: stable/8/sys/contrib/pf/net/pfvar.h
==============================================================================
--- stable/8/sys/contrib/pf/net/pfvar.h Sat Jan 23 00:30:17 2010 (r202851)
+++ stable/8/sys/contrib/pf/net/pfvar.h Sat Jan 23 00:32:19 2010 (r202852)
@@ -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
More information about the svn-src-stable-8
mailing list