diff --git a/rules/stream-events.rules b/rules/stream-events.rules index 7ffeb6b5d3..5cc6169d11 100644 --- a/rules/stream-events.rules +++ b/rules/stream-events.rules @@ -10,8 +10,9 @@ alert tcp any any -> any any (msg:"SURICATA STREAM 3way handshake SYNACK resend alert tcp any any -> any any (msg:"SURICATA STREAM 3way handshake SYNACK resend with different seq"; stream-event:3whs_synack_resend_with_diff_seq; classtype:protocol-command-decode; sid:2210005; rev:2;) alert tcp any any -> any any (msg:"SURICATA STREAM 3way handshake SYNACK to server on SYN recv"; stream-event:3whs_synack_toserver_on_syn_recv; classtype:protocol-command-decode; sid:2210006; rev:2;) alert tcp any any -> any any (msg:"SURICATA STREAM 3way handshake SYNACK with wrong ack"; stream-event:3whs_synack_with_wrong_ack; classtype:protocol-command-decode; sid:2210007; rev:2;) -# Excessive SYN/ACKs within a session. Limit is set in stream engine, "stream.max-synack-queued". +# Excessive SYNs or SYN/ACKs within a session. Limit is set in stream engine, "stream.max-synack-queued". alert tcp any any -> any any (msg:"SURICATA STREAM 3way handshake excessive different SYN/ACKs"; stream-event:3whs_synack_flood; classtype:protocol-command-decode; sid:2210055; rev:2;) +alert tcp any any -> any any (msg:"SURICATA STREAM 3way handshake excessive different SYNs"; stream-event:3whs_syn_flood; classtype:protocol-command-decode; sid:2210063; rev:1;) alert tcp any any -> any any (msg:"SURICATA STREAM 3way handshake SYN resend different seq on SYN recv"; stream-event:3whs_syn_resend_diff_seq_on_syn_recv; classtype:protocol-command-decode; sid:2210008; rev:2;) alert tcp any any -> any any (msg:"SURICATA STREAM 3way handshake SYN to client on SYN recv"; stream-event:3whs_syn_toclient_on_syn_recv; classtype:protocol-command-decode; sid:2210009; rev:2;) alert tcp any any -> any any (msg:"SURICATA STREAM 3way handshake wrong seq wrong ack"; stream-event:3whs_wrong_seq_wrong_ack; classtype:protocol-command-decode; sid:2210010; rev:2;) @@ -101,5 +102,5 @@ alert tcp any any -> any any (msg:"SURICATA STREAM FIN SYN reuse"; stream-event: # Depth setting reached for a stream. Very common in normal traffic, so disable by default. #alert tcp any any -> any any (msg:"SURICATA STREAM reassembly depth reached"; stream-event:reassembly_depth_reached; classtype:protocol-command-decode; sid:2210062; rev:1;) -# next sid 2210063 +# next sid 2210064 diff --git a/src/decode-events.c b/src/decode-events.c index ee917b0f8d..0a2626beca 100644 --- a/src/decode-events.c +++ b/src/decode-events.c @@ -634,6 +634,10 @@ const struct DecodeEvents_ DEvents[] = { "stream.3whs_syn_toclient_on_syn_recv", STREAM_3WHS_SYN_TOCLIENT_ON_SYN_RECV, }, + { + "stream.3whs_syn_flood", + STREAM_3WHS_SYN_FLOOD, + }, { "stream.3whs_wrong_seq_wrong_ack", STREAM_3WHS_WRONG_SEQ_WRONG_ACK, diff --git a/src/decode-events.h b/src/decode-events.h index 2024054fda..150ec9f75c 100644 --- a/src/decode-events.h +++ b/src/decode-events.h @@ -235,6 +235,7 @@ enum { STREAM_3WHS_SYNACK_FLOOD, STREAM_3WHS_SYN_RESEND_DIFF_SEQ_ON_SYN_RECV, STREAM_3WHS_SYN_TOCLIENT_ON_SYN_RECV, + STREAM_3WHS_SYN_FLOOD, STREAM_3WHS_WRONG_SEQ_WRONG_ACK, STREAM_3WHS_ACK_DATA_INJECT, STREAM_4WHS_SYNACK_WITH_WRONG_ACK, diff --git a/src/stream-tcp-private.h b/src/stream-tcp-private.h index 6c2117a578..0a42f306f8 100644 --- a/src/stream-tcp-private.h +++ b/src/stream-tcp-private.h @@ -31,7 +31,7 @@ #define STREAMTCP_QUEUE_FLAG_WS 0x02 #define STREAMTCP_QUEUE_FLAG_SACK 0x04 -/** currently only SYN/ACK */ +/** Tracking SYNs and SYN/ACKs */ typedef struct TcpStateQueue_ { uint8_t flags; uint8_t wscale; diff --git a/src/stream-tcp.c b/src/stream-tcp.c index e1f65ec04a..cfdc4b7ec7 100644 --- a/src/stream-tcp.c +++ b/src/stream-tcp.c @@ -1540,155 +1540,190 @@ static inline bool StateSynSentValidateTimestamp(TcpSession *ssn, Packet *p) return true; } -/** - * \brief Function to handle the TCP_SYN_SENT state. The function handles - * SYN, SYN/ACK, RST packets and correspondingly changes the connection - * state. - * - * \param tv Thread Variable containing input/output queue, cpu affinity - * \param p Packet which has to be handled in this TCP state. - * \param stt Strean Thread module registered to handle the stream handling - */ - -static int StreamTcpPacketStateSynSent( - ThreadVars *tv, Packet *p, StreamTcpThread *stt, TcpSession *ssn) +static void TcpStateQueueInitFromSsnSyn(const TcpSession *ssn, TcpStateQueue *q) { - DEBUG_VALIDATE_BUG_ON(ssn == NULL); + BUG_ON(ssn->state != TCP_SYN_SENT); // TODO + memset(q, 0, sizeof(*q)); - SCLogDebug("ssn %p: pkt received: %s", ssn, PKT_IS_TOCLIENT(p) ? - "toclient":"toserver"); + /* SYN won't use wscale yet. So window should be limited to 16 bits. */ + DEBUG_VALIDATE_BUG_ON(ssn->server.window > UINT16_MAX); + q->win = (uint16_t)ssn->server.window; - /* check for bad responses */ - if (StateSynSentValidateTimestamp(ssn, p) == false) { - StreamTcpSetEvent(p, STREAM_PKT_INVALID_TIMESTAMP); - return -1; + q->pkt_ts = ssn->client.last_pkt_ts; + + if (ssn->flags & STREAMTCP_FLAG_CLIENT_SACKOK) { + q->flags |= STREAMTCP_QUEUE_FLAG_SACK; + } + if (ssn->flags & STREAMTCP_FLAG_SERVER_WSCALE) { + q->flags |= STREAMTCP_QUEUE_FLAG_WS; + q->wscale = ssn->server.wscale; + } + if (ssn->client.flags & STREAMTCP_STREAM_FLAG_TIMESTAMP) { + q->flags |= STREAMTCP_QUEUE_FLAG_TS; + q->ts = ssn->client.last_ts; } - /* RST */ - if (p->tcph->th_flags & TH_RST) { - if (!StreamTcpValidateRst(ssn, p)) - return -1; + SCLogDebug("ssn %p: state:%p, isn:%u/win:%u/has_ts:%s/tsval:%u", ssn, q, q->seq, q->win, + BOOL2STR(q->flags & STREAMTCP_QUEUE_FLAG_TS), q->ts); +} - if (PKT_IS_TOSERVER(p)) { - if (SEQ_EQ(TCP_GET_SEQ(p), ssn->client.isn) && - SEQ_EQ(TCP_GET_WINDOW(p), 0) && - SEQ_EQ(TCP_GET_ACK(p), (ssn->client.isn + 1))) - { - SCLogDebug("ssn->server.flags |= STREAMTCP_STREAM_FLAG_RST_RECV"); - ssn->server.flags |= STREAMTCP_STREAM_FLAG_RST_RECV; - StreamTcpCloseSsnWithReset(p, ssn); - } - } else { - ssn->client.flags |= STREAMTCP_STREAM_FLAG_RST_RECV; - SCLogDebug("ssn->client.flags |= STREAMTCP_STREAM_FLAG_RST_RECV"); - StreamTcpCloseSsnWithReset(p, ssn); - } +static void TcpStateQueueInitFromPktSyn(const Packet *p, TcpStateQueue *q) +{ +#if defined(DEBUG_VALIDATION) || defined(DEBUG) + const TcpSession *ssn = p->flow->protoctx; + BUG_ON(ssn->state != TCP_SYN_SENT); +#endif + memset(q, 0, sizeof(*q)); - /* FIN */ - } else if (p->tcph->th_flags & TH_FIN) { - /** \todo */ + q->win = TCP_GET_WINDOW(p); + q->pkt_ts = SCTIME_SECS(p->ts); - /* SYN/ACK */ - } else if ((p->tcph->th_flags & (TH_SYN|TH_ACK)) == (TH_SYN|TH_ACK)) { - if ((ssn->flags & STREAMTCP_FLAG_4WHS) && PKT_IS_TOSERVER(p)) { - SCLogDebug("ssn %p: SYN/ACK received on 4WHS session", ssn); + if (TCP_GET_SACKOK(p) == 1) { + q->flags |= STREAMTCP_QUEUE_FLAG_SACK; + } + if (TCP_HAS_WSCALE(p)) { + q->flags |= STREAMTCP_QUEUE_FLAG_WS; + q->wscale = TCP_GET_WSCALE(p); + } + if (TCP_HAS_TS(p)) { + q->flags |= STREAMTCP_QUEUE_FLAG_TS; + q->ts = TCP_GET_TSVAL(p); + } - /* Check if the SYN/ACK packet ack's the earlier - * received SYN packet. */ - if (!(SEQ_EQ(TCP_GET_ACK(p), ssn->server.isn + 1))) { - StreamTcpSetEvent(p, STREAM_4WHS_SYNACK_WITH_WRONG_ACK); +#if defined(DEBUG) + SCLogDebug("ssn %p: state:%p, isn:%u/win:%u/has_ts:%s/tsval:%u", ssn, q, q->seq, q->win, + BOOL2STR(q->flags & STREAMTCP_QUEUE_FLAG_TS), q->ts); +#endif +} - SCLogDebug("ssn %p: 4WHS ACK mismatch, packet ACK %"PRIu32"" - " != %" PRIu32 " from stream", ssn, - TCP_GET_ACK(p), ssn->server.isn + 1); - return -1; - } +static void TcpStateQueueInitFromPktSynAck(const Packet *p, TcpStateQueue *q) +{ +#if defined(DEBUG_VALIDATION) || defined(DEBUG) + const TcpSession *ssn = p->flow->protoctx; + BUG_ON(ssn->state != TCP_SYN_SENT); +#endif + memset(q, 0, sizeof(*q)); - /* Check if the SYN/ACK packet SEQ's the *FIRST* received SYN - * packet. */ - if (!(SEQ_EQ(TCP_GET_SEQ(p), ssn->client.isn))) { - StreamTcpSetEvent(p, STREAM_4WHS_SYNACK_WITH_WRONG_SYN); + q->win = TCP_GET_WINDOW(p); + q->pkt_ts = SCTIME_SECS(p->ts); - SCLogDebug("ssn %p: 4WHS SEQ mismatch, packet SEQ %"PRIu32"" - " != %" PRIu32 " from *first* SYN pkt", ssn, - TCP_GET_SEQ(p), ssn->client.isn); - return -1; - } + if (TCP_GET_SACKOK(p) == 1) { + q->flags |= STREAMTCP_QUEUE_FLAG_SACK; + } + if (TCP_HAS_WSCALE(p)) { + q->flags |= STREAMTCP_QUEUE_FLAG_WS; + q->wscale = TCP_GET_WSCALE(p); + } + if (TCP_HAS_TS(p)) { + q->flags |= STREAMTCP_QUEUE_FLAG_TS; + q->ts = TCP_GET_TSECR(p); + } +#if defined(DEBUG) + SCLogDebug("ssn %p: state:%p, isn:%u/win:%u/has_ts:%s/tsval:%u", ssn, q, q->seq, q->win, + BOOL2STR(q->flags & STREAMTCP_QUEUE_FLAG_TS), q->ts); +#endif +} - /* update state */ - StreamTcpPacketSetState(p, ssn, TCP_SYN_RECV); - SCLogDebug("ssn %p: =~ 4WHS ssn state is now TCP_SYN_RECV", ssn); +/** \internal + * \brief Find the Queued SYN that is the same as this SYN/ACK + * \retval q or NULL */ +static const TcpStateQueue *StreamTcp3whsFindSyn(const TcpSession *ssn, TcpStateQueue *s) +{ + SCLogDebug("ssn %p: search state:%p, isn:%u/win:%u/has_ts:%s/tsval:%u", ssn, s, s->seq, s->win, + BOOL2STR(s->flags & STREAMTCP_QUEUE_FLAG_TS), s->ts); + + for (const TcpStateQueue *q = ssn->queue; q != NULL; q = q->next) { + SCLogDebug("ssn %p: queue state:%p, isn:%u/win:%u/has_ts:%s/tsval:%u", ssn, q, q->seq, + q->win, BOOL2STR(q->flags & STREAMTCP_QUEUE_FLAG_TS), q->ts); + if ((s->flags & STREAMTCP_QUEUE_FLAG_TS) == (q->flags & STREAMTCP_QUEUE_FLAG_TS) && + s->ts == q->ts) { + return q; + } + } + return NULL; +} - /* sequence number & window */ - ssn->client.isn = TCP_GET_SEQ(p); - STREAMTCP_SET_RA_BASE_SEQ(&ssn->client, ssn->client.isn); - ssn->client.next_seq = ssn->client.isn + 1; +/** \note the SEQ values *must* be the same */ +static int StreamTcp3whsStoreSyn(TcpSession *ssn, Packet *p) +{ + TcpStateQueue search; + TcpStateQueueInitFromSsnSyn(ssn, &search); - ssn->server.window = TCP_GET_WINDOW(p); - SCLogDebug("ssn %p: 4WHS window %" PRIu32 "", ssn, - ssn->client.window); + /* first see if this is already in our list */ + if (ssn->queue != NULL && StreamTcp3whsFindSyn(ssn, &search) != NULL) + return 0; - /* Set the timestamp values used to validate the timestamp of - * received packets. */ - if ((TCP_HAS_TS(p)) && - (ssn->server.flags & STREAMTCP_STREAM_FLAG_TIMESTAMP)) - { - ssn->client.last_ts = TCP_GET_TSVAL(p); - SCLogDebug("ssn %p: 4WHS ssn->client.last_ts %" PRIu32" " - "ssn->server.last_ts %" PRIu32"", ssn, - ssn->client.last_ts, ssn->server.last_ts); - ssn->flags |= STREAMTCP_FLAG_TIMESTAMP; - ssn->client.last_pkt_ts = SCTIME_SECS(p->ts); - if (ssn->client.last_ts == 0) - ssn->client.flags |= STREAMTCP_STREAM_FLAG_ZERO_TIMESTAMP; - } else { - ssn->server.last_ts = 0; - ssn->client.last_ts = 0; - ssn->server.flags &= ~STREAMTCP_STREAM_FLAG_ZERO_TIMESTAMP; - } + if (ssn->queue_len == stream_config.max_synack_queued) { // TODO + SCLogDebug("ssn %p: =~ SYN queue limit reached", ssn); + StreamTcpSetEvent(p, STREAM_3WHS_SYN_FLOOD); + return -1; + } - ssn->server.last_ack = TCP_GET_ACK(p); - ssn->client.last_ack = ssn->client.isn + 1; + if (StreamTcpCheckMemcap((uint32_t)sizeof(TcpStateQueue)) == 0) { + SCLogDebug("ssn %p: =~ SYN queue failed: stream memcap reached", ssn); + return -1; + } - /** check for the presense of the ws ptr to determine if we - * support wscale at all */ - if ((ssn->flags & STREAMTCP_FLAG_SERVER_WSCALE) && - (TCP_HAS_WSCALE(p))) - { - ssn->server.wscale = TCP_GET_WSCALE(p); - } else { - ssn->server.wscale = 0; - } + TcpStateQueue *q = SCCalloc(1, sizeof(*q)); + if (unlikely(q == NULL)) { + SCLogDebug("ssn %p: =~ SYN queue failed: alloc failed", ssn); + return -1; + } + StreamTcpIncrMemuse((uint64_t)sizeof(TcpStateQueue)); - if ((ssn->flags & STREAMTCP_FLAG_CLIENT_SACKOK) && - TCP_GET_SACKOK(p) == 1) { - ssn->flags |= STREAMTCP_FLAG_SACKOK; - SCLogDebug("ssn %p: SACK permitted for 4WHS session", ssn); - } + *q = search; + /* put in list */ + q->next = ssn->queue; + ssn->queue = q; + ssn->queue_len++; + return 0; +} - ssn->client.next_win = ssn->client.last_ack + ssn->client.window; - ssn->server.next_win = ssn->server.last_ack + ssn->server.window; - SCLogDebug("ssn %p: 4WHS ssn->client.next_win %" PRIu32 "", ssn, - ssn->client.next_win); - SCLogDebug("ssn %p: 4WHS ssn->server.next_win %" PRIu32 "", ssn, - ssn->server.next_win); - SCLogDebug("ssn %p: 4WHS ssn->client.isn %" PRIu32 ", " - "ssn->client.next_seq %" PRIu32 ", " - "ssn->client.last_ack %" PRIu32 " " - "(ssn->server.last_ack %" PRIu32 ")", ssn, - ssn->client.isn, ssn->client.next_seq, - ssn->client.last_ack, ssn->server.last_ack); +static inline void StreamTcp3whsStoreSynApplyToSsn(TcpSession *ssn, const TcpStateQueue *q) +{ + if (q->flags & STREAMTCP_QUEUE_FLAG_TS) { + ssn->client.last_pkt_ts = q->pkt_ts; + ssn->client.last_ts = q->ts; + ssn->client.flags |= STREAMTCP_STREAM_FLAG_TIMESTAMP; + SCLogDebug("ssn: %p client.last_ts updated to %u", ssn, ssn->client.last_ts); + } + if (q->flags & STREAMTCP_QUEUE_FLAG_WS) { + ssn->flags |= STREAMTCP_FLAG_SERVER_WSCALE; + ssn->server.wscale = q->wscale; + } else { + ssn->flags &= STREAMTCP_FLAG_SERVER_WSCALE; + ssn->server.wscale = 0; + } + ssn->server.window = q->win; - /* done here */ - return 0; - } + if (q->flags & STREAMTCP_QUEUE_FLAG_SACK) { + ssn->flags |= STREAMTCP_FLAG_CLIENT_SACKOK; + } else { + ssn->flags &= ~STREAMTCP_FLAG_CLIENT_SACKOK; + } +} - if (PKT_IS_TOSERVER(p)) { - StreamTcpSetEvent(p, STREAM_3WHS_SYNACK_IN_WRONG_DIRECTION); - SCLogDebug("ssn %p: SYN/ACK received in the wrong direction", ssn); - return -1; - } +/** + * \brief Function to handle the TCP_SYN_SENT state. The function handles + * SYN, SYN/ACK, RST packets and correspondingly changes the connection + * state. + * + * \param tv Thread Variable containing input/output queue, cpu affinity + * \param p Packet which has to be handled in this TCP state. + * \param stt Strean Thread module registered to handle the stream handling + */ + +static int StreamTcpPacketStateSynSent( + ThreadVars *tv, Packet *p, StreamTcpThread *stt, TcpSession *ssn) +{ + DEBUG_VALIDATE_BUG_ON(ssn == NULL); + + SCLogDebug("ssn %p: pkt received: %s", ssn, PKT_IS_TOCLIENT(p) ? "toclient" : "toserver"); + + /* common case: SYN/ACK from server to client */ + if ((p->tcph->th_flags & (TH_SYN | TH_ACK)) == (TH_SYN | TH_ACK) && PKT_IS_TOCLIENT(p)) { + SCLogDebug("ssn %p: SYN/ACK on SYN_SENT state for packet %" PRIu64, ssn, p->pcap_cnt); if (!(TCP_HAS_TFO(p) || (ssn->flags & STREAMTCP_FLAG_TCP_FAST_OPEN))) { /* Check if the SYN/ACK packet ack's the earlier @@ -1715,7 +1750,159 @@ static int StreamTcpPacketStateSynSent( ssn->flags |= STREAMTCP_FLAG_TCP_FAST_OPEN; StreamTcpPacketSetState(p, ssn, TCP_ESTABLISHED); } + + const bool ts_mismatch = !StateSynSentValidateTimestamp(ssn, p); + if (ts_mismatch) { + SCLogDebug("ssn %p: ts_mismatch:%s", ssn, BOOL2STR(ts_mismatch)); + if (ssn->queue) { + TcpStateQueue search; + TcpStateQueueInitFromPktSynAck(p, &search); + + const TcpStateQueue *q = StreamTcp3whsFindSyn(ssn, &search); + if (q == NULL) { + SCLogDebug("not found: mismatch"); + StreamTcpSetEvent(p, STREAM_PKT_INVALID_TIMESTAMP); + return -1; + } + SCLogDebug("ssn %p: found queued SYN state:%p, isn:%u/win:%u/has_ts:%s/tsval:%u", + ssn, q, q->seq, q->win, BOOL2STR(q->flags & STREAMTCP_QUEUE_FLAG_TS), + q->ts); + + StreamTcp3whsStoreSynApplyToSsn(ssn, q); + + } else { + SCLogDebug("not found: no queue"); + StreamTcpSetEvent(p, STREAM_PKT_INVALID_TIMESTAMP); + return -1; + } + } + + /* clear ssn->queue on state change: TcpSession can be reused by SYN/ACK */ + StreamTcp3wsFreeQueue(ssn); + StreamTcp3whsSynAckUpdate(ssn, p, /* no queue override */NULL); + return 0; + + } else if ((p->tcph->th_flags & (TH_SYN | TH_ACK)) == (TH_SYN | TH_ACK) && PKT_IS_TOSERVER(p)) { + + if (!(ssn->flags & STREAMTCP_FLAG_4WHS)) { + StreamTcpSetEvent(p, STREAM_3WHS_SYNACK_IN_WRONG_DIRECTION); + SCLogDebug("ssn %p: SYN/ACK received in the wrong direction", ssn); + return -1; + } + + SCLogDebug("ssn %p: SYN/ACK received on 4WHS session", ssn); + + /* Check if the SYN/ACK packet ack's the earlier + * received SYN packet. */ + if (!(SEQ_EQ(TCP_GET_ACK(p), ssn->server.isn + 1))) { + StreamTcpSetEvent(p, STREAM_4WHS_SYNACK_WITH_WRONG_ACK); + + SCLogDebug("ssn %p: 4WHS ACK mismatch, packet ACK %" PRIu32 "" + " != %" PRIu32 " from stream", + ssn, TCP_GET_ACK(p), ssn->server.isn + 1); + return -1; + } + + /* Check if the SYN/ACK packet SEQ's the *FIRST* received SYN + * packet. */ + if (!(SEQ_EQ(TCP_GET_SEQ(p), ssn->client.isn))) { + StreamTcpSetEvent(p, STREAM_4WHS_SYNACK_WITH_WRONG_SYN); + + SCLogDebug("ssn %p: 4WHS SEQ mismatch, packet SEQ %" PRIu32 "" + " != %" PRIu32 " from *first* SYN pkt", + ssn, TCP_GET_SEQ(p), ssn->client.isn); + return -1; + } + + /* update state */ + StreamTcpPacketSetState(p, ssn, TCP_SYN_RECV); + SCLogDebug("ssn %p: =~ 4WHS ssn state is now TCP_SYN_RECV", ssn); + + /* sequence number & window */ + ssn->client.isn = TCP_GET_SEQ(p); + STREAMTCP_SET_RA_BASE_SEQ(&ssn->client, ssn->client.isn); + ssn->client.next_seq = ssn->client.isn + 1; + + ssn->server.window = TCP_GET_WINDOW(p); + SCLogDebug("ssn %p: 4WHS window %" PRIu32 "", ssn, ssn->client.window); + + /* Set the timestamp values used to validate the timestamp of + * received packets. */ + if ((TCP_HAS_TS(p)) && (ssn->server.flags & STREAMTCP_STREAM_FLAG_TIMESTAMP)) { + ssn->client.last_ts = TCP_GET_TSVAL(p); + SCLogDebug("ssn %p: 4WHS ssn->client.last_ts %" PRIu32 " " + "ssn->server.last_ts %" PRIu32 "", + ssn, ssn->client.last_ts, ssn->server.last_ts); + ssn->flags |= STREAMTCP_FLAG_TIMESTAMP; + ssn->client.last_pkt_ts = SCTIME_SECS(p->ts); + if (ssn->client.last_ts == 0) + ssn->client.flags |= STREAMTCP_STREAM_FLAG_ZERO_TIMESTAMP; + } else { + ssn->server.last_ts = 0; + ssn->client.last_ts = 0; + ssn->server.flags &= ~STREAMTCP_STREAM_FLAG_ZERO_TIMESTAMP; + } + + ssn->server.last_ack = TCP_GET_ACK(p); + ssn->client.last_ack = ssn->client.isn + 1; + + /** check for the presense of the ws ptr to determine if we + * support wscale at all */ + if ((ssn->flags & STREAMTCP_FLAG_SERVER_WSCALE) && (TCP_HAS_WSCALE(p))) { + ssn->server.wscale = TCP_GET_WSCALE(p); + } else { + ssn->server.wscale = 0; + } + + if ((ssn->flags & STREAMTCP_FLAG_CLIENT_SACKOK) && TCP_GET_SACKOK(p) == 1) { + ssn->flags |= STREAMTCP_FLAG_SACKOK; + SCLogDebug("ssn %p: SACK permitted for 4WHS session", ssn); + } + + ssn->client.next_win = ssn->client.last_ack + ssn->client.window; + ssn->server.next_win = ssn->server.last_ack + ssn->server.window; + SCLogDebug("ssn %p: 4WHS ssn->client.next_win %" PRIu32 "", ssn, ssn->client.next_win); + SCLogDebug("ssn %p: 4WHS ssn->server.next_win %" PRIu32 "", ssn, ssn->server.next_win); + SCLogDebug("ssn %p: 4WHS ssn->client.isn %" PRIu32 ", " + "ssn->client.next_seq %" PRIu32 ", " + "ssn->client.last_ack %" PRIu32 " " + "(ssn->server.last_ack %" PRIu32 ")", + ssn, ssn->client.isn, ssn->client.next_seq, ssn->client.last_ack, + ssn->server.last_ack); + + /* done here */ + return 0; + } + + /* check for bad responses */ + if (StateSynSentValidateTimestamp(ssn, p) == false) { + StreamTcpSetEvent(p, STREAM_PKT_INVALID_TIMESTAMP); + return -1; + } + + /* RST */ + if (p->tcph->th_flags & TH_RST) { + + if (!StreamTcpValidateRst(ssn, p)) + return -1; + + if (PKT_IS_TOSERVER(p)) { + if (SEQ_EQ(TCP_GET_SEQ(p), ssn->client.isn) && SEQ_EQ(TCP_GET_WINDOW(p), 0) && + SEQ_EQ(TCP_GET_ACK(p), (ssn->client.isn + 1))) { + SCLogDebug("ssn->server.flags |= STREAMTCP_STREAM_FLAG_RST_RECV"); + ssn->server.flags |= STREAMTCP_STREAM_FLAG_RST_RECV; + StreamTcpCloseSsnWithReset(p, ssn); + } + } else { + ssn->client.flags |= STREAMTCP_STREAM_FLAG_RST_RECV; + SCLogDebug("ssn->client.flags |= STREAMTCP_STREAM_FLAG_RST_RECV"); + StreamTcpCloseSsnWithReset(p, ssn); + } + + /* FIN */ + } else if (p->tcph->th_flags & TH_FIN) { + /** \todo */ } else if (p->tcph->th_flags & TH_SYN) { SCLogDebug("ssn %p: SYN packet on state SYN_SENT... resent", ssn); @@ -1781,27 +1968,23 @@ static int StreamTcpPacketStateSynSent( ssn->client.isn, ssn->client.next_seq, ssn->client.last_ack); } else if (PKT_IS_TOSERVER(p)) { - /* - * On retransmitted SYN packets, the timestamp value must be updated, - * to avoid dropping any SYN+ACK packets that respond to a retransmitted SYN - * with an updated timestamp in StateSynSentValidateTimestamp. - */ - if ((ssn->client.flags & STREAMTCP_STREAM_FLAG_TIMESTAMP) && TCP_HAS_TS(p)) { - uint32_t ts_val = TCP_GET_TSVAL(p); + /* on a SYN resend we queue up the SYN's until a SYN/ACK moves the state + * to SYN_RECV. We update the ssn to the most recent, as it is most likely + * to be correct. */ - // Check whether packets have been received in the correct order (only ever update) - if (ssn->client.last_ts < ts_val) { - ssn->client.last_ts = ts_val; - ssn->client.last_pkt_ts = SCTIME_SECS(p->ts); - } + TcpStateQueue syn_pkt, syn_ssn; + TcpStateQueueInitFromPktSyn(p, &syn_pkt); + TcpStateQueueInitFromSsnSyn(ssn, &syn_ssn); - SCLogDebug("ssn %p: Retransmitted SYN. Updated timestamp from packet %" PRIu64, ssn, - p->pcap_cnt); + if (memcmp(&syn_pkt, &syn_ssn, sizeof(TcpStateQueue)) != 0) { + /* store the old session settings */ + StreamTcp3whsStoreSyn(ssn, p); + SCLogDebug("ssn %p: Retransmitted SYN. Updating ssn from packet %" PRIu64 + ". Stored previous state", + ssn, p->pcap_cnt); } + StreamTcp3whsStoreSynApplyToSsn(ssn, &syn_pkt); } - - /** \todo check if it's correct or set event */ - } else if (p->tcph->th_flags & TH_ACK) { /* Handle the asynchronous stream, when we receive a SYN packet and now instead of receiving a SYN/ACK we receive a ACK from the