]> git.ipfire.org Git - thirdparty/suricata.git/commitdiff
stream: improve handling of GAPs at stream start
authorVictor Julien <victor@inliniac.net>
Mon, 31 Aug 2015 17:00:35 +0000 (19:00 +0200)
committerVictor Julien <victor@inliniac.net>
Fri, 18 Sep 2015 14:55:18 +0000 (16:55 +0200)
Detect and handle gaps at the start of the stream, when there may
be no segments in the list (yet).

src/stream-tcp-reassemble.c

index dbf9c71eb74e81a2059be4eed45b4a71bf50f697..a947fab35140be327482259db44c5083530e4c80 100644 (file)
@@ -2895,6 +2895,49 @@ int StreamTcpReassembleAppLayer (ThreadVars *tv, TcpReassemblyThreadCtx *ra_ctx,
     GetSessionSize(ssn, p);
 #endif
 
+    /* Check if we have a gap at the start of the stream. 2 conditions:
+     * 1. no segments, but last_ack moved fwd
+     * 2. segments, but clearly some missing: if last_ack is
+     *    bigger than the list start and the list start is bigger than
+     *    next_seq, we know we are missing data that has been ack'd. That
+     *    won't get retransmitted, so it's a data gap.
+     */
+    if (!(ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED)) {
+        int ackadd = (ssn->state >= TCP_FIN_WAIT2) ? 2 : 1;
+        if ((stream->seg_list == NULL && /*1*/
+                    stream->ra_app_base_seq == stream->isn &&
+                    SEQ_GT(stream->last_ack, stream->isn + ackadd))
+                ||
+            (stream->seg_list != NULL && /*2*/
+                    SEQ_GT(stream->seg_list->seq, stream->ra_app_base_seq+1) &&
+                    SEQ_LT(stream->seg_list->seq, stream->last_ack)))
+        {
+            if (stream->seg_list == NULL) {
+                SCLogDebug("no segs, last_ack moved fwd so GAP "
+                        "(base %u, isn %u, last_ack %u => diff %u) p %"PRIu64,
+                        stream->ra_app_base_seq, stream->isn, stream->last_ack,
+                        stream->last_ack - (stream->isn + ackadd), p->pcap_cnt);
+            }
+
+            /* send gap signal */
+            AppLayerHandleTCPData(tv, ra_ctx, p, p->flow, ssn, stream,
+                    NULL, 0,
+                    StreamGetAppLayerFlags(ssn, stream, p)|STREAM_GAP);
+            AppLayerProfilingStore(ra_ctx->app_tctx, p);
+
+            /* set a GAP flag and make sure not bothering this stream anymore */
+            SCLogDebug("STREAMTCP_STREAM_FLAG_GAP set");
+            stream->flags |= STREAMTCP_STREAM_FLAG_GAP;
+
+            StreamTcpSetEvent(p, STREAM_REASSEMBLY_SEQ_GAP);
+            StatsIncr(tv, ra_ctx->counter_tcp_reass_gap);
+#ifdef DEBUG
+            dbg_app_layer_gap++;
+#endif
+            SCReturnInt(0);
+        }
+    }
+
     /* if no segments are in the list or all are already processed,
      * and state is beyond established, we send an empty msg */
     TcpSegment *seg_tail = stream->seg_list_tail;
@@ -2941,32 +2984,6 @@ int StreamTcpReassembleAppLayer (ThreadVars *tv, TcpReassemblyThreadCtx *ra_ctx,
     /* loop through the segments and fill one or more msgs */
     TcpSegment *seg = stream->seg_list;
     SCLogDebug("pre-loop seg %p", seg);
-
-    /* Check if we have a gap at the start of the list. If last_ack is
-     * bigger than the list start and the list start is bigger than
-     * next_seq, we know we are missing data that has been ack'd. That
-     * won't get retransmitted, so it's a data gap.
-     */
-    if (!(ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED)) {
-        if (SEQ_GT(seg->seq, next_seq) && SEQ_LT(seg->seq, stream->last_ack)) {
-            /* send gap signal */
-            AppLayerHandleTCPData(tv, ra_ctx, p, p->flow, ssn, stream,
-                    NULL, 0,
-                    StreamGetAppLayerFlags(ssn, stream, p)|STREAM_GAP);
-            AppLayerProfilingStore(ra_ctx->app_tctx, p);
-
-            /* set a GAP flag and make sure not bothering this stream anymore */
-            SCLogDebug("STREAMTCP_STREAM_FLAG_GAP set");
-            stream->flags |= STREAMTCP_STREAM_FLAG_GAP;
-
-            StreamTcpSetEvent(p, STREAM_REASSEMBLY_SEQ_GAP);
-            StatsIncr(tv, ra_ctx->counter_tcp_reass_gap);
-#ifdef DEBUG
-            dbg_app_layer_gap++;
-#endif
-            SCReturnInt(0);
-        }
-    }
 #ifdef DEBUG_VALIDATION
     uint64_t bytes = 0;
 #endif
@@ -5434,6 +5451,9 @@ static int StreamTcpReassembleTest30 (void)
     th_flag = TH_ACK|TH_PUSH;
     th_flags = TH_ACK;
 
+    ssn.client.last_ack = 2;
+    ssn.client.isn = 1;
+
     ssn.server.last_ack = 22;
     ssn.server.ra_raw_base_seq = ssn.server.ra_app_base_seq = 9;
     ssn.server.isn = 9;
@@ -6117,7 +6137,7 @@ static int StreamTcpReassembleTest38 (void)
     ssn.server.last_ack = 60;
     ssn.client.ra_raw_base_seq = ssn.client.ra_app_base_seq = 9;
     ssn.client.isn = 9;
-    ssn.client.last_ack = 60;
+    ssn.client.last_ack = 9;
     f.alproto = ALPROTO_UNKNOWN;
 
     f.flags |= FLOW_IPV4;
@@ -7235,7 +7255,7 @@ static int StreamTcpReassembleTest45 (void)
     ssn.server.last_ack = 60;
     STREAMTCP_SET_RA_BASE_SEQ(&ssn.client, 9);
     ssn.client.isn = 9;
-    ssn.client.last_ack = 60;
+    ssn.client.last_ack = 9;
 
     f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 200, 220);
     if (f == NULL)
@@ -7354,7 +7374,7 @@ static int StreamTcpReassembleTest46 (void)
     ssn.server.next_seq = ssn.server.isn;
     STREAMTCP_SET_RA_BASE_SEQ(&ssn.client, 9);
     ssn.client.isn = 9;
-    ssn.client.last_ack = 60;
+    ssn.client.last_ack = 9;
     ssn.client.next_seq = ssn.client.isn;
 
     f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 200, 220);
@@ -8422,6 +8442,9 @@ static int StreamTcpReassembleInlineTest10(void)
     StreamTcpUTInitInline();
     StreamTcpUTSetupSession(&ssn);
     StreamTcpUTSetupStream(&ssn.server, 1);
+    ssn.server.last_ack = 2;
+    StreamTcpUTSetupStream(&ssn.client, 1);
+    ssn.client.last_ack = 2;
 
     f = UTHBuildFlow(AF_INET, "1.1.1.1", "2.2.2.2", 1024, 80);
     if (f == NULL)