From: Victor Julien Date: Wed, 8 Aug 2012 12:29:31 +0000 (+0200) Subject: stream: improve TCP flags handling X-Git-Tag: suricata-1.3.1~13 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=c44f4c13fcf7cfd3afa92503a547a035e190d522;p=thirdparty%2Fsuricata.git stream: improve TCP flags handling --- diff --git a/rules/stream-events.rules b/rules/stream-events.rules index 9fbb342a97..5d6f913b2e 100644 --- a/rules/stream-events.rules +++ b/rules/stream-events.rules @@ -50,8 +50,11 @@ alert tcp any any -> any any (msg:"SURICATA STREAM TIMEWAIT ACK with wrong seq"; alert tcp any any -> any any (msg:"SURICATA STREAM TIMEWAIT invalid ack"; stream-event:timewait_invalid_ack; sid:2210043; rev:1;) alert tcp any any -> any any (msg:"SURICATA STREAM Packet with invalid timestamp"; stream-event:pkt_invalid_timestamp; sid:2210044; rev:1;) alert tcp any any -> any any (msg:"SURICATA STREAM Packet with invalid ack"; stream-event:pkt_invalid_ack; sid:2210045; rev:1;) -alert tcp any any -> any any (msg:"SURICATA STREAM RST invalid ack"; stream-event:rst_invalid_ack; sid:2210046; rev:1;) +alert tcp any any -> any any (msg:"SURICATA STREAM SHUTDOWN RST invalid ack"; stream-event:rst_invalid_ack; sid:2210046; rev:1;) +# SYN (re)send during shutdown (closing, closewait, finwait1, finwait2, lastack, timewait states) +#alert tcp any any -> any any (msg:"SURICATA STREAM SYN resend"; stream-event:shutdown_syn_resend; sid:2210049; rev:1;) #alert tcp any any -> any any (msg:"SURICATA STREAM reassembly segment before base seq"; stream-event:reassembly_segment_before_base_seq; sid:2210047; rev:1;) # Sequence gap: missing data in the reassembly engine. Usually due to packet loss. Will be very noisy on a overloaded link / sensor. #alert tcp any any -> any any (msg:"SURICATA STREAM reassembly sequence GAP -- missing packet(s)"; stream-event:reassembly_seq_gap; sid:2210048; rev:1;) +# next sid 2210050 diff --git a/src/decode-events.h b/src/decode-events.h index b2d7d516cd..179e4fd7a5 100644 --- a/src/decode-events.h +++ b/src/decode-events.h @@ -173,6 +173,7 @@ enum { STREAM_RST_BUT_NO_SESSION, STREAM_TIMEWAIT_ACK_WRONG_SEQ, STREAM_TIMEWAIT_INVALID_ACK, + STREAM_SHUTDOWN_SYN_RESEND, STREAM_PKT_INVALID_TIMESTAMP, STREAM_PKT_INVALID_ACK, STREAM_RST_INVALID_ACK, diff --git a/src/detect-engine-event.h b/src/detect-engine-event.h index 0cb96a7262..5ab28194c1 100644 --- a/src/detect-engine-event.h +++ b/src/detect-engine-event.h @@ -163,6 +163,7 @@ struct DetectEngineEvents_ { { "stream.pkt_invalid_timestamp", STREAM_PKT_INVALID_TIMESTAMP, }, { "stream.pkt_invalid_ack", STREAM_PKT_INVALID_ACK, }, { "stream.rst_invalid_ack", STREAM_RST_INVALID_ACK, }, + { "stream.shutdown_syn_resend", STREAM_SHUTDOWN_SYN_RESEND, }, { "stream.reassembly_segment_before_base_seq", STREAM_REASSEMBLY_SEGMENT_BEFORE_BASE_SEQ, }, { "stream.reassembly_no_segment", STREAM_REASSEMBLY_NO_SEGMENT, }, { "stream.reassembly_seq_gap", STREAM_REASSEMBLY_SEQ_GAP, }, diff --git a/src/stream-tcp.c b/src/stream-tcp.c index c40a708188..6686649886 100644 --- a/src/stream-tcp.c +++ b/src/stream-tcp.c @@ -633,6 +633,7 @@ void StreamTcpSetOSPolicy(TcpStream *stream, Packet *p) } /** + * \internal * \brief Function to handle the TCP_CLOSED or NONE state. The function handles * packets while the session state is None which means a newly * initialized structure, or a fully closed session. @@ -647,258 +648,222 @@ void StreamTcpSetOSPolicy(TcpStream *stream, Packet *p) static int StreamTcpPacketStateNone(ThreadVars *tv, Packet *p, StreamTcpThread *stt, TcpSession *ssn, PacketQueue *pq) { - switch (p->tcph->th_flags) { - /* The following cases will allow us to create the tcp sessions in congestion - * conditions, where the client open a connection with SYN and - * any of the following flags: - * TH_CWR -> Establish a new connection reducing window - * TH_ECN -> Echo Congestion flag - */ - case TH_SYN: - case TH_SYN | TH_ECN: - case TH_SYN | TH_CWR: - case TH_SYN | TH_PUSH: - case TH_SYN | TH_URG: - case TH_SYN | TH_CWR | TH_ECN: - case TH_SYN | TH_PUSH | TH_URG: - { - if (ssn == NULL) { - ssn = StreamTcpNewSession(p); - if (ssn == NULL) { - SCPerfCounterIncr(stt->counter_tcp_ssn_memcap, tv->sc_perf_pca); - return -1; - } - - SCPerfCounterIncr(stt->counter_tcp_sessions, tv->sc_perf_pca); - } - - /* set the state */ - StreamTcpPacketSetState(p, ssn, TCP_SYN_SENT); - SCLogDebug("ssn %p: =~ ssn state is now TCP_SYN_SENT", ssn); - - /* set the sequence numbers and 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; + if (p->tcph->th_flags & TH_RST) { + StreamTcpSetEvent(p, STREAM_RST_BUT_NO_SESSION); + SCLogDebug("RST packet received, no session setup"); + return -1; - /* Set the stream timestamp value, if packet has timestamp option - * enabled. */ - if (p->tcpvars.ts != NULL) { - ssn->client.last_ts = TCP_GET_TSVAL(p); - SCLogDebug("ssn %p: p->tcpvars.ts %p, %02x", ssn, p->tcpvars.ts, - ssn->client.last_ts); + } else if (p->tcph->th_flags & TH_FIN) { + StreamTcpSetEvent(p, STREAM_FIN_BUT_NO_SESSION); + SCLogDebug("FIN packet received, no session setup"); + return -1; - if (ssn->client.last_ts == 0) - ssn->client.flags |= STREAMTCP_FLAG_ZERO_TIMESTAMP; + /* SYN/ACK */ + } else if ((p->tcph->th_flags & (TH_SYN|TH_ACK)) == (TH_SYN|TH_ACK)) { + if (stream_config.midstream == FALSE && + stream_config.async_oneside == FALSE) + return 0; - ssn->client.last_pkt_ts = p->ts.tv_sec; - ssn->client.flags |= STREAMTCP_FLAG_TIMESTAMP; + if (ssn == NULL) { + ssn = StreamTcpNewSession(p); + if (ssn == NULL) { + SCPerfCounterIncr(stt->counter_tcp_ssn_memcap, tv->sc_perf_pca); + return -1; } + SCPerfCounterIncr(stt->counter_tcp_sessions, tv->sc_perf_pca); + } + /* set the state */ + StreamTcpPacketSetState(p, ssn, TCP_SYN_RECV); + SCLogDebug("ssn %p: =~ midstream picked ssn state is now " + "TCP_SYN_RECV", ssn); + ssn->flags |= STREAMTCP_FLAG_MIDSTREAM; + /* Flag used to change the direct in the later stage in the session */ + ssn->flags |= STREAMTCP_FLAG_MIDSTREAM_SYNACK; + + /* sequence number & window */ + ssn->server.isn = TCP_GET_SEQ(p); + STREAMTCP_SET_RA_BASE_SEQ(&ssn->server, ssn->server.isn); + ssn->server.next_seq = ssn->server.isn + 1; + ssn->server.window = TCP_GET_WINDOW(p); + SCLogDebug("ssn %p: server window %u", ssn, ssn->server.window); + + ssn->client.isn = TCP_GET_ACK(p) - 1; + STREAMTCP_SET_RA_BASE_SEQ(&ssn->client, ssn->client.isn); + ssn->client.next_seq = ssn->client.isn + 1; + + ssn->client.last_ack = TCP_GET_ACK(p); + /** If the client has a wscale option the server had it too, + * so set the wscale for the server to max. Otherwise none + * will have the wscale opt just like it should. */ + if (p->tcpvars.ws != NULL) { + ssn->client.wscale = TCP_GET_WSCALE(p); + ssn->server.wscale = TCP_WSCALE_MAX; + } - ssn->server.window = TCP_GET_WINDOW(p); - if (p->tcpvars.ws != NULL) { - ssn->flags |= STREAMTCP_FLAG_SERVER_WSCALE; - ssn->server.wscale = TCP_GET_WSCALE(p); - } + SCLogDebug("ssn %p: ssn->client.isn %"PRIu32", ssn->client.next_seq" + " %"PRIu32", ssn->client.last_ack %"PRIu32"", ssn, + ssn->client.isn, ssn->client.next_seq, + ssn->client.last_ack); + SCLogDebug("ssn %p: ssn->server.isn %"PRIu32", ssn->server.next_seq" + " %"PRIu32", ssn->server.last_ack %"PRIu32"", ssn, + ssn->server.isn, ssn->server.next_seq, + ssn->server.last_ack); + + /* Set the timestamp value for both streams, if packet has timestamp + * option enabled.*/ + if (p->tcpvars.ts != NULL) { + ssn->server.last_ts = TCP_GET_TSVAL(p); + ssn->client.last_ts = TCP_GET_TSECR(p); + SCLogDebug("ssn %p: ssn->server.last_ts %" PRIu32" " + "ssn->client.last_ts %" PRIu32"", ssn, + ssn->server.last_ts, ssn->client.last_ts); + + ssn->flags |= STREAMTCP_FLAG_TIMESTAMP; + + ssn->server.last_pkt_ts = p->ts.tv_sec; + if (ssn->server.last_ts == 0) + ssn->server.flags |= STREAMTCP_FLAG_ZERO_TIMESTAMP; + if (ssn->client.last_ts == 0) + ssn->client.flags |= STREAMTCP_FLAG_ZERO_TIMESTAMP; - if (TCP_GET_SACKOK(p) == 1) { - ssn->flags |= STREAMTCP_FLAG_CLIENT_SACKOK; - SCLogDebug("ssn %p: SACK permited on SYN packet", ssn); - } + } else { + ssn->server.last_ts = 0; + ssn->client.last_ts = 0; + } - SCLogDebug("ssn %p: ssn->client.isn %" PRIu32 ", " - "ssn->client.next_seq %" PRIu32 ", ssn->client.last_ack " - "%"PRIu32"", ssn, ssn->client.isn, ssn->client.next_seq, - ssn->client.last_ack); - break; + if (TCP_GET_SACKOK(p) == 1) { + ssn->flags |= STREAMTCP_FLAG_SACKOK; + SCLogDebug("ssn %p: SYN/ACK with SACK permitted, assuming " + "SACK permitted for both sides", ssn); } - case TH_SYN|TH_ACK: - case TH_SYN|TH_ACK|TH_ECN: - case TH_SYN|TH_ACK|TH_ECN|TH_CWR: - if (stream_config.midstream == FALSE && - stream_config.async_oneside == FALSE) - break; + } else if (p->tcph->th_flags & TH_SYN) { + if (ssn == NULL) { + ssn = StreamTcpNewSession(p); if (ssn == NULL) { - ssn = StreamTcpNewSession(p); - if (ssn == NULL) { - SCPerfCounterIncr(stt->counter_tcp_ssn_memcap, tv->sc_perf_pca); - return -1; - } - SCPerfCounterIncr(stt->counter_tcp_sessions, tv->sc_perf_pca); - } - /* set the state */ - StreamTcpPacketSetState(p, ssn, TCP_SYN_RECV); - SCLogDebug("ssn %p: =~ midstream picked ssn state is now " - "TCP_SYN_RECV", ssn); - ssn->flags |= STREAMTCP_FLAG_MIDSTREAM; - /* Flag used to change the direct in the later stage in the session */ - ssn->flags |= STREAMTCP_FLAG_MIDSTREAM_SYNACK; - - /* sequence number & window */ - ssn->server.isn = TCP_GET_SEQ(p); - STREAMTCP_SET_RA_BASE_SEQ(&ssn->server, ssn->server.isn); - ssn->server.next_seq = ssn->server.isn + 1; - ssn->server.window = TCP_GET_WINDOW(p); - SCLogDebug("ssn %p: server window %u", ssn, ssn->server.window); - - ssn->client.isn = TCP_GET_ACK(p) - 1; - STREAMTCP_SET_RA_BASE_SEQ(&ssn->client, ssn->client.isn); - ssn->client.next_seq = ssn->client.isn + 1; - - ssn->client.last_ack = TCP_GET_ACK(p); - /** If the client has a wscale option the server had it too, - * so set the wscale for the server to max. Otherwise none - * will have the wscale opt just like it should. */ - if (p->tcpvars.ws != NULL) { - ssn->client.wscale = TCP_GET_WSCALE(p); - ssn->server.wscale = TCP_WSCALE_MAX; + SCPerfCounterIncr(stt->counter_tcp_ssn_memcap, tv->sc_perf_pca); + return -1; } - SCLogDebug("ssn %p: ssn->client.isn %"PRIu32", ssn->client.next_seq" - " %"PRIu32", ssn->client.last_ack %"PRIu32"", ssn, - ssn->client.isn, ssn->client.next_seq, - ssn->client.last_ack); - SCLogDebug("ssn %p: ssn->server.isn %"PRIu32", ssn->server.next_seq" - " %"PRIu32", ssn->server.last_ack %"PRIu32"", ssn, - ssn->server.isn, ssn->server.next_seq, - ssn->server.last_ack); - - /* Set the timestamp value for both streams, if packet has timestamp - * option enabled.*/ - if (p->tcpvars.ts != NULL) { - ssn->server.last_ts = TCP_GET_TSVAL(p); - ssn->client.last_ts = TCP_GET_TSECR(p); - SCLogDebug("ssn %p: ssn->server.last_ts %" PRIu32" " - "ssn->client.last_ts %" PRIu32"", ssn, - ssn->server.last_ts, ssn->client.last_ts); + SCPerfCounterIncr(stt->counter_tcp_sessions, tv->sc_perf_pca); + } - ssn->flags |= STREAMTCP_FLAG_TIMESTAMP; + /* set the state */ + StreamTcpPacketSetState(p, ssn, TCP_SYN_SENT); + SCLogDebug("ssn %p: =~ ssn state is now TCP_SYN_SENT", ssn); - ssn->server.last_pkt_ts = p->ts.tv_sec; - if (ssn->server.last_ts == 0) - ssn->server.flags |= STREAMTCP_FLAG_ZERO_TIMESTAMP; - if (ssn->client.last_ts == 0) - ssn->client.flags |= STREAMTCP_FLAG_ZERO_TIMESTAMP; + /* set the sequence numbers and 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; - } else { - ssn->server.last_ts = 0; - ssn->client.last_ts = 0; - } + /* Set the stream timestamp value, if packet has timestamp option + * enabled. */ + if (p->tcpvars.ts != NULL) { + ssn->client.last_ts = TCP_GET_TSVAL(p); + SCLogDebug("ssn %p: p->tcpvars.ts %p, %02x", ssn, p->tcpvars.ts, + ssn->client.last_ts); - if (TCP_GET_SACKOK(p) == 1) { - ssn->flags |= STREAMTCP_FLAG_SACKOK; - SCLogDebug("ssn %p: SYN/ACK with SACK permitted, assuming " - "SACK permitted for both sides", ssn); - } + if (ssn->client.last_ts == 0) + ssn->client.flags |= STREAMTCP_FLAG_ZERO_TIMESTAMP; - break; - /* Handle SYN/ACK and 3WHS shake missed together as it is almost - * similar. */ - case TH_ACK: - case TH_ACK| TH_URG: - case TH_ACK|TH_ECN: - case TH_ACK|TH_ECN|TH_CWR: - case TH_ACK|TH_PUSH: - case TH_ACK|TH_PUSH|TH_URG: - case TH_ACK|TH_PUSH|TH_ECN: - case TH_ACK|TH_PUSH|TH_ECN|TH_CWR: - if (stream_config.midstream == FALSE) - break; - if (ssn == NULL) { - ssn = StreamTcpNewSession(p); - if (ssn == NULL) { - SCPerfCounterIncr(stt->counter_tcp_ssn_memcap, tv->sc_perf_pca); - return -1; - } - SCPerfCounterIncr(stt->counter_tcp_sessions, tv->sc_perf_pca); - } - /* set the state */ - StreamTcpPacketSetState(p, ssn, TCP_ESTABLISHED); - SCLogDebug("ssn %p: =~ midstream picked ssn state is now " - "TCP_ESTABLISHED", ssn); + ssn->client.last_pkt_ts = p->ts.tv_sec; + ssn->client.flags |= STREAMTCP_FLAG_TIMESTAMP; + } - ssn->flags = STREAMTCP_FLAG_MIDSTREAM; - ssn->flags |= STREAMTCP_FLAG_MIDSTREAM_ESTABLISHED; + ssn->server.window = TCP_GET_WINDOW(p); + if (p->tcpvars.ws != NULL) { + ssn->flags |= STREAMTCP_FLAG_SERVER_WSCALE; + ssn->server.wscale = TCP_GET_WSCALE(p); + } - /* set the sequence numbers and window */ - ssn->client.isn = TCP_GET_SEQ(p) - 1; - STREAMTCP_SET_RA_BASE_SEQ(&ssn->client, ssn->client.isn); - ssn->client.next_seq = TCP_GET_SEQ(p) + p->payload_len; - ssn->client.window = TCP_GET_WINDOW(p); - ssn->client.last_ack = TCP_GET_SEQ(p); - ssn->client.next_win = ssn->client.last_ack + ssn->client.window; - SCLogDebug("ssn %p: ssn->client.isn %u, ssn->client.next_seq %u", - ssn, ssn->client.isn, ssn->client.next_seq); + if (TCP_GET_SACKOK(p) == 1) { + ssn->flags |= STREAMTCP_FLAG_CLIENT_SACKOK; + SCLogDebug("ssn %p: SACK permited on SYN packet", ssn); + } - ssn->server.isn = TCP_GET_ACK(p) - 1; - STREAMTCP_SET_RA_BASE_SEQ(&ssn->server, ssn->server.isn); - ssn->server.next_seq = ssn->server.isn + 1; - ssn->server.last_ack = TCP_GET_ACK(p); - ssn->server.next_win = ssn->server.last_ack; - - SCLogDebug("ssn %p: ssn->client.next_win %"PRIu32", " - "ssn->server.next_win %"PRIu32"", ssn, - ssn->client.next_win, ssn->server.next_win); - SCLogDebug("ssn %p: ssn->client.last_ack %"PRIu32", " - "ssn->server.last_ack %"PRIu32"", ssn, - ssn->client.last_ack, ssn->server.last_ack); - - /** window scaling for midstream pickups, we can't do much other - * than assume that it's set to the max value: 14 */ - ssn->client.wscale = TCP_WSCALE_MAX; - ssn->server.wscale = TCP_WSCALE_MAX; + SCLogDebug("ssn %p: ssn->client.isn %" PRIu32 ", " + "ssn->client.next_seq %" PRIu32 ", ssn->client.last_ack " + "%"PRIu32"", ssn, ssn->client.isn, ssn->client.next_seq, + ssn->client.last_ack); - /* Set the timestamp value for both streams, if packet has timestamp - * option enabled.*/ - if (p->tcpvars.ts != NULL) { - ssn->client.last_ts = TCP_GET_TSVAL(p); - ssn->server.last_ts = TCP_GET_TSECR(p); - SCLogDebug("ssn %p: ssn->server.last_ts %" PRIu32" " - "ssn->client.last_ts %" PRIu32"", ssn, - ssn->server.last_ts, ssn->client.last_ts); + } else if (p->tcph->th_flags & TH_ACK) { + if (stream_config.midstream == FALSE) + return 0; - ssn->flags |= STREAMTCP_FLAG_TIMESTAMP; + if (ssn == NULL) { + ssn = StreamTcpNewSession(p); + if (ssn == NULL) { + SCPerfCounterIncr(stt->counter_tcp_ssn_memcap, tv->sc_perf_pca); + return -1; + } + SCPerfCounterIncr(stt->counter_tcp_sessions, tv->sc_perf_pca); + } + /* set the state */ + StreamTcpPacketSetState(p, ssn, TCP_ESTABLISHED); + SCLogDebug("ssn %p: =~ midstream picked ssn state is now " + "TCP_ESTABLISHED", ssn); + + ssn->flags = STREAMTCP_FLAG_MIDSTREAM; + ssn->flags |= STREAMTCP_FLAG_MIDSTREAM_ESTABLISHED; + + /* set the sequence numbers and window */ + ssn->client.isn = TCP_GET_SEQ(p) - 1; + STREAMTCP_SET_RA_BASE_SEQ(&ssn->client, ssn->client.isn); + ssn->client.next_seq = TCP_GET_SEQ(p) + p->payload_len; + ssn->client.window = TCP_GET_WINDOW(p); + ssn->client.last_ack = TCP_GET_SEQ(p); + ssn->client.next_win = ssn->client.last_ack + ssn->client.window; + SCLogDebug("ssn %p: ssn->client.isn %u, ssn->client.next_seq %u", + ssn, ssn->client.isn, ssn->client.next_seq); + + ssn->server.isn = TCP_GET_ACK(p) - 1; + STREAMTCP_SET_RA_BASE_SEQ(&ssn->server, ssn->server.isn); + ssn->server.next_seq = ssn->server.isn + 1; + ssn->server.last_ack = TCP_GET_ACK(p); + ssn->server.next_win = ssn->server.last_ack; + + SCLogDebug("ssn %p: ssn->client.next_win %"PRIu32", " + "ssn->server.next_win %"PRIu32"", ssn, + ssn->client.next_win, ssn->server.next_win); + SCLogDebug("ssn %p: ssn->client.last_ack %"PRIu32", " + "ssn->server.last_ack %"PRIu32"", ssn, + ssn->client.last_ack, ssn->server.last_ack); + + /** window scaling for midstream pickups, we can't do much other + * than assume that it's set to the max value: 14 */ + ssn->client.wscale = TCP_WSCALE_MAX; + ssn->server.wscale = TCP_WSCALE_MAX; + + /* Set the timestamp value for both streams, if packet has timestamp + * option enabled.*/ + if (p->tcpvars.ts != NULL) { + ssn->client.last_ts = TCP_GET_TSVAL(p); + ssn->server.last_ts = TCP_GET_TSECR(p); + SCLogDebug("ssn %p: ssn->server.last_ts %" PRIu32" " + "ssn->client.last_ts %" PRIu32"", ssn, + ssn->server.last_ts, ssn->client.last_ts); + + ssn->flags |= STREAMTCP_FLAG_TIMESTAMP; + + ssn->client.last_pkt_ts = p->ts.tv_sec; + if (ssn->server.last_ts == 0) + ssn->server.flags |= STREAMTCP_FLAG_ZERO_TIMESTAMP; + if (ssn->client.last_ts == 0) + ssn->client.flags |= STREAMTCP_FLAG_ZERO_TIMESTAMP; - ssn->client.last_pkt_ts = p->ts.tv_sec; - if (ssn->server.last_ts == 0) - ssn->server.flags |= STREAMTCP_FLAG_ZERO_TIMESTAMP; - if (ssn->client.last_ts == 0) - ssn->client.flags |= STREAMTCP_FLAG_ZERO_TIMESTAMP; + } else { + ssn->server.last_ts = 0; + ssn->client.last_ts = 0; + } - } else { - ssn->server.last_ts = 0; - ssn->client.last_ts = 0; - } + StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn, &ssn->client, p, pq); - StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn, &ssn->client, p, pq); + ssn->flags |= STREAMTCP_FLAG_SACKOK; + SCLogDebug("ssn %p: assuming SACK permitted for both sides", ssn); - ssn->flags |= STREAMTCP_FLAG_SACKOK; - SCLogDebug("ssn %p: assuming SACK permitted for both sides", ssn); - break; - case TH_RST: - case TH_RST|TH_ACK: - case TH_RST|TH_ACK|TH_ECN: - case TH_RST|TH_ACK|TH_ECN|TH_CWR: - case TH_RST|TH_ACK|TH_PUSH: - case TH_RST|TH_ACK|TH_PUSH|TH_ECN: - case TH_RST|TH_ACK|TH_PUSH|TH_ECN|TH_CWR: - StreamTcpSetEvent(p, STREAM_RST_BUT_NO_SESSION); - SCLogDebug("RST packet received, no session setup"); - break; - case TH_FIN: - case TH_FIN|TH_ACK: - case TH_FIN|TH_ACK|TH_ECN: - case TH_FIN|TH_ACK|TH_ECN|TH_CWR: - case TH_FIN|TH_ACK|TH_PUSH: - case TH_FIN|TH_ACK|TH_PUSH|TH_ECN: - case TH_FIN|TH_ACK|TH_PUSH|TH_ECN|TH_CWR: - StreamTcpSetEvent(p, STREAM_FIN_BUT_NO_SESSION); - SCLogDebug("FIN packet received, no session setup"); - break; - default: - SCLogDebug("default case"); - break; + } else { + SCLogDebug("default case"); } return 0; @@ -906,7 +871,7 @@ static int StreamTcpPacketStateNone(ThreadVars *tv, Packet *p, /** * \brief Function to handle the TCP_SYN_SENT state. The function handles - * SYN, SYN/ACK, RSTpackets and correspondingly changes the connection + * SYN, SYN/ACK, RST packets and correspondingly changes the connection * state. * * \param tv Thread Variable containig input/output queue, cpu affinity @@ -923,365 +888,353 @@ static int StreamTcpPacketStateSynSent(ThreadVars *tv, Packet *p, SCLogDebug("ssn %p: pkt received: %s", ssn, PKT_IS_TOCLIENT(p) ? "toclient":"toserver"); - switch (p->tcph->th_flags) { - case TH_SYN: - case TH_SYN|TH_URG: - case TH_SYN|TH_CWR: - case TH_SYN|TH_CWR|TH_ECN: - SCLogDebug("ssn %p: SYN packet on state SYN_SENT... resent", ssn); - if (ssn->flags & STREAMTCP_FLAG_4WHS) { - SCLogDebug("ssn %p: SYN packet on state SYN_SENT... resent of " - "4WHS SYN", ssn); - } - - if (PKT_IS_TOCLIENT(p)) { - /** a SYN only packet in the opposite direction could be: - * http://www.breakingpointsystems.com/community/blog/tcp- - * portals-the-three-way-handshake-is-a-lie - * - * \todo improve resetting the session */ - - /* indicate that we're dealing with 4WHS here */ - ssn->flags |= STREAMTCP_FLAG_4WHS; - SCLogDebug("ssn %p: STREAMTCP_FLAG_4WHS flag set", ssn); - - /* set the sequence numbers and window for server - * We leave the ssn->client.isn in place as we will - * check the SYN/ACK pkt with that. - */ - ssn->server.isn = TCP_GET_SEQ(p); - STREAMTCP_SET_RA_BASE_SEQ(&ssn->server, ssn->server.isn); - ssn->server.next_seq = ssn->server.isn + 1; - - /* Set the stream timestamp value, if packet has timestamp - * option enabled. */ - if (p->tcpvars.ts != NULL) { - ssn->server.last_ts = TCP_GET_TSVAL(p); - SCLogDebug("ssn %p: p->tcpvars.ts %p, %02x", ssn, - p->tcpvars.ts, ssn->server.last_ts); - - if (ssn->server.last_ts == 0) - ssn->server.flags |= STREAMTCP_FLAG_ZERO_TIMESTAMP; - ssn->server.last_pkt_ts = p->ts.tv_sec; - ssn->server.flags |= STREAMTCP_FLAG_TIMESTAMP; - } - - ssn->server.window = TCP_GET_WINDOW(p); - if (p->tcpvars.ws != NULL) { - ssn->flags |= STREAMTCP_FLAG_SERVER_WSCALE; - ssn->server.wscale = TCP_GET_WSCALE(p); - } else { - ssn->flags &= ~STREAMTCP_FLAG_SERVER_WSCALE; - ssn->server.wscale = 0; - } - - if (TCP_GET_SACKOK(p) == 1) { - ssn->flags |= STREAMTCP_FLAG_CLIENT_SACKOK; - } else { - ssn->flags &= ~STREAMTCP_FLAG_CLIENT_SACKOK; - } + /* RST */ + if (p->tcph->th_flags & TH_RST) { + if (!StreamTcpValidateRst(ssn, p)) + return -1; - SCLogDebug("ssn %p: 4WHS ssn->server.isn %" PRIu32 ", " - "ssn->server.next_seq %" PRIu32 ", " - "ssn->server.last_ack %"PRIu32"", ssn, - ssn->server.isn, ssn->server.next_seq, - ssn->server.last_ack); - SCLogDebug("ssn %p: 4WHS ssn->client.isn %" PRIu32 ", " - "ssn->client.next_seq %" PRIu32 ", " - "ssn->client.last_ack %"PRIu32"", ssn, - ssn->client.isn, ssn->client.next_seq, - ssn->client.last_ack); + 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))) + { + StreamTcpPacketSetState(p, ssn, TCP_CLOSED); + ssn->server.flags |= STREAMTCP_STREAM_FLAG_CLOSE_INITIATED; + ssn->client.flags |= STREAMTCP_STREAM_FLAG_CLOSE_INITIATED; + SCLogDebug("ssn %p: Reset received and state changed to " + "TCP_CLOSED", ssn); } + } else { + StreamTcpPacketSetState(p, ssn, TCP_CLOSED); + ssn->server.flags |= STREAMTCP_STREAM_FLAG_CLOSE_INITIATED; + ssn->client.flags |= STREAMTCP_STREAM_FLAG_CLOSE_INITIATED; + SCLogDebug("ssn %p: Reset received and state changed to " + "TCP_CLOSED", ssn); + } - /** \todo check if it's correct or set event */ - - break; - case TH_SYN|TH_ACK: - case TH_SYN|TH_ACK|TH_ECN: - case TH_SYN|TH_ACK|TH_ECN|TH_CWR: - if (ssn->flags & STREAMTCP_FLAG_4WHS && PKT_IS_TOSERVER(p)) { - 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 ((p->tcpvars.ts != NULL) && (ssn->server.flags & - STREAMTCP_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->server.flags &= ~STREAMTCP_FLAG_TIMESTAMP; - ssn->flags |= STREAMTCP_FLAG_TIMESTAMP; - ssn->client.last_pkt_ts = p->ts.tv_sec; - if (ssn->client.last_ts == 0) - ssn->client.flags |= STREAMTCP_FLAG_ZERO_TIMESTAMP; - } else { - ssn->server.last_ts = 0; - ssn->client.last_ts = 0; - ssn->server.flags &= ~STREAMTCP_FLAG_TIMESTAMP; - ssn->server.flags &= ~STREAMTCP_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) && - (p->tcpvars.ws != NULL)) - { - ssn->server.wscale = TCP_GET_WSCALE(p); - } else { - ssn->server.wscale = 0; - } + /* FIN */ + } else if (p->tcph->th_flags & TH_FIN) { + /** \todo */ - 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); - } + /* 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); - 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 */ - break; - } + /* 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 (PKT_IS_TOSERVER(p)) { - StreamTcpSetEvent(p, STREAM_3WHS_SYNACK_IN_WRONG_DIRECTION); - SCLogDebug("ssn %p: SYN/ACK received in the wrong direction", ssn); + 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 ack's the earlier - * received SYN packet. */ - if (!(SEQ_EQ(TCP_GET_ACK(p), ssn->client.isn + 1))) { - StreamTcpSetEvent(p, STREAM_3WHS_SYNACK_WITH_WRONG_ACK); - SCLogDebug("ssn %p: ACK mismatch, packet ACK %" PRIu32 " != " - "%" PRIu32 " from stream", ssn, TCP_GET_ACK(p), - ssn->client.isn + 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: =~ ssn state is now TCP_SYN_RECV", ssn); + SCLogDebug("ssn %p: =~ 4WHS ssn state is now TCP_SYN_RECV", ssn); /* sequence number & window */ - ssn->server.isn = TCP_GET_SEQ(p); - STREAMTCP_SET_RA_BASE_SEQ(&ssn->server, ssn->server.isn); - ssn->server.next_seq = ssn->server.isn + 1; + 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->client.window = TCP_GET_WINDOW(p); - SCLogDebug("ssn %p: window %" PRIu32 "", ssn, ssn->server.window); + 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 ((p->tcpvars.ts != NULL) && - (ssn->client.flags & STREAMTCP_FLAG_TIMESTAMP)) + * received packets. */ + if ((p->tcpvars.ts != NULL) && (ssn->server.flags & + STREAMTCP_FLAG_TIMESTAMP)) { - ssn->server.last_ts = TCP_GET_TSVAL(p); - SCLogDebug("ssn %p: ssn->server.last_ts %" PRIu32" " - "ssn->client.last_ts %" PRIu32"", ssn, - ssn->server.last_ts, ssn->client.last_ts); - ssn->client.flags &= ~STREAMTCP_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->server.flags &= ~STREAMTCP_FLAG_TIMESTAMP; ssn->flags |= STREAMTCP_FLAG_TIMESTAMP; - ssn->server.last_pkt_ts = p->ts.tv_sec; - if (ssn->server.last_ts == 0) - ssn->server.flags |= STREAMTCP_FLAG_ZERO_TIMESTAMP; + ssn->client.last_pkt_ts = p->ts.tv_sec; + if (ssn->client.last_ts == 0) + ssn->client.flags |= STREAMTCP_FLAG_ZERO_TIMESTAMP; } else { - ssn->client.last_ts = 0; ssn->server.last_ts = 0; - ssn->client.flags &= ~STREAMTCP_FLAG_TIMESTAMP; - ssn->client.flags &= ~STREAMTCP_FLAG_ZERO_TIMESTAMP; + ssn->client.last_ts = 0; + ssn->server.flags &= ~STREAMTCP_FLAG_TIMESTAMP; + ssn->server.flags &= ~STREAMTCP_FLAG_ZERO_TIMESTAMP; } - ssn->client.last_ack = TCP_GET_ACK(p); - ssn->server.last_ack = ssn->server.isn + 1; + 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) && (p->tcpvars.ws != NULL)) { - ssn->client.wscale = TCP_GET_WSCALE(p); + ssn->server.wscale = TCP_GET_WSCALE(p); } else { - ssn->client.wscale = 0; + ssn->server.wscale = 0; } if ((ssn->flags & STREAMTCP_FLAG_CLIENT_SACKOK) && - TCP_GET_SACKOK(p) == 1) { + TCP_GET_SACKOK(p) == 1) { ssn->flags |= STREAMTCP_FLAG_SACKOK; - SCLogDebug("ssn %p: SACK permitted for session", ssn); + SCLogDebug("ssn %p: SACK permitted for 4WHS session", ssn); } - ssn->server.next_win = ssn->server.last_ack + ssn->server.window; ssn->client.next_win = ssn->client.last_ack + ssn->client.window; - SCLogDebug("ssn %p: ssn->server.next_win %" PRIu32 "", ssn, - ssn->server.next_win); - SCLogDebug("ssn %p: ssn->client.next_win %" PRIu32 "", ssn, - ssn->client.next_win); - SCLogDebug("ssn %p: ssn->server.isn %" PRIu32 ", " - "ssn->server.next_seq %" PRIu32 ", " - "ssn->server.last_ack %" PRIu32 " " - "(ssn->client.last_ack %" PRIu32 ")", ssn, - ssn->server.isn, ssn->server.next_seq, - ssn->server.last_ack, ssn->client.last_ack); - - /* unset the 4WHS flag as we received this SYN/ACK as part of a - * (so far) valid 3WHS */ - if (ssn->flags & STREAMTCP_FLAG_4WHS) - SCLogDebug("ssn %p: STREAMTCP_FLAG_4WHS unset, normal SYN/ACK" - " so considering 3WHS", ssn); - - ssn->flags &=~ STREAMTCP_FLAG_4WHS; - break; - case TH_ACK: - case TH_ACK|TH_URG: - case TH_ACK|TH_ECN: - case TH_ACK|TH_ECN|TH_CWR: - case TH_ACK|TH_PUSH: - case TH_ACK|TH_PUSH|TH_URG: - case TH_ACK|TH_PUSH|TH_ECN: - case TH_ACK|TH_PUSH|TH_ECN|TH_CWR: - /* Handle the asynchronous stream, when we receive a SYN packet - and now istead of receving a SYN/ACK we receive a ACK from the - same host, which sent the SYN, this suggests the ASNYC streams.*/ - if (stream_config.async_oneside == FALSE) - break; + 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; + } + + 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; + } - /* we are in AYNC (one side) mode now. */ + /* Check if the SYN/ACK packet ack's the earlier + * received SYN packet. */ + if (!(SEQ_EQ(TCP_GET_ACK(p), ssn->client.isn + 1))) { + StreamTcpSetEvent(p, STREAM_3WHS_SYNACK_WITH_WRONG_ACK); + SCLogDebug("ssn %p: ACK mismatch, packet ACK %" PRIu32 " != " + "%" PRIu32 " from stream", ssn, TCP_GET_ACK(p), + ssn->client.isn + 1); + return -1; + } - /* one side async means we won't see a SYN/ACK, so we can - * only check the SYN. */ - if (!(SEQ_EQ(TCP_GET_SEQ(p), ssn->client.next_seq))) { - StreamTcpSetEvent(p, STREAM_3WHS_ASYNC_WRONG_SEQ); + /* update state */ + StreamTcpPacketSetState(p, ssn, TCP_SYN_RECV); + SCLogDebug("ssn %p: =~ ssn state is now TCP_SYN_RECV", ssn); - SCLogDebug("ssn %p: SEQ mismatch, packet SEQ %" PRIu32 " != " - "%" PRIu32 " from stream",ssn, TCP_GET_SEQ(p), - ssn->client.next_seq); - return -1; - } + /* sequence number & window */ + ssn->server.isn = TCP_GET_SEQ(p); + STREAMTCP_SET_RA_BASE_SEQ(&ssn->server, ssn->server.isn); + ssn->server.next_seq = ssn->server.isn + 1; - ssn->flags |= STREAMTCP_FLAG_ASYNC; - StreamTcpPacketSetState(p, ssn, TCP_ESTABLISHED); - SCLogDebug("ssn %p: =~ ssn state is now TCP_ESTABLISHED", ssn); + ssn->client.window = TCP_GET_WINDOW(p); + SCLogDebug("ssn %p: window %" PRIu32 "", ssn, ssn->server.window); - ssn->client.window = TCP_GET_WINDOW(p); - ssn->client.last_ack = TCP_GET_SEQ(p); - ssn->client.next_win = ssn->client.last_ack + ssn->client.window; + /* Set the timestamp values used to validate the timestamp of + * received packets.*/ + if ((p->tcpvars.ts != NULL) && + (ssn->client.flags & STREAMTCP_FLAG_TIMESTAMP)) + { + ssn->server.last_ts = TCP_GET_TSVAL(p); + SCLogDebug("ssn %p: ssn->server.last_ts %" PRIu32" " + "ssn->client.last_ts %" PRIu32"", ssn, + ssn->server.last_ts, ssn->client.last_ts); + ssn->client.flags &= ~STREAMTCP_FLAG_TIMESTAMP; + ssn->flags |= STREAMTCP_FLAG_TIMESTAMP; + ssn->server.last_pkt_ts = p->ts.tv_sec; + if (ssn->server.last_ts == 0) + ssn->server.flags |= STREAMTCP_FLAG_ZERO_TIMESTAMP; + } else { + ssn->client.last_ts = 0; + ssn->server.last_ts = 0; + ssn->client.flags &= ~STREAMTCP_FLAG_TIMESTAMP; + ssn->client.flags &= ~STREAMTCP_FLAG_ZERO_TIMESTAMP; + } + + ssn->client.last_ack = TCP_GET_ACK(p); + ssn->server.last_ack = ssn->server.isn + 1; - /* Set the server side parameters */ - ssn->server.isn = TCP_GET_ACK(p) - 1; + /** check for the presense of the ws ptr to determine if we + * support wscale at all */ + if ((ssn->flags & STREAMTCP_FLAG_SERVER_WSCALE) && + (p->tcpvars.ws != NULL)) + { + ssn->client.wscale = TCP_GET_WSCALE(p); + } else { + ssn->client.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 session", ssn); + } + + ssn->server.next_win = ssn->server.last_ack + ssn->server.window; + ssn->client.next_win = ssn->client.last_ack + ssn->client.window; + SCLogDebug("ssn %p: ssn->server.next_win %" PRIu32 "", ssn, + ssn->server.next_win); + SCLogDebug("ssn %p: ssn->client.next_win %" PRIu32 "", ssn, + ssn->client.next_win); + SCLogDebug("ssn %p: ssn->server.isn %" PRIu32 ", " + "ssn->server.next_seq %" PRIu32 ", " + "ssn->server.last_ack %" PRIu32 " " + "(ssn->client.last_ack %" PRIu32 ")", ssn, + ssn->server.isn, ssn->server.next_seq, + ssn->server.last_ack, ssn->client.last_ack); + + /* unset the 4WHS flag as we received this SYN/ACK as part of a + * (so far) valid 3WHS */ + if (ssn->flags & STREAMTCP_FLAG_4WHS) + SCLogDebug("ssn %p: STREAMTCP_FLAG_4WHS unset, normal SYN/ACK" + " so considering 3WHS", ssn); + + ssn->flags &=~ STREAMTCP_FLAG_4WHS; + + } else if (p->tcph->th_flags & TH_SYN) { + SCLogDebug("ssn %p: SYN packet on state SYN_SENT... resent", ssn); + if (ssn->flags & STREAMTCP_FLAG_4WHS) { + SCLogDebug("ssn %p: SYN packet on state SYN_SENT... resent of " + "4WHS SYN", ssn); + } + + if (PKT_IS_TOCLIENT(p)) { + /** a SYN only packet in the opposite direction could be: + * http://www.breakingpointsystems.com/community/blog/tcp- + * portals-the-three-way-handshake-is-a-lie + * + * \todo improve resetting the session */ + + /* indicate that we're dealing with 4WHS here */ + ssn->flags |= STREAMTCP_FLAG_4WHS; + SCLogDebug("ssn %p: STREAMTCP_FLAG_4WHS flag set", ssn); + + /* set the sequence numbers and window for server + * We leave the ssn->client.isn in place as we will + * check the SYN/ACK pkt with that. + */ + ssn->server.isn = TCP_GET_SEQ(p); STREAMTCP_SET_RA_BASE_SEQ(&ssn->server, ssn->server.isn); ssn->server.next_seq = ssn->server.isn + 1; - ssn->server.last_ack = ssn->server.next_seq; - ssn->server.next_win = ssn->server.last_ack; - SCLogDebug("ssn %p: synsent => Asynchronous stream, packet SEQ" - " %" PRIu32 ", payload size %" PRIu32 " (%" PRIu32 "), " - "ssn->client.next_seq %" PRIu32 "" - ,ssn, TCP_GET_SEQ(p), p->payload_len, TCP_GET_SEQ(p) - + p->payload_len, ssn->client.next_seq); + /* Set the stream timestamp value, if packet has timestamp + * option enabled. */ + if (p->tcpvars.ts != NULL) { + ssn->server.last_ts = TCP_GET_TSVAL(p); + SCLogDebug("ssn %p: p->tcpvars.ts %p, %02x", ssn, + p->tcpvars.ts, ssn->server.last_ts); - ssn->client.wscale = TCP_WSCALE_MAX; - ssn->server.wscale = TCP_WSCALE_MAX; + if (ssn->server.last_ts == 0) + ssn->server.flags |= STREAMTCP_FLAG_ZERO_TIMESTAMP; + ssn->server.last_pkt_ts = p->ts.tv_sec; + ssn->server.flags |= STREAMTCP_FLAG_TIMESTAMP; + } - /* Set the timestamp values used to validate the timestamp of - * received packets.*/ - if (p->tcpvars.ts != NULL && - (ssn->client.flags & STREAMTCP_FLAG_TIMESTAMP)) - { - ssn->flags |= STREAMTCP_FLAG_TIMESTAMP; - ssn->client.flags &= ~STREAMTCP_FLAG_TIMESTAMP; - ssn->client.last_pkt_ts = p->ts.tv_sec; + ssn->server.window = TCP_GET_WINDOW(p); + if (p->tcpvars.ws != NULL) { + ssn->flags |= STREAMTCP_FLAG_SERVER_WSCALE; + ssn->server.wscale = TCP_GET_WSCALE(p); } else { - ssn->client.last_ts = 0; - ssn->client.flags &= ~STREAMTCP_FLAG_TIMESTAMP; - ssn->client.flags &= ~STREAMTCP_FLAG_ZERO_TIMESTAMP; + ssn->flags &= ~STREAMTCP_FLAG_SERVER_WSCALE; + ssn->server.wscale = 0; } - if (ssn->flags & STREAMTCP_FLAG_CLIENT_SACKOK) { - ssn->flags |= STREAMTCP_FLAG_SACKOK; + if (TCP_GET_SACKOK(p) == 1) { + ssn->flags |= STREAMTCP_FLAG_CLIENT_SACKOK; + } else { + ssn->flags &= ~STREAMTCP_FLAG_CLIENT_SACKOK; } - break; - case TH_RST: - case TH_RST|TH_ACK: - case TH_RST|TH_ACK|TH_ECN: - case TH_RST|TH_ACK|TH_ECN|TH_CWR: - if (StreamTcpValidateRst(ssn, p)) { - 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))) - { - StreamTcpPacketSetState(p, ssn, TCP_CLOSED); - ssn->server.flags |= STREAMTCP_STREAM_FLAG_CLOSE_INITIATED; - ssn->client.flags |= STREAMTCP_STREAM_FLAG_CLOSE_INITIATED; - SCLogDebug("ssn %p: Reset received and state changed to " - "TCP_CLOSED", ssn); - } - } else { - StreamTcpPacketSetState(p, ssn, TCP_CLOSED); - ssn->server.flags |= STREAMTCP_STREAM_FLAG_CLOSE_INITIATED; - ssn->client.flags |= STREAMTCP_STREAM_FLAG_CLOSE_INITIATED; - SCLogDebug("ssn %p: Reset received and state changed to " - "TCP_CLOSED", ssn); - } - } else - return -1; - break; - default: - SCLogDebug("ssn %p: default case", ssn); - break; + + SCLogDebug("ssn %p: 4WHS ssn->server.isn %" PRIu32 ", " + "ssn->server.next_seq %" PRIu32 ", " + "ssn->server.last_ack %"PRIu32"", ssn, + ssn->server.isn, ssn->server.next_seq, + ssn->server.last_ack); + SCLogDebug("ssn %p: 4WHS ssn->client.isn %" PRIu32 ", " + "ssn->client.next_seq %" PRIu32 ", " + "ssn->client.last_ack %"PRIu32"", ssn, + ssn->client.isn, ssn->client.next_seq, + ssn->client.last_ack); + } + + /** \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 istead of receving a SYN/ACK we receive a ACK from the + same host, which sent the SYN, this suggests the ASNYC streams.*/ + if (stream_config.async_oneside == FALSE) + return 0; + + /* we are in AYNC (one side) mode now. */ + + /* one side async means we won't see a SYN/ACK, so we can + * only check the SYN. */ + if (!(SEQ_EQ(TCP_GET_SEQ(p), ssn->client.next_seq))) { + StreamTcpSetEvent(p, STREAM_3WHS_ASYNC_WRONG_SEQ); + + SCLogDebug("ssn %p: SEQ mismatch, packet SEQ %" PRIu32 " != " + "%" PRIu32 " from stream",ssn, TCP_GET_SEQ(p), + ssn->client.next_seq); + return -1; + } + + ssn->flags |= STREAMTCP_FLAG_ASYNC; + StreamTcpPacketSetState(p, ssn, TCP_ESTABLISHED); + SCLogDebug("ssn %p: =~ ssn state is now TCP_ESTABLISHED", ssn); + + ssn->client.window = TCP_GET_WINDOW(p); + ssn->client.last_ack = TCP_GET_SEQ(p); + ssn->client.next_win = ssn->client.last_ack + ssn->client.window; + + /* Set the server side parameters */ + ssn->server.isn = TCP_GET_ACK(p) - 1; + STREAMTCP_SET_RA_BASE_SEQ(&ssn->server, ssn->server.isn); + ssn->server.next_seq = ssn->server.isn + 1; + ssn->server.last_ack = ssn->server.next_seq; + ssn->server.next_win = ssn->server.last_ack; + + SCLogDebug("ssn %p: synsent => Asynchronous stream, packet SEQ" + " %" PRIu32 ", payload size %" PRIu32 " (%" PRIu32 "), " + "ssn->client.next_seq %" PRIu32 "" + ,ssn, TCP_GET_SEQ(p), p->payload_len, TCP_GET_SEQ(p) + + p->payload_len, ssn->client.next_seq); + + ssn->client.wscale = TCP_WSCALE_MAX; + ssn->server.wscale = TCP_WSCALE_MAX; + + /* Set the timestamp values used to validate the timestamp of + * received packets.*/ + if (p->tcpvars.ts != NULL && + (ssn->client.flags & STREAMTCP_FLAG_TIMESTAMP)) + { + ssn->flags |= STREAMTCP_FLAG_TIMESTAMP; + ssn->client.flags &= ~STREAMTCP_FLAG_TIMESTAMP; + ssn->client.last_pkt_ts = p->ts.tv_sec; + } else { + ssn->client.last_ts = 0; + ssn->client.flags &= ~STREAMTCP_FLAG_TIMESTAMP; + ssn->client.flags &= ~STREAMTCP_FLAG_ZERO_TIMESTAMP; + } + + if (ssn->flags & STREAMTCP_FLAG_CLIENT_SACKOK) { + ssn->flags |= STREAMTCP_FLAG_SACKOK; + } + + } else { + SCLogDebug("ssn %p: default case", ssn); } return 0; @@ -1306,308 +1259,287 @@ static int StreamTcpPacketStateSynRecv(ThreadVars *tv, Packet *p, if (ssn == NULL) return -1; - switch (p->tcph->th_flags) { - case TH_SYN: - case TH_SYN|TH_URG: - case TH_SYN|TH_CWR: - case TH_SYN|TH_CWR|TH_ECN: - SCLogDebug("ssn %p: SYN packet on state SYN_RECV... resent", ssn); - - if (PKT_IS_TOCLIENT(p)) { - SCLogDebug("ssn %p: SYN-pkt to client in SYN_RECV state", ssn); + if (p->tcph->th_flags & TH_RST) { + if (!StreamTcpValidateRst(ssn, p)) + return -1; - StreamTcpSetEvent(p, STREAM_3WHS_SYN_TOCLIENT_ON_SYN_RECV); - return -1; + uint8_t reset = TRUE; + /* After receiveing the RST in SYN_RECV state and if detection + evasion flags has been set, then the following operating + systems will not closed the connection. As they consider the + packet as stray packet and not belonging to the current + session, for more information check + http://www.packetstan.com/2010/06/recently-ive-been-on-campaign-to-make.html */ + if (ssn->flags & STREAMTCP_FLAG_DETECTION_EVASION_ATTEMPT) { + if (PKT_IS_TOSERVER(p)) { + if ((ssn->server.os_policy == OS_POLICY_LINUX) || + (ssn->server.os_policy == OS_POLICY_OLD_LINUX) || + (ssn->server.os_policy == OS_POLICY_SOLARIS)) + { + reset = FALSE; + SCLogDebug("Detection evasion has been attempted, so" + " not resetting the connection !!"); + } + } else { + if ((ssn->client.os_policy == OS_POLICY_LINUX) | + (ssn->client.os_policy == OS_POLICY_OLD_LINUX) || + (ssn->client.os_policy == OS_POLICY_SOLARIS)) + { + reset = FALSE; + SCLogDebug("Detection evasion has been attempted, so" + " not resetting the connection !!"); + } } + } - if (!(SEQ_EQ(TCP_GET_SEQ(p), ssn->client.isn))) { - SCLogDebug("ssn %p: SYN with different SEQ on SYN_RECV state", ssn); + if (reset == TRUE) { + StreamTcpPacketSetState(p, ssn, TCP_CLOSED); + ssn->server.flags |= STREAMTCP_STREAM_FLAG_CLOSE_INITIATED; + ssn->client.flags |= STREAMTCP_STREAM_FLAG_CLOSE_INITIATED; + SCLogDebug("ssn %p: Reset received and state changed to " + "TCP_CLOSED", ssn); - StreamTcpSetEvent(p, STREAM_3WHS_SYN_RESEND_DIFF_SEQ_ON_SYN_RECV); - return -1; + if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) { + StreamTcpHandleTimestamp(ssn, p); } + } - break; - case TH_SYN|TH_ACK: - case TH_SYN|TH_ACK|TH_ECN: - case TH_SYN|TH_ACK|TH_ECN|TH_CWR: - SCLogDebug("ssn %p: SYN/ACK packet on state SYN_RECV. resent", ssn); + } else if (p->tcph->th_flags & TH_FIN) { + /* FIN is handled in the same way as in TCP_ESTABLISHED case */; + if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) { + if (!StreamTcpValidateTimestamp(ssn, p)) + return -1; + } - if (PKT_IS_TOSERVER(p)) { - SCLogDebug("ssn %p: SYN/ACK-pkt to server in SYN_RECV state", ssn); + if ((StreamTcpHandleFin(tv, stt, ssn, p, pq)) == -1) + return -1; - StreamTcpSetEvent(p, STREAM_3WHS_SYNACK_TOSERVER_ON_SYN_RECV); - return -1; - } + /* SYN/ACK */ + } else if ((p->tcph->th_flags & (TH_SYN|TH_ACK)) == (TH_SYN|TH_ACK)) { + SCLogDebug("ssn %p: SYN/ACK packet on state SYN_RECV. resent", ssn); + + if (PKT_IS_TOSERVER(p)) { + SCLogDebug("ssn %p: SYN/ACK-pkt to server in SYN_RECV state", ssn); + + StreamTcpSetEvent(p, STREAM_3WHS_SYNACK_TOSERVER_ON_SYN_RECV); + return -1; + } + + /* Check if the SYN/ACK packets ACK matches the earlier + * received SYN/ACK packet. */ + if (!(SEQ_EQ(TCP_GET_ACK(p), ssn->client.last_ack))) { + SCLogDebug("ssn %p: ACK mismatch, packet ACK %" PRIu32 " != " + "%" PRIu32 " from stream", ssn, TCP_GET_ACK(p), + ssn->client.isn + 1); + + StreamTcpSetEvent(p, STREAM_3WHS_SYNACK_RESEND_WITH_DIFFERENT_ACK); + return -1; + } + + /* Check if the SYN/ACK packet SEQ the earlier + * received SYN packet. */ + if (!(SEQ_EQ(TCP_GET_SEQ(p), ssn->server.isn))) { + SCLogDebug("ssn %p: SEQ mismatch, packet SEQ %" PRIu32 " != " + "%" PRIu32 " from stream", ssn, TCP_GET_ACK(p), + ssn->client.isn + 1); + + StreamTcpSetEvent(p, STREAM_3WHS_SYNACK_RESEND_WITH_DIFF_SEQ); + return -1; + } + + } else if (p->tcph->th_flags & TH_SYN) { + SCLogDebug("ssn %p: SYN packet on state SYN_RECV... resent", ssn); - /* Check if the SYN/ACK packets ACK matches the earlier - * received SYN/ACK packet. */ - if (!(SEQ_EQ(TCP_GET_ACK(p), ssn->client.last_ack))) { - SCLogDebug("ssn %p: ACK mismatch, packet ACK %" PRIu32 " != " - "%" PRIu32 " from stream", ssn, TCP_GET_ACK(p), - ssn->client.isn + 1); + if (PKT_IS_TOCLIENT(p)) { + SCLogDebug("ssn %p: SYN-pkt to client in SYN_RECV state", ssn); - StreamTcpSetEvent(p, STREAM_3WHS_SYNACK_RESEND_WITH_DIFFERENT_ACK); + StreamTcpSetEvent(p, STREAM_3WHS_SYN_TOCLIENT_ON_SYN_RECV); + return -1; + } + + if (!(SEQ_EQ(TCP_GET_SEQ(p), ssn->client.isn))) { + SCLogDebug("ssn %p: SYN with different SEQ on SYN_RECV state", ssn); + + StreamTcpSetEvent(p, STREAM_3WHS_SYN_RESEND_DIFF_SEQ_ON_SYN_RECV); + return -1; + } + + } else if (p->tcph->th_flags & TH_ACK) { + /* If the timestamp option is enabled for both the streams, then + * validate the received packet timestamp value against the + * stream->last_ts. If the timestamp is valid then process the + * packet normally otherwise the drop the packet (RFC 1323)*/ + if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) { + if (!(StreamTcpValidateTimestamp(ssn, p))) { return -1; } + } - /* Check if the SYN/ACK packet SEQ the earlier - * received SYN packet. */ - if (!(SEQ_EQ(TCP_GET_SEQ(p), ssn->server.isn))) { - SCLogDebug("ssn %p: SEQ mismatch, packet SEQ %" PRIu32 " != " - "%" PRIu32 " from stream", ssn, TCP_GET_ACK(p), - ssn->client.isn + 1); + if (ssn->flags & STREAMTCP_FLAG_4WHS && PKT_IS_TOCLIENT(p)) { + SCLogDebug("ssn %p: ACK received on 4WHS session",ssn); - StreamTcpSetEvent(p, STREAM_3WHS_SYNACK_RESEND_WITH_DIFF_SEQ); + if (!(SEQ_EQ(TCP_GET_SEQ(p), ssn->server.next_seq))) { + SCLogDebug("ssn %p: 4WHS wrong seq nr on packet", ssn); + StreamTcpSetEvent(p, STREAM_4WHS_WRONG_SEQ); return -1; } - break; - case TH_ACK: - case TH_ACK|TH_URG: - case TH_ACK|TH_ECN: - case TH_ACK|TH_ECN|TH_CWR: - case TH_ACK|TH_PUSH: - case TH_ACK|TH_PUSH|TH_URG: - case TH_ACK|TH_PUSH|TH_ECN: - case TH_ACK|TH_PUSH|TH_ECN|TH_CWR: - /* If the timestamp option is enabled for both the streams, then - * validate the received packet timestamp value against the - * stream->last_ts. If the timestamp is valid then process the - * packet normally otherwise the drop the packet (RFC 1323)*/ - if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) { - if (!(StreamTcpValidateTimestamp(ssn, p))) { - return -1; - } + if (StreamTcpValidateAck(&ssn->client, p) == -1) { + SCLogDebug("ssn %p: 4WHS invalid ack nr on packet", ssn); + StreamTcpSetEvent(p, STREAM_4WHS_INVALID_ACK); + return -1; } - if (ssn->flags & STREAMTCP_FLAG_4WHS && PKT_IS_TOCLIENT(p)) { - SCLogDebug("ssn %p: ACK received on 4WHS session",ssn); + SCLogDebug("4WHS normal pkt"); + SCLogDebug("ssn %p: pkt (%" PRIu32 ") is to client: SEQ " + "%" PRIu32 ", ACK %" PRIu32 "", ssn, p->payload_len, + TCP_GET_SEQ(p), TCP_GET_ACK(p)); - if (!(SEQ_EQ(TCP_GET_SEQ(p), ssn->server.next_seq))) { - SCLogDebug("ssn %p: 4WHS wrong seq nr on packet", ssn); - StreamTcpSetEvent(p, STREAM_4WHS_WRONG_SEQ); - return -1; - } - - if (StreamTcpValidateAck(&ssn->client, p) == -1) { - SCLogDebug("ssn %p: 4WHS invalid ack nr on packet", ssn); - StreamTcpSetEvent(p, STREAM_4WHS_INVALID_ACK); - return -1; - } + if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) { + StreamTcpHandleTimestamp(ssn, p); + } - SCLogDebug("4WHS normal pkt"); - SCLogDebug("ssn %p: pkt (%" PRIu32 ") is to client: SEQ " - "%" PRIu32 ", ACK %" PRIu32 "", ssn, p->payload_len, - TCP_GET_SEQ(p), TCP_GET_ACK(p)); + StreamTcpUpdateLastAck(ssn, &ssn->client, TCP_GET_ACK(p)); + ssn->server.next_seq += p->payload_len; + ssn->client.window = TCP_GET_WINDOW(p) << ssn->client.wscale; + ssn->client.next_win = ssn->client.last_ack + ssn->client.window; - if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) { - StreamTcpHandleTimestamp(ssn, p); - } + StreamTcpPacketSetState(p, ssn, TCP_ESTABLISHED); + SCLogDebug("ssn %p: =~ ssn state is now TCP_ESTABLISHED", ssn); - StreamTcpUpdateLastAck(ssn, &ssn->client, TCP_GET_ACK(p)); - ssn->server.next_seq += p->payload_len; - ssn->client.window = TCP_GET_WINDOW(p) << ssn->client.wscale; - ssn->client.next_win = ssn->client.last_ack + ssn->client.window; + StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn, + &ssn->server, p, pq); - StreamTcpPacketSetState(p, ssn, TCP_ESTABLISHED); - SCLogDebug("ssn %p: =~ ssn state is now TCP_ESTABLISHED", ssn); + SCLogDebug("ssn %p: ssn->client.next_win %" PRIu32 ", " + "ssn->client.last_ack %"PRIu32"", ssn, + ssn->client.next_win, ssn->client.last_ack); + return 0; + } - StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn, - &ssn->server, p, pq); + /* Check if the ACK received is in right direction. But when we have + * picked up a mid stream session after missing the initial SYN pkt, + * in this case the ACK packet can arrive from either client (normal + * case) or from server itself (asynchronous streams). Therefore + * the check has been avoided in this case */ + if (PKT_IS_TOCLIENT(p)) { + /* special case, handle 4WHS, so SYN/ACK in the opposite + * direction */ + if (ssn->flags & STREAMTCP_FLAG_MIDSTREAM_SYNACK) { + SCLogDebug("ssn %p: ACK received on midstream SYN/ACK " + "pickup session",ssn); + /* fall through */ + } else { + SCLogDebug("ssn %p: ACK received in the wrong direction", + ssn); - SCLogDebug("ssn %p: ssn->client.next_win %" PRIu32 ", " - "ssn->client.last_ack %"PRIu32"", ssn, - ssn->client.next_win, ssn->client.last_ack); - break; + StreamTcpSetEvent(p, STREAM_3WHS_ACK_IN_WRONG_DIR); + return -1; } + } - /* Check if the ACK received is in right direction. But when we have - * picked up a mid stream session after missing the initial SYN pkt, - * in this case the ACK packet can arrive from either client (normal - * case) or from server itself (asynchronous streams). Therefore - * the check has been avoided in this case */ - if (PKT_IS_TOCLIENT(p)) { - /* special case, handle 4WHS, so SYN/ACK in the opposite - * direction */ - if (ssn->flags & STREAMTCP_FLAG_MIDSTREAM_SYNACK) { - SCLogDebug("ssn %p: ACK received on midstream SYN/ACK " - "pickup session",ssn); - /* fall through */ - } else { - SCLogDebug("ssn %p: ACK received in the wrong direction", - ssn); + SCLogDebug("ssn %p: pkt (%" PRIu32 ") is to server: SEQ %" PRIu32 "" + ", ACK %" PRIu32 "", ssn, p->payload_len, TCP_GET_SEQ(p), + TCP_GET_ACK(p)); - StreamTcpSetEvent(p, STREAM_3WHS_ACK_IN_WRONG_DIR); - return -1; - } - } + /* Check both seq and ack number before accepting the packet and + changing to ESTABLISHED state */ + if ((SEQ_EQ(TCP_GET_SEQ(p), ssn->client.next_seq)) && + SEQ_EQ(TCP_GET_ACK(p), ssn->server.next_seq)) { + SCLogDebug("normal pkt"); - SCLogDebug("ssn %p: pkt (%" PRIu32 ") is to server: SEQ %" PRIu32 "" - ", ACK %" PRIu32 "", ssn, p->payload_len, TCP_GET_SEQ(p), - TCP_GET_ACK(p)); + /* process the packet normal, No Async streams :) */ - /* Check both seq and ack number before accepting the packet and - changing to ESTABLISHED state */ - if ((SEQ_EQ(TCP_GET_SEQ(p), ssn->client.next_seq)) && - SEQ_EQ(TCP_GET_ACK(p), ssn->server.next_seq)) { - SCLogDebug("normal pkt"); + if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) { + StreamTcpHandleTimestamp(ssn, p); + } - /* process the packet normal, No Async streams :) */ + StreamTcpUpdateLastAck(ssn, &ssn->server, TCP_GET_ACK(p)); - if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) { - StreamTcpHandleTimestamp(ssn, p); - } + ssn->client.next_seq += p->payload_len; + ssn->server.window = TCP_GET_WINDOW(p) << ssn->server.wscale; - StreamTcpUpdateLastAck(ssn, &ssn->server, TCP_GET_ACK(p)); + ssn->server.next_win = ssn->server.last_ack + ssn->server.window; - ssn->client.next_seq += p->payload_len; - ssn->server.window = TCP_GET_WINDOW(p) << ssn->server.wscale; - - ssn->server.next_win = ssn->server.last_ack + ssn->server.window; - - if (ssn->flags & STREAMTCP_FLAG_MIDSTREAM) { - ssn->client.window = TCP_GET_WINDOW(p); - ssn->server.next_win = ssn->server.last_ack + - ssn->server.window; - /* window scaling for midstream pickups, we can't do much - * other than assume that it's set to the max value: 14 */ - ssn->server.wscale = TCP_WSCALE_MAX; - ssn->client.wscale = TCP_WSCALE_MAX; - ssn->flags |= STREAMTCP_FLAG_SACKOK; - } + if (ssn->flags & STREAMTCP_FLAG_MIDSTREAM) { + ssn->client.window = TCP_GET_WINDOW(p); + ssn->server.next_win = ssn->server.last_ack + + ssn->server.window; + /* window scaling for midstream pickups, we can't do much + * other than assume that it's set to the max value: 14 */ + ssn->server.wscale = TCP_WSCALE_MAX; + ssn->client.wscale = TCP_WSCALE_MAX; + ssn->flags |= STREAMTCP_FLAG_SACKOK; + } - StreamTcpPacketSetState(p, ssn, TCP_ESTABLISHED); - SCLogDebug("ssn %p: =~ ssn state is now TCP_ESTABLISHED", ssn); + StreamTcpPacketSetState(p, ssn, TCP_ESTABLISHED); + SCLogDebug("ssn %p: =~ ssn state is now TCP_ESTABLISHED", ssn); - StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn, - &ssn->client, p, pq); + StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn, + &ssn->client, p, pq); - /* If asynchronous stream handling is allowed then set the session, - if packet's seq number is equal the expected seq no.*/ - } else if (stream_config.async_oneside == TRUE && - (SEQ_EQ(TCP_GET_SEQ(p), ssn->server.next_seq))) - { - /*set the ASYNC flag used to indicate the session as async stream - and helps in relaxing the windows checks.*/ - ssn->flags |= STREAMTCP_FLAG_ASYNC; - ssn->server.next_seq += p->payload_len; - ssn->server.last_ack = TCP_GET_SEQ(p); + /* If asynchronous stream handling is allowed then set the session, + if packet's seq number is equal the expected seq no.*/ + } else if (stream_config.async_oneside == TRUE && + (SEQ_EQ(TCP_GET_SEQ(p), ssn->server.next_seq))) + { + /*set the ASYNC flag used to indicate the session as async stream + and helps in relaxing the windows checks.*/ + ssn->flags |= STREAMTCP_FLAG_ASYNC; + ssn->server.next_seq += p->payload_len; + ssn->server.last_ack = TCP_GET_SEQ(p); - ssn->client.window = TCP_GET_WINDOW(p) << ssn->client.wscale; - ssn->client.last_ack = TCP_GET_ACK(p); + ssn->client.window = TCP_GET_WINDOW(p) << ssn->client.wscale; + ssn->client.last_ack = TCP_GET_ACK(p); - if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) { - StreamTcpHandleTimestamp(ssn, p); - } + if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) { + StreamTcpHandleTimestamp(ssn, p); + } - if (ssn->flags & STREAMTCP_FLAG_MIDSTREAM) { - ssn->server.window = TCP_GET_WINDOW(p); - ssn->client.next_win = ssn->server.last_ack + - ssn->server.window; - /* window scaling for midstream pickups, we can't do much - * other than assume that it's set to the max value: 14 */ - ssn->server.wscale = TCP_WSCALE_MAX; - ssn->client.wscale = TCP_WSCALE_MAX; - ssn->flags |= STREAMTCP_FLAG_SACKOK; - } + if (ssn->flags & STREAMTCP_FLAG_MIDSTREAM) { + ssn->server.window = TCP_GET_WINDOW(p); + ssn->client.next_win = ssn->server.last_ack + + ssn->server.window; + /* window scaling for midstream pickups, we can't do much + * other than assume that it's set to the max value: 14 */ + ssn->server.wscale = TCP_WSCALE_MAX; + ssn->client.wscale = TCP_WSCALE_MAX; + ssn->flags |= STREAMTCP_FLAG_SACKOK; + } - SCLogDebug("ssn %p: synrecv => Asynchronous stream, packet SEQ" - " %" PRIu32 ", payload size %" PRIu32 " (%" PRIu32 "), " - "ssn->server.next_seq %" PRIu32 "\n" - , ssn, TCP_GET_SEQ(p), p->payload_len, TCP_GET_SEQ(p) - + p->payload_len, ssn->server.next_seq); + SCLogDebug("ssn %p: synrecv => Asynchronous stream, packet SEQ" + " %" PRIu32 ", payload size %" PRIu32 " (%" PRIu32 "), " + "ssn->server.next_seq %" PRIu32 "\n" + , ssn, TCP_GET_SEQ(p), p->payload_len, TCP_GET_SEQ(p) + + p->payload_len, ssn->server.next_seq); - StreamTcpPacketSetState(p, ssn, TCP_ESTABLISHED); - SCLogDebug("ssn %p: =~ ssn state is now TCP_ESTABLISHED", ssn); + StreamTcpPacketSetState(p, ssn, TCP_ESTABLISHED); + SCLogDebug("ssn %p: =~ ssn state is now TCP_ESTABLISHED", ssn); - StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn, - &ssn->server, p, pq); + StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn, + &ssn->server, p, pq); /* Upon receiving the packet with correct seq number and wrong ACK number, it causes the other end to send RST. But some target system (Linux & solaris) does not RST the connection, so it is likely to avoid the detection */ - } else if (SEQ_EQ(TCP_GET_SEQ(p), ssn->client.next_seq)){ - ssn->flags |= STREAMTCP_FLAG_DETECTION_EVASION_ATTEMPT; - SCLogDebug("ssn %p: wrong ack nr on packet, possible evasion!!", - ssn); - - StreamTcpSetEvent(p, STREAM_3WHS_RIGHT_SEQ_WRONG_ACK_EVASION); - return -1; - } else { - SCLogDebug("ssn %p: wrong seq nr on packet", ssn); - - StreamTcpSetEvent(p, STREAM_3WHS_WRONG_SEQ_WRONG_ACK); - return -1; - } - - SCLogDebug("ssn %p: ssn->server.next_win %" PRIu32 ", " - "ssn->server.last_ack %"PRIu32"", ssn, - ssn->server.next_win, ssn->server.last_ack); - break; - case TH_RST: - case TH_RST|TH_ACK: - case TH_RST|TH_ACK|TH_ECN: - case TH_RST|TH_ACK|TH_ECN|TH_CWR: - - if(StreamTcpValidateRst(ssn, p)) { - uint8_t reset = TRUE; - /* After receiveing the RST in SYN_RECV state and if detection - evasion flags has been set, then the following operating - systems will not closed the connection. As they consider the - packet as stray packet and not belonging to the current - session, for more information check - http://www.packetstan.com/2010/06/recently-ive-been-on-campaign-to-make.html */ - if (ssn->flags & STREAMTCP_FLAG_DETECTION_EVASION_ATTEMPT) { - if (PKT_IS_TOSERVER(p)) { - if ((ssn->server.os_policy == OS_POLICY_LINUX) || - (ssn->server.os_policy == OS_POLICY_OLD_LINUX) || - (ssn->server.os_policy == OS_POLICY_SOLARIS)) - { - reset = FALSE; - SCLogDebug("Detection evasion has been attempted, so" - " not resetting the connection !!"); - } - } else { - if ((ssn->client.os_policy == OS_POLICY_LINUX) | - (ssn->client.os_policy == OS_POLICY_OLD_LINUX) || - (ssn->client.os_policy == OS_POLICY_SOLARIS)) - { - reset = FALSE; - SCLogDebug("Detection evasion has been attempted, so" - " not resetting the connection !!"); - } - } - } - - if (reset == TRUE) { - StreamTcpPacketSetState(p, ssn, TCP_CLOSED); - ssn->server.flags |= STREAMTCP_STREAM_FLAG_CLOSE_INITIATED; - ssn->client.flags |= STREAMTCP_STREAM_FLAG_CLOSE_INITIATED; - SCLogDebug("ssn %p: Reset received and state changed to " - "TCP_CLOSED", ssn); + } else if (SEQ_EQ(TCP_GET_SEQ(p), ssn->client.next_seq)){ + ssn->flags |= STREAMTCP_FLAG_DETECTION_EVASION_ATTEMPT; + SCLogDebug("ssn %p: wrong ack nr on packet, possible evasion!!", + ssn); - if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) { - StreamTcpHandleTimestamp(ssn, p); - } - } - } else - return -1; - break; - case TH_FIN: + StreamTcpSetEvent(p, STREAM_3WHS_RIGHT_SEQ_WRONG_ACK_EVASION); + return -1; + } else { + SCLogDebug("ssn %p: wrong seq nr on packet", ssn); - /* FIN is handled in the same way as in TCP_ESTABLISHED case */; - if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) { - if (!StreamTcpValidateTimestamp(ssn, p)) - return -1; - } + StreamTcpSetEvent(p, STREAM_3WHS_WRONG_SEQ_WRONG_ACK); + return -1; + } - if((StreamTcpHandleFin(tv, stt, ssn, p, pq)) == -1) - return -1; - break; - default: - SCLogDebug("ssn %p: default case", ssn); - break; + SCLogDebug("ssn %p: ssn->server.next_win %" PRIu32 ", " + "ssn->server.last_ack %"PRIu32"", ssn, + ssn->server.next_win, ssn->server.last_ack); + } else { + SCLogDebug("ssn %p: default case", ssn); } return 0; @@ -1901,222 +1833,192 @@ static int StreamTcpPacketStateEstablished(ThreadVars *tv, Packet *p, if (ssn == NULL) return -1; - switch (p->tcph->th_flags) { - case TH_SYN: - case TH_SYN|TH_URG: - case TH_SYN|TH_CWR: - case TH_SYN|TH_CWR|TH_ECN: - SCLogDebug("ssn %p: SYN packet on state ESTABLISED... resent", ssn); - if (PKT_IS_TOCLIENT(p)) { - SCLogDebug("ssn %p: SYN-pkt to client in EST state", ssn); + if (p->tcph->th_flags & TH_RST) { + if (!StreamTcpValidateRst(ssn, p)) + return -1; - StreamTcpSetEvent(p, STREAM_EST_SYN_TOCLIENT); - return -1; - } + /* force both streams to reassemble, if necessary */ + StreamTcpPseudoPacketCreateStreamEndPacket(p, ssn, pq); + SCPerfCounterIncr(stt->counter_tcp_pseudo, tv->sc_perf_pca); - if (!(SEQ_EQ(TCP_GET_SEQ(p), ssn->client.isn))) { - SCLogDebug("ssn %p: SYN with different SEQ on SYN_RECV state", ssn); + if (PKT_IS_TOSERVER(p)) { + StreamTcpPacketSetState(p, ssn, TCP_CLOSED); + ssn->server.flags |= STREAMTCP_STREAM_FLAG_CLOSE_INITIATED; + ssn->client.flags |= STREAMTCP_STREAM_FLAG_CLOSE_INITIATED; + SCLogDebug("ssn %p: Reset received and state changed to " + "TCP_CLOSED", ssn); - StreamTcpSetEvent(p, STREAM_EST_SYN_RESEND_DIFF_SEQ); - return -1; + ssn->server.next_seq = TCP_GET_ACK(p); + ssn->client.next_seq = TCP_GET_SEQ(p) + p->payload_len; + SCLogDebug("ssn %p: ssn->server.next_seq %" PRIu32 "", ssn, + ssn->server.next_seq); + ssn->client.window = TCP_GET_WINDOW(p) << ssn->client.wscale; + + StreamTcpUpdateLastAck(ssn, &ssn->server, + StreamTcpResetGetMaxAck(&ssn->server, TCP_GET_ACK(p))); + + StreamTcpUpdateLastAck(ssn, &ssn->client, + StreamTcpResetGetMaxAck(&ssn->client, TCP_GET_SEQ(p))); + + if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) { + StreamTcpHandleTimestamp(ssn, p); } - /* a resend of a SYN while we are established already -- fishy */ - StreamTcpSetEvent(p, STREAM_EST_SYN_RESEND); - return -1; - break; + StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn, + &ssn->client, p, pq); + SCLogDebug("ssn %p: =+ next SEQ %" PRIu32 ", last ACK " + "%" PRIu32 "", ssn, ssn->client.next_seq, + ssn->server.last_ack); - case TH_SYN|TH_ACK: - case TH_SYN|TH_ACK|TH_ECN: - case TH_SYN|TH_ACK|TH_ECN|TH_CWR: - SCLogDebug("ssn %p: SYN/ACK packet on state ESTABLISHED... resent", - ssn); + /* don't return packets to pools here just yet, the pseudo + * packet will take care, otherwise the normal session + * cleanup. */ + } else { + StreamTcpPacketSetState(p, ssn, TCP_CLOSED); + ssn->server.flags |= STREAMTCP_STREAM_FLAG_CLOSE_INITIATED; + ssn->client.flags |= STREAMTCP_STREAM_FLAG_CLOSE_INITIATED; + SCLogDebug("ssn %p: Reset received and state changed to " + "TCP_CLOSED", ssn); - if (PKT_IS_TOSERVER(p)) { - SCLogDebug("ssn %p: SYN/ACK-pkt to server in ESTABLISHED state", ssn); + ssn->server.next_seq = TCP_GET_SEQ(p) + p->payload_len + 1; + ssn->client.next_seq = TCP_GET_ACK(p); - StreamTcpSetEvent(p, STREAM_EST_SYNACK_TOSERVER); - return -1; - } + SCLogDebug("ssn %p: ssn->server.next_seq %" PRIu32 "", ssn, + ssn->server.next_seq); + ssn->server.window = TCP_GET_WINDOW(p) << ssn->server.wscale; - /* Check if the SYN/ACK packets ACK matches the earlier - * received SYN/ACK packet. */ - if (!(SEQ_EQ(TCP_GET_ACK(p), ssn->client.last_ack))) { - SCLogDebug("ssn %p: ACK mismatch, packet ACK %" PRIu32 " != " - "%" PRIu32 " from stream", ssn, TCP_GET_ACK(p), - ssn->client.isn + 1); + StreamTcpUpdateLastAck(ssn, &ssn->client, + StreamTcpResetGetMaxAck(&ssn->client, TCP_GET_ACK(p))); - StreamTcpSetEvent(p, STREAM_EST_SYNACK_RESEND_WITH_DIFFERENT_ACK); - return -1; + StreamTcpUpdateLastAck(ssn, &ssn->server, + StreamTcpResetGetMaxAck(&ssn->server, TCP_GET_SEQ(p))); + + if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) { + StreamTcpHandleTimestamp(ssn, p); } - /* Check if the SYN/ACK packet SEQ the earlier - * received SYN packet. */ - if (!(SEQ_EQ(TCP_GET_SEQ(p), ssn->server.isn))) { - SCLogDebug("ssn %p: SEQ mismatch, packet SEQ %" PRIu32 " != " - "%" PRIu32 " from stream", ssn, TCP_GET_ACK(p), - ssn->client.isn + 1); + StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn, + &ssn->server, p, pq); + SCLogDebug("ssn %p: =+ next SEQ %" PRIu32 ", last ACK " + "%" PRIu32 "", ssn, ssn->server.next_seq, + ssn->client.last_ack); - StreamTcpSetEvent(p, STREAM_EST_SYNACK_RESEND_WITH_DIFF_SEQ); + /* don't return packets to pools here just yet, the pseudo + * packet will take care, otherwise the normal session + * cleanup. */ + } + + } else if (p->tcph->th_flags & TH_FIN) { + if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) { + if (!StreamTcpValidateTimestamp(ssn, p)) return -1; - } + } - /* a resend of a SYN while we are established already -- fishy */ - StreamTcpSetEvent(p, STREAM_EST_SYNACK_RESEND); + SCLogDebug("ssn (%p: FIN received SEQ" + " %" PRIu32 ", last ACK %" PRIu32 ", next win %"PRIu32"," + " win %" PRIu32 "", ssn, ssn->server.next_seq, + ssn->client.last_ack, ssn->server.next_win, + ssn->server.window); + + if ((StreamTcpHandleFin(tv, stt, ssn, p, pq)) == -1) return -1; - break; - case TH_ACK: - case TH_ACK|TH_URG: - case TH_ACK|TH_CWR: - case TH_ACK|TH_ECN: - case TH_ACK|TH_PUSH: - case TH_ACK|TH_PUSH|TH_ECN: - case TH_ACK|TH_PUSH|TH_ECN|TH_CWR: - case TH_ACK|TH_PUSH|TH_URG: - case TH_ACK|TH_PUSH|TH_CWR: - /* Urgent pointer size can be more than the payload size, as it tells - * the future coming data from the sender will be handled urgently - * until data of size equal to urgent offset has been processed - * (RFC 2147) */ - - /* If the timestamp option is enabled for both the streams, then - * validate the received packet timestamp value against the - * stream->last_ts. If the timestamp is valid then process the - * packet normally otherwise the drop the packet (RFC 1323) */ - if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) { - if (!StreamTcpValidateTimestamp(ssn, p)) - return -1; - } - if (PKT_IS_TOSERVER(p)) { - /* Process the received packet to server */ - HandleEstablishedPacketToServer(tv, ssn, p, stt, pq); + /* SYN/ACK */ + } else if ((p->tcph->th_flags & (TH_SYN|TH_ACK)) == (TH_SYN|TH_ACK)) { + SCLogDebug("ssn %p: SYN/ACK packet on state ESTABLISHED... resent", + ssn); - SCLogDebug("ssn %p: next SEQ %" PRIu32 ", last ACK %" PRIu32 "," - " next win %" PRIu32 ", win %" PRIu32 "", ssn, - ssn->client.next_seq, ssn->server.last_ack - ,ssn->client.next_win, ssn->client.window); + if (PKT_IS_TOSERVER(p)) { + SCLogDebug("ssn %p: SYN/ACK-pkt to server in ESTABLISHED state", ssn); - } else { /* implied to client */ + StreamTcpSetEvent(p, STREAM_EST_SYNACK_TOSERVER); + return -1; + } - /* Process the received packet to client */ - HandleEstablishedPacketToClient(tv, ssn, p, stt, pq); + /* Check if the SYN/ACK packets ACK matches the earlier + * received SYN/ACK packet. */ + if (!(SEQ_EQ(TCP_GET_ACK(p), ssn->client.last_ack))) { + SCLogDebug("ssn %p: ACK mismatch, packet ACK %" PRIu32 " != " + "%" PRIu32 " from stream", ssn, TCP_GET_ACK(p), + ssn->client.isn + 1); - SCLogDebug("ssn %p: next SEQ %" PRIu32 ", last ACK %" PRIu32 "," - " next win %" PRIu32 ", win %" PRIu32 "", ssn, - ssn->server.next_seq, ssn->client.last_ack, - ssn->server.next_win, ssn->server.window); - } - break; + StreamTcpSetEvent(p, STREAM_EST_SYNACK_RESEND_WITH_DIFFERENT_ACK); + return -1; + } - case TH_FIN: - case TH_FIN|TH_ACK: - case TH_FIN|TH_ACK|TH_ECN: - case TH_FIN|TH_ACK|TH_ECN|TH_CWR: - case TH_FIN|TH_ACK|TH_PUSH: - case TH_FIN|TH_ACK|TH_PUSH|TH_ECN: - case TH_FIN|TH_ACK|TH_PUSH|TH_ECN|TH_CWR: + /* Check if the SYN/ACK packet SEQ the earlier + * received SYN packet. */ + if (!(SEQ_EQ(TCP_GET_SEQ(p), ssn->server.isn))) { + SCLogDebug("ssn %p: SEQ mismatch, packet SEQ %" PRIu32 " != " + "%" PRIu32 " from stream", ssn, TCP_GET_ACK(p), + ssn->client.isn + 1); - if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) { - if (!StreamTcpValidateTimestamp(ssn, p)) - return -1; - } + StreamTcpSetEvent(p, STREAM_EST_SYNACK_RESEND_WITH_DIFF_SEQ); + return -1; + } - SCLogDebug("ssn (%p: FIN received SEQ" - " %" PRIu32 ", last ACK %" PRIu32 ", next win %"PRIu32"," - " win %" PRIu32 "", ssn, ssn->server.next_seq, - ssn->client.last_ack, ssn->server.next_win, - ssn->server.window); + /* a resend of a SYN while we are established already -- fishy */ + StreamTcpSetEvent(p, STREAM_EST_SYNACK_RESEND); + return -1; - if((StreamTcpHandleFin(tv, stt, ssn, p, pq)) == -1) - return -1; - break; + } else if (p->tcph->th_flags & TH_SYN) { + SCLogDebug("ssn %p: SYN packet on state ESTABLISED... resent", ssn); + if (PKT_IS_TOCLIENT(p)) { + SCLogDebug("ssn %p: SYN-pkt to client in EST state", ssn); - case TH_RST: - case TH_RST|TH_ACK: - case TH_RST|TH_ACK|TH_ECN: - case TH_RST|TH_ACK|TH_ECN|TH_CWR: - - if (StreamTcpValidateRst(ssn, p)) { - /* force both streams to reassemble, if necessary */ - StreamTcpPseudoPacketCreateStreamEndPacket(p, ssn, pq); - SCPerfCounterIncr(stt->counter_tcp_pseudo, tv->sc_perf_pca); - - if(PKT_IS_TOSERVER(p)) { - StreamTcpPacketSetState(p, ssn, TCP_CLOSED); - ssn->server.flags |= STREAMTCP_STREAM_FLAG_CLOSE_INITIATED; - ssn->client.flags |= STREAMTCP_STREAM_FLAG_CLOSE_INITIATED; - SCLogDebug("ssn %p: Reset received and state changed to " - "TCP_CLOSED", ssn); - - ssn->server.next_seq = TCP_GET_ACK(p); - ssn->client.next_seq = TCP_GET_SEQ(p) + p->payload_len; - SCLogDebug("ssn %p: ssn->server.next_seq %" PRIu32 "", ssn, - ssn->server.next_seq); - ssn->client.window = TCP_GET_WINDOW(p) << ssn->client.wscale; + StreamTcpSetEvent(p, STREAM_EST_SYN_TOCLIENT); + return -1; + } - StreamTcpUpdateLastAck(ssn, &ssn->server, - StreamTcpResetGetMaxAck(&ssn->server, TCP_GET_ACK(p))); + if (!(SEQ_EQ(TCP_GET_SEQ(p), ssn->client.isn))) { + SCLogDebug("ssn %p: SYN with different SEQ on SYN_RECV state", ssn); - StreamTcpUpdateLastAck(ssn, &ssn->client, - StreamTcpResetGetMaxAck(&ssn->client, TCP_GET_SEQ(p))); + StreamTcpSetEvent(p, STREAM_EST_SYN_RESEND_DIFF_SEQ); + return -1; + } - if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) { - StreamTcpHandleTimestamp(ssn, p); - } + /* a resend of a SYN while we are established already -- fishy */ + StreamTcpSetEvent(p, STREAM_EST_SYN_RESEND); + return -1; - StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn, - &ssn->client, p, pq); - SCLogDebug("ssn %p: =+ next SEQ %" PRIu32 ", last ACK " - "%" PRIu32 "", ssn, ssn->client.next_seq, - ssn->server.last_ack); - - /* don't return packets to pools here just yet, the pseudo - * packet will take care, otherwise the normal session - * cleanup. */ - } else { - StreamTcpPacketSetState(p, ssn, TCP_CLOSED); - ssn->server.flags |= STREAMTCP_STREAM_FLAG_CLOSE_INITIATED; - ssn->client.flags |= STREAMTCP_STREAM_FLAG_CLOSE_INITIATED; - SCLogDebug("ssn %p: Reset received and state changed to " - "TCP_CLOSED", ssn); + } else if (p->tcph->th_flags & TH_ACK) { + /* Urgent pointer size can be more than the payload size, as it tells + * the future coming data from the sender will be handled urgently + * until data of size equal to urgent offset has been processed + * (RFC 2147) */ - ssn->server.next_seq = TCP_GET_SEQ(p) + p->payload_len + 1; - ssn->client.next_seq = TCP_GET_ACK(p); + /* If the timestamp option is enabled for both the streams, then + * validate the received packet timestamp value against the + * stream->last_ts. If the timestamp is valid then process the + * packet normally otherwise the drop the packet (RFC 1323) */ + if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) { + if (!StreamTcpValidateTimestamp(ssn, p)) + return -1; + } - SCLogDebug("ssn %p: ssn->server.next_seq %" PRIu32 "", ssn, - ssn->server.next_seq); - ssn->server.window = TCP_GET_WINDOW(p) << ssn->server.wscale; + if (PKT_IS_TOSERVER(p)) { + /* Process the received packet to server */ + HandleEstablishedPacketToServer(tv, ssn, p, stt, pq); - StreamTcpUpdateLastAck(ssn, &ssn->client, - StreamTcpResetGetMaxAck(&ssn->client, TCP_GET_ACK(p))); + SCLogDebug("ssn %p: next SEQ %" PRIu32 ", last ACK %" PRIu32 "," + " next win %" PRIu32 ", win %" PRIu32 "", ssn, + ssn->client.next_seq, ssn->server.last_ack + ,ssn->client.next_win, ssn->client.window); - StreamTcpUpdateLastAck(ssn, &ssn->server, - StreamTcpResetGetMaxAck(&ssn->server, TCP_GET_SEQ(p))); + } else { /* implied to client */ - if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) { - StreamTcpHandleTimestamp(ssn, p); - } + /* Process the received packet to client */ + HandleEstablishedPacketToClient(tv, ssn, p, stt, pq); - StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn, - &ssn->server, p, pq); - SCLogDebug("ssn %p: =+ next SEQ %" PRIu32 ", last ACK " - "%" PRIu32 "", ssn, ssn->server.next_seq, - ssn->client.last_ack); - - /* don't return packets to pools here just yet, the pseudo - * packet will take care, otherwise the normal session - * cleanup. */ - } - } else { - /* invalid RST, error is given in StreamTcpValidateRst() */ - return -1; - } - break; - default: - SCLogDebug("ssn %p: default case", ssn); - break; + SCLogDebug("ssn %p: next SEQ %" PRIu32 ", last ACK %" PRIu32 "," + " next win %" PRIu32 ", win %" PRIu32 "", ssn, + ssn->server.next_seq, ssn->client.last_ack, + ssn->server.next_win, ssn->server.window); + } + } else { + SCLogDebug("ssn %p: default case", ssn); } + return 0; } @@ -2255,310 +2157,293 @@ static int StreamTcpPacketStateFinWait1(ThreadVars *tv, Packet *p, if (ssn == NULL) return -1; - switch (p->tcph->th_flags) { - case TH_ACK: - case TH_ACK|TH_PUSH: - case TH_ACK|TH_URG: - case TH_ACK|TH_ECN: - case TH_ACK|TH_ECN|TH_CWR: - - if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) { - if (!StreamTcpValidateTimestamp(ssn, p)) - return -1; - } + if (p->tcph->th_flags & TH_RST) { + if (!StreamTcpValidateRst(ssn, p)) + return -1; - if (PKT_IS_TOSERVER(p)) { - SCLogDebug("ssn %p: pkt (%" PRIu32 ") is to server: SEQ " - "%" PRIu32 ", ACK %" PRIu32 "", ssn, p->payload_len, - TCP_GET_SEQ(p), TCP_GET_ACK(p)); - - if (StreamTcpValidateAck(&ssn->server, p) == -1) { - SCLogDebug("ssn %p: rejecting because of invalid ack value", ssn); - StreamTcpSetEvent(p, STREAM_FIN1_INVALID_ACK); - return -1; - } + /* force both streams to reassemble, if necessary */ + StreamTcpPseudoPacketCreateStreamEndPacket(p, ssn, pq); + SCPerfCounterIncr(stt->counter_tcp_pseudo, tv->sc_perf_pca); - if (SEQ_LEQ(TCP_GET_SEQ(p) + p->payload_len, ssn->client.next_win) || - (ssn->flags & STREAMTCP_FLAG_MIDSTREAM) || - ssn->flags & STREAMTCP_FLAG_ASYNC) - { - SCLogDebug("ssn %p: seq %"PRIu32" in window, ssn->client.next_win " - "%" PRIu32 "", ssn, TCP_GET_SEQ(p), ssn->client.next_win); + StreamTcpPacketSetState(p, ssn, TCP_CLOSED); + ssn->server.flags |= STREAMTCP_STREAM_FLAG_CLOSE_INITIATED; + ssn->client.flags |= STREAMTCP_STREAM_FLAG_CLOSE_INITIATED; + SCLogDebug("ssn %p: Reset received state changed to TCP_CLOSED", + ssn); - if (TCP_GET_SEQ(p) == ssn->client.next_seq) { - StreamTcpPacketSetState(p, ssn, TCP_FIN_WAIT2); - SCLogDebug("ssn %p: state changed to TCP_FIN_WAIT2", ssn); - } - } else { - SCLogDebug("ssn %p: -> SEQ mismatch, packet SEQ %" PRIu32 "" - " != %" PRIu32 " from stream", ssn, - TCP_GET_SEQ(p), ssn->client.next_seq); + if (PKT_IS_TOSERVER(p)) { + StreamTcpUpdateLastAck(ssn, &ssn->server, + StreamTcpResetGetMaxAck(&ssn->server, TCP_GET_ACK(p))); - StreamTcpSetEvent(p, STREAM_FIN1_ACK_WRONG_SEQ); - return -1; - } + StreamTcpUpdateLastAck(ssn, &ssn->client, + StreamTcpResetGetMaxAck(&ssn->client, TCP_GET_SEQ(p))); - ssn->server.window = TCP_GET_WINDOW(p) << ssn->server.wscale; + if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) { + StreamTcpHandleTimestamp(ssn, p); + } - StreamTcpUpdateLastAck(ssn, &ssn->server, TCP_GET_ACK(p)); + StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn, + &ssn->client, p, pq); + } else { + StreamTcpUpdateLastAck(ssn, &ssn->client, + StreamTcpResetGetMaxAck(&ssn->client, TCP_GET_ACK(p))); - if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) { - StreamTcpHandleTimestamp(ssn, p); - } + StreamTcpUpdateLastAck(ssn, &ssn->server, + StreamTcpResetGetMaxAck(&ssn->server, TCP_GET_SEQ(p))); - /* Update the next_seq, in case if we have missed the client - packet and server has already received and acked it */ - if (SEQ_LT(ssn->server.next_seq, TCP_GET_ACK(p))) - ssn->server.next_seq = TCP_GET_ACK(p); + if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) { + StreamTcpHandleTimestamp(ssn, p); + } - if (SEQ_EQ(ssn->client.next_seq, TCP_GET_SEQ(p))) { - ssn->client.next_seq += p->payload_len; - SCLogDebug("ssn %p: ssn->client.next_seq %" PRIu32 "", - ssn, ssn->client.next_seq); - } + StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn, + &ssn->server, p, pq); + } - StreamTcpSackUpdatePacket(&ssn->server, p); + } else if (p->tcph->th_flags & TH_FIN) { + if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) { + if (!StreamTcpValidateTimestamp(ssn, p)) + return -1; + } - /* update next_win */ - StreamTcpUpdateNextWin(ssn, &ssn->server, (ssn->server.last_ack + ssn->server.window)); + if (PKT_IS_TOSERVER(p)) { + SCLogDebug("ssn %p: pkt (%" PRIu32 ") is to server: SEQ " + "%" PRIu32 ", ACK %" PRIu32 "", ssn, p->payload_len, + TCP_GET_SEQ(p), TCP_GET_ACK(p)); - StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn, - &ssn->client, p, pq); + if (SEQ_LT(TCP_GET_SEQ(p), ssn->client.next_seq) || + SEQ_GT(TCP_GET_SEQ(p), (ssn->client.last_ack + ssn->client.window))) + { + SCLogDebug("ssn %p: -> SEQ mismatch, packet SEQ %" PRIu32 "" + " != %" PRIu32 " from stream", ssn, + TCP_GET_SEQ(p), ssn->client.next_seq); + StreamTcpSetEvent(p, STREAM_FIN1_FIN_WRONG_SEQ); + return -1; + } - SCLogDebug("ssn %p: =+ next SEQ %" PRIu32 ", last ACK " - "%" PRIu32 "", ssn, ssn->client.next_seq, - ssn->server.last_ack); + if (StreamTcpValidateAck(&ssn->server, p) == -1) { + SCLogDebug("ssn %p: rejecting because of invalid ack value", ssn); + StreamTcpSetEvent(p, STREAM_FIN1_INVALID_ACK); + return -1; + } - } else { /* implied to client */ + StreamTcpPacketSetState(p, ssn, TCP_TIME_WAIT); + ssn->client.flags |= STREAMTCP_STREAM_FLAG_CLOSE_INITIATED; + SCLogDebug("ssn %p: state changed to TCP_TIME_WAIT", ssn); - SCLogDebug("ssn %p: pkt (%" PRIu32 ") is to client: SEQ " - "%" PRIu32 ", ACK %" PRIu32 "", ssn, p->payload_len, - TCP_GET_SEQ(p), TCP_GET_ACK(p)); + ssn->server.window = TCP_GET_WINDOW(p) << ssn->server.wscale; - if (StreamTcpValidateAck(&ssn->client, p) == -1) { - SCLogDebug("ssn %p: rejecting because of invalid ack value", ssn); - StreamTcpSetEvent(p, STREAM_FIN1_INVALID_ACK); - return -1; - } + StreamTcpUpdateLastAck(ssn, &ssn->server, TCP_GET_ACK(p)); - if (SEQ_LEQ(TCP_GET_SEQ(p) + p->payload_len, ssn->server.next_win) || - (ssn->flags & STREAMTCP_FLAG_MIDSTREAM) || - ssn->flags & STREAMTCP_FLAG_ASYNC) - { - SCLogDebug("ssn %p: seq %"PRIu32" in window, ssn->server.next_win " - "%" PRIu32 "", ssn, TCP_GET_SEQ(p), ssn->server.next_win); + if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) { + StreamTcpHandleTimestamp(ssn, p); + } - if (TCP_GET_SEQ(p) == ssn->server.next_seq) { - StreamTcpPacketSetState(p, ssn, TCP_FIN_WAIT2); - SCLogDebug("ssn %p: state changed to TCP_FIN_WAIT2", ssn); - } - } else { - SCLogDebug("ssn %p: -> SEQ mismatch, packet SEQ %" PRIu32 "" - " != %" PRIu32 " from stream", ssn, - TCP_GET_SEQ(p), ssn->server.next_seq); - StreamTcpSetEvent(p, STREAM_FIN1_ACK_WRONG_SEQ); - return -1; - } + /* Update the next_seq, in case if we have missed the client + packet and server has already received and acked it */ + if (SEQ_LT(ssn->server.next_seq, TCP_GET_ACK(p))) + ssn->server.next_seq = TCP_GET_ACK(p); - ssn->client.window = TCP_GET_WINDOW(p) << ssn->client.wscale; + if (SEQ_EQ(ssn->client.next_seq, TCP_GET_SEQ(p))) { + ssn->client.next_seq += p->payload_len; + SCLogDebug("ssn %p: ssn->client.next_seq %" PRIu32 "", + ssn, ssn->client.next_seq); + } - StreamTcpUpdateLastAck(ssn, &ssn->client, TCP_GET_ACK(p)); + StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn, + &ssn->client, p, pq); - if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) { - StreamTcpHandleTimestamp(ssn, p); - } + SCLogDebug("ssn %p: =+ next SEQ %" PRIu32 ", last ACK " + "%" PRIu32 "", ssn, ssn->client.next_seq, + ssn->server.last_ack); + } else { /* implied to client */ + SCLogDebug("ssn %p: pkt (%" PRIu32 ") is to client: SEQ " + "%" PRIu32 ", ACK %" PRIu32 "", ssn, p->payload_len, + TCP_GET_SEQ(p), TCP_GET_ACK(p)); - /* Update the next_seq, in case if we have missed the client - packet and server has already received and acked it */ - if (SEQ_LT(ssn->client.next_seq, TCP_GET_ACK(p))) - ssn->client.next_seq = TCP_GET_ACK(p); + if (SEQ_LT(TCP_GET_SEQ(p), ssn->server.next_seq) || + SEQ_GT(TCP_GET_SEQ(p), (ssn->server.last_ack + ssn->server.window))) + { + SCLogDebug("ssn %p: -> SEQ mismatch, packet SEQ %" PRIu32 "" + " != %" PRIu32 " from stream", ssn, + TCP_GET_SEQ(p), ssn->server.next_seq); + StreamTcpSetEvent(p, STREAM_FIN1_FIN_WRONG_SEQ); + return -1; + } - if (SEQ_EQ(ssn->server.next_seq, TCP_GET_SEQ(p))) { - ssn->server.next_seq += p->payload_len; - SCLogDebug("ssn %p: ssn->server.next_seq %" PRIu32 "", - ssn, ssn->server.next_seq); - } + if (StreamTcpValidateAck(&ssn->client, p) == -1) { + SCLogDebug("ssn %p: rejecting because of invalid ack value", ssn); + StreamTcpSetEvent(p, STREAM_FIN1_INVALID_ACK); + return -1; + } - StreamTcpSackUpdatePacket(&ssn->client, p); + StreamTcpPacketSetState(p, ssn, TCP_TIME_WAIT); + ssn->server.flags |= STREAMTCP_STREAM_FLAG_CLOSE_INITIATED; + SCLogDebug("ssn %p: state changed to TCP_TIME_WAIT", ssn); - /* update next_win */ - StreamTcpUpdateNextWin(ssn, &ssn->client, (ssn->client.last_ack + ssn->client.window)); + ssn->client.window = TCP_GET_WINDOW(p) << ssn->client.wscale; - StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn, - &ssn->server, p, pq); + StreamTcpUpdateLastAck(ssn, &ssn->client, TCP_GET_ACK(p)); - SCLogDebug("ssn %p: =+ next SEQ %" PRIu32 ", last ACK " - "%" PRIu32 "", ssn, ssn->server.next_seq, - ssn->client.last_ack); + if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) { + StreamTcpHandleTimestamp(ssn, p); } - break; - case TH_FIN: - case TH_FIN|TH_ACK: - case TH_FIN|TH_ACK|TH_ECN: - case TH_FIN|TH_ACK|TH_ECN|TH_CWR: - case TH_FIN|TH_ACK|TH_PUSH: - case TH_FIN|TH_ACK|TH_PUSH|TH_ECN: - case TH_FIN|TH_ACK|TH_PUSH|TH_ECN|TH_CWR: + /* Update the next_seq, in case if we have missed the client + packet and server has already received and acked it */ + if (SEQ_LT(ssn->client.next_seq, TCP_GET_ACK(p))) + ssn->client.next_seq = TCP_GET_ACK(p); - if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) { - if (!StreamTcpValidateTimestamp(ssn, p)) - return -1; + if (SEQ_EQ(ssn->server.next_seq, TCP_GET_SEQ(p))) { + ssn->server.next_seq += p->payload_len; + SCLogDebug("ssn %p: ssn->server.next_seq %" PRIu32 "", + ssn, ssn->server.next_seq); } - if (PKT_IS_TOSERVER(p)) { - SCLogDebug("ssn %p: pkt (%" PRIu32 ") is to server: SEQ " - "%" PRIu32 ", ACK %" PRIu32 "", ssn, p->payload_len, - TCP_GET_SEQ(p), TCP_GET_ACK(p)); - - if (SEQ_LT(TCP_GET_SEQ(p), ssn->client.next_seq) || - SEQ_GT(TCP_GET_SEQ(p), (ssn->client.last_ack + ssn->client.window))) - { - SCLogDebug("ssn %p: -> SEQ mismatch, packet SEQ %" PRIu32 "" - " != %" PRIu32 " from stream", ssn, - TCP_GET_SEQ(p), ssn->client.next_seq); - StreamTcpSetEvent(p, STREAM_FIN1_FIN_WRONG_SEQ); - return -1; - } + StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn, + &ssn->server, p, pq); - if (StreamTcpValidateAck(&ssn->server, p) == -1) { - SCLogDebug("ssn %p: rejecting because of invalid ack value", ssn); - StreamTcpSetEvent(p, STREAM_FIN1_INVALID_ACK); - return -1; - } + SCLogDebug("ssn %p: =+ next SEQ %" PRIu32 ", last ACK " + "%" PRIu32 "", ssn, ssn->server.next_seq, + ssn->client.last_ack); + } - StreamTcpPacketSetState(p, ssn, TCP_TIME_WAIT); - ssn->client.flags |= STREAMTCP_STREAM_FLAG_CLOSE_INITIATED; - SCLogDebug("ssn %p: state changed to TCP_TIME_WAIT", ssn); + } else if (p->tcph->th_flags & TH_SYN) { + SCLogDebug("ssn (%p): SYN pkt on FinWait1", ssn); + StreamTcpSetEvent(p, STREAM_SHUTDOWN_SYN_RESEND); + return -1; - ssn->server.window = TCP_GET_WINDOW(p) << ssn->server.wscale; + } else if (p->tcph->th_flags & TH_ACK) { + if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) { + if (!StreamTcpValidateTimestamp(ssn, p)) + return -1; + } - StreamTcpUpdateLastAck(ssn, &ssn->server, TCP_GET_ACK(p)); + if (PKT_IS_TOSERVER(p)) { + SCLogDebug("ssn %p: pkt (%" PRIu32 ") is to server: SEQ " + "%" PRIu32 ", ACK %" PRIu32 "", ssn, p->payload_len, + TCP_GET_SEQ(p), TCP_GET_ACK(p)); - if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) { - StreamTcpHandleTimestamp(ssn, p); - } + if (StreamTcpValidateAck(&ssn->server, p) == -1) { + SCLogDebug("ssn %p: rejecting because of invalid ack value", ssn); + StreamTcpSetEvent(p, STREAM_FIN1_INVALID_ACK); + return -1; + } - /* Update the next_seq, in case if we have missed the client - packet and server has already received and acked it */ - if (SEQ_LT(ssn->server.next_seq, TCP_GET_ACK(p))) - ssn->server.next_seq = TCP_GET_ACK(p); + if (SEQ_LEQ(TCP_GET_SEQ(p) + p->payload_len, ssn->client.next_win) || + (ssn->flags & STREAMTCP_FLAG_MIDSTREAM) || + ssn->flags & STREAMTCP_FLAG_ASYNC) + { + SCLogDebug("ssn %p: seq %"PRIu32" in window, ssn->client.next_win " + "%" PRIu32 "", ssn, TCP_GET_SEQ(p), ssn->client.next_win); - if (SEQ_EQ(ssn->client.next_seq, TCP_GET_SEQ(p))) { - ssn->client.next_seq += p->payload_len; - SCLogDebug("ssn %p: ssn->client.next_seq %" PRIu32 "", - ssn, ssn->client.next_seq); + if (TCP_GET_SEQ(p) == ssn->client.next_seq) { + StreamTcpPacketSetState(p, ssn, TCP_FIN_WAIT2); + SCLogDebug("ssn %p: state changed to TCP_FIN_WAIT2", ssn); } + } else { + SCLogDebug("ssn %p: -> SEQ mismatch, packet SEQ %" PRIu32 "" + " != %" PRIu32 " from stream", ssn, + TCP_GET_SEQ(p), ssn->client.next_seq); - StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn, - &ssn->client, p, pq); + StreamTcpSetEvent(p, STREAM_FIN1_ACK_WRONG_SEQ); + return -1; + } - SCLogDebug("ssn %p: =+ next SEQ %" PRIu32 ", last ACK " - "%" PRIu32 "", ssn, ssn->client.next_seq, - ssn->server.last_ack); - } else { /* implied to client */ - SCLogDebug("ssn %p: pkt (%" PRIu32 ") is to client: SEQ " - "%" PRIu32 ", ACK %" PRIu32 "", ssn, p->payload_len, - TCP_GET_SEQ(p), TCP_GET_ACK(p)); + ssn->server.window = TCP_GET_WINDOW(p) << ssn->server.wscale; - if (SEQ_LT(TCP_GET_SEQ(p), ssn->server.next_seq) || - SEQ_GT(TCP_GET_SEQ(p), (ssn->server.last_ack + ssn->server.window))) - { - SCLogDebug("ssn %p: -> SEQ mismatch, packet SEQ %" PRIu32 "" - " != %" PRIu32 " from stream", ssn, - TCP_GET_SEQ(p), ssn->server.next_seq); - StreamTcpSetEvent(p, STREAM_FIN1_FIN_WRONG_SEQ); - return -1; - } + StreamTcpUpdateLastAck(ssn, &ssn->server, TCP_GET_ACK(p)); - if (StreamTcpValidateAck(&ssn->client, p) == -1) { - SCLogDebug("ssn %p: rejecting because of invalid ack value", ssn); - StreamTcpSetEvent(p, STREAM_FIN1_INVALID_ACK); - return -1; - } + if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) { + StreamTcpHandleTimestamp(ssn, p); + } - StreamTcpPacketSetState(p, ssn, TCP_TIME_WAIT); - ssn->server.flags |= STREAMTCP_STREAM_FLAG_CLOSE_INITIATED; - SCLogDebug("ssn %p: state changed to TCP_TIME_WAIT", ssn); + /* Update the next_seq, in case if we have missed the client + packet and server has already received and acked it */ + if (SEQ_LT(ssn->server.next_seq, TCP_GET_ACK(p))) + ssn->server.next_seq = TCP_GET_ACK(p); - ssn->client.window = TCP_GET_WINDOW(p) << ssn->client.wscale; + if (SEQ_EQ(ssn->client.next_seq, TCP_GET_SEQ(p))) { + ssn->client.next_seq += p->payload_len; + SCLogDebug("ssn %p: ssn->client.next_seq %" PRIu32 "", + ssn, ssn->client.next_seq); + } - StreamTcpUpdateLastAck(ssn, &ssn->client, TCP_GET_ACK(p)); + StreamTcpSackUpdatePacket(&ssn->server, p); - if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) { - StreamTcpHandleTimestamp(ssn, p); - } + /* update next_win */ + StreamTcpUpdateNextWin(ssn, &ssn->server, (ssn->server.last_ack + ssn->server.window)); - /* Update the next_seq, in case if we have missed the client - packet and server has already received and acked it */ - if (SEQ_LT(ssn->client.next_seq, TCP_GET_ACK(p))) - ssn->client.next_seq = TCP_GET_ACK(p); + StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn, + &ssn->client, p, pq); - if (SEQ_EQ(ssn->server.next_seq, TCP_GET_SEQ(p))) { - ssn->server.next_seq += p->payload_len; - SCLogDebug("ssn %p: ssn->server.next_seq %" PRIu32 "", - ssn, ssn->server.next_seq); - } + SCLogDebug("ssn %p: =+ next SEQ %" PRIu32 ", last ACK " + "%" PRIu32 "", ssn, ssn->client.next_seq, + ssn->server.last_ack); - StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn, - &ssn->server, p, pq); + } else { /* implied to client */ - SCLogDebug("ssn %p: =+ next SEQ %" PRIu32 ", last ACK " - "%" PRIu32 "", ssn, ssn->server.next_seq, - ssn->client.last_ack); + SCLogDebug("ssn %p: pkt (%" PRIu32 ") is to client: SEQ " + "%" PRIu32 ", ACK %" PRIu32 "", ssn, p->payload_len, + TCP_GET_SEQ(p), TCP_GET_ACK(p)); + + if (StreamTcpValidateAck(&ssn->client, p) == -1) { + SCLogDebug("ssn %p: rejecting because of invalid ack value", ssn); + StreamTcpSetEvent(p, STREAM_FIN1_INVALID_ACK); + return -1; } - break; - case TH_RST: - case TH_RST|TH_ACK: - case TH_RST|TH_ACK|TH_ECN: - case TH_RST|TH_ACK|TH_ECN|TH_CWR: + if (SEQ_LEQ(TCP_GET_SEQ(p) + p->payload_len, ssn->server.next_win) || + (ssn->flags & STREAMTCP_FLAG_MIDSTREAM) || + ssn->flags & STREAMTCP_FLAG_ASYNC) + { + SCLogDebug("ssn %p: seq %"PRIu32" in window, ssn->server.next_win " + "%" PRIu32 "", ssn, TCP_GET_SEQ(p), ssn->server.next_win); + + if (TCP_GET_SEQ(p) == ssn->server.next_seq) { + StreamTcpPacketSetState(p, ssn, TCP_FIN_WAIT2); + SCLogDebug("ssn %p: state changed to TCP_FIN_WAIT2", ssn); + } + } else { + SCLogDebug("ssn %p: -> SEQ mismatch, packet SEQ %" PRIu32 "" + " != %" PRIu32 " from stream", ssn, + TCP_GET_SEQ(p), ssn->server.next_seq); + StreamTcpSetEvent(p, STREAM_FIN1_ACK_WRONG_SEQ); + return -1; + } - if (StreamTcpValidateRst(ssn, p)) { - /* force both streams to reassemble, if necessary */ - StreamTcpPseudoPacketCreateStreamEndPacket(p, ssn, pq); - SCPerfCounterIncr(stt->counter_tcp_pseudo, tv->sc_perf_pca); + ssn->client.window = TCP_GET_WINDOW(p) << ssn->client.wscale; - StreamTcpPacketSetState(p, ssn, TCP_CLOSED); - ssn->server.flags |= STREAMTCP_STREAM_FLAG_CLOSE_INITIATED; - ssn->client.flags |= STREAMTCP_STREAM_FLAG_CLOSE_INITIATED; - SCLogDebug("ssn %p: Reset received state changed to TCP_CLOSED", - ssn); + StreamTcpUpdateLastAck(ssn, &ssn->client, TCP_GET_ACK(p)); - if (PKT_IS_TOSERVER(p)) { - StreamTcpUpdateLastAck(ssn, &ssn->server, - StreamTcpResetGetMaxAck(&ssn->server, TCP_GET_ACK(p))); + if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) { + StreamTcpHandleTimestamp(ssn, p); + } - StreamTcpUpdateLastAck(ssn, &ssn->client, - StreamTcpResetGetMaxAck(&ssn->client, TCP_GET_SEQ(p))); + /* Update the next_seq, in case if we have missed the client + packet and server has already received and acked it */ + if (SEQ_LT(ssn->client.next_seq, TCP_GET_ACK(p))) + ssn->client.next_seq = TCP_GET_ACK(p); - if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) { - StreamTcpHandleTimestamp(ssn, p); - } + if (SEQ_EQ(ssn->server.next_seq, TCP_GET_SEQ(p))) { + ssn->server.next_seq += p->payload_len; + SCLogDebug("ssn %p: ssn->server.next_seq %" PRIu32 "", + ssn, ssn->server.next_seq); + } - StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn, - &ssn->client, p, pq); - } else { - StreamTcpUpdateLastAck(ssn, &ssn->client, - StreamTcpResetGetMaxAck(&ssn->client, TCP_GET_ACK(p))); + StreamTcpSackUpdatePacket(&ssn->client, p); - StreamTcpUpdateLastAck(ssn, &ssn->server, - StreamTcpResetGetMaxAck(&ssn->server, TCP_GET_SEQ(p))); + /* update next_win */ + StreamTcpUpdateNextWin(ssn, &ssn->client, (ssn->client.last_ack + ssn->client.window)); - if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) { - StreamTcpHandleTimestamp(ssn, p); - } + StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn, + &ssn->server, p, pq); - StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn, - &ssn->server, p, pq); - } - } - else - return -1; - break; - default: - SCLogDebug("ssn (%p): default case", ssn); - break; + SCLogDebug("ssn %p: =+ next SEQ %" PRIu32 ", last ACK " + "%" PRIu32 "", ssn, ssn->server.next_seq, + ssn->client.last_ack); + } + } else { + SCLogDebug("ssn (%p): default case", ssn); } return 0; @@ -2580,279 +2465,262 @@ static int StreamTcpPacketStateFinWait2(ThreadVars *tv, Packet *p, if (ssn == NULL) return -1; - switch (p->tcph->th_flags) { - case TH_ACK: - case TH_ACK|TH_PUSH: - case TH_ACK|TH_URG: - case TH_ACK|TH_ECN: - case TH_ACK|TH_ECN|TH_CWR: + if (p->tcph->th_flags & TH_RST) { + if (!StreamTcpValidateRst(ssn, p)) + return -1; - if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) { - if (!StreamTcpValidateTimestamp(ssn, p)) - return -1; - } + /* force both streams to reassemble, if necessary */ + StreamTcpPseudoPacketCreateStreamEndPacket(p, ssn, pq); + SCPerfCounterIncr(stt->counter_tcp_pseudo, tv->sc_perf_pca); - if (PKT_IS_TOSERVER(p)) { - SCLogDebug("ssn %p: pkt (%" PRIu32 ") is to server: SEQ " - "%" PRIu32 ", ACK %" PRIu32 "", ssn, p->payload_len, - TCP_GET_SEQ(p), TCP_GET_ACK(p)); - - if (StreamTcpValidateAck(&ssn->server, p) == -1) { - SCLogDebug("ssn %p: rejecting because of invalid ack value", ssn); - StreamTcpSetEvent(p, STREAM_FIN2_INVALID_ACK); - return -1; - } + StreamTcpPacketSetState(p, ssn, TCP_CLOSED); + ssn->server.flags |= STREAMTCP_STREAM_FLAG_CLOSE_INITIATED; + ssn->client.flags |= STREAMTCP_STREAM_FLAG_CLOSE_INITIATED; + SCLogDebug("ssn %p: Reset received state changed to TCP_CLOSED", + ssn); - if (SEQ_LEQ(TCP_GET_SEQ(p) + p->payload_len, ssn->client.next_win) || - (ssn->flags & STREAMTCP_FLAG_MIDSTREAM) || - ssn->flags & STREAMTCP_FLAG_ASYNC) - { - SCLogDebug("ssn %p: seq %"PRIu32" in window, ssn->client.next_win " - "%" PRIu32 "", ssn, TCP_GET_SEQ(p), ssn->client.next_win); + if (PKT_IS_TOSERVER(p)) { + StreamTcpUpdateLastAck(ssn, &ssn->server, + StreamTcpResetGetMaxAck(&ssn->server, TCP_GET_ACK(p))); - } else { - SCLogDebug("ssn %p: -> SEQ mismatch, packet SEQ %" PRIu32 "" - " != %" PRIu32 " from stream", ssn, - TCP_GET_SEQ(p), ssn->client.next_seq); - StreamTcpSetEvent(p, STREAM_FIN2_ACK_WRONG_SEQ); - return -1; - } + StreamTcpUpdateLastAck(ssn, &ssn->client, + StreamTcpResetGetMaxAck(&ssn->client, TCP_GET_SEQ(p))); - ssn->server.window = TCP_GET_WINDOW(p) << ssn->server.wscale; + if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) { + StreamTcpHandleTimestamp(ssn, p); + } - StreamTcpUpdateLastAck(ssn, &ssn->server, TCP_GET_ACK(p)); + StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn, + &ssn->client, p, pq); + } else { + StreamTcpUpdateLastAck(ssn, &ssn->client, + StreamTcpResetGetMaxAck(&ssn->client, TCP_GET_ACK(p))); - if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) { - StreamTcpHandleTimestamp(ssn, p); - } + StreamTcpUpdateLastAck(ssn, &ssn->server, + StreamTcpResetGetMaxAck(&ssn->server, TCP_GET_SEQ(p))); - if (SEQ_EQ(ssn->client.next_seq, TCP_GET_SEQ(p))) { - ssn->client.next_seq += p->payload_len; - SCLogDebug("ssn %p: ssn->client.next_seq %" PRIu32 "", - ssn, ssn->client.next_seq); - } + if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) { + StreamTcpHandleTimestamp(ssn, p); + } - StreamTcpSackUpdatePacket(&ssn->server, p); + StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn, + &ssn->server, p, pq); + } - /* update next_win */ - StreamTcpUpdateNextWin(ssn, &ssn->server, (ssn->server.last_ack + ssn->server.window)); + } else if (p->tcph->th_flags & TH_FIN) { + if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) { + if (!StreamTcpValidateTimestamp(ssn, p)) + return -1; + } - StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn, - &ssn->client, p, pq); + if (PKT_IS_TOSERVER(p)) { + SCLogDebug("ssn %p: pkt (%" PRIu32 ") is to server: SEQ " + "%" PRIu32 ", ACK %" PRIu32 "", ssn, p->payload_len, + TCP_GET_SEQ(p), TCP_GET_ACK(p)); - SCLogDebug("ssn %p: =+ next SEQ %" PRIu32 ", last ACK " - "%" PRIu32 "", ssn, ssn->client.next_seq, - ssn->server.last_ack); - } else { /* implied to client */ - SCLogDebug("ssn %p: pkt (%" PRIu32 ") is to client: SEQ " - "%" PRIu32 ", ACK %" PRIu32 "", ssn, p->payload_len, - TCP_GET_SEQ(p), TCP_GET_ACK(p)); - - if (StreamTcpValidateAck(&ssn->client, p) == -1) { - SCLogDebug("ssn %p: rejecting because of invalid ack value", ssn); - StreamTcpSetEvent(p, STREAM_FIN2_INVALID_ACK); - return -1; - } + if (SEQ_LT(TCP_GET_SEQ(p), ssn->client.next_seq) || + SEQ_GT(TCP_GET_SEQ(p), (ssn->client.last_ack + ssn->client.window))) + { + SCLogDebug("ssn %p: -> SEQ mismatch, packet SEQ " + "%" PRIu32 " != %" PRIu32 " from stream", ssn, + TCP_GET_SEQ(p), ssn->client.next_seq); + StreamTcpSetEvent(p, STREAM_FIN2_FIN_WRONG_SEQ); + return -1; + } - if (SEQ_LEQ(TCP_GET_SEQ(p) + p->payload_len, ssn->server.next_win) || - (ssn->flags & STREAMTCP_FLAG_MIDSTREAM) || - ssn->flags & STREAMTCP_FLAG_ASYNC) - { - SCLogDebug("ssn %p: seq %"PRIu32" in window, ssn->server.next_win " - "%" PRIu32 "", ssn, TCP_GET_SEQ(p), ssn->server.next_win); - } else { - SCLogDebug("ssn %p: -> SEQ mismatch, packet SEQ %" PRIu32 "" - " != %" PRIu32 " from stream", ssn, - TCP_GET_SEQ(p), ssn->server.next_seq); - StreamTcpSetEvent(p, STREAM_FIN2_ACK_WRONG_SEQ); - return -1; - } + if (StreamTcpValidateAck(&ssn->server, p) == -1) { + SCLogDebug("ssn %p: rejecting because of invalid ack value", ssn); + StreamTcpSetEvent(p, STREAM_FIN2_INVALID_ACK); + return -1; + } - ssn->client.window = TCP_GET_WINDOW(p) << ssn->client.wscale; + StreamTcpPacketSetState(p, ssn, TCP_TIME_WAIT); + ssn->client.flags |= STREAMTCP_STREAM_FLAG_CLOSE_INITIATED; + SCLogDebug("ssn %p: state changed to TCP_TIME_WAIT", ssn); - StreamTcpUpdateLastAck(ssn, &ssn->client, TCP_GET_ACK(p)); + ssn->server.window = TCP_GET_WINDOW(p) << ssn->server.wscale; - if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) { - StreamTcpHandleTimestamp(ssn, p); - } + StreamTcpUpdateLastAck(ssn, &ssn->server, TCP_GET_ACK(p)); - if (SEQ_EQ(ssn->server.next_seq, TCP_GET_SEQ(p))) { - ssn->server.next_seq += p->payload_len; - SCLogDebug("ssn %p: ssn->server.next_seq %" PRIu32 "", - ssn, ssn->server.next_seq); - } + if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) { + StreamTcpHandleTimestamp(ssn, p); + } + + /* Update the next_seq, in case if we have missed the client + packet and server has already received and acked it */ + if (SEQ_LT(ssn->server.next_seq, TCP_GET_ACK(p))) + ssn->server.next_seq = TCP_GET_ACK(p); - StreamTcpSackUpdatePacket(&ssn->client, p); + StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn, + &ssn->client, p, pq); - /* update next_win */ - StreamTcpUpdateNextWin(ssn, &ssn->client, (ssn->client.last_ack + ssn->client.window)); + SCLogDebug("ssn %p: =+ next SEQ %" PRIu32 ", last ACK " + "%" PRIu32 "", ssn, ssn->client.next_seq, + ssn->server.last_ack); - StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn, - &ssn->server, p, pq); + StreamTcpPseudoPacketCreateStreamEndPacket(p, ssn, pq); + } else { /* implied to client */ + SCLogDebug("ssn %p: pkt (%" PRIu32 ") is to client: SEQ " + "%" PRIu32 ", ACK %" PRIu32 "", ssn, p->payload_len, + TCP_GET_SEQ(p), TCP_GET_ACK(p)); - SCLogDebug("ssn %p: =+ next SEQ %" PRIu32 ", last ACK " - "%" PRIu32 "", ssn, ssn->server.next_seq, - ssn->client.last_ack); + if (SEQ_LT(TCP_GET_SEQ(p), ssn->server.next_seq) || + SEQ_GT(TCP_GET_SEQ(p), (ssn->server.last_ack + ssn->server.window))) + { + SCLogDebug("ssn %p: -> SEQ mismatch, packet SEQ " + "%" PRIu32 " != %" PRIu32 " from stream", ssn, + TCP_GET_SEQ(p), ssn->server.next_seq); + StreamTcpSetEvent(p, STREAM_FIN2_FIN_WRONG_SEQ); + return -1; } - break; - case TH_RST: - case TH_RST|TH_ACK: - case TH_RST|TH_ACK|TH_ECN: - case TH_RST|TH_ACK|TH_ECN|TH_CWR: + if (StreamTcpValidateAck(&ssn->client, p) == -1) { + SCLogDebug("ssn %p: rejecting because of invalid ack value", ssn); + StreamTcpSetEvent(p, STREAM_FIN2_INVALID_ACK); + return -1; + } - if (StreamTcpValidateRst(ssn, p)) { - /* force both streams to reassemble, if necessary */ - StreamTcpPseudoPacketCreateStreamEndPacket(p, ssn, pq); - SCPerfCounterIncr(stt->counter_tcp_pseudo, tv->sc_perf_pca); + StreamTcpPacketSetState(p, ssn, TCP_TIME_WAIT); + ssn->server.flags |= STREAMTCP_STREAM_FLAG_CLOSE_INITIATED; + SCLogDebug("ssn %p: state changed to TCP_TIME_WAIT", ssn); - StreamTcpPacketSetState(p, ssn, TCP_CLOSED); - ssn->server.flags |= STREAMTCP_STREAM_FLAG_CLOSE_INITIATED; - ssn->client.flags |= STREAMTCP_STREAM_FLAG_CLOSE_INITIATED; - SCLogDebug("ssn %p: Reset received state changed to TCP_CLOSED", - ssn); + ssn->client.window = TCP_GET_WINDOW(p) << ssn->client.wscale; - if (PKT_IS_TOSERVER(p)) { - StreamTcpUpdateLastAck(ssn, &ssn->server, - StreamTcpResetGetMaxAck(&ssn->server, TCP_GET_ACK(p))); + StreamTcpUpdateLastAck(ssn, &ssn->client, TCP_GET_ACK(p)); - StreamTcpUpdateLastAck(ssn, &ssn->client, - StreamTcpResetGetMaxAck(&ssn->client, TCP_GET_SEQ(p))); + if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) { + StreamTcpHandleTimestamp(ssn, p); + } - if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) { - StreamTcpHandleTimestamp(ssn, p); - } + /* Update the next_seq, in case if we have missed the client + packet and server has already received and acked it */ + if (SEQ_LT(ssn->client.next_seq, TCP_GET_ACK(p))) + ssn->client.next_seq = TCP_GET_ACK(p); - StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn, - &ssn->client, p, pq); - } else { - StreamTcpUpdateLastAck(ssn, &ssn->client, - StreamTcpResetGetMaxAck(&ssn->client, TCP_GET_ACK(p))); + StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn, + &ssn->server, p, pq); + SCLogDebug("ssn %p: =+ next SEQ %" PRIu32 ", last ACK " + "%" PRIu32 "", ssn, ssn->server.next_seq, + ssn->client.last_ack); - StreamTcpUpdateLastAck(ssn, &ssn->server, - StreamTcpResetGetMaxAck(&ssn->server, TCP_GET_SEQ(p))); + StreamTcpPseudoPacketCreateStreamEndPacket(p, ssn, pq); + } - if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) { - StreamTcpHandleTimestamp(ssn, p); - } + } else if (p->tcph->th_flags & TH_SYN) { + SCLogDebug("ssn (%p): SYN pkt on FinWait2", ssn); + StreamTcpSetEvent(p, STREAM_SHUTDOWN_SYN_RESEND); + return -1; - StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn, - &ssn->server, p, pq); - } - } - else + } else if (p->tcph->th_flags & TH_ACK) { + if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) { + if (!StreamTcpValidateTimestamp(ssn, p)) return -1; - break; + } - case TH_FIN: - case TH_FIN|TH_ACK: - case TH_FIN|TH_ACK|TH_ECN: - case TH_FIN|TH_ACK|TH_ECN|TH_CWR: - case TH_FIN|TH_ACK|TH_PUSH: - case TH_FIN|TH_ACK|TH_PUSH|TH_ECN: - case TH_FIN|TH_ACK|TH_PUSH|TH_ECN|TH_CWR: + if (PKT_IS_TOSERVER(p)) { + SCLogDebug("ssn %p: pkt (%" PRIu32 ") is to server: SEQ " + "%" PRIu32 ", ACK %" PRIu32 "", ssn, p->payload_len, + TCP_GET_SEQ(p), TCP_GET_ACK(p)); - if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) { - if (!StreamTcpValidateTimestamp(ssn, p)) - return -1; + if (StreamTcpValidateAck(&ssn->server, p) == -1) { + SCLogDebug("ssn %p: rejecting because of invalid ack value", ssn); + StreamTcpSetEvent(p, STREAM_FIN2_INVALID_ACK); + return -1; } - if (PKT_IS_TOSERVER(p)) { - SCLogDebug("ssn %p: pkt (%" PRIu32 ") is to server: SEQ " - "%" PRIu32 ", ACK %" PRIu32 "", ssn, p->payload_len, - TCP_GET_SEQ(p), TCP_GET_ACK(p)); + if (SEQ_LEQ(TCP_GET_SEQ(p) + p->payload_len, ssn->client.next_win) || + (ssn->flags & STREAMTCP_FLAG_MIDSTREAM) || + ssn->flags & STREAMTCP_FLAG_ASYNC) + { + SCLogDebug("ssn %p: seq %"PRIu32" in window, ssn->client.next_win " + "%" PRIu32 "", ssn, TCP_GET_SEQ(p), ssn->client.next_win); - if (SEQ_LT(TCP_GET_SEQ(p), ssn->client.next_seq) || - SEQ_GT(TCP_GET_SEQ(p), (ssn->client.last_ack + ssn->client.window))) - { - SCLogDebug("ssn %p: -> SEQ mismatch, packet SEQ " - "%" PRIu32 " != %" PRIu32 " from stream", ssn, - TCP_GET_SEQ(p), ssn->client.next_seq); - StreamTcpSetEvent(p, STREAM_FIN2_FIN_WRONG_SEQ); - return -1; - } + } else { + SCLogDebug("ssn %p: -> SEQ mismatch, packet SEQ %" PRIu32 "" + " != %" PRIu32 " from stream", ssn, + TCP_GET_SEQ(p), ssn->client.next_seq); + StreamTcpSetEvent(p, STREAM_FIN2_ACK_WRONG_SEQ); + return -1; + } - if (StreamTcpValidateAck(&ssn->server, p) == -1) { - SCLogDebug("ssn %p: rejecting because of invalid ack value", ssn); - StreamTcpSetEvent(p, STREAM_FIN2_INVALID_ACK); - return -1; - } + ssn->server.window = TCP_GET_WINDOW(p) << ssn->server.wscale; - StreamTcpPacketSetState(p, ssn, TCP_TIME_WAIT); - ssn->client.flags |= STREAMTCP_STREAM_FLAG_CLOSE_INITIATED; - SCLogDebug("ssn %p: state changed to TCP_TIME_WAIT", ssn); + StreamTcpUpdateLastAck(ssn, &ssn->server, TCP_GET_ACK(p)); - ssn->server.window = TCP_GET_WINDOW(p) << ssn->server.wscale; + if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) { + StreamTcpHandleTimestamp(ssn, p); + } - StreamTcpUpdateLastAck(ssn, &ssn->server, TCP_GET_ACK(p)); + if (SEQ_EQ(ssn->client.next_seq, TCP_GET_SEQ(p))) { + ssn->client.next_seq += p->payload_len; + SCLogDebug("ssn %p: ssn->client.next_seq %" PRIu32 "", + ssn, ssn->client.next_seq); + } - if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) { - StreamTcpHandleTimestamp(ssn, p); - } + StreamTcpSackUpdatePacket(&ssn->server, p); - /* Update the next_seq, in case if we have missed the client - packet and server has already received and acked it */ - if (SEQ_LT(ssn->server.next_seq, TCP_GET_ACK(p))) - ssn->server.next_seq = TCP_GET_ACK(p); + /* update next_win */ + StreamTcpUpdateNextWin(ssn, &ssn->server, (ssn->server.last_ack + ssn->server.window)); - StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn, - &ssn->client, p, pq); + StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn, + &ssn->client, p, pq); - SCLogDebug("ssn %p: =+ next SEQ %" PRIu32 ", last ACK " - "%" PRIu32 "", ssn, ssn->client.next_seq, - ssn->server.last_ack); + SCLogDebug("ssn %p: =+ next SEQ %" PRIu32 ", last ACK " + "%" PRIu32 "", ssn, ssn->client.next_seq, + ssn->server.last_ack); + } else { /* implied to client */ + SCLogDebug("ssn %p: pkt (%" PRIu32 ") is to client: SEQ " + "%" PRIu32 ", ACK %" PRIu32 "", ssn, p->payload_len, + TCP_GET_SEQ(p), TCP_GET_ACK(p)); - StreamTcpPseudoPacketCreateStreamEndPacket(p, ssn, pq); - } else { /* implied to client */ - SCLogDebug("ssn %p: pkt (%" PRIu32 ") is to client: SEQ " - "%" PRIu32 ", ACK %" PRIu32 "", ssn, p->payload_len, - TCP_GET_SEQ(p), TCP_GET_ACK(p)); + if (StreamTcpValidateAck(&ssn->client, p) == -1) { + SCLogDebug("ssn %p: rejecting because of invalid ack value", ssn); + StreamTcpSetEvent(p, STREAM_FIN2_INVALID_ACK); + return -1; + } - if (SEQ_LT(TCP_GET_SEQ(p), ssn->server.next_seq) || - SEQ_GT(TCP_GET_SEQ(p), (ssn->server.last_ack + ssn->server.window))) - { - SCLogDebug("ssn %p: -> SEQ mismatch, packet SEQ " - "%" PRIu32 " != %" PRIu32 " from stream", ssn, - TCP_GET_SEQ(p), ssn->server.next_seq); - StreamTcpSetEvent(p, STREAM_FIN2_FIN_WRONG_SEQ); - return -1; - } + if (SEQ_LEQ(TCP_GET_SEQ(p) + p->payload_len, ssn->server.next_win) || + (ssn->flags & STREAMTCP_FLAG_MIDSTREAM) || + ssn->flags & STREAMTCP_FLAG_ASYNC) + { + SCLogDebug("ssn %p: seq %"PRIu32" in window, ssn->server.next_win " + "%" PRIu32 "", ssn, TCP_GET_SEQ(p), ssn->server.next_win); + } else { + SCLogDebug("ssn %p: -> SEQ mismatch, packet SEQ %" PRIu32 "" + " != %" PRIu32 " from stream", ssn, + TCP_GET_SEQ(p), ssn->server.next_seq); + StreamTcpSetEvent(p, STREAM_FIN2_ACK_WRONG_SEQ); + return -1; + } - if (StreamTcpValidateAck(&ssn->client, p) == -1) { - SCLogDebug("ssn %p: rejecting because of invalid ack value", ssn); - StreamTcpSetEvent(p, STREAM_FIN2_INVALID_ACK); - return -1; - } + ssn->client.window = TCP_GET_WINDOW(p) << ssn->client.wscale; - StreamTcpPacketSetState(p, ssn, TCP_TIME_WAIT); - ssn->server.flags |= STREAMTCP_STREAM_FLAG_CLOSE_INITIATED; - SCLogDebug("ssn %p: state changed to TCP_TIME_WAIT", ssn); + StreamTcpUpdateLastAck(ssn, &ssn->client, TCP_GET_ACK(p)); - ssn->client.window = TCP_GET_WINDOW(p) << ssn->client.wscale; + if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) { + StreamTcpHandleTimestamp(ssn, p); + } - StreamTcpUpdateLastAck(ssn, &ssn->client, TCP_GET_ACK(p)); + if (SEQ_EQ(ssn->server.next_seq, TCP_GET_SEQ(p))) { + ssn->server.next_seq += p->payload_len; + SCLogDebug("ssn %p: ssn->server.next_seq %" PRIu32 "", + ssn, ssn->server.next_seq); + } - if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) { - StreamTcpHandleTimestamp(ssn, p); - } + StreamTcpSackUpdatePacket(&ssn->client, p); - /* Update the next_seq, in case if we have missed the client - packet and server has already received and acked it */ - if (SEQ_LT(ssn->client.next_seq, TCP_GET_ACK(p))) - ssn->client.next_seq = TCP_GET_ACK(p); + /* update next_win */ + StreamTcpUpdateNextWin(ssn, &ssn->client, (ssn->client.last_ack + ssn->client.window)); - StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn, - &ssn->server, p, pq); - SCLogDebug("ssn %p: =+ next SEQ %" PRIu32 ", last ACK " - "%" PRIu32 "", ssn, ssn->server.next_seq, - ssn->client.last_ack); + StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn, + &ssn->server, p, pq); - StreamTcpPseudoPacketCreateStreamEndPacket(p, ssn, pq); - } - break; - default: - SCLogDebug("ssn %p: default case", ssn); - break; + SCLogDebug("ssn %p: =+ next SEQ %" PRIu32 ", last ACK " + "%" PRIu32 "", ssn, ssn->server.next_seq, + ssn->client.last_ack); + } + } else { + SCLogDebug("ssn %p: default case", ssn); } return 0; @@ -2874,151 +2742,143 @@ static int StreamTcpPacketStateClosing(ThreadVars *tv, Packet *p, if (ssn == NULL) return -1; - switch(p->tcph->th_flags) { - case TH_ACK: - case TH_ACK|TH_PUSH: - case TH_ACK|TH_ECN: - case TH_ACK|TH_ECN|TH_CWR: - - if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) { - if (!StreamTcpValidateTimestamp(ssn, p)) - return -1; - } - - if (PKT_IS_TOSERVER(p)) { - SCLogDebug("ssn %p: pkt (%" PRIu32 ") is to server: SEQ " - "%" PRIu32 ", ACK %" PRIu32 "", ssn, p->payload_len, - TCP_GET_SEQ(p), TCP_GET_ACK(p)); - - if (TCP_GET_SEQ(p) != ssn->client.next_seq) { - SCLogDebug("ssn %p: -> SEQ mismatch, packet SEQ %" PRIu32 "" - " != %" PRIu32 " from stream", ssn, - TCP_GET_SEQ(p), ssn->client.next_seq); - StreamTcpSetEvent(p, STREAM_CLOSING_ACK_WRONG_SEQ); - return -1; - } + if (p->tcph->th_flags & TH_RST) { + if (!StreamTcpValidateRst(ssn, p)) + return -1; - if (StreamTcpValidateAck(&ssn->server, p) == -1) { - SCLogDebug("ssn %p: rejecting because of invalid ack value", ssn); - StreamTcpSetEvent(p, STREAM_CLOSING_INVALID_ACK); - return -1; - } + /* force both streams to reassemble, if necessary */ + StreamTcpPseudoPacketCreateStreamEndPacket(p, ssn, pq); + SCPerfCounterIncr(stt->counter_tcp_pseudo, tv->sc_perf_pca); - StreamTcpPacketSetState(p, ssn, TCP_TIME_WAIT); - SCLogDebug("ssn %p: state changed to TCP_TIME_WAIT", ssn); + StreamTcpPacketSetState(p, ssn, TCP_CLOSED); + ssn->server.flags |= STREAMTCP_STREAM_FLAG_CLOSE_INITIATED; + ssn->client.flags |= STREAMTCP_STREAM_FLAG_CLOSE_INITIATED; + SCLogDebug("ssn %p: Reset received state changed to TCP_CLOSED", + ssn); - ssn->client.window = TCP_GET_WINDOW(p) << ssn->client.wscale; + if (PKT_IS_TOSERVER(p)) { + StreamTcpUpdateLastAck(ssn, &ssn->server, + StreamTcpResetGetMaxAck(&ssn->server, TCP_GET_ACK(p))); - StreamTcpUpdateLastAck(ssn, &ssn->server, TCP_GET_ACK(p)); + StreamTcpUpdateLastAck(ssn, &ssn->client, + StreamTcpResetGetMaxAck(&ssn->client, TCP_GET_SEQ(p))); - if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) { - StreamTcpHandleTimestamp(ssn, p); - } - /* Update the next_seq, in case if we have missed the client - packet and server has already received and acked it */ - if (SEQ_LT(ssn->server.next_seq, TCP_GET_ACK(p))) - ssn->server.next_seq = TCP_GET_ACK(p); - - StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn, - &ssn->client, p, pq); - SCLogDebug("ssn %p: =+ next SEQ %" PRIu32 ", last ACK " - "%" PRIu32 "", ssn, ssn->client.next_seq, - ssn->server.last_ack); - } else { /* implied to client */ - SCLogDebug("ssn %p: pkt (%" PRIu32 ") is to client: SEQ " - "%" PRIu32 ", ACK %" PRIu32 "", ssn, p->payload_len, - TCP_GET_SEQ(p), TCP_GET_ACK(p)); - - if (TCP_GET_SEQ(p) != ssn->server.next_seq) { - SCLogDebug("ssn %p: -> SEQ mismatch, packet SEQ %" PRIu32 "" - " != %" PRIu32 " from stream", ssn, - TCP_GET_SEQ(p), ssn->server.next_seq); - StreamTcpSetEvent(p, STREAM_CLOSING_ACK_WRONG_SEQ); - return -1; - } + if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) { + StreamTcpHandleTimestamp(ssn, p); + } - if (StreamTcpValidateAck(&ssn->client, p) == -1) { - SCLogDebug("ssn %p: rejecting because of invalid ack value", ssn); - StreamTcpSetEvent(p, STREAM_CLOSING_INVALID_ACK); - return -1; - } + StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn, + &ssn->client, p, pq); + } else { + StreamTcpUpdateLastAck(ssn, &ssn->client, + StreamTcpResetGetMaxAck(&ssn->client, TCP_GET_ACK(p))); - StreamTcpPacketSetState(p, ssn, TCP_TIME_WAIT); - SCLogDebug("ssn %p: state changed to TCP_TIME_WAIT", ssn); + StreamTcpUpdateLastAck(ssn, &ssn->server, + StreamTcpResetGetMaxAck(&ssn->server, TCP_GET_SEQ(p))); - ssn->client.window = TCP_GET_WINDOW(p) << ssn->client.wscale; + if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) { + StreamTcpHandleTimestamp(ssn, p); + } - StreamTcpUpdateLastAck(ssn, &ssn->client, TCP_GET_ACK(p)); + StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn, + &ssn->server, p, pq); + } - if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) { - StreamTcpHandleTimestamp(ssn, p); - } + } else if (p->tcph->th_flags & TH_SYN) { + SCLogDebug("ssn (%p): SYN pkt on Closing", ssn); + StreamTcpSetEvent(p, STREAM_SHUTDOWN_SYN_RESEND); + return -1; - /* Update the next_seq, in case if we have missed the client - packet and server has already received and acked it */ - if (SEQ_LT(ssn->client.next_seq, TCP_GET_ACK(p))) - ssn->client.next_seq = TCP_GET_ACK(p); + } else if (p->tcph->th_flags & TH_ACK) { + if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) { + if (!StreamTcpValidateTimestamp(ssn, p)) + return -1; + } - StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn, - &ssn->server, p, pq); - SCLogDebug("StreamTcpPacketStateClosing (%p): =+ next SEQ " - "%" PRIu32 ", last ACK %" PRIu32 "", ssn, - ssn->server.next_seq, ssn->client.last_ack); + if (PKT_IS_TOSERVER(p)) { + SCLogDebug("ssn %p: pkt (%" PRIu32 ") is to server: SEQ " + "%" PRIu32 ", ACK %" PRIu32 "", ssn, p->payload_len, + TCP_GET_SEQ(p), TCP_GET_ACK(p)); + + if (TCP_GET_SEQ(p) != ssn->client.next_seq) { + SCLogDebug("ssn %p: -> SEQ mismatch, packet SEQ %" PRIu32 "" + " != %" PRIu32 " from stream", ssn, + TCP_GET_SEQ(p), ssn->client.next_seq); + StreamTcpSetEvent(p, STREAM_CLOSING_ACK_WRONG_SEQ); + return -1; } - break; - case TH_RST: - case TH_RST|TH_ACK: - case TH_RST|TH_ACK|TH_ECN: - case TH_RST|TH_ACK|TH_ECN|TH_CWR: + if (StreamTcpValidateAck(&ssn->server, p) == -1) { + SCLogDebug("ssn %p: rejecting because of invalid ack value", ssn); + StreamTcpSetEvent(p, STREAM_CLOSING_INVALID_ACK); + return -1; + } - if (StreamTcpValidateRst(ssn, p)) { - /* force both streams to reassemble, if necessary */ - StreamTcpPseudoPacketCreateStreamEndPacket(p, ssn, pq); - SCPerfCounterIncr(stt->counter_tcp_pseudo, tv->sc_perf_pca); + StreamTcpPacketSetState(p, ssn, TCP_TIME_WAIT); + SCLogDebug("ssn %p: state changed to TCP_TIME_WAIT", ssn); - StreamTcpPacketSetState(p, ssn, TCP_CLOSED); - ssn->server.flags |= STREAMTCP_STREAM_FLAG_CLOSE_INITIATED; - ssn->client.flags |= STREAMTCP_STREAM_FLAG_CLOSE_INITIATED; - SCLogDebug("ssn %p: Reset received state changed to TCP_CLOSED", - ssn); + ssn->client.window = TCP_GET_WINDOW(p) << ssn->client.wscale; - if (PKT_IS_TOSERVER(p)) { - StreamTcpUpdateLastAck(ssn, &ssn->server, - StreamTcpResetGetMaxAck(&ssn->server, TCP_GET_ACK(p))); + StreamTcpUpdateLastAck(ssn, &ssn->server, TCP_GET_ACK(p)); - StreamTcpUpdateLastAck(ssn, &ssn->client, - StreamTcpResetGetMaxAck(&ssn->client, TCP_GET_SEQ(p))); + if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) { + StreamTcpHandleTimestamp(ssn, p); + } + /* Update the next_seq, in case if we have missed the client + packet and server has already received and acked it */ + if (SEQ_LT(ssn->server.next_seq, TCP_GET_ACK(p))) + ssn->server.next_seq = TCP_GET_ACK(p); + + StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn, + &ssn->client, p, pq); + SCLogDebug("ssn %p: =+ next SEQ %" PRIu32 ", last ACK " + "%" PRIu32 "", ssn, ssn->client.next_seq, + ssn->server.last_ack); + } else { /* implied to client */ + SCLogDebug("ssn %p: pkt (%" PRIu32 ") is to client: SEQ " + "%" PRIu32 ", ACK %" PRIu32 "", ssn, p->payload_len, + TCP_GET_SEQ(p), TCP_GET_ACK(p)); + + if (TCP_GET_SEQ(p) != ssn->server.next_seq) { + SCLogDebug("ssn %p: -> SEQ mismatch, packet SEQ %" PRIu32 "" + " != %" PRIu32 " from stream", ssn, + TCP_GET_SEQ(p), ssn->server.next_seq); + StreamTcpSetEvent(p, STREAM_CLOSING_ACK_WRONG_SEQ); + return -1; + } - if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) { - StreamTcpHandleTimestamp(ssn, p); - } + if (StreamTcpValidateAck(&ssn->client, p) == -1) { + SCLogDebug("ssn %p: rejecting because of invalid ack value", ssn); + StreamTcpSetEvent(p, STREAM_CLOSING_INVALID_ACK); + return -1; + } - StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn, - &ssn->client, p, pq); - } else { - StreamTcpUpdateLastAck(ssn, &ssn->client, - StreamTcpResetGetMaxAck(&ssn->client, TCP_GET_ACK(p))); + StreamTcpPacketSetState(p, ssn, TCP_TIME_WAIT); + SCLogDebug("ssn %p: state changed to TCP_TIME_WAIT", ssn); - StreamTcpUpdateLastAck(ssn, &ssn->server, - StreamTcpResetGetMaxAck(&ssn->server, TCP_GET_SEQ(p))); + ssn->client.window = TCP_GET_WINDOW(p) << ssn->client.wscale; - if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) { - StreamTcpHandleTimestamp(ssn, p); - } + StreamTcpUpdateLastAck(ssn, &ssn->client, TCP_GET_ACK(p)); - StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn, - &ssn->server, p, pq); - } + if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) { + StreamTcpHandleTimestamp(ssn, p); } - else - return -1; - break; - default: - SCLogDebug("ssn %p: default case", ssn); - break; + /* Update the next_seq, in case if we have missed the client + packet and server has already received and acked it */ + if (SEQ_LT(ssn->client.next_seq, TCP_GET_ACK(p))) + ssn->client.next_seq = TCP_GET_ACK(p); + + StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn, + &ssn->server, p, pq); + SCLogDebug("StreamTcpPacketStateClosing (%p): =+ next SEQ " + "%" PRIu32 ", last ACK %" PRIu32 "", ssn, + ssn->server.next_seq, ssn->client.last_ack); + } + } else { + SCLogDebug("ssn %p: default case", ssn); } + return 0; } @@ -3042,262 +2902,250 @@ static int StreamTcpPacketStateCloseWait(ThreadVars *tv, Packet *p, } if (PKT_IS_TOCLIENT(p)) { - SCLogDebug("ssn %p: pkt (%" PRIu32 ") is to client: SEQ " - "%" PRIu32 ", ACK %" PRIu32 "", ssn, p->payload_len, - TCP_GET_SEQ(p), TCP_GET_ACK(p)); + SCLogDebug("ssn %p: pkt (%" PRIu32 ") is to client: SEQ " + "%" PRIu32 ", ACK %" PRIu32 "", ssn, p->payload_len, + TCP_GET_SEQ(p), TCP_GET_ACK(p)); } else { - SCLogDebug("ssn %p: pkt (%" PRIu32 ") is to server: SEQ " - "%" PRIu32 ", ACK %" PRIu32 "", ssn, p->payload_len, - TCP_GET_SEQ(p), TCP_GET_ACK(p)); + SCLogDebug("ssn %p: pkt (%" PRIu32 ") is to server: SEQ " + "%" PRIu32 ", ACK %" PRIu32 "", ssn, p->payload_len, + TCP_GET_SEQ(p), TCP_GET_ACK(p)); } - switch(p->tcph->th_flags) { - case TH_FIN: - case TH_FIN|TH_ACK: - case TH_FIN|TH_ACK|TH_ECN: - case TH_FIN|TH_ACK|TH_ECN|TH_CWR: - - if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) { - if (!StreamTcpValidateTimestamp(ssn, p)) - SCReturnInt(-1); - } - - if (PKT_IS_TOSERVER(p)) { - SCLogDebug("ssn %p: pkt (%" PRIu32 ") is to server: SEQ " - "%" PRIu32 ", ACK %" PRIu32 "", ssn, p->payload_len, - TCP_GET_SEQ(p), TCP_GET_ACK(p)); + if (p->tcph->th_flags & TH_RST) { + if (!StreamTcpValidateRst(ssn, p)) + return -1; - if (SEQ_LT(TCP_GET_SEQ(p), ssn->client.next_seq) || - SEQ_GT(TCP_GET_SEQ(p), (ssn->client.last_ack + ssn->client.window))) - { - SCLogDebug("ssn %p: -> SEQ mismatch, packet SEQ %" PRIu32 "" - " != %" PRIu32 " from stream", ssn, - TCP_GET_SEQ(p), ssn->client.next_seq); - StreamTcpSetEvent(p, STREAM_CLOSEWAIT_FIN_OUT_OF_WINDOW); - SCReturnInt(-1); - } + /* force both streams to reassemble, if necessary */ + StreamTcpPseudoPacketCreateStreamEndPacket(p, ssn, pq); + SCPerfCounterIncr(stt->counter_tcp_pseudo, tv->sc_perf_pca); - if (StreamTcpValidateAck(&ssn->server, p) == -1) { - SCLogDebug("ssn %p: rejecting because of invalid ack value", ssn); - StreamTcpSetEvent(p, STREAM_CLOSEWAIT_INVALID_ACK); - SCReturnInt(-1); - } + StreamTcpPacketSetState(p, ssn, TCP_CLOSED); + ssn->server.flags |= STREAMTCP_STREAM_FLAG_CLOSE_INITIATED; + ssn->client.flags |= STREAMTCP_STREAM_FLAG_CLOSE_INITIATED; + SCLogDebug("ssn %p: Reset received state changed to TCP_CLOSED", + ssn); - StreamTcpPacketSetState(p, ssn, TCP_LAST_ACK); - ssn->client.flags |= STREAMTCP_STREAM_FLAG_CLOSE_INITIATED; - SCLogDebug("ssn %p: state changed to TCP_LAST_ACK", ssn); + if (PKT_IS_TOSERVER(p)) { + StreamTcpUpdateLastAck(ssn, &ssn->server, + StreamTcpResetGetMaxAck(&ssn->server, TCP_GET_ACK(p))); - ssn->server.window = TCP_GET_WINDOW(p) << ssn->server.wscale; + StreamTcpUpdateLastAck(ssn, &ssn->client, + StreamTcpResetGetMaxAck(&ssn->client, TCP_GET_SEQ(p))); - StreamTcpUpdateLastAck(ssn, &ssn->server, TCP_GET_ACK(p)); + if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) { + StreamTcpHandleTimestamp(ssn, p); + } - if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) { - StreamTcpHandleTimestamp(ssn, p); - } + StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn, + &ssn->client, p, pq); + } else { + StreamTcpUpdateLastAck(ssn, &ssn->client, + StreamTcpResetGetMaxAck(&ssn->client, TCP_GET_ACK(p))); - /* Update the next_seq, in case if we have missed the client - packet and server has already received and acked it */ - if (SEQ_LT(ssn->server.next_seq, TCP_GET_ACK(p))) - ssn->server.next_seq = TCP_GET_ACK(p); + StreamTcpUpdateLastAck(ssn, &ssn->server, + StreamTcpResetGetMaxAck(&ssn->server, TCP_GET_SEQ(p))); - StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn, - &ssn->client, p, pq); - SCLogDebug("ssn %p: =+ next SEQ %" PRIu32 ", last ACK " - "%" PRIu32 "", ssn, ssn->client.next_seq, - ssn->server.last_ack); - } else { - SCLogDebug("ssn %p: pkt (%" PRIu32 ") is to client: SEQ " - "%" PRIu32 ", ACK %" PRIu32 "", ssn, p->payload_len, - TCP_GET_SEQ(p), TCP_GET_ACK(p)); + if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) { + StreamTcpHandleTimestamp(ssn, p); + } - if (SEQ_LT(TCP_GET_SEQ(p), ssn->server.next_seq) || - SEQ_GT(TCP_GET_SEQ(p), (ssn->server.last_ack + ssn->server.window))) - { - SCLogDebug("ssn %p: -> SEQ mismatch, packet SEQ %" PRIu32 "" - " != %" PRIu32 " from stream", ssn, - TCP_GET_SEQ(p), ssn->server.next_seq); - StreamTcpSetEvent(p, STREAM_CLOSEWAIT_FIN_OUT_OF_WINDOW); - SCReturnInt(-1); - } + StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn, + &ssn->server, p, pq); + } - if (StreamTcpValidateAck(&ssn->client, p) == -1) { - SCLogDebug("ssn %p: rejecting because of invalid ack value", ssn); - StreamTcpSetEvent(p, STREAM_CLOSEWAIT_INVALID_ACK); - SCReturnInt(-1); - } + } else if (p->tcph->th_flags & TH_FIN) { + if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) { + if (!StreamTcpValidateTimestamp(ssn, p)) + SCReturnInt(-1); + } - StreamTcpPacketSetState(p, ssn, TCP_LAST_ACK); - ssn->server.flags |= STREAMTCP_STREAM_FLAG_CLOSE_INITIATED; - SCLogDebug("ssn %p: state changed to TCP_LAST_ACK", ssn); + if (PKT_IS_TOSERVER(p)) { + SCLogDebug("ssn %p: pkt (%" PRIu32 ") is to server: SEQ " + "%" PRIu32 ", ACK %" PRIu32 "", ssn, p->payload_len, + TCP_GET_SEQ(p), TCP_GET_ACK(p)); - ssn->client.window = TCP_GET_WINDOW(p) << ssn->client.wscale; + if (SEQ_LT(TCP_GET_SEQ(p), ssn->client.next_seq) || + SEQ_GT(TCP_GET_SEQ(p), (ssn->client.last_ack + ssn->client.window))) + { + SCLogDebug("ssn %p: -> SEQ mismatch, packet SEQ %" PRIu32 "" + " != %" PRIu32 " from stream", ssn, + TCP_GET_SEQ(p), ssn->client.next_seq); + StreamTcpSetEvent(p, STREAM_CLOSEWAIT_FIN_OUT_OF_WINDOW); + SCReturnInt(-1); + } - StreamTcpUpdateLastAck(ssn, &ssn->client, TCP_GET_ACK(p)); + if (StreamTcpValidateAck(&ssn->server, p) == -1) { + SCLogDebug("ssn %p: rejecting because of invalid ack value", ssn); + StreamTcpSetEvent(p, STREAM_CLOSEWAIT_INVALID_ACK); + SCReturnInt(-1); + } - if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) { - StreamTcpHandleTimestamp(ssn, p); - } + StreamTcpPacketSetState(p, ssn, TCP_LAST_ACK); + ssn->client.flags |= STREAMTCP_STREAM_FLAG_CLOSE_INITIATED; + SCLogDebug("ssn %p: state changed to TCP_LAST_ACK", ssn); - /* Update the next_seq, in case if we have missed the client - packet and server has already received and acked it */ - if (SEQ_LT(ssn->client.next_seq, TCP_GET_ACK(p))) - ssn->client.next_seq = TCP_GET_ACK(p); + ssn->server.window = TCP_GET_WINDOW(p) << ssn->server.wscale; - StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn, - &ssn->server, p, pq); - SCLogDebug("ssn %p: =+ next SEQ %" PRIu32 ", last ACK " - "%" PRIu32 "", ssn, ssn->server.next_seq, - ssn->client.last_ack); - } - break; + StreamTcpUpdateLastAck(ssn, &ssn->server, TCP_GET_ACK(p)); - case TH_ACK: - case TH_ACK|TH_PUSH: - case TH_ACK|TH_ECN: - case TH_ACK|TH_ECN|TH_CWR: if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) { - if (!StreamTcpValidateTimestamp(ssn, p)) - SCReturnInt(-1); + StreamTcpHandleTimestamp(ssn, p); } - if (PKT_IS_TOSERVER(p)) { - SCLogDebug("ssn %p: pkt (%" PRIu32 ") is to server: SEQ " - "%" PRIu32 ", ACK %" PRIu32 "", ssn, p->payload_len, - TCP_GET_SEQ(p), TCP_GET_ACK(p)); + /* Update the next_seq, in case if we have missed the client + packet and server has already received and acked it */ + if (SEQ_LT(ssn->server.next_seq, TCP_GET_ACK(p))) + ssn->server.next_seq = TCP_GET_ACK(p); - if (SEQ_LT(TCP_GET_SEQ(p), ssn->client.next_seq) || - SEQ_GT(TCP_GET_SEQ(p), (ssn->client.last_ack + ssn->client.window))) - { - SCLogDebug("ssn %p: -> SEQ mismatch, packet SEQ %" PRIu32 "" - " != %" PRIu32 " from stream", ssn, - TCP_GET_SEQ(p), ssn->client.next_seq); - SCReturnInt(-1); - StreamTcpSetEvent(p, STREAM_CLOSEWAIT_ACK_OUT_OF_WINDOW); - } + StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn, + &ssn->client, p, pq); + SCLogDebug("ssn %p: =+ next SEQ %" PRIu32 ", last ACK " + "%" PRIu32 "", ssn, ssn->client.next_seq, + ssn->server.last_ack); + } else { + SCLogDebug("ssn %p: pkt (%" PRIu32 ") is to client: SEQ " + "%" PRIu32 ", ACK %" PRIu32 "", ssn, p->payload_len, + TCP_GET_SEQ(p), TCP_GET_ACK(p)); - if (StreamTcpValidateAck(&ssn->server, p) == -1) { - SCLogDebug("ssn %p: rejecting because of invalid ack value", ssn); - StreamTcpSetEvent(p, STREAM_CLOSEWAIT_INVALID_ACK); - SCReturnInt(-1); - } + if (SEQ_LT(TCP_GET_SEQ(p), ssn->server.next_seq) || + SEQ_GT(TCP_GET_SEQ(p), (ssn->server.last_ack + ssn->server.window))) + { + SCLogDebug("ssn %p: -> SEQ mismatch, packet SEQ %" PRIu32 "" + " != %" PRIu32 " from stream", ssn, + TCP_GET_SEQ(p), ssn->server.next_seq); + StreamTcpSetEvent(p, STREAM_CLOSEWAIT_FIN_OUT_OF_WINDOW); + SCReturnInt(-1); + } + + if (StreamTcpValidateAck(&ssn->client, p) == -1) { + SCLogDebug("ssn %p: rejecting because of invalid ack value", ssn); + StreamTcpSetEvent(p, STREAM_CLOSEWAIT_INVALID_ACK); + SCReturnInt(-1); + } - ssn->server.window = TCP_GET_WINDOW(p) << ssn->server.wscale; + StreamTcpPacketSetState(p, ssn, TCP_LAST_ACK); + ssn->server.flags |= STREAMTCP_STREAM_FLAG_CLOSE_INITIATED; + SCLogDebug("ssn %p: state changed to TCP_LAST_ACK", ssn); - StreamTcpUpdateLastAck(ssn, &ssn->server, TCP_GET_ACK(p)); + ssn->client.window = TCP_GET_WINDOW(p) << ssn->client.wscale; - if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) { - StreamTcpHandleTimestamp(ssn, p); - } + StreamTcpUpdateLastAck(ssn, &ssn->client, TCP_GET_ACK(p)); - /* Update the next_seq, in case if we have missed the client - packet and server has already received and acked it */ - if (SEQ_LT(ssn->server.next_seq, TCP_GET_ACK(p))) - ssn->server.next_seq = TCP_GET_ACK(p); + if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) { + StreamTcpHandleTimestamp(ssn, p); + } - if (SEQ_EQ(TCP_GET_SEQ(p),ssn->client.next_seq)) - ssn->client.next_seq += p->payload_len; + /* Update the next_seq, in case if we have missed the client + packet and server has already received and acked it */ + if (SEQ_LT(ssn->client.next_seq, TCP_GET_ACK(p))) + ssn->client.next_seq = TCP_GET_ACK(p); - StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn, - &ssn->client, p, pq); - SCLogDebug("ssn %p: =+ next SEQ %" PRIu32 ", last ACK " - "%" PRIu32 "", ssn, ssn->client.next_seq, - ssn->server.last_ack); - } else { - SCLogDebug("ssn %p: pkt (%" PRIu32 ") is to client: SEQ " - "%" PRIu32 ", ACK %" PRIu32 "", ssn, p->payload_len, - TCP_GET_SEQ(p), TCP_GET_ACK(p)); + StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn, + &ssn->server, p, pq); + SCLogDebug("ssn %p: =+ next SEQ %" PRIu32 ", last ACK " + "%" PRIu32 "", ssn, ssn->server.next_seq, + ssn->client.last_ack); + } - if (SEQ_LT(TCP_GET_SEQ(p), ssn->server.next_seq) || - SEQ_GT(TCP_GET_SEQ(p), (ssn->server.last_ack + ssn->server.window))) - { - SCLogDebug("ssn %p: -> SEQ mismatch, packet SEQ %" PRIu32 "" - " != %" PRIu32 " from stream", ssn, - TCP_GET_SEQ(p), ssn->server.next_seq); - StreamTcpSetEvent(p, STREAM_CLOSEWAIT_ACK_OUT_OF_WINDOW); - SCReturnInt(-1); - } + } else if (p->tcph->th_flags & TH_SYN) { + SCLogDebug("ssn (%p): SYN pkt on CloseWait", ssn); + StreamTcpSetEvent(p, STREAM_SHUTDOWN_SYN_RESEND); + SCReturnInt(-1); - if (StreamTcpValidateAck(&ssn->client, p) == -1) { - SCLogDebug("ssn %p: rejecting because of invalid ack value", ssn); - StreamTcpSetEvent(p, STREAM_CLOSEWAIT_INVALID_ACK); - SCReturnInt(-1); - } + } else if (p->tcph->th_flags & TH_ACK) { + if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) { + if (!StreamTcpValidateTimestamp(ssn, p)) + SCReturnInt(-1); + } - ssn->client.window = TCP_GET_WINDOW(p) << ssn->client.wscale; + if (PKT_IS_TOSERVER(p)) { + SCLogDebug("ssn %p: pkt (%" PRIu32 ") is to server: SEQ " + "%" PRIu32 ", ACK %" PRIu32 "", ssn, p->payload_len, + TCP_GET_SEQ(p), TCP_GET_ACK(p)); - StreamTcpUpdateLastAck(ssn, &ssn->client, TCP_GET_ACK(p)); + if (SEQ_LT(TCP_GET_SEQ(p), ssn->client.next_seq) || + SEQ_GT(TCP_GET_SEQ(p), (ssn->client.last_ack + ssn->client.window))) + { + SCLogDebug("ssn %p: -> SEQ mismatch, packet SEQ %" PRIu32 "" + " != %" PRIu32 " from stream", ssn, + TCP_GET_SEQ(p), ssn->client.next_seq); + SCReturnInt(-1); + StreamTcpSetEvent(p, STREAM_CLOSEWAIT_ACK_OUT_OF_WINDOW); + } - if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) { - StreamTcpHandleTimestamp(ssn, p); - } + if (StreamTcpValidateAck(&ssn->server, p) == -1) { + SCLogDebug("ssn %p: rejecting because of invalid ack value", ssn); + StreamTcpSetEvent(p, STREAM_CLOSEWAIT_INVALID_ACK); + SCReturnInt(-1); + } - /* Update the next_seq, in case if we have missed the client - packet and server has already received and acked it */ - if (SEQ_LT(ssn->client.next_seq, TCP_GET_ACK(p))) - ssn->client.next_seq = TCP_GET_ACK(p); + ssn->server.window = TCP_GET_WINDOW(p) << ssn->server.wscale; - if (SEQ_EQ(TCP_GET_SEQ(p),ssn->server.next_seq)) - ssn->server.next_seq += p->payload_len; + StreamTcpUpdateLastAck(ssn, &ssn->server, TCP_GET_ACK(p)); - StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn, - &ssn->server, p, pq); - SCLogDebug("ssn %p: =+ next SEQ %" PRIu32 ", last ACK " - "%" PRIu32 "", ssn, ssn->server.next_seq, - ssn->client.last_ack); + if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) { + StreamTcpHandleTimestamp(ssn, p); } - break; - case TH_RST: - case TH_RST|TH_ACK: - case TH_RST|TH_ACK|TH_ECN: - case TH_RST|TH_ACK|TH_ECN|TH_CWR: + /* Update the next_seq, in case if we have missed the client + packet and server has already received and acked it */ + if (SEQ_LT(ssn->server.next_seq, TCP_GET_ACK(p))) + ssn->server.next_seq = TCP_GET_ACK(p); - if (StreamTcpValidateRst(ssn, p)) { - /* force both streams to reassemble, if necessary */ - StreamTcpPseudoPacketCreateStreamEndPacket(p, ssn, pq); - SCPerfCounterIncr(stt->counter_tcp_pseudo, tv->sc_perf_pca); + if (SEQ_EQ(TCP_GET_SEQ(p),ssn->client.next_seq)) + ssn->client.next_seq += p->payload_len; - StreamTcpPacketSetState(p, ssn, TCP_CLOSED); - ssn->server.flags |= STREAMTCP_STREAM_FLAG_CLOSE_INITIATED; - ssn->client.flags |= STREAMTCP_STREAM_FLAG_CLOSE_INITIATED; - SCLogDebug("ssn %p: Reset received state changed to TCP_CLOSED", - ssn); + StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn, + &ssn->client, p, pq); + SCLogDebug("ssn %p: =+ next SEQ %" PRIu32 ", last ACK " + "%" PRIu32 "", ssn, ssn->client.next_seq, + ssn->server.last_ack); + } else { + SCLogDebug("ssn %p: pkt (%" PRIu32 ") is to client: SEQ " + "%" PRIu32 ", ACK %" PRIu32 "", ssn, p->payload_len, + TCP_GET_SEQ(p), TCP_GET_ACK(p)); - if (PKT_IS_TOSERVER(p)) { - StreamTcpUpdateLastAck(ssn, &ssn->server, - StreamTcpResetGetMaxAck(&ssn->server, TCP_GET_ACK(p))); + if (SEQ_LT(TCP_GET_SEQ(p), ssn->server.next_seq) || + SEQ_GT(TCP_GET_SEQ(p), (ssn->server.last_ack + ssn->server.window))) + { + SCLogDebug("ssn %p: -> SEQ mismatch, packet SEQ %" PRIu32 "" + " != %" PRIu32 " from stream", ssn, + TCP_GET_SEQ(p), ssn->server.next_seq); + StreamTcpSetEvent(p, STREAM_CLOSEWAIT_ACK_OUT_OF_WINDOW); + SCReturnInt(-1); + } + + if (StreamTcpValidateAck(&ssn->client, p) == -1) { + SCLogDebug("ssn %p: rejecting because of invalid ack value", ssn); + StreamTcpSetEvent(p, STREAM_CLOSEWAIT_INVALID_ACK); + SCReturnInt(-1); + } - StreamTcpUpdateLastAck(ssn, &ssn->client, - StreamTcpResetGetMaxAck(&ssn->client, TCP_GET_SEQ(p))); + ssn->client.window = TCP_GET_WINDOW(p) << ssn->client.wscale; - if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) { - StreamTcpHandleTimestamp(ssn, p); - } + StreamTcpUpdateLastAck(ssn, &ssn->client, TCP_GET_ACK(p)); - StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn, - &ssn->client, p, pq); - } else { - StreamTcpUpdateLastAck(ssn, &ssn->client, - StreamTcpResetGetMaxAck(&ssn->client, TCP_GET_ACK(p))); + if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) { + StreamTcpHandleTimestamp(ssn, p); + } - StreamTcpUpdateLastAck(ssn, &ssn->server, - StreamTcpResetGetMaxAck(&ssn->server, TCP_GET_SEQ(p))); + /* Update the next_seq, in case if we have missed the client + packet and server has already received and acked it */ + if (SEQ_LT(ssn->client.next_seq, TCP_GET_ACK(p))) + ssn->client.next_seq = TCP_GET_ACK(p); - if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) { - StreamTcpHandleTimestamp(ssn, p); - } + if (SEQ_EQ(TCP_GET_SEQ(p),ssn->server.next_seq)) + ssn->server.next_seq += p->payload_len; - StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn, - &ssn->server, p, pq); - } - } - else - return -1; - break; + StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn, + &ssn->server, p, pq); + SCLogDebug("ssn %p: =+ next SEQ %" PRIu32 ", last ACK " + "%" PRIu32 "", ssn, ssn->server.next_seq, + ssn->client.last_ack); + } - default: - SCLogDebug("ssn %p: default case", ssn); - break; + } else { + SCLogDebug("ssn %p: default case", ssn); } SCReturnInt(0); } @@ -3318,112 +3166,107 @@ static int StreamTcpPakcetStateLastAck(ThreadVars *tv, Packet *p, if (ssn == NULL) return -1; - switch(p->tcph->th_flags) { - case TH_ACK: - case TH_ACK|TH_PUSH: - case TH_ACK|TH_ECN: - case TH_ACK|TH_ECN|TH_CWR: - - if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) { - if (!StreamTcpValidateTimestamp(ssn, p)) - return -1; - } + if (p->tcph->th_flags & TH_RST) { + if (!StreamTcpValidateRst(ssn, p)) + return -1; - if (PKT_IS_TOSERVER(p)) { - SCLogDebug("ssn %p: pkt (%" PRIu32 ") is to server: SEQ " - "%" PRIu32 ", ACK %" PRIu32 "", ssn, p->payload_len, - TCP_GET_SEQ(p), TCP_GET_ACK(p)); - - if (TCP_GET_SEQ(p) != ssn->client.next_seq) { - SCLogDebug("ssn %p: -> SEQ mismatch, packet SEQ %" PRIu32 "" - " != %" PRIu32 " from stream", ssn, - TCP_GET_SEQ(p), ssn->client.next_seq); - StreamTcpSetEvent(p, STREAM_LASTACK_ACK_WRONG_SEQ); - return -1; - } + /* force both streams to reassemble, if necessary */ + StreamTcpPseudoPacketCreateStreamEndPacket(p, ssn, pq); + SCPerfCounterIncr(stt->counter_tcp_pseudo, tv->sc_perf_pca); - if (StreamTcpValidateAck(&ssn->server, p) == -1) { - SCLogDebug("ssn %p: rejecting because of invalid ack value", ssn); - StreamTcpSetEvent(p, STREAM_LASTACK_INVALID_ACK); - SCReturnInt(-1); - } + StreamTcpPacketSetState(p, ssn, TCP_CLOSED); + ssn->server.flags |= STREAMTCP_STREAM_FLAG_CLOSE_INITIATED; + ssn->client.flags |= STREAMTCP_STREAM_FLAG_CLOSE_INITIATED; + SCLogDebug("ssn %p: Reset received state changed to TCP_CLOSED", + ssn); - StreamTcpPacketSetState(p, ssn, TCP_CLOSED); - SCLogDebug("ssn %p: state changed to TCP_CLOSED", ssn); + if (PKT_IS_TOSERVER(p)) { + StreamTcpUpdateLastAck(ssn, &ssn->server, + StreamTcpResetGetMaxAck(&ssn->server, TCP_GET_ACK(p))); - ssn->server.window = TCP_GET_WINDOW(p) << ssn->server.wscale; + StreamTcpUpdateLastAck(ssn, &ssn->client, + StreamTcpResetGetMaxAck(&ssn->client, TCP_GET_SEQ(p))); - StreamTcpUpdateLastAck(ssn, &ssn->server, TCP_GET_ACK(p)); + if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) { + StreamTcpHandleTimestamp(ssn, p); + } - if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) { - StreamTcpHandleTimestamp(ssn, p); - } + StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn, + &ssn->client, p, pq); + } else { + StreamTcpUpdateLastAck(ssn, &ssn->client, + StreamTcpResetGetMaxAck(&ssn->client, TCP_GET_ACK(p))); - /* Update the next_seq, in case if we have missed the client - packet and server has already received and acked it */ - if (SEQ_LT(ssn->server.next_seq, TCP_GET_ACK(p))) - ssn->server.next_seq = TCP_GET_ACK(p); + StreamTcpUpdateLastAck(ssn, &ssn->server, + StreamTcpResetGetMaxAck(&ssn->server, TCP_GET_SEQ(p))); - StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn, - &ssn->client, p, pq); - SCLogDebug("ssn %p: =+ next SEQ %" PRIu32 ", last ACK " - "%" PRIu32 "", ssn, ssn->client.next_seq, - ssn->server.last_ack); + if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) { + StreamTcpHandleTimestamp(ssn, p); } - break; - case TH_RST: - case TH_RST|TH_ACK: - case TH_RST|TH_ACK|TH_ECN: - case TH_RST|TH_ACK|TH_ECN|TH_CWR: + StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn, + &ssn->server, p, pq); + } - if (StreamTcpValidateRst(ssn, p)) { - /* force both streams to reassemble, if necessary */ - StreamTcpPseudoPacketCreateStreamEndPacket(p, ssn, pq); - SCPerfCounterIncr(stt->counter_tcp_pseudo, tv->sc_perf_pca); + } else if (p->tcph->th_flags & TH_FIN) { + /** \todo */ - StreamTcpPacketSetState(p, ssn, TCP_CLOSED); - ssn->server.flags |= STREAMTCP_STREAM_FLAG_CLOSE_INITIATED; - ssn->client.flags |= STREAMTCP_STREAM_FLAG_CLOSE_INITIATED; - SCLogDebug("ssn %p: Reset received state changed to TCP_CLOSED", - ssn); + } else if (p->tcph->th_flags & TH_SYN) { + SCLogDebug("ssn (%p): SYN pkt on LastAck", ssn); + StreamTcpSetEvent(p, STREAM_SHUTDOWN_SYN_RESEND); + return -1; - if (PKT_IS_TOSERVER(p)) { - StreamTcpUpdateLastAck(ssn, &ssn->server, - StreamTcpResetGetMaxAck(&ssn->server, TCP_GET_ACK(p))); + } else if (p->tcph->th_flags & TH_ACK) { + if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) { + if (!StreamTcpValidateTimestamp(ssn, p)) + return -1; + } - StreamTcpUpdateLastAck(ssn, &ssn->client, - StreamTcpResetGetMaxAck(&ssn->client, TCP_GET_SEQ(p))); + if (PKT_IS_TOSERVER(p)) { + SCLogDebug("ssn %p: pkt (%" PRIu32 ") is to server: SEQ " + "%" PRIu32 ", ACK %" PRIu32 "", ssn, p->payload_len, + TCP_GET_SEQ(p), TCP_GET_ACK(p)); + + if (TCP_GET_SEQ(p) != ssn->client.next_seq) { + SCLogDebug("ssn %p: -> SEQ mismatch, packet SEQ %" PRIu32 "" + " != %" PRIu32 " from stream", ssn, + TCP_GET_SEQ(p), ssn->client.next_seq); + StreamTcpSetEvent(p, STREAM_LASTACK_ACK_WRONG_SEQ); + return -1; + } - if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) { - StreamTcpHandleTimestamp(ssn, p); - } + if (StreamTcpValidateAck(&ssn->server, p) == -1) { + SCLogDebug("ssn %p: rejecting because of invalid ack value", ssn); + StreamTcpSetEvent(p, STREAM_LASTACK_INVALID_ACK); + SCReturnInt(-1); + } - StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn, - &ssn->client, p, pq); - } else { - StreamTcpUpdateLastAck(ssn, &ssn->client, - StreamTcpResetGetMaxAck(&ssn->client, TCP_GET_ACK(p))); + StreamTcpPacketSetState(p, ssn, TCP_CLOSED); + SCLogDebug("ssn %p: state changed to TCP_CLOSED", ssn); - StreamTcpUpdateLastAck(ssn, &ssn->server, - StreamTcpResetGetMaxAck(&ssn->server, TCP_GET_SEQ(p))); + ssn->server.window = TCP_GET_WINDOW(p) << ssn->server.wscale; - if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) { - StreamTcpHandleTimestamp(ssn, p); - } + StreamTcpUpdateLastAck(ssn, &ssn->server, TCP_GET_ACK(p)); - StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn, - &ssn->server, p, pq); - } + if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) { + StreamTcpHandleTimestamp(ssn, p); } - else - return -1; - break; - default: - SCLogDebug("ssn %p: default case", ssn); - break; + /* Update the next_seq, in case if we have missed the client + packet and server has already received and acked it */ + if (SEQ_LT(ssn->server.next_seq, TCP_GET_ACK(p))) + ssn->server.next_seq = TCP_GET_ACK(p); + + StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn, + &ssn->client, p, pq); + SCLogDebug("ssn %p: =+ next SEQ %" PRIu32 ", last ACK " + "%" PRIu32 "", ssn, ssn->client.next_seq, + ssn->server.last_ack); + } + } else { + SCLogDebug("ssn %p: default case", ssn); } + return 0; } @@ -3443,157 +3286,152 @@ static int StreamTcpPacketStateTimeWait(ThreadVars *tv, Packet *p, if (ssn == NULL) return -1; - switch(p->tcph->th_flags) { - case TH_ACK: - case TH_ACK|TH_PUSH: - case TH_ACK|TH_ECN: - case TH_ACK|TH_ECN|TH_CWR: + if (p->tcph->th_flags & TH_RST) { + if (!StreamTcpValidateRst(ssn, p)) + return -1; - if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) { - if (!StreamTcpValidateTimestamp(ssn, p)) - return -1; - } + /* force both streams to reassemble, if necessary */ + StreamTcpPseudoPacketCreateStreamEndPacket(p, ssn, pq); + SCPerfCounterIncr(stt->counter_tcp_pseudo, tv->sc_perf_pca); - if (PKT_IS_TOSERVER(p)) { - SCLogDebug("ssn %p: pkt (%" PRIu32 ") is to server: SEQ " - "%" PRIu32 ", ACK %" PRIu32 "", ssn, p->payload_len, - TCP_GET_SEQ(p), TCP_GET_ACK(p)); - - if (TCP_GET_SEQ(p) != ssn->client.next_seq) { - SCLogDebug("ssn %p: -> SEQ mismatch, packet SEQ %" PRIu32 "" - " != %" PRIu32 " from stream", ssn, - TCP_GET_SEQ(p), ssn->client.next_seq); - StreamTcpSetEvent(p, STREAM_TIMEWAIT_ACK_WRONG_SEQ); - return -1; - } + StreamTcpPacketSetState(p, ssn, TCP_CLOSED); + ssn->server.flags |= STREAMTCP_STREAM_FLAG_CLOSE_INITIATED; + ssn->client.flags |= STREAMTCP_STREAM_FLAG_CLOSE_INITIATED; + SCLogDebug("ssn %p: Reset received state changed to TCP_CLOSED", + ssn); - if (StreamTcpValidateAck(&ssn->server, p) == -1) { - SCLogDebug("ssn %p: rejecting because of invalid ack value", ssn); - StreamTcpSetEvent(p, STREAM_TIMEWAIT_INVALID_ACK); - SCReturnInt(-1); - } + if (PKT_IS_TOSERVER(p)) { + StreamTcpUpdateLastAck(ssn, &ssn->server, + StreamTcpResetGetMaxAck(&ssn->server, TCP_GET_ACK(p))); - StreamTcpPacketSetState(p, ssn, TCP_CLOSED); - SCLogDebug("ssn %p: state changed to TCP_CLOSED", ssn); + StreamTcpUpdateLastAck(ssn, &ssn->client, + StreamTcpResetGetMaxAck(&ssn->client, TCP_GET_SEQ(p))); - ssn->server.window = TCP_GET_WINDOW(p) << ssn->server.wscale; + if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) { + StreamTcpHandleTimestamp(ssn, p); + } - StreamTcpUpdateLastAck(ssn, &ssn->server, TCP_GET_ACK(p)); + StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn, + &ssn->client, p, pq); + } else { + StreamTcpUpdateLastAck(ssn, &ssn->client, + StreamTcpResetGetMaxAck(&ssn->client, TCP_GET_ACK(p))); - if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) { - StreamTcpHandleTimestamp(ssn, p); - } + StreamTcpUpdateLastAck(ssn, &ssn->server, + StreamTcpResetGetMaxAck(&ssn->server, TCP_GET_SEQ(p))); - /* Update the next_seq, in case if we have missed the client - packet and server has already received and acked it */ - if (SEQ_LT(ssn->server.next_seq, TCP_GET_ACK(p))) - ssn->server.next_seq = TCP_GET_ACK(p); + if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) { + StreamTcpHandleTimestamp(ssn, p); + } - StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn, - &ssn->client, p, pq); - SCLogDebug("ssn %p: =+ next SEQ %" PRIu32 ", last ACK " - "%" PRIu32 "", ssn, ssn->client.next_seq, - ssn->server.last_ack); + StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn, + &ssn->server, p, pq); + } - StreamTcpPseudoPacketCreateStreamEndPacket(p, ssn, pq); - } else { - SCLogDebug("ssn %p: pkt (%" PRIu32 ") is to client: SEQ " - "%" PRIu32 ", ACK %" PRIu32 "", ssn, p->payload_len, - TCP_GET_SEQ(p), TCP_GET_ACK(p)); - - if (TCP_GET_SEQ(p) != ssn->server.next_seq) { - SCLogDebug("ssn %p: -> SEQ mismatch, packet SEQ %" PRIu32 "" - " != %" PRIu32 " from stream", ssn, - TCP_GET_SEQ(p), ssn->server.next_seq); - StreamTcpSetEvent(p, STREAM_TIMEWAIT_ACK_WRONG_SEQ); - return -1; - } + } else if (p->tcph->th_flags & TH_FIN) { + /** \todo */ - if (StreamTcpValidateAck(&ssn->client, p) == -1) { - SCLogDebug("ssn %p: rejecting because of invalid ack value", ssn); - StreamTcpSetEvent(p, STREAM_TIMEWAIT_INVALID_ACK); - SCReturnInt(-1); - } + } else if (p->tcph->th_flags & TH_SYN) { + SCLogDebug("ssn (%p): SYN pkt on TimeWait", ssn); + StreamTcpSetEvent(p, STREAM_SHUTDOWN_SYN_RESEND); + return -1; - StreamTcpPacketSetState(p, ssn, TCP_CLOSED); - SCLogDebug("ssn %p: state changed to TCP_CLOSED", ssn); + } else if (p->tcph->th_flags & TH_ACK) { + if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) { + if (!StreamTcpValidateTimestamp(ssn, p)) + return -1; + } - ssn->client.window = TCP_GET_WINDOW(p) << ssn->client.wscale; + if (PKT_IS_TOSERVER(p)) { + SCLogDebug("ssn %p: pkt (%" PRIu32 ") is to server: SEQ " + "%" PRIu32 ", ACK %" PRIu32 "", ssn, p->payload_len, + TCP_GET_SEQ(p), TCP_GET_ACK(p)); + + if (TCP_GET_SEQ(p) != ssn->client.next_seq) { + SCLogDebug("ssn %p: -> SEQ mismatch, packet SEQ %" PRIu32 "" + " != %" PRIu32 " from stream", ssn, + TCP_GET_SEQ(p), ssn->client.next_seq); + StreamTcpSetEvent(p, STREAM_TIMEWAIT_ACK_WRONG_SEQ); + return -1; + } - StreamTcpUpdateLastAck(ssn, &ssn->client, TCP_GET_ACK(p)); + if (StreamTcpValidateAck(&ssn->server, p) == -1) { + SCLogDebug("ssn %p: rejecting because of invalid ack value", ssn); + StreamTcpSetEvent(p, STREAM_TIMEWAIT_INVALID_ACK); + SCReturnInt(-1); + } - if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) { - StreamTcpHandleTimestamp(ssn, p); - } + StreamTcpPacketSetState(p, ssn, TCP_CLOSED); + SCLogDebug("ssn %p: state changed to TCP_CLOSED", ssn); - /* Update the next_seq, in case if we have missed the client - packet and server has already received and acked it */ - if (SEQ_LT(ssn->client.next_seq, TCP_GET_ACK(p))) - ssn->client.next_seq = TCP_GET_ACK(p); + ssn->server.window = TCP_GET_WINDOW(p) << ssn->server.wscale; - StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn, - &ssn->server, p, pq); - SCLogDebug("ssn %p: =+ next SEQ %" PRIu32 ", last ACK " - "%" PRIu32 "", ssn, ssn->server.next_seq, - ssn->client.last_ack); + StreamTcpUpdateLastAck(ssn, &ssn->server, TCP_GET_ACK(p)); - StreamTcpPseudoPacketCreateStreamEndPacket(p, ssn, pq); + if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) { + StreamTcpHandleTimestamp(ssn, p); } - break; - case TH_RST: - case TH_RST|TH_ACK: - case TH_RST|TH_ACK|TH_ECN: - case TH_RST|TH_ACK|TH_ECN|TH_CWR: + /* Update the next_seq, in case if we have missed the client + packet and server has already received and acked it */ + if (SEQ_LT(ssn->server.next_seq, TCP_GET_ACK(p))) + ssn->server.next_seq = TCP_GET_ACK(p); - if (StreamTcpValidateRst(ssn, p)) { - /* force both streams to reassemble, if necessary */ - StreamTcpPseudoPacketCreateStreamEndPacket(p, ssn, pq); - SCPerfCounterIncr(stt->counter_tcp_pseudo, tv->sc_perf_pca); + StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn, + &ssn->client, p, pq); + SCLogDebug("ssn %p: =+ next SEQ %" PRIu32 ", last ACK " + "%" PRIu32 "", ssn, ssn->client.next_seq, + ssn->server.last_ack); - StreamTcpPacketSetState(p, ssn, TCP_CLOSED); - ssn->server.flags |= STREAMTCP_STREAM_FLAG_CLOSE_INITIATED; - ssn->client.flags |= STREAMTCP_STREAM_FLAG_CLOSE_INITIATED; - SCLogDebug("ssn %p: Reset received state changed to TCP_CLOSED", - ssn); + StreamTcpPseudoPacketCreateStreamEndPacket(p, ssn, pq); + } else { + SCLogDebug("ssn %p: pkt (%" PRIu32 ") is to client: SEQ " + "%" PRIu32 ", ACK %" PRIu32 "", ssn, p->payload_len, + TCP_GET_SEQ(p), TCP_GET_ACK(p)); + + if (TCP_GET_SEQ(p) != ssn->server.next_seq) { + SCLogDebug("ssn %p: -> SEQ mismatch, packet SEQ %" PRIu32 "" + " != %" PRIu32 " from stream", ssn, + TCP_GET_SEQ(p), ssn->server.next_seq); + StreamTcpSetEvent(p, STREAM_TIMEWAIT_ACK_WRONG_SEQ); + return -1; + } - if (PKT_IS_TOSERVER(p)) { - StreamTcpUpdateLastAck(ssn, &ssn->server, - StreamTcpResetGetMaxAck(&ssn->server, TCP_GET_ACK(p))); + if (StreamTcpValidateAck(&ssn->client, p) == -1) { + SCLogDebug("ssn %p: rejecting because of invalid ack value", ssn); + StreamTcpSetEvent(p, STREAM_TIMEWAIT_INVALID_ACK); + SCReturnInt(-1); + } - StreamTcpUpdateLastAck(ssn, &ssn->client, - StreamTcpResetGetMaxAck(&ssn->client, TCP_GET_SEQ(p))); + StreamTcpPacketSetState(p, ssn, TCP_CLOSED); + SCLogDebug("ssn %p: state changed to TCP_CLOSED", ssn); - if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) { - StreamTcpHandleTimestamp(ssn, p); - } + ssn->client.window = TCP_GET_WINDOW(p) << ssn->client.wscale; - StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn, - &ssn->client, p, pq); - } else { - StreamTcpUpdateLastAck(ssn, &ssn->client, - StreamTcpResetGetMaxAck(&ssn->client, TCP_GET_ACK(p))); + StreamTcpUpdateLastAck(ssn, &ssn->client, TCP_GET_ACK(p)); - StreamTcpUpdateLastAck(ssn, &ssn->server, - StreamTcpResetGetMaxAck(&ssn->server, TCP_GET_SEQ(p))); + if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) { + StreamTcpHandleTimestamp(ssn, p); + } - if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) { - StreamTcpHandleTimestamp(ssn, p); - } + /* Update the next_seq, in case if we have missed the client + packet and server has already received and acked it */ + if (SEQ_LT(ssn->client.next_seq, TCP_GET_ACK(p))) + ssn->client.next_seq = TCP_GET_ACK(p); - StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn, - &ssn->server, p, pq); - } - } - else - return -1; - break; + StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn, + &ssn->server, p, pq); + SCLogDebug("ssn %p: =+ next SEQ %" PRIu32 ", last ACK " + "%" PRIu32 "", ssn, ssn->server.next_seq, + ssn->client.last_ack); - default: - SCLogDebug("ssn %p: default case", ssn); - break; + StreamTcpPseudoPacketCreateStreamEndPacket(p, ssn, pq); + } + } else { + SCLogDebug("ssn %p: default case", ssn); } + return 0; }