]> git.ipfire.org Git - thirdparty/suricata.git/commitdiff
tcp: zero copy fast path in app-layer reassembly
authorVictor Julien <victor@inliniac.net>
Fri, 16 Jan 2015 10:53:29 +0000 (11:53 +0100)
committerVictor Julien <victor@inliniac.net>
Wed, 4 Feb 2015 10:23:46 +0000 (11:23 +0100)
Create 2 'fast paths' for app layer reassembly. Both are about reducing
copying. In the cases described below, we pass the segment's data
directly to the app layer API, instead of first copying it into a buffer
than we then pass. This safes a copy.

The first is for the case when we have just one single segment that was
just ack'd. As we know that we won't use any other segment this round,
we can just use the segment data.

The second case is more aggressive. When the segment meets a certain
size limit (currently hardcoded at 128 bytes), we pass it to the
app-layer API directly. Thus invoking the app-layer somewhat more often
to safe some copies.

src/stream-tcp-reassemble.c

index bd5c9029fc94ae0ec59f6cb6af907a9bcf40fc92..b61748dd105936fc13deeb7fd96247ce9d08c453 100644 (file)
@@ -2572,6 +2572,42 @@ static inline int DoReassemble(ThreadVars *tv, TcpReassemblyThreadCtx *ra_ctx,
                  TcpSession *ssn, TcpStream *stream, TcpSegment *seg, ReassembleData *rd,
                  Packet *p)
 {
+    /* fast path 1: segment is exactly what we need */
+    if (likely(rd->data_len == 0 && SEQ_EQ(seg->seq, rd->ra_base_seq+1) && SEQ_EQ(stream->last_ack, (seg->seq + seg->payload_len)))) {
+        /* process single segment directly */
+        AppLayerHandleTCPData(tv, ra_ctx, p, p->flow, ssn, stream,
+                seg->payload, seg->payload_len,
+                StreamGetAppLayerFlags(ssn, stream, p));
+        AppLayerProfilingStore(ra_ctx->app_tctx, p);
+        rd->data_sent += seg->payload_len;
+        rd->ra_base_seq += seg->payload_len;
+
+        /* if after the first data chunk we have no alproto yet,
+         * there is no point in continueing here. */
+        if (!StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(stream)) {
+            SCLogDebug("no alproto after first data chunk");
+            return 0;
+        }
+        return 1;
+    /* fast path 2: segment acked completely, meets minimal size req for 0copy processing */
+    } else if (rd->data_len == 0 && SEQ_EQ(seg->seq, rd->ra_base_seq+1) && SEQ_GT(stream->last_ack, (seg->seq + seg->payload_len)) && seg->payload_len >= 128) {
+        /* process single segment directly */
+        AppLayerHandleTCPData(tv, ra_ctx, p, p->flow, ssn, stream,
+                seg->payload, seg->payload_len,
+                StreamGetAppLayerFlags(ssn, stream, p));
+        AppLayerProfilingStore(ra_ctx->app_tctx, p);
+        rd->data_sent += seg->payload_len;
+        rd->ra_base_seq += seg->payload_len;
+
+        /* if after the first data chunk we have no alproto yet,
+         * there is no point in continueing here. */
+        if (!StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(stream)) {
+            SCLogDebug("no alproto after first data chunk");
+            return 0;
+        }
+        return 1;
+    }
+
     uint16_t payload_offset = 0;
     uint16_t payload_len = 0;