]> git.ipfire.org Git - thirdparty/suricata.git/commitdiff
stream: change how retransmissions are handled and detected.
authorVictor Julien <victor@inliniac.net>
Wed, 17 Oct 2012 07:25:52 +0000 (09:25 +0200)
committerVictor Julien <victor@inliniac.net>
Wed, 17 Oct 2012 07:25:52 +0000 (09:25 +0200)
rules/stream-events.rules
src/decode-events.h
src/detect-engine-event.h
src/stream-tcp.c

index af129f10147a164e20ba677a4caf3f6ae7274c15..f97eb1a7d0dd7ae5963552666a4e815efc9c8e96 100644 (file)
@@ -20,12 +20,9 @@ alert tcp any any -> any any (msg:"SURICATA STREAM 4way handshake invalid ack";
 alert tcp any any -> any any (msg:"SURICATA STREAM CLOSEWAIT ACK out of window"; stream-event:closewait_ack_out_of_window; sid:2210015; rev:1;)
 alert tcp any any -> any any (msg:"SURICATA STREAM CLOSEWAIT FIN out of window"; stream-event:closewait_fin_out_of_window; sid:2210016; rev:1;)
 alert tcp any any -> any any (msg:"SURICATA STREAM CLOSEWAIT invalid ACK"; stream-event:closewait_invalid_ack; sid:2210017; rev:1;)
-#alert tcp any any -> any any (msg:"SURICATA STREAM CLOSEWAIT retransmission packet before last ack"; stream-event:closewait_pkt_before_last_ack; sid:2210052; rev:2;)
 alert tcp any any -> any any (msg:"SURICATA STREAM CLOSING ACK wrong seq"; stream-event:closing_ack_wrong_seq; sid:2210018; rev:1;)
 alert tcp any any -> any any (msg:"SURICATA STREAM CLOSING invalid ACK"; stream-event:closing_invalid_ack; sid:2210019; rev:1;)
 alert tcp any any -> any any (msg:"SURICATA STREAM ESTABLISHED packet out of window"; stream-event:est_packet_out_of_window; sid:2210020; rev:1;)
-# "regular" retransmissions
-#alert tcp any any -> any any (msg:"SURICATA STREAM ESTABLISHED retransmission packet before last ack"; stream-event:est_pkt_before_last_ack; sid:2210021; rev:2;)
 alert tcp any any -> any any (msg:"SURICATA STREAM ESTABLISHED SYNACK resend"; stream-event:est_synack_resend; sid:2210022; rev:1;)
 alert tcp any any -> any any (msg:"SURICATA STREAM ESTABLISHED SYNACK resend with different ACK"; stream-event:est_synack_resend_with_different_ack; sid:2210023; rev:1;)
 alert tcp any any -> any any (msg:"SURICATA STREAM ESTABLISHED SYNACK resend with different seq"; stream-event:est_synack_resend_with_diff_seq; sid:2210024; rev:1;)
@@ -58,9 +55,27 @@ alert tcp any any -> any any (msg:"SURICATA STREAM Packet with invalid ack"; str
 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;)
 alert tcp any any -> any any (msg:"SURICATA STREAM reassembly overlap with different data"; stream-event:reassembly_overlap_different_data; sid:2210050; rev:1;)
-# next sid 2210053
+
+# retransmission detection
+#
+# The rules below match on retransmissions detected in various stages of the
+# stream engine. They are all "noalert" rules that increment the counter
+# tcp.retransmission.count. The last rule sid:2210054 matches if the counter
+# reaches 10. Increase this number if the rule is too noisy.
+#
+# "regular" retransmissions, only count
+alert tcp any any -> any any (msg:"SURICATA STREAM ESTABLISHED retransmission packet before last ack"; stream-event:est_pkt_before_last_ack; flowint:tcp.retransmission.count,+,1; noalert; classtype:protocol-command-decode; sid:2210021; rev:3;)
+# retransmission, only count
+alert tcp any any -> any any (msg:"SURICATA STREAM CLOSEWAIT retransmission packet before last ack"; stream-event:closewait_pkt_before_last_ack; flowint:tcp.retransmission.count,+,1; noalert; classtype:protocol-command-decode; sid:2210052; rev:3;)
+# retransmission of pkt before reassembly window, only count
+alert tcp any any -> any any (msg:"SURICATA STREAM reassembly segment before base seq (retransmission)"; stream-event:reassembly_segment_before_base_seq; flowint:tcp.retransmission.count,+,1; noalert; classtype:protocol-command-decode; sid:2210047; rev:2;)
+# count "general" retransmissions
+alert tcp any any -> any any (msg:"SURICATA STREAM Packet is retransmission"; stream-event:pkt_retransmission; flowint:tcp.retransmission.count,+,1; noalert; classtype:protocol-command-decode; sid:2210053; rev:1;)
+# rule to alert if a stream has excessive retransmissions
+alert tcp any any -> any any (msg:"SURICATA STREAM excessive retransmissions"; flowbits:isnotset,tcp.retransmission.alerted; flowint:tcp.retransmission.count,>=,10; flowbits:set,tcp.retransmission.alerted; classtype:protocol-command-decode; sid:2210054; rev:1;)
+
+# next sid 2210055
 
index 697279fdd84f3e267f2ad43204732c1861d6b276..f03b77ffd52376f3ff00a7af0ee4c754dba9b30e 100644 (file)
@@ -179,6 +179,7 @@ enum {
     STREAM_PKT_INVALID_ACK,
     STREAM_PKT_BROKEN_ACK,
     STREAM_RST_INVALID_ACK,
+    STREAM_PKT_RETRANSMISSION,
 
     STREAM_REASSEMBLY_SEGMENT_BEFORE_BASE_SEQ,
     STREAM_REASSEMBLY_NO_SEGMENT,
index 5bde8df7149ccdf407b2b25538b09c23e64fcf9e..5a4a56a76e8c8ee1594d2667558eaadd080689d2 100644 (file)
@@ -170,6 +170,7 @@ struct DetectEngineEvents_ {
     { "stream.pkt_broken_ack", STREAM_PKT_BROKEN_ACK, },
     { "stream.rst_invalid_ack", STREAM_RST_INVALID_ACK, },
     { "stream.shutdown_syn_resend", STREAM_SHUTDOWN_SYN_RESEND, },
+    { "stream.pkt_retransmission", STREAM_PKT_RETRANSMISSION, },
     { "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, },
index 1567c01504a99eabfd341dd032982dc20b02d6c3..f25379d0dac862e74a23e616ed568cc16b107c38 100644 (file)
@@ -636,6 +636,27 @@ void StreamTcpSetOSPolicy(TcpStream *stream, Packet *p)
     } \
 }
 
+static int StreamTcpPacketIsRetransmission(TcpStream *stream, Packet *p) {
+    if (p->payload_len == 0)
+        SCReturnInt(0);
+
+    /* retransmission of already ack'd data */
+    if (SEQ_LEQ((TCP_GET_SEQ(p) + p->payload_len), stream->last_ack)) {
+        StreamTcpSetEvent(p, STREAM_PKT_RETRANSMISSION);
+        SCReturnInt(1);
+    }
+
+    /* retransmission of in flight data */
+    if (SEQ_LEQ((TCP_GET_SEQ(p) + p->payload_len), stream->next_seq)) {
+        StreamTcpSetEvent(p, STREAM_PKT_RETRANSMISSION);
+        SCReturnInt(2);
+    }
+
+    SCLogDebug("seq %u payload_len %u => %u, last_ack %u", TCP_GET_SEQ(p),
+            p->payload_len, (TCP_GET_SEQ(p) + p->payload_len), stream->last_ack);
+    SCReturnInt(0);
+}
+
 /**
  *  \internal
  *  \brief  Function to handle the TCP_CLOSED or NONE state. The function handles
@@ -2258,8 +2279,13 @@ static int StreamTcpPacketStateFinWait1(ThreadVars *tv, Packet *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));
+            int retransmission = 0;
 
-            if (SEQ_LT(TCP_GET_SEQ(p), ssn->client.next_seq) ||
+            if (StreamTcpPacketIsRetransmission(&ssn->client, p)) {
+                SCLogDebug("ssn %p: packet is retransmission", ssn);
+                retransmission = 1;
+
+            } else 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 ""
@@ -2275,11 +2301,13 @@ static int StreamTcpPacketStateFinWait1(ThreadVars *tv, Packet *p,
                 return -1;
             }
 
-            StreamTcpPacketSetState(p, ssn, TCP_TIME_WAIT);
-            ssn->client.flags |= STREAMTCP_STREAM_FLAG_CLOSE_INITIATED;
-            SCLogDebug("ssn %p: state changed to TCP_TIME_WAIT", ssn);
+            if (!retransmission) {
+                StreamTcpPacketSetState(p, ssn, TCP_TIME_WAIT);
+                ssn->client.flags |= STREAMTCP_STREAM_FLAG_CLOSE_INITIATED;
+                SCLogDebug("ssn %p: state changed to TCP_TIME_WAIT", ssn);
 
-            ssn->server.window = TCP_GET_WINDOW(p) << ssn->server.wscale;
+                ssn->server.window = TCP_GET_WINDOW(p) << ssn->server.wscale;
+            }
 
             StreamTcpUpdateLastAck(ssn, &ssn->server, TCP_GET_ACK(p));
 
@@ -2308,8 +2336,13 @@ static int StreamTcpPacketStateFinWait1(ThreadVars *tv, Packet *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));
+            int retransmission = 0;
 
-            if (SEQ_LT(TCP_GET_SEQ(p), ssn->server.next_seq) ||
+            if (StreamTcpPacketIsRetransmission(&ssn->server, p)) {
+                SCLogDebug("ssn %p: packet is retransmission", ssn);
+                retransmission = 1;
+
+            } else 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 ""
@@ -2325,11 +2358,13 @@ static int StreamTcpPacketStateFinWait1(ThreadVars *tv, Packet *p,
                 return -1;
             }
 
-            StreamTcpPacketSetState(p, ssn, TCP_TIME_WAIT);
-            ssn->server.flags |= STREAMTCP_STREAM_FLAG_CLOSE_INITIATED;
-            SCLogDebug("ssn %p: state changed to TCP_TIME_WAIT", ssn);
+            if (!retransmission) {
+                StreamTcpPacketSetState(p, ssn, TCP_TIME_WAIT);
+                ssn->server.flags |= STREAMTCP_STREAM_FLAG_CLOSE_INITIATED;
+                SCLogDebug("ssn %p: state changed to TCP_TIME_WAIT", ssn);
 
-            ssn->client.window = TCP_GET_WINDOW(p) << ssn->client.wscale;
+                ssn->client.window = TCP_GET_WINDOW(p) << ssn->client.wscale;
+            }
 
             StreamTcpUpdateLastAck(ssn, &ssn->client, TCP_GET_ACK(p));
 
@@ -2366,8 +2401,13 @@ static int StreamTcpPacketStateFinWait1(ThreadVars *tv, Packet *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));
+            int retransmission = 0;
 
-            if (SEQ_LT(TCP_GET_SEQ(p), ssn->client.next_seq) ||
+            if (StreamTcpPacketIsRetransmission(&ssn->client, p)) {
+                SCLogDebug("ssn %p: packet is retransmission", ssn);
+                retransmission = 1;
+
+            } else 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 ""
@@ -2383,11 +2423,13 @@ static int StreamTcpPacketStateFinWait1(ThreadVars *tv, Packet *p,
                 return -1;
             }
 
-            StreamTcpPacketSetState(p, ssn, TCP_CLOSING);
-            ssn->client.flags |= STREAMTCP_STREAM_FLAG_CLOSE_INITIATED;
-            SCLogDebug("ssn %p: state changed to TCP_CLOSING", ssn);
+            if (!retransmission) {
+                StreamTcpPacketSetState(p, ssn, TCP_CLOSING);
+                ssn->client.flags |= STREAMTCP_STREAM_FLAG_CLOSE_INITIATED;
+                SCLogDebug("ssn %p: state changed to TCP_CLOSING", ssn);
 
-            ssn->server.window = TCP_GET_WINDOW(p) << ssn->server.wscale;
+                ssn->server.window = TCP_GET_WINDOW(p) << ssn->server.wscale;
+            }
 
             StreamTcpUpdateLastAck(ssn, &ssn->server, TCP_GET_ACK(p));
 
@@ -2417,7 +2459,13 @@ static int StreamTcpPacketStateFinWait1(ThreadVars *tv, Packet *p,
                     "%" PRIu32 ", ACK %" PRIu32 "", ssn, p->payload_len,
                     TCP_GET_SEQ(p), TCP_GET_ACK(p));
 
-            if (SEQ_LT(TCP_GET_SEQ(p), ssn->server.next_seq) ||
+            int retransmission = 0;
+
+            if (StreamTcpPacketIsRetransmission(&ssn->server, p)) {
+                SCLogDebug("ssn %p: packet is retransmission", ssn);
+                retransmission = 1;
+
+            } else 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 ""
@@ -2433,11 +2481,13 @@ static int StreamTcpPacketStateFinWait1(ThreadVars *tv, Packet *p,
                 return -1;
             }
 
-            StreamTcpPacketSetState(p, ssn, TCP_CLOSING);
-            ssn->server.flags |= STREAMTCP_STREAM_FLAG_CLOSE_INITIATED;
-            SCLogDebug("ssn %p: state changed to TCP_CLOSING", ssn);
+            if (!retransmission) {
+                StreamTcpPacketSetState(p, ssn, TCP_CLOSING);
+                ssn->server.flags |= STREAMTCP_STREAM_FLAG_CLOSE_INITIATED;
+                SCLogDebug("ssn %p: state changed to TCP_CLOSING", ssn);
 
-            ssn->client.window = TCP_GET_WINDOW(p) << ssn->client.wscale;
+                ssn->client.window = TCP_GET_WINDOW(p) << ssn->client.wscale;
+            }
 
             StreamTcpUpdateLastAck(ssn, &ssn->client, TCP_GET_ACK(p));
 
@@ -2478,6 +2528,12 @@ static int StreamTcpPacketStateFinWait1(ThreadVars *tv, Packet *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));
+            int retransmission = 0;
+
+            if (StreamTcpPacketIsRetransmission(&ssn->client, p)) {
+                SCLogDebug("ssn %p: packet is retransmission", ssn);
+                retransmission = 1;
+            }
 
             if (StreamTcpValidateAck(ssn, &ssn->server, p) == -1) {
                 SCLogDebug("ssn %p: rejecting because of invalid ack value", ssn);
@@ -2485,28 +2541,30 @@ static int StreamTcpPacketStateFinWait1(ThreadVars *tv, Packet *p,
                 return -1;
             }
 
-            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 (!retransmission) {
+                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 (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 (TCP_GET_SEQ(p) == ssn->client.next_seq) {
-                    StreamTcpPacketSetState(p, ssn, TCP_FIN_WAIT2);
-                    SCLogDebug("ssn %p: state changed to TCP_FIN_WAIT2", ssn);
+                    StreamTcpSetEvent(p, STREAM_FIN1_ACK_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_FIN1_ACK_WRONG_SEQ);
-                return -1;
+                ssn->server.window = TCP_GET_WINDOW(p) << ssn->server.wscale;
             }
 
-            ssn->server.window = TCP_GET_WINDOW(p) << ssn->server.wscale;
-
             StreamTcpUpdateLastAck(ssn, &ssn->server, TCP_GET_ACK(p));
 
             if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) {
@@ -2542,32 +2600,41 @@ static int StreamTcpPacketStateFinWait1(ThreadVars *tv, Packet *p,
                     "%" PRIu32 ", ACK %" PRIu32 "", ssn, p->payload_len,
                     TCP_GET_SEQ(p), TCP_GET_ACK(p));
 
+            int retransmission = 0;
+
+            if (StreamTcpPacketIsRetransmission(&ssn->server, p)) {
+                SCLogDebug("ssn %p: packet is retransmission", ssn);
+                retransmission = 1;
+            }
+
             if (StreamTcpValidateAck(ssn, &ssn->client, p) == -1) {
                 SCLogDebug("ssn %p: rejecting because of invalid ack value", ssn);
                 StreamTcpSetEvent(p, STREAM_FIN1_INVALID_ACK);
                 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);
+            if (!retransmission) {
+                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);
+                    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;
                 }
-            } 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;
-            }
 
-            ssn->client.window = TCP_GET_WINDOW(p) << ssn->client.wscale;
+                ssn->client.window = TCP_GET_WINDOW(p) << ssn->client.wscale;
+            }
 
             StreamTcpUpdateLastAck(ssn, &ssn->client, TCP_GET_ACK(p));
 
@@ -2679,6 +2746,9 @@ static int StreamTcpPacketStateFinWait2(ThreadVars *tv, Packet *p,
                 SEQ_EQ(TCP_GET_ACK(p), ssn->server.last_ack)) {
                 SCLogDebug("ssn %p: retransmission", ssn);
                 retransmission = 1;
+            } else if (StreamTcpPacketIsRetransmission(&ssn->client, p)) {
+                SCLogDebug("ssn %p: packet is retransmission", ssn);
+                retransmission = 1;
 
             } else if (SEQ_LT(TCP_GET_SEQ(p), ssn->client.next_seq) ||
                     SEQ_GT(TCP_GET_SEQ(p), (ssn->client.last_ack + ssn->client.window)))
@@ -2700,9 +2770,9 @@ static int StreamTcpPacketStateFinWait2(ThreadVars *tv, Packet *p,
                 StreamTcpPacketSetState(p, ssn, TCP_TIME_WAIT);
                 ssn->client.flags |= STREAMTCP_STREAM_FLAG_CLOSE_INITIATED;
                 SCLogDebug("ssn %p: state changed to TCP_TIME_WAIT", ssn);
-            }
 
-            ssn->server.window = TCP_GET_WINDOW(p) << ssn->server.wscale;
+                ssn->server.window = TCP_GET_WINDOW(p) << ssn->server.wscale;
+            }
 
             StreamTcpUpdateLastAck(ssn, &ssn->server, TCP_GET_ACK(p));
 
@@ -2733,6 +2803,9 @@ static int StreamTcpPacketStateFinWait2(ThreadVars *tv, Packet *p,
                 SEQ_EQ(TCP_GET_ACK(p), ssn->client.last_ack)) {
                 SCLogDebug("ssn %p: retransmission", ssn);
                 retransmission = 1;
+            } else if (StreamTcpPacketIsRetransmission(&ssn->server, p)) {
+                SCLogDebug("ssn %p: packet is retransmission", ssn);
+                retransmission = 1;
 
             } else if (SEQ_LT(TCP_GET_SEQ(p), ssn->server.next_seq) ||
                     SEQ_GT(TCP_GET_SEQ(p), (ssn->server.last_ack + ssn->server.window)))
@@ -2754,8 +2827,9 @@ static int StreamTcpPacketStateFinWait2(ThreadVars *tv, Packet *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);
+
+                ssn->client.window = TCP_GET_WINDOW(p) << ssn->client.wscale;
             }
-            ssn->client.window = TCP_GET_WINDOW(p) << ssn->client.wscale;
 
             StreamTcpUpdateLastAck(ssn, &ssn->client, TCP_GET_ACK(p));
 
@@ -2792,6 +2866,12 @@ static int StreamTcpPacketStateFinWait2(ThreadVars *tv, Packet *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));
+            int retransmission = 0;
+
+            if (StreamTcpPacketIsRetransmission(&ssn->client, p)) {
+                SCLogDebug("ssn %p: packet is retransmission", ssn);
+                retransmission = 1;
+            }
 
             if (StreamTcpValidateAck(ssn, &ssn->server, p) == -1) {
                 SCLogDebug("ssn %p: rejecting because of invalid ack value", ssn);
@@ -2799,22 +2879,24 @@ static int StreamTcpPacketStateFinWait2(ThreadVars *tv, Packet *p,
                 return -1;
             }
 
-            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 (!retransmission) {
+                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);
 
-            } 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;
-            }
+                } 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;
+                }
 
-            ssn->server.window = TCP_GET_WINDOW(p) << ssn->server.wscale;
+                ssn->server.window = TCP_GET_WINDOW(p) << ssn->server.wscale;
+            }
 
             StreamTcpUpdateLastAck(ssn, &ssn->server, TCP_GET_ACK(p));
 
@@ -2843,6 +2925,12 @@ static int StreamTcpPacketStateFinWait2(ThreadVars *tv, Packet *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));
+            int retransmission = 0;
+
+            if (StreamTcpPacketIsRetransmission(&ssn->server, p)) {
+                SCLogDebug("ssn %p: packet is retransmission", ssn);
+                retransmission = 1;
+            }
 
             if (StreamTcpValidateAck(ssn, &ssn->client, p) == -1) {
                 SCLogDebug("ssn %p: rejecting because of invalid ack value", ssn);
@@ -2850,21 +2938,23 @@ static int StreamTcpPacketStateFinWait2(ThreadVars *tv, Packet *p,
                 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 (!retransmission) {
+                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;
+                }
 
-            ssn->client.window = TCP_GET_WINDOW(p) << ssn->client.wscale;
+                ssn->client.window = TCP_GET_WINDOW(p) << ssn->client.wscale;
+            }
 
             StreamTcpUpdateLastAck(ssn, &ssn->client, TCP_GET_ACK(p));
 
@@ -2971,6 +3061,11 @@ static int StreamTcpPacketStateClosing(ThreadVars *tv, Packet *p,
                     "%" PRIu32 ", ACK %" PRIu32 "", ssn, p->payload_len,
                     TCP_GET_SEQ(p), TCP_GET_ACK(p));
 
+            if (StreamTcpPacketIsRetransmission(&ssn->client, p)) {
+                SCLogDebug("ssn %p: packet is retransmission", ssn);
+                SCReturnInt(-1);
+            }
+
             if (TCP_GET_SEQ(p) != ssn->client.next_seq) {
                 SCLogDebug("ssn %p: -> SEQ mismatch, packet SEQ %" PRIu32 ""
                         " != %" PRIu32 " from stream", ssn,
@@ -3010,6 +3105,11 @@ static int StreamTcpPacketStateClosing(ThreadVars *tv, Packet *p,
                     "%" PRIu32 ", ACK %" PRIu32 "", ssn, p->payload_len,
                     TCP_GET_SEQ(p), TCP_GET_ACK(p));
 
+            if (StreamTcpPacketIsRetransmission(&ssn->server, p)) {
+                SCLogDebug("ssn %p: packet is retransmission", ssn);
+                SCReturnInt(-1);
+            }
+
             if (TCP_GET_SEQ(p) != ssn->server.next_seq) {
                 SCLogDebug("ssn %p: -> SEQ mismatch, packet SEQ %" PRIu32 ""
                         " != %" PRIu32 " from stream", ssn,
@@ -3135,6 +3235,11 @@ static int StreamTcpPacketStateCloseWait(ThreadVars *tv, Packet *p,
                     "%" PRIu32 ", ACK %" PRIu32 "", ssn, p->payload_len,
                     TCP_GET_SEQ(p), TCP_GET_ACK(p));
 
+            if (StreamTcpPacketIsRetransmission(&ssn->client, p)) {
+                SCLogDebug("ssn %p: packet is retransmission", ssn);
+                SCReturnInt(-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)))
             {
@@ -3178,6 +3283,11 @@ static int StreamTcpPacketStateCloseWait(ThreadVars *tv, Packet *p,
                     "%" PRIu32 ", ACK %" PRIu32 "", ssn, p->payload_len,
                     TCP_GET_SEQ(p), TCP_GET_ACK(p));
 
+            if (StreamTcpPacketIsRetransmission(&ssn->server, p)) {
+                SCLogDebug("ssn %p: packet is retransmission", ssn);
+                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)))
             {
@@ -3234,6 +3344,11 @@ static int StreamTcpPacketStateCloseWait(ThreadVars *tv, Packet *p,
                     "%" PRIu32 ", ACK %" PRIu32 "", ssn, p->payload_len,
                     TCP_GET_SEQ(p), TCP_GET_ACK(p));
 
+            if (StreamTcpPacketIsRetransmission(&ssn->client, p)) {
+                SCLogDebug("ssn %p: packet is retransmission", ssn);
+                SCReturnInt(-1);
+            }
+
             if (p->payload_len > 0 && (SEQ_LEQ((TCP_GET_SEQ(p) + p->payload_len), ssn->client.last_ack))) {
                 SCLogDebug("ssn %p: -> retransmission", ssn);
                 StreamTcpSetEvent(p, STREAM_CLOSEWAIT_PKT_BEFORE_LAST_ACK);
@@ -3280,6 +3395,11 @@ static int StreamTcpPacketStateCloseWait(ThreadVars *tv, Packet *p,
                     "%" PRIu32 ", ACK %" PRIu32 "", ssn, p->payload_len,
                     TCP_GET_SEQ(p), TCP_GET_ACK(p));
 
+            if (StreamTcpPacketIsRetransmission(&ssn->server, p)) {
+                SCLogDebug("ssn %p: packet is retransmission", ssn);
+                SCReturnInt(-1);
+            }
+
             if (p->payload_len > 0 && (SEQ_LEQ((TCP_GET_SEQ(p) + p->payload_len), ssn->server.last_ack))) {
                 SCLogDebug("ssn %p: -> retransmission", ssn);
                 StreamTcpSetEvent(p, STREAM_CLOSEWAIT_PKT_BEFORE_LAST_ACK);
@@ -3406,6 +3526,11 @@ static int StreamTcpPakcetStateLastAck(ThreadVars *tv, Packet *p,
                     "%" PRIu32 ", ACK %" PRIu32 "", ssn, p->payload_len,
                     TCP_GET_SEQ(p), TCP_GET_ACK(p));
 
+            if (StreamTcpPacketIsRetransmission(&ssn->client, p)) {
+                SCLogDebug("ssn %p: packet is retransmission", ssn);
+                SCReturnInt(-1);
+            }
+
             if (TCP_GET_SEQ(p) != ssn->client.next_seq && TCP_GET_SEQ(p) != ssn->client.next_seq + 1) {
                 SCLogDebug("ssn %p: -> SEQ mismatch, packet SEQ %" PRIu32 ""
                         " != %" PRIu32 " from stream", ssn,
@@ -3526,6 +3651,11 @@ static int StreamTcpPacketStateTimeWait(ThreadVars *tv, Packet *p,
                     "%" PRIu32 ", ACK %" PRIu32 "", ssn, p->payload_len,
                     TCP_GET_SEQ(p), TCP_GET_ACK(p));
 
+            if (StreamTcpPacketIsRetransmission(&ssn->client, p)) {
+                SCLogDebug("ssn %p: packet is retransmission", ssn);
+                SCReturnInt(-1);
+            }
+
             if (TCP_GET_SEQ(p) != ssn->client.next_seq && TCP_GET_SEQ(p) != ssn->client.next_seq+1) {
                 SCLogDebug("ssn %p: -> SEQ mismatch, packet SEQ %" PRIu32 ""
                         " != %" PRIu32 " from stream", ssn,
@@ -3564,19 +3694,19 @@ static int StreamTcpPacketStateTimeWait(ThreadVars *tv, Packet *p,
 
             StreamTcpPseudoPacketCreateStreamEndPacket(p, ssn, pq);
         } else {
-            int retransmission = 0;
-
             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 (StreamTcpPacketIsRetransmission(&ssn->server, p)) {
+                SCLogDebug("ssn %p: packet is retransmission", ssn);
+                SCReturnInt(-1);
+            }
+
             if (TCP_GET_SEQ(p) != ssn->server.next_seq && TCP_GET_SEQ(p) != ssn->server.next_seq+1) {
                 if (p->payload_len > 0 && TCP_GET_SEQ(p) == ssn->server.last_ack) {
                     SCLogDebug("ssn %p: -> retransmission", ssn);
-                    retransmission = 1;
-                } else if (p->payload_len > 0 && SEQ_LEQ((TCP_GET_SEQ(p) + p->payload_len), ssn->server.last_ack)) {
-                    SCLogDebug("ssn %p: -> retransmission", ssn);
-                    retransmission = 1;
+                    SCReturnInt(0);
                 } else {
                     SCLogDebug("ssn %p: -> SEQ mismatch, packet SEQ %" PRIu32 ""
                             " != %" PRIu32 " from stream", ssn,
@@ -3592,10 +3722,8 @@ static int StreamTcpPacketStateTimeWait(ThreadVars *tv, Packet *p,
                 SCReturnInt(-1);
             }
 
-            if (!retransmission) {
-                StreamTcpPacketSetState(p, ssn, TCP_CLOSED);
-                SCLogDebug("ssn %p: state changed to TCP_CLOSED", ssn);
-            }
+            StreamTcpPacketSetState(p, ssn, TCP_CLOSED);
+            SCLogDebug("ssn %p: state changed to TCP_CLOSED", ssn);
 
             ssn->client.window = TCP_GET_WINDOW(p) << ssn->client.wscale;