aboutsummaryrefslogtreecommitdiffstats
path: root/framework/src/suricata/src/stream-tcp-reassemble.c
diff options
context:
space:
mode:
authorAshlee Young <ashlee@onosfw.com>2015-09-09 22:21:41 -0700
committerAshlee Young <ashlee@onosfw.com>2015-09-09 22:21:41 -0700
commit8879b125d26e8db1a5633de5a9c692eb2d1c4f83 (patch)
treec7259d85a991b83dfa85ab2e339360669fc1f58e /framework/src/suricata/src/stream-tcp-reassemble.c
parent13d05bc8458758ee39cb829098241e89616717ee (diff)
suricata checkin based on commit id a4bce14770beee46a537eda3c3f6e8e8565d5d0a
Change-Id: I9a214fa0ee95e58fc640e50bd604dac7f42db48f
Diffstat (limited to 'framework/src/suricata/src/stream-tcp-reassemble.c')
-rw-r--r--framework/src/suricata/src/stream-tcp-reassemble.c8717
1 files changed, 8717 insertions, 0 deletions
diff --git a/framework/src/suricata/src/stream-tcp-reassemble.c b/framework/src/suricata/src/stream-tcp-reassemble.c
new file mode 100644
index 00000000..caf955af
--- /dev/null
+++ b/framework/src/suricata/src/stream-tcp-reassemble.c
@@ -0,0 +1,8717 @@
+/* Copyright (C) 2007-2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Gurvinder Singh <gurvindersinghdahiya@gmail.com>
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ * Reference:
+ * Judy Novak, Steve Sturges: Target-Based TCP Stream Reassembly August, 2007
+ *
+ */
+
+#include "suricata-common.h"
+#include "suricata.h"
+#include "debug.h"
+#include "detect.h"
+#include "flow.h"
+#include "threads.h"
+#include "conf.h"
+
+#include "flow-util.h"
+
+#include "threadvars.h"
+#include "tm-threads.h"
+
+#include "util-pool.h"
+#include "util-unittest.h"
+#include "util-print.h"
+#include "util-host-os-info.h"
+#include "util-unittest-helper.h"
+#include "util-byte.h"
+
+#include "stream-tcp.h"
+#include "stream-tcp-private.h"
+#include "stream-tcp-reassemble.h"
+#include "stream-tcp-inline.h"
+#include "stream-tcp-util.h"
+
+#include "stream.h"
+
+#include "util-debug.h"
+#include "app-layer-protos.h"
+#include "app-layer.h"
+#include "app-layer-events.h"
+
+#include "detect-engine-state.h"
+
+#include "util-profiling.h"
+
+#define PSEUDO_PACKET_PAYLOAD_SIZE 65416 /* 64 Kb minus max IP and TCP header */
+
+#ifdef DEBUG
+static SCMutex segment_pool_memuse_mutex;
+static uint64_t segment_pool_memuse = 0;
+static uint64_t segment_pool_memcnt = 0;
+#endif
+
+/* We define several pools with prealloced segments with fixed size
+ * payloads. We do this to prevent having to do an SCMalloc call for every
+ * data segment we receive, which would be a large performance penalty.
+ * The cost is in memory of course. The number of pools and the properties
+ * of the pools are determined by the yaml. */
+static int segment_pool_num = 0;
+static Pool **segment_pool = NULL;
+static SCMutex *segment_pool_mutex = NULL;
+static uint16_t *segment_pool_pktsizes = NULL;
+#ifdef DEBUG
+static SCMutex segment_pool_cnt_mutex;
+static uint64_t segment_pool_cnt = 0;
+#endif
+/* index to the right pool for all packet sizes. */
+static uint16_t segment_pool_idx[65536]; /* O(1) lookups of the pool */
+static int check_overlap_different_data = 0;
+
+/* Memory use counter */
+SC_ATOMIC_DECLARE(uint64_t, ra_memuse);
+
+/* prototypes */
+static int HandleSegmentStartsBeforeListSegment(ThreadVars *, TcpReassemblyThreadCtx *,
+ TcpStream *, TcpSegment *, TcpSegment *, Packet *);
+static int HandleSegmentStartsAtSameListSegment(ThreadVars *, TcpReassemblyThreadCtx *,
+ TcpStream *, TcpSegment *, TcpSegment *, Packet *);
+static int HandleSegmentStartsAfterListSegment(ThreadVars *, TcpReassemblyThreadCtx *,
+ TcpStream *, TcpSegment *, TcpSegment *, Packet *);
+void StreamTcpSegmentDataReplace(TcpSegment *, TcpSegment *, uint32_t, uint16_t);
+void StreamTcpSegmentDataCopy(TcpSegment *, TcpSegment *);
+TcpSegment* StreamTcpGetSegment(ThreadVars *tv, TcpReassemblyThreadCtx *, uint16_t);
+void StreamTcpCreateTestPacket(uint8_t *, uint8_t, uint8_t, uint8_t);
+void StreamTcpReassemblePseudoPacketCreate(TcpStream *, Packet *, PacketQueue *);
+static int StreamTcpSegmentDataCompare(TcpSegment *dst_seg, TcpSegment *src_seg,
+ uint32_t start_point, uint16_t len);
+
+void StreamTcpReassembleConfigEnableOverlapCheck(void)
+{
+ check_overlap_different_data = 1;
+}
+
+/**
+ * \brief Function to Increment the memory usage counter for the TCP reassembly
+ * segments
+ *
+ * \param size Size of the TCP segment and its payload length memory allocated
+ */
+void StreamTcpReassembleIncrMemuse(uint64_t size)
+{
+ (void) SC_ATOMIC_ADD(ra_memuse, size);
+ return;
+}
+
+/**
+ * \brief Function to Decrease the memory usage counter for the TCP reassembly
+ * segments
+ *
+ * \param size Size of the TCP segment and its payload length memory allocated
+ */
+void StreamTcpReassembleDecrMemuse(uint64_t size)
+{
+ (void) SC_ATOMIC_SUB(ra_memuse, size);
+ return;
+}
+
+uint64_t StreamTcpReassembleMemuseGlobalCounter(void)
+{
+ uint64_t smemuse = SC_ATOMIC_GET(ra_memuse);
+ return smemuse;
+}
+
+/**
+ * \brief Function to Check the reassembly memory usage counter against the
+ * allowed max memory usgae for TCP segments.
+ *
+ * \param size Size of the TCP segment and its payload length memory allocated
+ * \retval 1 if in bounds
+ * \retval 0 if not in bounds
+ */
+int StreamTcpReassembleCheckMemcap(uint32_t size)
+{
+ if (stream_config.reassembly_memcap == 0 ||
+ (uint64_t)((uint64_t)size + SC_ATOMIC_GET(ra_memuse)) <= stream_config.reassembly_memcap)
+ return 1;
+ return 0;
+}
+
+/** \brief alloc a tcp segment pool entry */
+void *TcpSegmentPoolAlloc()
+{
+ if (StreamTcpReassembleCheckMemcap((uint32_t)sizeof(TcpSegment)) == 0) {
+ return NULL;
+ }
+
+ TcpSegment *seg = NULL;
+
+ seg = SCMalloc(sizeof (TcpSegment));
+ if (unlikely(seg == NULL))
+ return NULL;
+ return seg;
+}
+
+int TcpSegmentPoolInit(void *data, void *payload_len)
+{
+ TcpSegment *seg = (TcpSegment *) data;
+ uint16_t size = *((uint16_t *) payload_len);
+
+ /* do this before the can bail, so TcpSegmentPoolCleanup
+ * won't have uninitialized memory to consider. */
+ memset(seg, 0, sizeof (TcpSegment));
+
+ if (StreamTcpReassembleCheckMemcap((uint32_t)size + (uint32_t)sizeof(TcpSegment)) == 0) {
+ return 0;
+ }
+
+ seg->pool_size = size;
+ seg->payload_len = seg->pool_size;
+
+ seg->payload = SCMalloc(seg->payload_len);
+ if (seg->payload == NULL) {
+ return 0;
+ }
+
+#ifdef DEBUG
+ SCMutexLock(&segment_pool_memuse_mutex);
+ segment_pool_memuse += seg->payload_len;
+ segment_pool_memcnt++;
+ SCLogDebug("segment_pool_memcnt %"PRIu64"", segment_pool_memcnt);
+ SCMutexUnlock(&segment_pool_memuse_mutex);
+#endif
+
+ StreamTcpReassembleIncrMemuse((uint32_t)seg->pool_size + sizeof(TcpSegment));
+ return 1;
+}
+
+/** \brief clean up a tcp segment pool entry */
+void TcpSegmentPoolCleanup(void *ptr)
+{
+ if (ptr == NULL)
+ return;
+
+ TcpSegment *seg = (TcpSegment *) ptr;
+
+ StreamTcpReassembleDecrMemuse((uint32_t)seg->pool_size + sizeof(TcpSegment));
+
+#ifdef DEBUG
+ SCMutexLock(&segment_pool_memuse_mutex);
+ segment_pool_memuse -= seg->pool_size;
+ segment_pool_memcnt--;
+ SCLogDebug("segment_pool_memcnt %"PRIu64"", segment_pool_memcnt);
+ SCMutexUnlock(&segment_pool_memuse_mutex);
+#endif
+
+ SCFree(seg->payload);
+ return;
+}
+
+/**
+ * \brief Function to return the segment back to the pool.
+ *
+ * \param seg Segment which will be returned back to the pool.
+ */
+void StreamTcpSegmentReturntoPool(TcpSegment *seg)
+{
+ if (seg == NULL)
+ return;
+
+ seg->next = NULL;
+ seg->prev = NULL;
+
+ uint16_t idx = segment_pool_idx[seg->pool_size];
+ SCMutexLock(&segment_pool_mutex[idx]);
+ PoolReturn(segment_pool[idx], (void *) seg);
+ SCLogDebug("segment_pool[%"PRIu16"]->empty_stack_size %"PRIu32"",
+ idx,segment_pool[idx]->empty_stack_size);
+ SCMutexUnlock(&segment_pool_mutex[idx]);
+
+#ifdef DEBUG
+ SCMutexLock(&segment_pool_cnt_mutex);
+ segment_pool_cnt--;
+ SCMutexUnlock(&segment_pool_cnt_mutex);
+#endif
+}
+
+/**
+ * \brief return all segments in this stream into the pool(s)
+ *
+ * \param stream the stream to cleanup
+ */
+void StreamTcpReturnStreamSegments (TcpStream *stream)
+{
+ TcpSegment *seg = stream->seg_list;
+ TcpSegment *next_seg;
+
+ if (seg == NULL)
+ return;
+
+ while (seg != NULL) {
+ next_seg = seg->next;
+ StreamTcpSegmentReturntoPool(seg);
+ seg = next_seg;
+ }
+
+ stream->seg_list = NULL;
+ stream->seg_list_tail = NULL;
+}
+
+/** \param f locked flow */
+void StreamTcpDisableAppLayer(Flow *f)
+{
+ if (f->protoctx == NULL)
+ return;
+
+ TcpSession *ssn = (TcpSession *)f->protoctx;
+ StreamTcpSetStreamFlagAppProtoDetectionCompleted(&ssn->client);
+ StreamTcpSetStreamFlagAppProtoDetectionCompleted(&ssn->server);
+ StreamTcpDisableAppLayerReassembly(ssn);
+}
+
+/** \param f locked flow */
+int StreamTcpAppLayerIsDisabled(Flow *f)
+{
+ if (f->protoctx == NULL || f->proto != IPPROTO_TCP)
+ return 0;
+
+ TcpSession *ssn = (TcpSession *)f->protoctx;
+ return (ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED);
+}
+
+typedef struct SegmentSizes_
+{
+ uint16_t pktsize;
+ uint32_t prealloc;
+} SegmentSizes;
+
+/* sort small to big */
+static int SortByPktsize(const void *a, const void *b)
+{
+ const SegmentSizes *s0 = a;
+ const SegmentSizes *s1 = b;
+ return s0->pktsize - s1->pktsize;
+}
+
+int StreamTcpReassemblyConfig(char quiet)
+{
+ Pool **my_segment_pool = NULL;
+ SCMutex *my_segment_lock = NULL;
+ uint16_t *my_segment_pktsizes = NULL;
+ SegmentSizes sizes[256];
+ memset(&sizes, 0x00, sizeof(sizes));
+
+ int npools = 0;
+ ConfNode *segs = ConfGetNode("stream.reassembly.segments");
+ if (segs != NULL) {
+ ConfNode *seg;
+ TAILQ_FOREACH(seg, &segs->head, next) {
+ ConfNode *segsize = ConfNodeLookupChild(seg,"size");
+ if (segsize == NULL)
+ continue;
+ ConfNode *segpre = ConfNodeLookupChild(seg,"prealloc");
+ if (segpre == NULL)
+ continue;
+
+ if (npools >= 256) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT, "too many segment packet "
+ "pools defined, max is 256");
+ return -1;
+ }
+
+ SCLogDebug("segsize->val %s", segsize->val);
+ SCLogDebug("segpre->val %s", segpre->val);
+
+ uint16_t pktsize = 0;
+ if (ByteExtractStringUint16(&pktsize, 10, strlen(segsize->val),
+ segsize->val) == -1)
+ {
+ SCLogError(SC_ERR_INVALID_ARGUMENT, "segment packet size "
+ "of %s is invalid", segsize->val);
+ return -1;
+ }
+ uint32_t prealloc = 0;
+ if (ByteExtractStringUint32(&prealloc, 10, strlen(segpre->val),
+ segpre->val) == -1)
+ {
+ SCLogError(SC_ERR_INVALID_ARGUMENT, "segment prealloc of "
+ "%s is invalid", segpre->val);
+ return -1;
+ }
+
+ sizes[npools].pktsize = pktsize;
+ sizes[npools].prealloc = prealloc;
+ SCLogDebug("pktsize %u, prealloc %u", sizes[npools].pktsize,
+ sizes[npools].prealloc);
+ npools++;
+ }
+ }
+
+ SCLogDebug("npools %d", npools);
+ if (npools > 0) {
+ /* sort the array as the index code below relies on it */
+ qsort(&sizes, npools, sizeof(sizes[0]), SortByPktsize);
+ if (sizes[npools - 1].pktsize != 0xffff) {
+ sizes[npools].pktsize = 0xffff;
+ sizes[npools].prealloc = 8;
+ npools++;
+ SCLogInfo("appended a segment pool for pktsize 65536");
+ }
+ } else if (npools == 0) {
+ /* defaults */
+ sizes[0].pktsize = 4;
+ sizes[0].prealloc = 256;
+ sizes[1].pktsize = 16;
+ sizes[1].prealloc = 512;
+ sizes[2].pktsize = 112;
+ sizes[2].prealloc = 512;
+ sizes[3].pktsize = 248;
+ sizes[3].prealloc = 512;
+ sizes[4].pktsize = 512;
+ sizes[4].prealloc = 512;
+ sizes[5].pktsize = 768;
+ sizes[5].prealloc = 1024;
+ sizes[6].pktsize = 1448;
+ sizes[6].prealloc = 1024;
+ sizes[7].pktsize = 0xffff;
+ sizes[7].prealloc = 128;
+ npools = 8;
+ }
+
+ int i = 0;
+ for (i = 0; i < npools; i++) {
+ SCLogDebug("pktsize %u, prealloc %u", sizes[i].pktsize, sizes[i].prealloc);
+ }
+
+ my_segment_pool = SCMalloc(npools * sizeof(Pool *));
+ if (my_segment_pool == NULL) {
+ SCLogError(SC_ERR_MEM_ALLOC, "malloc failed");
+ return -1;
+ }
+ my_segment_lock = SCMalloc(npools * sizeof(SCMutex));
+ if (my_segment_lock == NULL) {
+ SCLogError(SC_ERR_MEM_ALLOC, "malloc failed");
+
+ SCFree(my_segment_pool);
+ return -1;
+ }
+ my_segment_pktsizes = SCMalloc(npools * sizeof(uint16_t));
+ if (my_segment_pktsizes == NULL) {
+ SCLogError(SC_ERR_MEM_ALLOC, "malloc failed");
+
+ SCFree(my_segment_lock);
+ SCFree(my_segment_pool);
+ return -1;
+ }
+ uint32_t my_segment_poolsizes[npools];
+
+ for (i = 0; i < npools; i++) {
+ my_segment_pktsizes[i] = sizes[i].pktsize;
+ my_segment_poolsizes[i] = sizes[i].prealloc;
+ SCMutexInit(&my_segment_lock[i], NULL);
+
+ /* setup the pool */
+ SCMutexLock(&my_segment_lock[i]);
+ my_segment_pool[i] = PoolInit(0, my_segment_poolsizes[i], 0,
+ TcpSegmentPoolAlloc, TcpSegmentPoolInit,
+ (void *) &my_segment_pktsizes[i],
+ TcpSegmentPoolCleanup, NULL);
+ SCMutexUnlock(&my_segment_lock[i]);
+
+ if (my_segment_pool[i] == NULL) {
+ SCLogError(SC_ERR_INITIALIZATION, "couldn't set up segment pool "
+ "for packet size %u. Memcap too low?", my_segment_pktsizes[i]);
+ exit(EXIT_FAILURE);
+ }
+
+ SCLogDebug("my_segment_pktsizes[i] %u, my_segment_poolsizes[i] %u",
+ my_segment_pktsizes[i], my_segment_poolsizes[i]);
+ if (!quiet)
+ SCLogInfo("segment pool: pktsize %u, prealloc %u",
+ my_segment_pktsizes[i], my_segment_poolsizes[i]);
+ }
+
+ uint16_t idx = 0;
+ uint16_t u16 = 0;
+ while (1) {
+ if (idx <= my_segment_pktsizes[u16]) {
+ segment_pool_idx[idx] = u16;
+ if (my_segment_pktsizes[u16] == idx)
+ u16++;
+ }
+
+ if (idx == 0xffff)
+ break;
+
+ idx++;
+ }
+ /* set the globals */
+ segment_pool = my_segment_pool;
+ segment_pool_mutex = my_segment_lock;
+ segment_pool_pktsizes = my_segment_pktsizes;
+ segment_pool_num = npools;
+
+ uint32_t stream_chunk_prealloc = 250;
+ ConfNode *chunk = ConfGetNode("stream.reassembly.chunk-prealloc");
+ if (chunk) {
+ uint32_t prealloc = 0;
+ if (ByteExtractStringUint32(&prealloc, 10, strlen(chunk->val), chunk->val) == -1)
+ {
+ SCLogError(SC_ERR_INVALID_ARGUMENT, "chunk-prealloc of "
+ "%s is invalid", chunk->val);
+ return -1;
+ }
+ stream_chunk_prealloc = prealloc;
+ }
+ if (!quiet)
+ SCLogInfo("stream.reassembly \"chunk-prealloc\": %u", stream_chunk_prealloc);
+ StreamMsgQueuesInit(stream_chunk_prealloc);
+
+ intmax_t zero_copy_size = 128;
+ if (ConfGetInt("stream.reassembly.zero-copy-size", &zero_copy_size) == 1) {
+ if (zero_copy_size < 0 || zero_copy_size > 0xffff) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT, "stream.reassembly.zero-copy-size of "
+ "%"PRIiMAX" is invalid: valid values are 0 to 65535", zero_copy_size);
+ return -1;
+ }
+ }
+ stream_config.zero_copy_size = (uint16_t)zero_copy_size;
+ if (!quiet)
+ SCLogInfo("stream.reassembly \"zero-copy-size\": %u", stream_config.zero_copy_size);
+
+ return 0;
+}
+
+int StreamTcpReassembleInit(char quiet)
+{
+ /* init the memcap/use tracker */
+ SC_ATOMIC_INIT(ra_memuse);
+
+ if (StreamTcpReassemblyConfig(quiet) < 0)
+ return -1;
+#ifdef DEBUG
+ SCMutexInit(&segment_pool_memuse_mutex, NULL);
+ SCMutexInit(&segment_pool_cnt_mutex, NULL);
+#endif
+
+ StatsRegisterGlobalCounter("tcp.reassembly_memuse",
+ StreamTcpReassembleMemuseGlobalCounter);
+ return 0;
+}
+
+#ifdef DEBUG
+static uint32_t dbg_app_layer_gap;
+static uint32_t dbg_app_layer_gap_candidate;
+#endif
+
+void StreamTcpReassembleFree(char quiet)
+{
+ uint16_t u16 = 0;
+ for (u16 = 0; u16 < segment_pool_num; u16++) {
+ SCMutexLock(&segment_pool_mutex[u16]);
+
+ if (quiet == FALSE) {
+ PoolPrintSaturation(segment_pool[u16]);
+ SCLogDebug("segment_pool[u16]->empty_stack_size %"PRIu32", "
+ "segment_pool[u16]->alloc_stack_size %"PRIu32", alloced "
+ "%"PRIu32"", segment_pool[u16]->empty_stack_size,
+ segment_pool[u16]->alloc_stack_size,
+ segment_pool[u16]->allocated);
+
+ if (segment_pool[u16]->max_outstanding > segment_pool[u16]->allocated) {
+ SCLogInfo("TCP segment pool of size %u had a peak use of %u segments, "
+ "more than the prealloc setting of %u", segment_pool_pktsizes[u16],
+ segment_pool[u16]->max_outstanding, segment_pool[u16]->allocated);
+ }
+ }
+ PoolFree(segment_pool[u16]);
+
+ SCMutexUnlock(&segment_pool_mutex[u16]);
+ SCMutexDestroy(&segment_pool_mutex[u16]);
+ }
+ SCFree(segment_pool);
+ SCFree(segment_pool_mutex);
+ SCFree(segment_pool_pktsizes);
+ segment_pool = NULL;
+ segment_pool_mutex = NULL;
+ segment_pool_pktsizes = NULL;
+
+ StreamMsgQueuesDeinit(quiet);
+
+#ifdef DEBUG
+ SCLogDebug("segment_pool_cnt %"PRIu64"", segment_pool_cnt);
+ SCLogDebug("segment_pool_memuse %"PRIu64"", segment_pool_memuse);
+ SCLogDebug("segment_pool_memcnt %"PRIu64"", segment_pool_memcnt);
+ SCMutexDestroy(&segment_pool_memuse_mutex);
+ SCMutexDestroy(&segment_pool_cnt_mutex);
+ SCLogInfo("dbg_app_layer_gap %u", dbg_app_layer_gap);
+ SCLogInfo("dbg_app_layer_gap_candidate %u", dbg_app_layer_gap_candidate);
+#endif
+}
+
+TcpReassemblyThreadCtx *StreamTcpReassembleInitThreadCtx(ThreadVars *tv)
+{
+ SCEnter();
+ TcpReassemblyThreadCtx *ra_ctx = SCMalloc(sizeof(TcpReassemblyThreadCtx));
+ if (unlikely(ra_ctx == NULL))
+ return NULL;
+
+ memset(ra_ctx, 0x00, sizeof(TcpReassemblyThreadCtx));
+
+ ra_ctx->app_tctx = AppLayerGetCtxThread(tv);
+
+ SCReturnPtr(ra_ctx, "TcpReassemblyThreadCtx");
+}
+
+void StreamTcpReassembleFreeThreadCtx(TcpReassemblyThreadCtx *ra_ctx)
+{
+ SCEnter();
+ AppLayerDestroyCtxThread(ra_ctx->app_tctx);
+#ifdef DEBUG
+ SCLogDebug("reassembly fast path stats: fp1 %"PRIu64" fp2 %"PRIu64" sp %"PRIu64,
+ ra_ctx->fp1, ra_ctx->fp2, ra_ctx->sp);
+#endif
+ SCFree(ra_ctx);
+ SCReturn;
+}
+
+void PrintList2(TcpSegment *seg)
+{
+ TcpSegment *prev_seg = NULL;
+
+ if (seg == NULL)
+ return;
+
+ uint32_t next_seq = seg->seq;
+
+ while (seg != NULL) {
+ if (SEQ_LT(next_seq,seg->seq)) {
+ SCLogDebug("missing segment(s) for %" PRIu32 " bytes of data",
+ (seg->seq - next_seq));
+ }
+
+ SCLogDebug("seg %10"PRIu32" len %" PRIu16 ", seg %p, prev %p, next %p",
+ seg->seq, seg->payload_len, seg, seg->prev, seg->next);
+
+ if (seg->prev != NULL && SEQ_LT(seg->seq,seg->prev->seq)) {
+ /* check for SEQ_LT cornercase where a - b is exactly 2147483648,
+ * which makes the marco return TRUE in both directions. This is
+ * a hack though, we're going to check next how we end up with
+ * a segment list with seq differences that big */
+ if (!(SEQ_LT(seg->prev->seq,seg->seq))) {
+ SCLogDebug("inconsistent list: SEQ_LT(seg->seq,seg->prev->seq)) =="
+ " TRUE, seg->seq %" PRIu32 ", seg->prev->seq %" PRIu32 ""
+ "", seg->seq, seg->prev->seq);
+ }
+ }
+
+ if (SEQ_LT(seg->seq,next_seq)) {
+ SCLogDebug("inconsistent list: SEQ_LT(seg->seq,next_seq)) == TRUE, "
+ "seg->seq %" PRIu32 ", next_seq %" PRIu32 "", seg->seq,
+ next_seq);
+ }
+
+ if (prev_seg != seg->prev) {
+ SCLogDebug("inconsistent list: prev_seg %p != seg->prev %p",
+ prev_seg, seg->prev);
+ }
+
+ next_seq = seg->seq + seg->payload_len;
+ SCLogDebug("next_seq is now %"PRIu32"", next_seq);
+ prev_seg = seg;
+ seg = seg->next;
+ }
+}
+
+void PrintList(TcpSegment *seg)
+{
+ TcpSegment *prev_seg = NULL;
+ TcpSegment *head_seg = seg;
+
+ if (seg == NULL)
+ return;
+
+ uint32_t next_seq = seg->seq;
+
+ while (seg != NULL) {
+ if (SEQ_LT(next_seq,seg->seq)) {
+ SCLogDebug("missing segment(s) for %" PRIu32 " bytes of data",
+ (seg->seq - next_seq));
+ }
+
+ SCLogDebug("seg %10"PRIu32" len %" PRIu16 ", seg %p, prev %p, next %p, flags 0x%02x",
+ seg->seq, seg->payload_len, seg, seg->prev, seg->next, seg->flags);
+
+ if (seg->prev != NULL && SEQ_LT(seg->seq,seg->prev->seq)) {
+ /* check for SEQ_LT cornercase where a - b is exactly 2147483648,
+ * which makes the marco return TRUE in both directions. This is
+ * a hack though, we're going to check next how we end up with
+ * a segment list with seq differences that big */
+ if (!(SEQ_LT(seg->prev->seq,seg->seq))) {
+ SCLogDebug("inconsistent list: SEQ_LT(seg->seq,seg->prev->seq)) == "
+ "TRUE, seg->seq %" PRIu32 ", seg->prev->seq %" PRIu32 "",
+ seg->seq, seg->prev->seq);
+ PrintList2(head_seg);
+ abort();
+ }
+ }
+
+ if (SEQ_LT(seg->seq,next_seq)) {
+ SCLogDebug("inconsistent list: SEQ_LT(seg->seq,next_seq)) == TRUE, "
+ "seg->seq %" PRIu32 ", next_seq %" PRIu32 "", seg->seq,
+ next_seq);
+ PrintList2(head_seg);
+ abort();
+ }
+
+ if (prev_seg != seg->prev) {
+ SCLogDebug("inconsistent list: prev_seg %p != seg->prev %p",
+ prev_seg, seg->prev);
+ PrintList2(head_seg);
+ abort();
+ }
+
+ next_seq = seg->seq + seg->payload_len;
+ SCLogDebug("next_seq is now %"PRIu32"", next_seq);
+ prev_seg = seg;
+ seg = seg->next;
+ }
+}
+
+/**
+ * \internal
+ * \brief Get the active ra_base_seq, considering stream gaps
+ *
+ * \retval seq the active ra_base_seq
+ */
+static inline uint32_t StreamTcpReassembleGetRaBaseSeq(TcpStream *stream)
+{
+ if (!(stream->flags & STREAMTCP_STREAM_FLAG_GAP)) {
+ SCReturnUInt(stream->ra_app_base_seq);
+ } else {
+ SCReturnUInt(stream->ra_raw_base_seq);
+ }
+}
+
+/**
+ * \internal
+ * \brief Function to handle the insertion newly arrived segment,
+ * The packet is handled based on its target OS.
+ *
+ * \param stream The given TCP stream to which this new segment belongs
+ * \param seg Newly arrived segment
+ * \param p received packet
+ *
+ * \retval 0 success
+ * \retval -1 error -- either we hit a memory issue (OOM/memcap) or we received
+ * a segment before ra_base_seq.
+ */
+int StreamTcpReassembleInsertSegment(ThreadVars *tv, TcpReassemblyThreadCtx *ra_ctx,
+ TcpStream *stream, TcpSegment *seg, Packet *p)
+{
+ SCEnter();
+
+ TcpSegment *list_seg = stream->seg_list;
+ TcpSegment *next_list_seg = NULL;
+
+#if DEBUG
+ PrintList(stream->seg_list);
+#endif
+
+ int ret_value = 0;
+ char return_seg = FALSE;
+
+ /* before our ra_app_base_seq we don't insert it in our list,
+ * or ra_raw_base_seq if in stream gap state */
+ if (SEQ_LT((TCP_GET_SEQ(p)+p->payload_len),(StreamTcpReassembleGetRaBaseSeq(stream)+1)))
+ {
+ SCLogDebug("not inserting: SEQ+payload %"PRIu32", last_ack %"PRIu32", "
+ "ra_(app|raw)_base_seq %"PRIu32, (TCP_GET_SEQ(p)+p->payload_len),
+ stream->last_ack, StreamTcpReassembleGetRaBaseSeq(stream)+1);
+ return_seg = TRUE;
+ ret_value = -1;
+
+ StreamTcpSetEvent(p, STREAM_REASSEMBLY_SEGMENT_BEFORE_BASE_SEQ);
+ goto end;
+ }
+
+ SCLogDebug("SEQ %"PRIu32", SEQ+payload %"PRIu32", last_ack %"PRIu32", "
+ "ra_app_base_seq %"PRIu32, TCP_GET_SEQ(p), (TCP_GET_SEQ(p)+p->payload_len),
+ stream->last_ack, stream->ra_app_base_seq);
+
+ if (seg == NULL) {
+ goto end;
+ }
+
+ /* fast track */
+ if (list_seg == NULL) {
+ SCLogDebug("empty list, inserting seg %p seq %" PRIu32 ", "
+ "len %" PRIu32 "", seg, seg->seq, seg->payload_len);
+ stream->seg_list = seg;
+ seg->prev = NULL;
+ stream->seg_list_tail = seg;
+ goto end;
+ }
+
+ /* insert the segment in the stream list using this fast track, if seg->seq
+ is equal or higher than stream->seg_list_tail.*/
+ if (SEQ_GEQ(seg->seq, (stream->seg_list_tail->seq +
+ stream->seg_list_tail->payload_len)))
+ {
+ stream->seg_list_tail->next = seg;
+ seg->prev = stream->seg_list_tail;
+ stream->seg_list_tail = seg;
+
+ goto end;
+ }
+
+ /* If the OS policy is not set then set the OS policy for this stream */
+ if (stream->os_policy == 0) {
+ StreamTcpSetOSPolicy(stream, p);
+ }
+
+ for (; list_seg != NULL; list_seg = next_list_seg) {
+ next_list_seg = list_seg->next;
+
+ SCLogDebug("seg %p, list_seg %p, list_prev %p list_seg->next %p, "
+ "segment length %" PRIu32 "", seg, list_seg, list_seg->prev,
+ list_seg->next, seg->payload_len);
+ SCLogDebug("seg->seq %"PRIu32", list_seg->seq %"PRIu32"",
+ seg->seq, list_seg->seq);
+
+ /* segment starts before list */
+ if (SEQ_LT(seg->seq, list_seg->seq)) {
+ /* seg is entirely before list_seg */
+ if (SEQ_LEQ((seg->seq + seg->payload_len), list_seg->seq)) {
+ SCLogDebug("before list seg: seg->seq %" PRIu32 ", list_seg->seq"
+ " %" PRIu32 ", list_seg->payload_len %" PRIu32 ", "
+ "list_seg->prev %p", seg->seq, list_seg->seq,
+ list_seg->payload_len, list_seg->prev);
+ seg->next = list_seg;
+ if (list_seg->prev == NULL) {
+ stream->seg_list = seg;
+ }
+ if (list_seg->prev != NULL) {
+ list_seg->prev->next = seg;
+ seg->prev = list_seg->prev;
+ }
+ list_seg->prev = seg;
+
+ goto end;
+
+ /* seg overlap with next seg(s) */
+ } else {
+ ret_value = HandleSegmentStartsBeforeListSegment(tv, ra_ctx, stream, list_seg, seg, p);
+ if (ret_value == 1) {
+ ret_value = 0;
+ return_seg = TRUE;
+ goto end;
+ } else if (ret_value == -1) {
+ SCLogDebug("HandleSegmentStartsBeforeListSegment failed");
+ ret_value = -1;
+ return_seg = TRUE;
+ goto end;
+ }
+ }
+
+ /* seg starts at same sequence number as list_seg */
+ } else if (SEQ_EQ(seg->seq, list_seg->seq)) {
+ ret_value = HandleSegmentStartsAtSameListSegment(tv, ra_ctx, stream, list_seg, seg, p);
+ if (ret_value == 1) {
+ ret_value = 0;
+ return_seg = TRUE;
+ goto end;
+ } else if (ret_value == -1) {
+ SCLogDebug("HandleSegmentStartsAtSameListSegment failed");
+ ret_value = -1;
+ return_seg = TRUE;
+ goto end;
+ }
+
+ /* seg starts at sequence number higher than list_seg */
+ } else if (SEQ_GT(seg->seq, list_seg->seq)) {
+ if (((SEQ_GEQ(seg->seq, (list_seg->seq + list_seg->payload_len))))
+ && SEQ_GT((seg->seq + seg->payload_len),
+ (list_seg->seq + list_seg->payload_len)))
+ {
+ SCLogDebug("starts beyond list end, ends after list end: "
+ "seg->seq %" PRIu32 ", list_seg->seq %" PRIu32 ", "
+ "list_seg->payload_len %" PRIu32 " (%" PRIu32 ")",
+ seg->seq, list_seg->seq, list_seg->payload_len,
+ list_seg->seq + list_seg->payload_len);
+
+ if (list_seg->next == NULL) {
+ list_seg->next = seg;
+ seg->prev = list_seg;
+ stream->seg_list_tail = seg;
+ goto end;
+ }
+ } else {
+ ret_value = HandleSegmentStartsAfterListSegment(tv, ra_ctx, stream, list_seg, seg, p);
+ if (ret_value == 1) {
+ ret_value = 0;
+ return_seg = TRUE;
+ goto end;
+ } else if (ret_value == -1) {
+ SCLogDebug("HandleSegmentStartsAfterListSegment failed");
+ ret_value = -1;
+ return_seg = TRUE;
+ goto end;
+ }
+ }
+ }
+ }
+
+end:
+ if (return_seg == TRUE && seg != NULL) {
+ StreamTcpSegmentReturntoPool(seg);
+ }
+
+#ifdef DEBUG
+ PrintList(stream->seg_list);
+#endif
+ SCReturnInt(ret_value);
+}
+
+/**
+ * \brief Function to handle the newly arrived segment, when newly arrived
+ * starts with the sequence number lower than the original segment and
+ * ends at different position relative to original segment.
+ * The packet is handled based on its target OS.
+ *
+ * \param list_seg Original Segment in the stream
+ * \param seg Newly arrived segment
+ * \param prev_seg Previous segment in the stream segment list
+ * \param p Packet
+ *
+ * \retval 1 success and done
+ * \retval 0 success, but not done yet
+ * \retval -1 error, will *only* happen on memory errors
+ */
+
+static int HandleSegmentStartsBeforeListSegment(ThreadVars *tv, TcpReassemblyThreadCtx *ra_ctx,
+ TcpStream *stream, TcpSegment *list_seg, TcpSegment *seg, Packet *p)
+{
+ SCEnter();
+
+ uint16_t overlap = 0;
+ uint16_t packet_length = 0;
+ uint32_t overlap_point = 0;
+ char end_before = FALSE;
+ char end_after = FALSE;
+ char end_same = FALSE;
+ char return_after = FALSE;
+ uint8_t os_policy = stream->os_policy;
+#ifdef DEBUG
+ SCLogDebug("seg->seq %" PRIu32 ", seg->payload_len %" PRIu32 "", seg->seq,
+ seg->payload_len);
+ PrintList(stream->seg_list);
+#endif
+
+ if (SEQ_GT((seg->seq + seg->payload_len), list_seg->seq) &&
+ SEQ_LT((seg->seq + seg->payload_len),(list_seg->seq +
+ list_seg->payload_len)))
+ {
+ /* seg starts before list seg, ends beyond it but before list end */
+ end_before = TRUE;
+
+ /* [aaaa[abab]bbbb] a = seg, b = list_seg, overlap is the part [abab]
+ * We know seg->seq + seg->payload_len is bigger than list_seg->seq */
+ overlap = (seg->seq + seg->payload_len) - list_seg->seq;
+ overlap_point = list_seg->seq;
+ SCLogDebug("starts before list seg, ends before list end: seg->seq "
+ "%" PRIu32 ", list_seg->seq %" PRIu32 ", "
+ "list_seg->payload_len %" PRIu16 " overlap is %" PRIu32 ", "
+ "overlap point %"PRIu32"", seg->seq, list_seg->seq,
+ list_seg->payload_len, overlap, overlap_point);
+ } else if (SEQ_EQ((seg->seq + seg->payload_len), (list_seg->seq +
+ list_seg->payload_len)))
+ {
+ /* seg fully overlaps list_seg, starts before, at end point
+ * [aaa[ababab]] where a = seg, b = list_seg
+ * overlap is [ababab], which is list_seg->payload_len */
+ overlap = list_seg->payload_len;
+ end_same = TRUE;
+ overlap_point = list_seg->seq;
+ SCLogDebug("starts before list seg, ends at list end: list prev %p"
+ "seg->seq %" PRIu32 ", list_seg->seq %" PRIu32 ","
+ "list_seg->payload_len %" PRIu32 " overlap is %" PRIu32 "",
+ list_seg->prev, seg->seq, list_seg->seq,
+ list_seg->payload_len, overlap);
+ /* seg fully overlaps list_seg, starts before, ends after list endpoint */
+ } else if (SEQ_GT((seg->seq + seg->payload_len), (list_seg->seq +
+ list_seg->payload_len)))
+ {
+ /* seg fully overlaps list_seg, starts before, ends after list endpoint
+ * [aaa[ababab]aaa] where a = seg, b = list_seg
+ * overlap is [ababab] which is list_seg->payload_len */
+ overlap = list_seg->payload_len;
+ end_after = TRUE;
+ overlap_point = list_seg->seq;
+ SCLogDebug("starts before list seg, ends after list end: seg->seq "
+ "%" PRIu32 ", seg->payload_len %"PRIu32" list_seg->seq "
+ "%" PRIu32 ", list_seg->payload_len %" PRIu32 " overlap is"
+ " %" PRIu32 "", seg->seq, seg->payload_len,
+ list_seg->seq, list_seg->payload_len, overlap);
+ }
+
+ if (overlap > 0) {
+ /* handle the case where we need to fill a gap before list_seg first */
+ if (list_seg->prev != NULL && SEQ_LT((list_seg->prev->seq + list_seg->prev->payload_len), list_seg->seq)) {
+ SCLogDebug("GAP to fill before list segment, size %u", list_seg->seq - (list_seg->prev->seq + list_seg->prev->payload_len));
+
+ uint32_t new_seq = (list_seg->prev->seq + list_seg->prev->payload_len);
+ if (SEQ_GT(seg->seq, new_seq)) {
+ new_seq = seg->seq;
+ }
+
+ packet_length = list_seg->seq - new_seq;
+ if (packet_length > seg->payload_len) {
+ packet_length = seg->payload_len;
+ }
+
+ TcpSegment *new_seg = StreamTcpGetSegment(tv, ra_ctx, packet_length);
+ if (new_seg == NULL) {
+ SCLogDebug("segment_pool[%"PRIu16"] is empty", segment_pool_idx[packet_length]);
+
+ StreamTcpSetEvent(p, STREAM_REASSEMBLY_NO_SEGMENT);
+ SCReturnInt(-1);
+ }
+ new_seg->payload_len = packet_length;
+
+ new_seg->seq = new_seq;
+
+ SCLogDebug("new_seg->seq %"PRIu32" and new->payload_len "
+ "%" PRIu16"", new_seg->seq, new_seg->payload_len);
+
+ new_seg->next = list_seg;
+ new_seg->prev = list_seg->prev;
+ list_seg->prev->next = new_seg;
+ list_seg->prev = new_seg;
+
+ /* create a new seg, copy the list_seg data over */
+ StreamTcpSegmentDataCopy(new_seg, seg);
+
+#ifdef DEBUG
+ PrintList(stream->seg_list);
+#endif
+ }
+
+ /* Handling case when the segment starts before the first segment in
+ * the list */
+ if (list_seg->prev == NULL) {
+ if (end_after == TRUE && list_seg->next != NULL &&
+ SEQ_LT(list_seg->next->seq, (seg->seq + seg->payload_len)))
+ {
+ packet_length = (list_seg->seq - seg->seq) + list_seg->payload_len;
+ } else {
+ packet_length = seg->payload_len + (list_seg->payload_len - overlap);
+ return_after = TRUE;
+ }
+
+ SCLogDebug("entered here packet_length %" PRIu32 ", seg->payload_len"
+ " %" PRIu32 ", list->payload_len %" PRIu32 "",
+ packet_length, seg->payload_len, list_seg->payload_len);
+
+ TcpSegment *new_seg = StreamTcpGetSegment(tv, ra_ctx, packet_length);
+ if (new_seg == NULL) {
+ SCLogDebug("segment_pool[%"PRIu16"] is empty", segment_pool_idx[packet_length]);
+
+ StreamTcpSetEvent(p, STREAM_REASSEMBLY_NO_SEGMENT);
+ SCReturnInt(-1);
+ }
+ new_seg->payload_len = packet_length;
+ new_seg->seq = seg->seq;
+ new_seg->next = list_seg->next;
+ new_seg->prev = list_seg->prev;
+
+ StreamTcpSegmentDataCopy(new_seg, list_seg);
+
+ /* first the data before the list_seg->seq */
+ uint16_t replace = (uint16_t) (list_seg->seq - seg->seq);
+ SCLogDebug("copying %"PRIu16" bytes to new_seg", replace);
+ StreamTcpSegmentDataReplace(new_seg, seg, seg->seq, replace);
+
+ /* if any, data after list_seg->seq + list_seg->payload_len */
+ if (SEQ_GT((seg->seq + seg->payload_len), (list_seg->seq +
+ list_seg->payload_len)) && return_after == TRUE)
+ {
+ replace = (uint16_t)(((seg->seq + seg->payload_len) -
+ (list_seg->seq +
+ list_seg->payload_len)));
+ SCLogDebug("replacing %"PRIu16"", replace);
+ StreamTcpSegmentDataReplace(new_seg, seg, (list_seg->seq +
+ list_seg->payload_len), replace);
+ }
+
+ /* update the stream last_seg in case of removal of list_seg */
+ if (stream->seg_list_tail == list_seg)
+ stream->seg_list_tail = new_seg;
+
+ StreamTcpSegmentReturntoPool(list_seg);
+ list_seg = new_seg;
+ if (new_seg->prev != NULL) {
+ new_seg->prev->next = new_seg;
+ }
+ if (new_seg->next != NULL) {
+ new_seg->next->prev = new_seg;
+ }
+
+ stream->seg_list = new_seg;
+ SCLogDebug("list_seg now %p, stream->seg_list now %p", list_seg,
+ stream->seg_list);
+
+ } else if (end_before == TRUE || end_same == TRUE) {
+ /* Handling overlapping with more than one segment and filling gap */
+ if (SEQ_GT(list_seg->seq, (list_seg->prev->seq +
+ list_seg->prev->payload_len)))
+ {
+ SCLogDebug("list_seg->prev %p list_seg->prev->seq %"PRIu32" "
+ "list_seg->prev->payload_len %"PRIu16"",
+ list_seg->prev, list_seg->prev->seq,
+ list_seg->prev->payload_len);
+ if (SEQ_LT(list_seg->prev->seq, seg->seq)) {
+ packet_length = list_seg->payload_len + (list_seg->seq -
+ seg->seq);
+ } else {
+ packet_length = list_seg->payload_len + (list_seg->seq -
+ (list_seg->prev->seq + list_seg->prev->payload_len));
+ }
+
+ TcpSegment *new_seg = StreamTcpGetSegment(tv, ra_ctx, packet_length);
+ if (new_seg == NULL) {
+ SCLogDebug("segment_pool[%"PRIu16"] is empty", segment_pool_idx[packet_length]);
+
+ StreamTcpSetEvent(p, STREAM_REASSEMBLY_NO_SEGMENT);
+ SCReturnInt(-1);
+ }
+
+ new_seg->payload_len = packet_length;
+ if (SEQ_GT((list_seg->prev->seq + list_seg->prev->payload_len),
+ seg->seq))
+ {
+ new_seg->seq = (list_seg->prev->seq +
+ list_seg->prev->payload_len);
+ } else {
+ new_seg->seq = seg->seq;
+ }
+ SCLogDebug("new_seg->seq %"PRIu32" and new->payload_len "
+ "%" PRIu16"", new_seg->seq, new_seg->payload_len);
+ new_seg->next = list_seg->next;
+ new_seg->prev = list_seg->prev;
+
+ StreamTcpSegmentDataCopy(new_seg, list_seg);
+
+ uint16_t copy_len = (uint16_t) (list_seg->seq - seg->seq);
+ SCLogDebug("copy_len %" PRIu32 " (%" PRIu32 " - %" PRIu32 ")",
+ copy_len, list_seg->seq, seg->seq);
+ StreamTcpSegmentDataReplace(new_seg, seg, seg->seq, copy_len);
+
+ /*update the stream last_seg in case of removal of list_seg*/
+ if (stream->seg_list_tail == list_seg)
+ stream->seg_list_tail = new_seg;
+
+ StreamTcpSegmentReturntoPool(list_seg);
+ list_seg = new_seg;
+ if (new_seg->prev != NULL) {
+ new_seg->prev->next = new_seg;
+ }
+ if (new_seg->next != NULL) {
+ new_seg->next->prev = new_seg;
+ }
+ }
+ } else if (end_after == TRUE) {
+ if (list_seg->next != NULL) {
+ if (SEQ_LEQ((seg->seq + seg->payload_len), list_seg->next->seq))
+ {
+ if (SEQ_GT(seg->seq, (list_seg->prev->seq +
+ list_seg->prev->payload_len)))
+ {
+ packet_length = list_seg->payload_len + (list_seg->seq -
+ seg->seq);
+ } else {
+ packet_length = list_seg->payload_len + (list_seg->seq -
+ (list_seg->prev->seq +
+ list_seg->prev->payload_len));
+ }
+
+ packet_length += (seg->seq + seg->payload_len) -
+ (list_seg->seq + list_seg->payload_len);
+
+ TcpSegment *new_seg = StreamTcpGetSegment(tv, ra_ctx, packet_length);
+ if (new_seg == NULL) {
+ SCLogDebug("segment_pool[%"PRIu16"] is empty", segment_pool_idx[packet_length]);
+
+ StreamTcpSetEvent(p, STREAM_REASSEMBLY_NO_SEGMENT);
+ SCReturnInt(-1);
+ }
+ new_seg->payload_len = packet_length;
+ if (SEQ_GT((list_seg->prev->seq +
+ list_seg->prev->payload_len), seg->seq))
+ {
+ new_seg->seq = (list_seg->prev->seq +
+ list_seg->prev->payload_len);
+ } else {
+ new_seg->seq = seg->seq;
+ }
+ SCLogDebug("new_seg->seq %"PRIu32" and new->payload_len "
+ "%" PRIu16"", new_seg->seq, new_seg->payload_len);
+ new_seg->next = list_seg->next;
+ new_seg->prev = list_seg->prev;
+
+ /* create a new seg, copy the list_seg data over */
+ StreamTcpSegmentDataCopy(new_seg, list_seg);
+
+ /* copy the part before list_seg */
+ uint16_t copy_len = list_seg->seq - new_seg->seq;
+ StreamTcpSegmentDataReplace(new_seg, seg, new_seg->seq,
+ copy_len);
+
+ /* copy the part after list_seg */
+ copy_len = (seg->seq + seg->payload_len) -
+ (list_seg->seq + list_seg->payload_len);
+ StreamTcpSegmentDataReplace(new_seg, seg, (list_seg->seq +
+ list_seg->payload_len), copy_len);
+
+ if (new_seg->prev != NULL) {
+ new_seg->prev->next = new_seg;
+ }
+ if (new_seg->next != NULL) {
+ new_seg->next->prev = new_seg;
+ }
+ /*update the stream last_seg in case of removal of list_seg*/
+ if (stream->seg_list_tail == list_seg)
+ stream->seg_list_tail = new_seg;
+
+ StreamTcpSegmentReturntoPool(list_seg);
+ list_seg = new_seg;
+ return_after = TRUE;
+ }
+ /* Handle the case, when list_seg is the end of segment list, but
+ seg is ending after the list_seg. So we need to copy the data
+ from newly received segment. After copying return the newly
+ received seg to pool */
+ } else {
+ if (SEQ_GT(seg->seq, (list_seg->prev->seq +
+ list_seg->prev->payload_len)))
+ {
+ packet_length = list_seg->payload_len + (list_seg->seq -
+ seg->seq);
+ } else {
+ packet_length = list_seg->payload_len + (list_seg->seq -
+ (list_seg->prev->seq +
+ list_seg->prev->payload_len));
+ }
+
+ packet_length += (seg->seq + seg->payload_len) -
+ (list_seg->seq + list_seg->payload_len);
+
+ TcpSegment *new_seg = StreamTcpGetSegment(tv, ra_ctx, packet_length);
+ if (new_seg == NULL) {
+ SCLogDebug("segment_pool[%"PRIu16"] is empty",
+ segment_pool_idx[packet_length]);
+
+ StreamTcpSetEvent(p, STREAM_REASSEMBLY_NO_SEGMENT);
+ SCReturnInt(-1);
+ }
+ new_seg->payload_len = packet_length;
+
+ if (SEQ_GT((list_seg->prev->seq +
+ list_seg->prev->payload_len), seg->seq))
+ {
+ new_seg->seq = (list_seg->prev->seq +
+ list_seg->prev->payload_len);
+ } else {
+ new_seg->seq = seg->seq;
+ }
+ SCLogDebug("new_seg->seq %"PRIu32" and new->payload_len "
+ "%" PRIu16"", new_seg->seq, new_seg->payload_len);
+ new_seg->next = list_seg->next;
+ new_seg->prev = list_seg->prev;
+
+ /* create a new seg, copy the list_seg data over */
+ StreamTcpSegmentDataCopy(new_seg, list_seg);
+
+ /* copy the part before list_seg */
+ uint16_t copy_len = list_seg->seq - new_seg->seq;
+ StreamTcpSegmentDataReplace(new_seg, seg, new_seg->seq,
+ copy_len);
+
+ /* copy the part after list_seg */
+ copy_len = (seg->seq + seg->payload_len) -
+ (list_seg->seq + list_seg->payload_len);
+ StreamTcpSegmentDataReplace(new_seg, seg, (list_seg->seq +
+ list_seg->payload_len), copy_len);
+
+ if (new_seg->prev != NULL) {
+ new_seg->prev->next = new_seg;
+ }
+
+ /*update the stream last_seg in case of removal of list_seg*/
+ if (stream->seg_list_tail == list_seg)
+ stream->seg_list_tail = new_seg;
+
+ StreamTcpSegmentReturntoPool(list_seg);
+ list_seg = new_seg;
+ return_after = TRUE;
+ }
+ }
+
+ if (check_overlap_different_data &&
+ !StreamTcpSegmentDataCompare(seg, list_seg, list_seg->seq, overlap)) {
+ /* interesting, overlap with different data */
+ StreamTcpSetEvent(p, STREAM_REASSEMBLY_OVERLAP_DIFFERENT_DATA);
+ }
+
+ if (StreamTcpInlineMode()) {
+ if (StreamTcpInlineSegmentCompare(seg, list_seg) != 0) {
+ StreamTcpInlineSegmentReplacePacket(p, list_seg);
+ }
+ } else {
+ switch (os_policy) {
+ case OS_POLICY_SOLARIS:
+ case OS_POLICY_HPUX11:
+ if (end_after == TRUE || end_same == TRUE) {
+ StreamTcpSegmentDataReplace(list_seg, seg, overlap_point,
+ overlap);
+ } else {
+ SCLogDebug("using old data in starts before list case, "
+ "list_seg->seq %" PRIu32 " policy %" PRIu32 " "
+ "overlap %" PRIu32 "", list_seg->seq, os_policy,
+ overlap);
+ }
+ break;
+ case OS_POLICY_VISTA:
+ case OS_POLICY_FIRST:
+ SCLogDebug("using old data in starts before list case, "
+ "list_seg->seq %" PRIu32 " policy %" PRIu32 " "
+ "overlap %" PRIu32 "", list_seg->seq, os_policy,
+ overlap);
+ break;
+ case OS_POLICY_BSD:
+ case OS_POLICY_HPUX10:
+ case OS_POLICY_IRIX:
+ case OS_POLICY_WINDOWS:
+ case OS_POLICY_WINDOWS2K3:
+ case OS_POLICY_OLD_LINUX:
+ case OS_POLICY_LINUX:
+ case OS_POLICY_MACOS:
+ case OS_POLICY_LAST:
+ default:
+ SCLogDebug("replacing old data in starts before list seg "
+ "list_seg->seq %" PRIu32 " policy %" PRIu32 " "
+ "overlap %" PRIu32 "", list_seg->seq, os_policy,
+ overlap);
+ StreamTcpSegmentDataReplace(list_seg, seg, overlap_point,
+ overlap);
+ break;
+ }
+ }
+ /* To return from for loop as seg is finished with current list_seg
+ no need to check further (improve performance) */
+ if (end_before == TRUE || end_same == TRUE || return_after == TRUE) {
+ SCReturnInt(1);
+ }
+ }
+
+ SCReturnInt(0);
+}
+
+/**
+ * \brief Function to handle the newly arrived segment, when newly arrived
+ * starts with the same sequence number as the original segment and
+ * ends at different position relative to original segment.
+ * The packet is handled based on its target OS.
+ *
+ * \param list_seg Original Segment in the stream
+ * \param seg Newly arrived segment
+ * \param prev_seg Previous segment in the stream segment list
+ *
+ * \retval 1 success and done
+ * \retval 0 success, but not done yet
+ * \retval -1 error, will *only* happen on memory errors
+ */
+
+static int HandleSegmentStartsAtSameListSegment(ThreadVars *tv, TcpReassemblyThreadCtx *ra_ctx,
+ TcpStream *stream, TcpSegment *list_seg, TcpSegment *seg, Packet *p)
+{
+ uint16_t overlap = 0;
+ uint16_t packet_length;
+ char end_before = FALSE;
+ char end_after = FALSE;
+ char end_same = FALSE;
+ char handle_beyond = FALSE;
+ uint8_t os_policy = stream->os_policy;
+
+ if (SEQ_LT((seg->seq + seg->payload_len), (list_seg->seq +
+ list_seg->payload_len)))
+ {
+ /* seg->seg == list_seg->seq and list_seg->payload_len > seg->payload_len
+ * [[ababab]bbbb] where a = seg, b = list_seg
+ * overlap is the [ababab] part, which equals seg->payload_len. */
+ overlap = seg->payload_len;
+ end_before = TRUE;
+ SCLogDebug("starts at list seq, ends before list end: seg->seq "
+ "%" PRIu32 ", list_seg->seq %" PRIu32 ", "
+ "list_seg->payload_len %" PRIu32 " overlap is %" PRIu32,
+ seg->seq, list_seg->seq, list_seg->payload_len, overlap);
+
+ } else if (SEQ_EQ((seg->seq + seg->payload_len), (list_seg->seq +
+ list_seg->payload_len)))
+ {
+ /* seg starts at seq, ends at seq, retransmission.
+ * both segments are the same, so overlap is either
+ * seg->payload_len or list_seg->payload_len */
+
+ /* check csum, ack, other differences? */
+ overlap = seg->payload_len;
+ end_same = TRUE;
+ SCLogDebug("(retransmission) starts at list seq, ends at list end: "
+ "seg->seq %" PRIu32 ", list_seg->seq %" PRIu32 ", "
+ "list_seg->payload_len %" PRIu32 " overlap is %"PRIu32"",
+ seg->seq, list_seg->seq, list_seg->payload_len, overlap);
+
+ } else if (SEQ_GT((seg->seq + seg->payload_len),
+ (list_seg->seq + list_seg->payload_len))) {
+ /* seg starts at seq, ends beyond seq. */
+ /* seg->seg == list_seg->seq and seg->payload_len > list_seg->payload_len
+ * [[ababab]aaaa] where a = seg, b = list_seg
+ * overlap is the [ababab] part, which equals list_seg->payload_len. */
+ overlap = list_seg->payload_len;
+ end_after = TRUE;
+ SCLogDebug("starts at list seq, ends beyond list end: seg->seq "
+ "%" PRIu32 ", list_seg->seq %" PRIu32 ", "
+ "list_seg->payload_len %" PRIu32 " overlap is %" PRIu32 "",
+ seg->seq, list_seg->seq, list_seg->payload_len, overlap);
+ }
+ if (overlap > 0) {
+ /*Handle the case when newly arrived segment ends after original
+ segment and original segment is the last segment in the list
+ or the next segment in the list starts after the end of new segment*/
+ if (end_after == TRUE) {
+ char fill_gap = FALSE;
+
+ if (list_seg->next != NULL) {
+ /* first see if we have space left to fill up */
+ if (SEQ_LT((list_seg->seq + list_seg->payload_len),
+ list_seg->next->seq))
+ {
+ fill_gap = TRUE;
+ }
+
+ /* then see if we overlap (partly) with the next seg */
+ if (SEQ_GT((seg->seq + seg->payload_len), list_seg->next->seq))
+ {
+ handle_beyond = TRUE;
+ }
+ /* Handle the case, when list_seg is the end of segment list, but
+ seg is ending after the list_seg. So we need to copy the data
+ from newly received segment. After copying return the newly
+ received seg to pool */
+ } else {
+ fill_gap = TRUE;
+ }
+
+ SCLogDebug("fill_gap %s, handle_beyond %s", fill_gap?"TRUE":"FALSE",
+ handle_beyond?"TRUE":"FALSE");
+
+ if (fill_gap == TRUE) {
+ /* if there is a gap after this list_seg we fill it now with a
+ * new seg */
+ SCLogDebug("filling gap: list_seg->next->seq %"PRIu32"",
+ list_seg->next?list_seg->next->seq:0);
+ if (handle_beyond == TRUE) {
+ packet_length = list_seg->next->seq -
+ (list_seg->seq + list_seg->payload_len);
+ } else {
+ packet_length = seg->payload_len - list_seg->payload_len;
+ }
+
+ SCLogDebug("packet_length %"PRIu16"", packet_length);
+
+ TcpSegment *new_seg = StreamTcpGetSegment(tv, ra_ctx, packet_length);
+ if (new_seg == NULL) {
+ SCLogDebug("egment_pool[%"PRIu16"] is empty", segment_pool_idx[packet_length]);
+
+ StreamTcpSetEvent(p, STREAM_REASSEMBLY_NO_SEGMENT);
+ return -1;
+ }
+ new_seg->payload_len = packet_length;
+ new_seg->seq = list_seg->seq + list_seg->payload_len;
+ new_seg->next = list_seg->next;
+ if (new_seg->next != NULL)
+ new_seg->next->prev = new_seg;
+ new_seg->prev = list_seg;
+ list_seg->next = new_seg;
+ SCLogDebug("new_seg %p, new_seg->next %p, new_seg->prev %p, "
+ "list_seg->next %p", new_seg, new_seg->next,
+ new_seg->prev, list_seg->next);
+ StreamTcpSegmentDataReplace(new_seg, seg, new_seg->seq,
+ new_seg->payload_len);
+
+ /*update the stream last_seg in case of removal of list_seg*/
+ if (stream->seg_list_tail == list_seg)
+ stream->seg_list_tail = new_seg;
+ }
+ }
+
+ if (check_overlap_different_data &&
+ !StreamTcpSegmentDataCompare(list_seg, seg, seg->seq, overlap)) {
+ /* interesting, overlap with different data */
+ StreamTcpSetEvent(p, STREAM_REASSEMBLY_OVERLAP_DIFFERENT_DATA);
+ }
+
+ if (StreamTcpInlineMode()) {
+ if (StreamTcpInlineSegmentCompare(list_seg, seg) != 0) {
+ StreamTcpInlineSegmentReplacePacket(p, list_seg);
+ }
+ } else {
+ switch (os_policy) {
+ case OS_POLICY_OLD_LINUX:
+ case OS_POLICY_SOLARIS:
+ case OS_POLICY_HPUX11:
+ if (end_after == TRUE || end_same == TRUE) {
+ StreamTcpSegmentDataReplace(list_seg, seg, seg->seq, overlap);
+ } else {
+ SCLogDebug("using old data in starts at list case, "
+ "list_seg->seq %" PRIu32 " policy %" PRIu32 " "
+ "overlap %" PRIu32 "", list_seg->seq, os_policy,
+ overlap);
+ }
+ break;
+ case OS_POLICY_LAST:
+ StreamTcpSegmentDataReplace(list_seg, seg, seg->seq, overlap);
+ break;
+ case OS_POLICY_LINUX:
+ if (end_after == TRUE) {
+ StreamTcpSegmentDataReplace(list_seg, seg, seg->seq, overlap);
+ } else {
+ SCLogDebug("using old data in starts at list case, "
+ "list_seg->seq %" PRIu32 " policy %" PRIu32 " "
+ "overlap %" PRIu32 "", list_seg->seq, os_policy,
+ overlap);
+ }
+ break;
+ case OS_POLICY_BSD:
+ case OS_POLICY_HPUX10:
+ case OS_POLICY_IRIX:
+ case OS_POLICY_WINDOWS:
+ case OS_POLICY_WINDOWS2K3:
+ case OS_POLICY_VISTA:
+ case OS_POLICY_MACOS:
+ case OS_POLICY_FIRST:
+ default:
+ SCLogDebug("using old data in starts at list case, list_seg->seq"
+ " %" PRIu32 " policy %" PRIu32 " overlap %" PRIu32 "",
+ list_seg->seq, os_policy, overlap);
+ break;
+ }
+ }
+
+ /* return 1 if we're done */
+ if (end_before == TRUE || end_same == TRUE || handle_beyond == FALSE) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/**
+ * \internal
+ * \brief Function to handle the newly arrived segment, when newly arrived
+ * starts with the sequence number higher than the original segment and
+ * ends at different position relative to original segment.
+ * The packet is handled based on its target OS.
+ *
+ * \param list_seg Original Segment in the stream
+ * \param seg Newly arrived segment
+ * \param prev_seg Previous segment in the stream segment list
+
+ * \retval 1 success and done
+ * \retval 0 success, but not done yet
+ * \retval -1 error, will *only* happen on memory errors
+ */
+
+static int HandleSegmentStartsAfterListSegment(ThreadVars *tv, TcpReassemblyThreadCtx *ra_ctx,
+ TcpStream *stream, TcpSegment *list_seg, TcpSegment *seg, Packet *p)
+{
+ SCEnter();
+ uint16_t overlap = 0;
+ uint16_t packet_length;
+ char end_before = FALSE;
+ char end_after = FALSE;
+ char end_same = FALSE;
+ char handle_beyond = FALSE;
+ uint8_t os_policy = stream->os_policy;
+
+ if (SEQ_LT((seg->seq + seg->payload_len), (list_seg->seq +
+ list_seg->payload_len)))
+ {
+ /* seg starts after list, ends before list end
+ * [bbbb[ababab]bbbb] where a = seg, b = list_seg
+ * overlap is the part [ababab] which is seg->payload_len */
+ overlap = seg->payload_len;
+ end_before = TRUE;
+
+ SCLogDebug("starts beyond list seq, ends before list end: seg->seq"
+ " %" PRIu32 ", list_seg->seq %" PRIu32 ", list_seg->payload_len "
+ "%" PRIu32 " overlap is %" PRIu32 "", seg->seq, list_seg->seq,
+ list_seg->payload_len, overlap);
+
+ } else if (SEQ_EQ((seg->seq + seg->payload_len),
+ (list_seg->seq + list_seg->payload_len))) {
+ /* seg starts after seq, before end, ends at seq
+ * [bbbb[ababab]] where a = seg, b = list_seg
+ * overlapping part is [ababab], thus seg->payload_len */
+ overlap = seg->payload_len;
+ end_same = TRUE;
+
+ SCLogDebug("starts beyond list seq, ends at list end: seg->seq"
+ " %" PRIu32 ", list_seg->seq %" PRIu32 ", list_seg->payload_len "
+ "%" PRIu32 " overlap is %" PRIu32 "", seg->seq, list_seg->seq,
+ list_seg->payload_len, overlap);
+
+ } else if (SEQ_LT(seg->seq, list_seg->seq + list_seg->payload_len) &&
+ SEQ_GT((seg->seq + seg->payload_len), (list_seg->seq +
+ list_seg->payload_len)))
+ {
+ /* seg starts after seq, before end, ends beyond seq.
+ *
+ * [bbb[ababab]aaa] where a = seg, b = list_seg.
+ * overlap is the [ababab] part, which can be get using:
+ * (list_seg->seq + list_seg->payload_len) - seg->seg */
+ overlap = (list_seg->seq + list_seg->payload_len) - seg->seq;
+ end_after = TRUE;
+
+ SCLogDebug("starts beyond list seq, ends after list seq end: "
+ "seg->seq %" PRIu32 ", seg->payload_len %"PRIu16" (%"PRIu32") "
+ "list_seg->seq %" PRIu32 ", list_seg->payload_len %" PRIu32 " "
+ "(%"PRIu32") overlap is %" PRIu32 "", seg->seq, seg->payload_len,
+ seg->seq + seg->payload_len, list_seg->seq, list_seg->payload_len,
+ list_seg->seq + list_seg->payload_len, overlap);
+ }
+ if (overlap > 0) {
+ /*Handle the case when newly arrived segment ends after original
+ segment and original segment is the last segment in the list*/
+ if (end_after == TRUE) {
+ char fill_gap = FALSE;
+
+ if (list_seg->next != NULL) {
+ /* first see if we have space left to fill up */
+ if (SEQ_LT((list_seg->seq + list_seg->payload_len),
+ list_seg->next->seq))
+ {
+ fill_gap = TRUE;
+ }
+
+ /* then see if we overlap (partly) with the next seg */
+ if (SEQ_GT((seg->seq + seg->payload_len), list_seg->next->seq))
+ {
+ handle_beyond = TRUE;
+ }
+ } else {
+ fill_gap = TRUE;
+ }
+
+ SCLogDebug("fill_gap %s, handle_beyond %s", fill_gap?"TRUE":"FALSE",
+ handle_beyond?"TRUE":"FALSE");
+
+ if (fill_gap == TRUE) {
+ /* if there is a gap after this list_seg we fill it now with a
+ * new seg */
+ if (list_seg->next != NULL) {
+ SCLogDebug("filling gap: list_seg->next->seq %"PRIu32"",
+ list_seg->next?list_seg->next->seq:0);
+
+ packet_length = list_seg->next->seq - (list_seg->seq +
+ list_seg->payload_len);
+ } else {
+ packet_length = seg->payload_len - overlap;
+ }
+ if (packet_length > (seg->payload_len - overlap)) {
+ packet_length = seg->payload_len - overlap;
+ }
+ SCLogDebug("packet_length %"PRIu16"", packet_length);
+
+ TcpSegment *new_seg = StreamTcpGetSegment(tv, ra_ctx, packet_length);
+ if (new_seg == NULL) {
+ SCLogDebug("segment_pool[%"PRIu16"] is empty", segment_pool_idx[packet_length]);
+
+ StreamTcpSetEvent(p, STREAM_REASSEMBLY_NO_SEGMENT);
+ SCReturnInt(-1);
+ }
+ new_seg->payload_len = packet_length;
+ new_seg->seq = list_seg->seq + list_seg->payload_len;
+ new_seg->next = list_seg->next;
+ if (new_seg->next != NULL)
+ new_seg->next->prev = new_seg;
+ new_seg->prev = list_seg;
+ list_seg->next = new_seg;
+
+ SCLogDebug("new_seg %p, new_seg->next %p, new_seg->prev %p, "
+ "list_seg->next %p new_seg->seq %"PRIu32"", new_seg,
+ new_seg->next, new_seg->prev, list_seg->next,
+ new_seg->seq);
+
+ StreamTcpSegmentDataReplace(new_seg, seg, new_seg->seq,
+ new_seg->payload_len);
+
+ /* update the stream last_seg in case of removal of list_seg */
+ if (stream->seg_list_tail == list_seg)
+ stream->seg_list_tail = new_seg;
+ }
+ }
+
+ if (check_overlap_different_data &&
+ !StreamTcpSegmentDataCompare(list_seg, seg, seg->seq, overlap)) {
+ /* interesting, overlap with different data */
+ StreamTcpSetEvent(p, STREAM_REASSEMBLY_OVERLAP_DIFFERENT_DATA);
+ }
+
+ if (StreamTcpInlineMode()) {
+ if (StreamTcpInlineSegmentCompare(list_seg, seg) != 0) {
+ StreamTcpInlineSegmentReplacePacket(p, list_seg);
+ }
+ } else {
+ switch (os_policy) {
+ case OS_POLICY_SOLARIS:
+ case OS_POLICY_HPUX11:
+ if (end_after == TRUE) {
+ StreamTcpSegmentDataReplace(list_seg, seg, seg->seq, overlap);
+ } else {
+ SCLogDebug("using old data in starts beyond list case, "
+ "list_seg->seq %" PRIu32 " policy %" PRIu32 " "
+ "overlap %" PRIu32 "", list_seg->seq, os_policy,
+ overlap);
+ }
+ break;
+ case OS_POLICY_LAST:
+ StreamTcpSegmentDataReplace(list_seg, seg, seg->seq, overlap);
+ break;
+ case OS_POLICY_BSD:
+ case OS_POLICY_HPUX10:
+ case OS_POLICY_IRIX:
+ case OS_POLICY_WINDOWS:
+ case OS_POLICY_WINDOWS2K3:
+ case OS_POLICY_VISTA:
+ case OS_POLICY_OLD_LINUX:
+ case OS_POLICY_LINUX:
+ case OS_POLICY_MACOS:
+ case OS_POLICY_FIRST:
+ default: /* DEFAULT POLICY */
+ SCLogDebug("using old data in starts beyond list case, "
+ "list_seg->seq %" PRIu32 " policy %" PRIu32 " "
+ "overlap %" PRIu32 "", list_seg->seq, os_policy,
+ overlap);
+ break;
+ }
+ }
+ if (end_before == TRUE || end_same == TRUE || handle_beyond == FALSE) {
+ SCReturnInt(1);
+ }
+ }
+ SCReturnInt(0);
+}
+
+/**
+ * \brief check if stream in pkt direction has depth reached
+ *
+ * \param p packet with *LOCKED* flow
+ *
+ * \retval 1 stream has depth reached
+ * \retval 0 stream does not have depth reached
+ */
+int StreamTcpReassembleDepthReached(Packet *p)
+{
+ if (p->flow != NULL && p->flow->protoctx != NULL) {
+ TcpSession *ssn = p->flow->protoctx;
+ TcpStream *stream;
+ if (p->flowflags & FLOW_PKT_TOSERVER) {
+ stream = &ssn->client;
+ } else {
+ stream = &ssn->server;
+ }
+
+ return (stream->flags & STREAMTCP_STREAM_FLAG_DEPTH_REACHED) ? 1 : 0;
+ }
+
+ return 0;
+}
+
+/**
+ * \internal
+ * \brief Function to Check the reassembly depth valuer against the
+ * allowed max depth of the stream reassmbly for TCP streams.
+ *
+ * \param stream stream direction
+ * \param seq sequence number where "size" starts
+ * \param size size of the segment that is added
+ *
+ * \retval size Part of the size that fits in the depth, 0 if none
+ */
+static uint32_t StreamTcpReassembleCheckDepth(TcpStream *stream,
+ uint32_t seq, uint32_t size)
+{
+ SCEnter();
+
+ /* if the configured depth value is 0, it means there is no limit on
+ reassembly depth. Otherwise carry on my boy ;) */
+ if (stream_config.reassembly_depth == 0) {
+ SCReturnUInt(size);
+ }
+
+ /* if the final flag is set, we're not accepting anymore */
+ if (stream->flags & STREAMTCP_STREAM_FLAG_DEPTH_REACHED) {
+ SCReturnUInt(0);
+ }
+
+ /* if the ra_base_seq has moved passed the depth window we stop
+ * checking and just reject the rest of the packets including
+ * retransmissions. Saves us the hassle of dealing with sequence
+ * wraps as well */
+ if (SEQ_GEQ((StreamTcpReassembleGetRaBaseSeq(stream)+1),(stream->isn + stream_config.reassembly_depth))) {
+ stream->flags |= STREAMTCP_STREAM_FLAG_DEPTH_REACHED;
+ SCReturnUInt(0);
+ }
+
+ SCLogDebug("full Depth not yet reached: %"PRIu32" <= %"PRIu32,
+ (StreamTcpReassembleGetRaBaseSeq(stream)+1),
+ (stream->isn + stream_config.reassembly_depth));
+
+ if (SEQ_GEQ(seq, stream->isn) && SEQ_LT(seq, (stream->isn + stream_config.reassembly_depth))) {
+ /* packet (partly?) fits the depth window */
+
+ if (SEQ_LEQ((seq + size),(stream->isn + stream_config.reassembly_depth))) {
+ /* complete fit */
+ SCReturnUInt(size);
+ } else {
+ /* partial fit, return only what fits */
+ uint32_t part = (stream->isn + stream_config.reassembly_depth) - seq;
+#if DEBUG
+ BUG_ON(part > size);
+#else
+ if (part > size)
+ part = size;
+#endif
+ SCReturnUInt(part);
+ }
+ }
+
+ SCReturnUInt(0);
+}
+
+static void StreamTcpStoreStreamChunk(TcpSession *ssn, StreamMsg *smsg, const Packet *p, int streaminline)
+{
+ uint8_t direction = 0;
+
+ if ((!streaminline && (p->flowflags & FLOW_PKT_TOSERVER)) ||
+ ( streaminline && (p->flowflags & FLOW_PKT_TOCLIENT)))
+ {
+ direction = STREAM_TOCLIENT;
+ SCLogDebug("stream chunk is to_client");
+ } else {
+ direction = STREAM_TOSERVER;
+ SCLogDebug("stream chunk is to_server");
+ }
+
+ /* store the smsg in the tcp stream */
+ if (direction == STREAM_TOSERVER) {
+ SCLogDebug("storing smsg in the to_server");
+
+ /* put the smsg in the stream list */
+ if (ssn->toserver_smsg_head == NULL) {
+ ssn->toserver_smsg_head = smsg;
+ ssn->toserver_smsg_tail = smsg;
+ smsg->next = NULL;
+ smsg->prev = NULL;
+ } else {
+ StreamMsg *cur = ssn->toserver_smsg_tail;
+ cur->next = smsg;
+ smsg->prev = cur;
+ smsg->next = NULL;
+ ssn->toserver_smsg_tail = smsg;
+ }
+ } else {
+ SCLogDebug("storing smsg in the to_client");
+
+ /* put the smsg in the stream list */
+ if (ssn->toclient_smsg_head == NULL) {
+ ssn->toclient_smsg_head = smsg;
+ ssn->toclient_smsg_tail = smsg;
+ smsg->next = NULL;
+ smsg->prev = NULL;
+ } else {
+ StreamMsg *cur = ssn->toclient_smsg_tail;
+ cur->next = smsg;
+ smsg->prev = cur;
+ smsg->next = NULL;
+ ssn->toclient_smsg_tail = smsg;
+ }
+ }
+}
+
+/**
+ * \brief Insert a packets TCP data into the stream reassembly engine.
+ *
+ * \retval 0 good segment, as far as we checked.
+ * \retval -1 badness, reason to drop in inline mode
+ *
+ * If the retval is 0 the segment is inserted correctly, or overlap is handled,
+ * or it wasn't added because of reassembly depth.
+ *
+ */
+int StreamTcpReassembleHandleSegmentHandleData(ThreadVars *tv, TcpReassemblyThreadCtx *ra_ctx,
+ TcpSession *ssn, TcpStream *stream, Packet *p)
+{
+ SCEnter();
+
+ if (ssn->data_first_seen_dir == 0) {
+ if (p->flowflags & FLOW_PKT_TOSERVER) {
+ ssn->data_first_seen_dir = STREAM_TOSERVER;
+ } else {
+ ssn->data_first_seen_dir = STREAM_TOCLIENT;
+ }
+ }
+
+ if ((ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED) &&
+ (stream->flags & STREAMTCP_STREAM_FLAG_NEW_RAW_DISABLED)) {
+ SCLogDebug("ssn %p: both app and raw reassembly disabled, not reassembling", ssn);
+ SCReturnInt(0);
+ }
+
+ /* If we have reached the defined depth for either of the stream, then stop
+ reassembling the TCP session */
+ uint32_t size = StreamTcpReassembleCheckDepth(stream, TCP_GET_SEQ(p), p->payload_len);
+ SCLogDebug("ssn %p: check depth returned %"PRIu32, ssn, size);
+
+ if (stream->flags & STREAMTCP_STREAM_FLAG_DEPTH_REACHED) {
+ /* increment stream depth counter */
+ StatsIncr(tv, ra_ctx->counter_tcp_stream_depth);
+
+ stream->flags |= STREAMTCP_STREAM_FLAG_NOREASSEMBLY;
+ SCLogDebug("ssn %p: reassembly depth reached, "
+ "STREAMTCP_STREAM_FLAG_NOREASSEMBLY set", ssn);
+ }
+ if (size == 0) {
+ SCLogDebug("ssn %p: depth reached, not reassembling", ssn);
+ SCReturnInt(0);
+ }
+
+#if DEBUG
+ BUG_ON(size > p->payload_len);
+#else
+ if (size > p->payload_len)
+ size = p->payload_len;
+#endif
+
+ TcpSegment *seg = StreamTcpGetSegment(tv, ra_ctx, size);
+ if (seg == NULL) {
+ SCLogDebug("segment_pool[%"PRIu16"] is empty", segment_pool_idx[size]);
+
+ StreamTcpSetEvent(p, STREAM_REASSEMBLY_NO_SEGMENT);
+ SCReturnInt(-1);
+ }
+
+ memcpy(seg->payload, p->payload, size);
+ seg->payload_len = size;
+ seg->seq = TCP_GET_SEQ(p);
+
+ if (ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED)
+ seg->flags |= SEGMENTTCP_FLAG_APPLAYER_PROCESSED;
+
+ /* if raw reassembly is disabled for new segments, flag each
+ * segment as complete for raw before insert */
+ if (stream->flags & STREAMTCP_STREAM_FLAG_NEW_RAW_DISABLED) {
+ seg->flags |= SEGMENTTCP_FLAG_RAW_PROCESSED;
+ SCLogDebug("segment %p flagged with SEGMENTTCP_FLAG_RAW_PROCESSED, "
+ "flags %02x", seg, seg->flags);
+ }
+
+ /* proto detection skipped, but now we do get data. Set event. */
+ if (stream->seg_list == NULL &&
+ stream->flags & STREAMTCP_STREAM_FLAG_APPPROTO_DETECTION_SKIPPED) {
+
+ AppLayerDecoderEventsSetEventRaw(&p->app_layer_events,
+ APPLAYER_PROTO_DETECTION_SKIPPED);
+ }
+
+ if (StreamTcpReassembleInsertSegment(tv, ra_ctx, stream, seg, p) != 0) {
+ SCLogDebug("StreamTcpReassembleInsertSegment failed");
+ SCReturnInt(-1);
+ }
+
+ SCReturnInt(0);
+}
+
+static uint8_t StreamGetAppLayerFlags(TcpSession *ssn, TcpStream *stream,
+ Packet *p)
+{
+ uint8_t flag = 0;
+
+ if (!(stream->flags & STREAMTCP_STREAM_FLAG_APPPROTO_DETECTION_COMPLETED)) {
+ flag |= STREAM_START;
+ }
+
+ if (ssn->state == TCP_CLOSED) {
+ flag |= STREAM_EOF;
+ }
+ if (p->flags & PKT_PSEUDO_STREAM_END) {
+ flag |= STREAM_EOF;
+ }
+
+ if (StreamTcpInlineMode() == 0) {
+ if (p->flowflags & FLOW_PKT_TOSERVER) {
+ flag |= STREAM_TOCLIENT;
+ } else {
+ flag |= STREAM_TOSERVER;
+ }
+ } else {
+ if (p->flowflags & FLOW_PKT_TOSERVER) {
+ flag |= STREAM_TOSERVER;
+ } else {
+ flag |= STREAM_TOCLIENT;
+ }
+ }
+
+ if (stream->flags & STREAMTCP_STREAM_FLAG_DEPTH_REACHED) {
+ flag |= STREAM_DEPTH;
+ }
+ return flag;
+}
+
+static void StreamTcpSetupMsg(TcpSession *ssn, TcpStream *stream, Packet *p,
+ StreamMsg *smsg)
+{
+ SCEnter();
+ smsg->data_len = 0;
+ SCLogDebug("smsg %p", smsg);
+ SCReturn;
+}
+
+/**
+ * \brief Check the minimum size limits for reassembly.
+ *
+ * \retval 0 don't reassemble yet
+ * \retval 1 do reassemble
+ */
+static int StreamTcpReassembleRawCheckLimit(TcpSession *ssn, TcpStream *stream,
+ Packet *p)
+{
+ SCEnter();
+
+ if (stream->flags & STREAMTCP_STREAM_FLAG_NOREASSEMBLY) {
+ SCLogDebug("reassembling now as STREAMTCP_STREAM_FLAG_NOREASSEMBLY is set, so not expecting any new packets");
+ SCReturnInt(1);
+ }
+
+ if (ssn->flags & STREAMTCP_FLAG_TRIGGER_RAW_REASSEMBLY) {
+ SCLogDebug("reassembling now as STREAMTCP_FLAG_TRIGGER_RAW_REASSEMBLY is set");
+ ssn->flags &= ~STREAMTCP_FLAG_TRIGGER_RAW_REASSEMBLY;
+ SCReturnInt(1);
+ }
+
+ if (stream->flags & STREAMTCP_STREAM_FLAG_NEW_RAW_DISABLED) {
+ SCLogDebug("reassembling now as STREAMTCP_STREAM_FLAG_NEW_RAW_DISABLED is set, "
+ "so no new segments will be considered");
+ SCReturnInt(1);
+ }
+
+ /* some states mean we reassemble no matter how much data we have */
+ if (ssn->state >= TCP_TIME_WAIT)
+ SCReturnInt(1);
+
+ if (p->flags & PKT_PSEUDO_STREAM_END)
+ SCReturnInt(1);
+
+ /* check if we have enough data to send to L7 */
+ if (p->flowflags & FLOW_PKT_TOCLIENT) {
+ SCLogDebug("StreamMsgQueueGetMinChunkLen(STREAM_TOSERVER) %"PRIu32,
+ StreamMsgQueueGetMinChunkLen(FLOW_PKT_TOSERVER));
+
+ if (StreamMsgQueueGetMinChunkLen(FLOW_PKT_TOSERVER) >
+ (stream->last_ack - stream->ra_raw_base_seq)) {
+ SCLogDebug("toserver min chunk len not yet reached: "
+ "last_ack %"PRIu32", ra_raw_base_seq %"PRIu32", %"PRIu32" < "
+ "%"PRIu32"", stream->last_ack, stream->ra_raw_base_seq,
+ (stream->last_ack - stream->ra_raw_base_seq),
+ StreamMsgQueueGetMinChunkLen(FLOW_PKT_TOSERVER));
+ SCReturnInt(0);
+ }
+ } else {
+ SCLogDebug("StreamMsgQueueGetMinChunkLen(STREAM_TOCLIENT) %"PRIu32,
+ StreamMsgQueueGetMinChunkLen(FLOW_PKT_TOCLIENT));
+
+ if (StreamMsgQueueGetMinChunkLen(FLOW_PKT_TOCLIENT) >
+ (stream->last_ack - stream->ra_raw_base_seq)) {
+ SCLogDebug("toclient min chunk len not yet reached: "
+ "last_ack %"PRIu32", ra_base_seq %"PRIu32", %"PRIu32" < "
+ "%"PRIu32"", stream->last_ack, stream->ra_raw_base_seq,
+ (stream->last_ack - stream->ra_raw_base_seq),
+ StreamMsgQueueGetMinChunkLen(FLOW_PKT_TOCLIENT));
+ SCReturnInt(0);
+ }
+ }
+
+ SCReturnInt(1);
+}
+
+/**
+ * \brief see if app layer is done with a segment
+ *
+ * \retval 1 app layer is done with this segment
+ * \retval 0 not done yet
+ */
+#define StreamTcpAppLayerSegmentProcessed(ssn, stream, segment) \
+ (( ( (ssn)->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED) || \
+ ( (stream)->flags & STREAMTCP_STREAM_FLAG_GAP ) || \
+ ( (segment)->flags & SEGMENTTCP_FLAG_APPLAYER_PROCESSED ) ? 1 :0 ))
+
+/** \internal
+ * \brief check if we can remove a segment from our segment list
+ *
+ * If a segment is entirely before the oldest smsg, we can discard it. Otherwise
+ * we keep it around to be able to log it.
+ *
+ * \retval 1 yes
+ * \retval 0 no
+ */
+static inline int StreamTcpReturnSegmentCheck(const Flow *f, TcpSession *ssn, TcpStream *stream, TcpSegment *seg)
+{
+ if (stream == &ssn->client && ssn->toserver_smsg_head != NULL) {
+ /* not (seg is entirely before first smsg, skip) */
+ if (!(SEQ_LEQ(seg->seq + seg->payload_len, ssn->toserver_smsg_head->seq))) {
+ SCReturnInt(0);
+ }
+ } else if (stream == &ssn->server && ssn->toclient_smsg_head != NULL) {
+ /* not (seg is entirely before first smsg, skip) */
+ if (!(SEQ_LEQ(seg->seq + seg->payload_len, ssn->toclient_smsg_head->seq))) {
+ SCReturnInt(0);
+ }
+ }
+
+ /* if proto detect isn't done, we're not returning */
+ if (!(StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(stream))) {
+ SCReturnInt(0);
+ }
+
+ /* check app layer conditions */
+ if (!(StreamTcpAppLayerSegmentProcessed(ssn, stream, seg))) {
+ SCReturnInt(0);
+ }
+
+ /* check raw reassembly conditions */
+ if (!(seg->flags & SEGMENTTCP_FLAG_RAW_PROCESSED)) {
+ SCReturnInt(0);
+ }
+
+ SCReturnInt(1);
+}
+
+static void StreamTcpRemoveSegmentFromStream(TcpStream *stream, TcpSegment *seg)
+{
+ if (seg->prev == NULL) {
+ stream->seg_list = seg->next;
+ if (stream->seg_list != NULL)
+ stream->seg_list->prev = NULL;
+ } else {
+ seg->prev->next = seg->next;
+ if (seg->next != NULL)
+ seg->next->prev = seg->prev;
+ }
+
+ if (stream->seg_list_tail == seg)
+ stream->seg_list_tail = seg->prev;
+}
+
+/**
+ * \brief Update the stream reassembly upon receiving a data segment
+ *
+ * | left edge | right edge based on sliding window size
+ * [aaa]
+ * [aaabbb]
+ * ...
+ * [aaabbbcccdddeeefff]
+ * [bbbcccdddeeefffggg] <- cut off aaa to adhere to the window size
+ *
+ * GAP situation: each chunk that is uninterrupted has it's own smsg
+ * [aaabbb].[dddeeefff]
+ * [aaa].[ccc].[eeefff]
+ *
+ * A flag will be set to indicate where the *NEW* payload starts. This
+ * is to aid the detection code for alert only sigs.
+ *
+ * \todo this function is too long, we need to break it up. It needs it BAD
+ */
+static int StreamTcpReassembleInlineRaw (TcpReassemblyThreadCtx *ra_ctx,
+ TcpSession *ssn, TcpStream *stream, Packet *p)
+{
+ SCEnter();
+ SCLogDebug("start p %p, seq %"PRIu32, p, TCP_GET_SEQ(p));
+
+ if (ssn->flags & STREAMTCP_FLAG_DISABLE_RAW)
+ SCReturnInt(0);
+ if (stream->seg_list == NULL) {
+ SCReturnInt(0);
+ }
+
+ uint32_t ra_base_seq = stream->ra_raw_base_seq;
+ StreamMsg *smsg = NULL;
+ uint32_t smsg_offset = 0;
+ uint16_t payload_offset = 0;
+ uint16_t payload_len = 0;
+ TcpSegment *seg = stream->seg_list;
+ uint32_t next_seq = ra_base_seq + 1;
+ int gap = 0;
+
+ uint32_t chunk_size = PKT_IS_TOSERVER(p) ?
+ stream_config.reassembly_toserver_chunk_size :
+ stream_config.reassembly_toclient_chunk_size;
+
+ /* determine the left edge and right edge */
+ uint32_t right_edge = TCP_GET_SEQ(p) + p->payload_len;
+ uint32_t left_edge = right_edge - chunk_size;
+
+ /* shift the window to the right if the left edge doesn't cover segments */
+ if (SEQ_GT(seg->seq,left_edge)) {
+ right_edge += (seg->seq - left_edge);
+ left_edge = seg->seq;
+ }
+
+ SCLogDebug("left_edge %"PRIu32", right_edge %"PRIu32, left_edge, right_edge);
+
+ /* loop through the segments and fill one or more msgs */
+ for (; seg != NULL && SEQ_LT(seg->seq, right_edge); ) {
+ SCLogDebug("seg %p", seg);
+
+ /* If packets are fully before ra_base_seq, skip them. We do this
+ * because we've reassembled up to the ra_base_seq point already,
+ * so we won't do anything with segments before it anyway. */
+ SCLogDebug("checking for pre ra_base_seq %"PRIu32" seg %p seq %"PRIu32""
+ " len %"PRIu16", combined %"PRIu32" and right_edge "
+ "%"PRIu32"", ra_base_seq, seg, seg->seq,
+ seg->payload_len, seg->seq+seg->payload_len, right_edge);
+
+ /* Remove the segments which are completely before the ra_base_seq */
+ if (SEQ_LT((seg->seq + seg->payload_len), (ra_base_seq - chunk_size)))
+ {
+ SCLogDebug("removing pre ra_base_seq %"PRIu32" seg %p seq %"PRIu32""
+ " len %"PRIu16"", ra_base_seq, seg, seg->seq,
+ seg->payload_len);
+
+ /* only remove if app layer reassembly is ready too */
+ if (StreamTcpAppLayerSegmentProcessed(ssn, stream, seg)) {
+ TcpSegment *next_seg = seg->next;
+ StreamTcpRemoveSegmentFromStream(stream, seg);
+ StreamTcpSegmentReturntoPool(seg);
+ seg = next_seg;
+ /* otherwise, just flag it for removal */
+ } else {
+ seg->flags |= SEGMENTTCP_FLAG_RAW_PROCESSED;
+ seg = seg->next;
+ }
+ continue;
+ }
+
+ /* if app layer protocol has been detected, then remove all the segments
+ * which has been previously processed and reassembled
+ *
+ * If the stream is in GAP state the app layer flag won't be set */
+ if (StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(stream) &&
+ (seg->flags & SEGMENTTCP_FLAG_RAW_PROCESSED) &&
+ StreamTcpAppLayerSegmentProcessed(ssn, stream, seg))
+ {
+ SCLogDebug("segment(%p) of length %"PRIu16" has been processed,"
+ " so return it to pool", seg, seg->payload_len);
+ TcpSegment *next_seg = seg->next;
+ StreamTcpRemoveSegmentFromStream(stream, seg);
+ StreamTcpSegmentReturntoPool(seg);
+ seg = next_seg;
+ continue;
+ }
+
+ /* we've run into a sequence gap, wrap up any existing smsg and
+ * queue it so the next chunk (if any) is in a new smsg */
+ if (SEQ_GT(seg->seq, next_seq)) {
+ /* pass on pre existing smsg (if any) */
+ if (smsg != NULL && smsg->data_len > 0) {
+ StreamTcpStoreStreamChunk(ssn, smsg, p, 1);
+ stream->ra_raw_base_seq = ra_base_seq;
+ smsg = NULL;
+ }
+
+ gap = 1;
+ }
+
+ /* if the segment ends beyond left_edge we need to consider it */
+ if (SEQ_GT((seg->seq + seg->payload_len), left_edge)) {
+ SCLogDebug("seg->seq %" PRIu32 ", seg->payload_len %" PRIu32 ", "
+ "left_edge %" PRIu32 "", seg->seq,
+ seg->payload_len, left_edge);
+
+ /* handle segments partly before ra_base_seq */
+ if (SEQ_GT(left_edge, seg->seq)) {
+ payload_offset = left_edge - seg->seq;
+
+ if (SEQ_LT(right_edge, (seg->seq + seg->payload_len))) {
+ payload_len = (right_edge - seg->seq) - payload_offset;
+ } else {
+ payload_len = seg->payload_len - payload_offset;
+ }
+
+ if (SCLogDebugEnabled()) {
+ BUG_ON(payload_offset > seg->payload_len);
+ BUG_ON((payload_len + payload_offset) > seg->payload_len);
+ }
+ } else {
+ payload_offset = 0;
+
+ if (SEQ_LT(right_edge, (seg->seq + seg->payload_len))) {
+ payload_len = right_edge - seg->seq;
+ } else {
+ payload_len = seg->payload_len;
+ }
+ }
+ SCLogDebug("payload_offset is %"PRIu16", payload_len is %"PRIu16""
+ " and stream->last_ack is %"PRIu32"", payload_offset,
+ payload_len, stream->last_ack);
+
+ if (payload_len == 0) {
+ SCLogDebug("no payload_len, so bail out");
+ break;
+ }
+
+ if (smsg == NULL) {
+ smsg = StreamMsgGetFromPool();
+ if (smsg == NULL) {
+ SCLogDebug("stream_msg_pool is empty");
+ return -1;
+ }
+
+ smsg_offset = 0;
+
+ StreamTcpSetupMsg(ssn, stream, p, smsg);
+ smsg->seq = ra_base_seq + 1;
+ }
+
+ /* copy the data into the smsg */
+ uint32_t copy_size = smsg->data_size - smsg_offset;
+ if (copy_size > payload_len) {
+ copy_size = payload_len;
+ }
+ if (SCLogDebugEnabled()) {
+ BUG_ON(copy_size > smsg->data_size);
+ }
+ SCLogDebug("copy_size is %"PRIu16"", copy_size);
+ memcpy(smsg->data + smsg_offset, seg->payload + payload_offset,
+ copy_size);
+ smsg_offset += copy_size;
+
+ SCLogDebug("seg total %u, seq %u off %u copy %u, ra_base_seq %u",
+ (seg->seq + payload_offset + copy_size), seg->seq,
+ payload_offset, copy_size, ra_base_seq);
+ if (gap == 0 && SEQ_GT((seg->seq + payload_offset + copy_size),ra_base_seq+1)) {
+ ra_base_seq += copy_size;
+ }
+ SCLogDebug("ra_base_seq %"PRIu32, ra_base_seq);
+
+ smsg->data_len += copy_size;
+
+ /* queue the smsg if it's full */
+ if (smsg->data_len == smsg->data_size) {
+ StreamTcpStoreStreamChunk(ssn, smsg, p, 1);
+ stream->ra_raw_base_seq = ra_base_seq;
+ smsg = NULL;
+ }
+
+ /* if the payload len is bigger than what we copied, we handle the
+ * rest of the payload next... */
+ if (copy_size < payload_len) {
+ SCLogDebug("copy_size %" PRIu32 " < %" PRIu32 "", copy_size,
+ payload_len);
+ payload_offset += copy_size;
+ payload_len -= copy_size;
+ SCLogDebug("payload_offset is %"PRIu16", seg->payload_len is "
+ "%"PRIu16" and stream->last_ack is %"PRIu32"",
+ payload_offset, seg->payload_len, stream->last_ack);
+ if (SCLogDebugEnabled()) {
+ BUG_ON(payload_offset > seg->payload_len);
+ }
+
+ /* we need a while loop here as the packets theoretically can be
+ * 64k */
+ char segment_done = FALSE;
+ while (segment_done == FALSE) {
+ SCLogDebug("new msg at offset %" PRIu32 ", payload_len "
+ "%" PRIu32 "", payload_offset, payload_len);
+
+ /* get a new message
+ XXX we need a setup function */
+ smsg = StreamMsgGetFromPool();
+ if (smsg == NULL) {
+ SCLogDebug("stream_msg_pool is empty");
+ SCReturnInt(-1);
+ }
+ smsg_offset = 0;
+
+ StreamTcpSetupMsg(ssn, stream,p,smsg);
+ smsg->seq = ra_base_seq + 1;
+
+ copy_size = smsg->data_size - smsg_offset;
+ if ((int32_t)copy_size > (seg->payload_len - payload_offset)) {
+ copy_size = (seg->payload_len - payload_offset);
+ }
+ if (SCLogDebugEnabled()) {
+ BUG_ON(copy_size > smsg->data_size);
+ }
+
+ SCLogDebug("copy payload_offset %" PRIu32 ", smsg_offset "
+ "%" PRIu32 ", copy_size %" PRIu32 "",
+ payload_offset, smsg_offset, copy_size);
+ memcpy(smsg->data + smsg_offset, seg->payload +
+ payload_offset, copy_size);
+ smsg_offset += copy_size;
+ if (gap == 0 && SEQ_GT((seg->seq + payload_offset + copy_size),ra_base_seq+1)) {
+ ra_base_seq += copy_size;
+ }
+ SCLogDebug("ra_base_seq %"PRIu32, ra_base_seq);
+ smsg->data_len += copy_size;
+ SCLogDebug("copied payload_offset %" PRIu32 ", "
+ "smsg_offset %" PRIu32 ", copy_size %" PRIu32 "",
+ payload_offset, smsg_offset, copy_size);
+ if (smsg->data_len == smsg->data_size) {
+ StreamTcpStoreStreamChunk(ssn, smsg, p, 1);
+ stream->ra_raw_base_seq = ra_base_seq;
+ smsg = NULL;
+ }
+
+ /* see if we have segment payload left to process */
+ if ((copy_size + payload_offset) < seg->payload_len) {
+ payload_offset += copy_size;
+ payload_len -= copy_size;
+
+ if (SCLogDebugEnabled()) {
+ BUG_ON(payload_offset > seg->payload_len);
+ }
+ } else {
+ payload_offset = 0;
+ segment_done = TRUE;
+ }
+ }
+ }
+ }
+
+ /* done with this segment, return it to the pool */
+ TcpSegment *next_seg = seg->next;
+ next_seq = seg->seq + seg->payload_len;
+
+ if (SEQ_LT((seg->seq + seg->payload_len), (ra_base_seq - chunk_size))) {
+ if (seg->flags & SEGMENTTCP_FLAG_APPLAYER_PROCESSED) {
+ StreamTcpRemoveSegmentFromStream(stream, seg);
+ SCLogDebug("removing seg %p, seg->next %p", seg, seg->next);
+ StreamTcpSegmentReturntoPool(seg);
+ } else {
+ seg->flags |= SEGMENTTCP_FLAG_RAW_PROCESSED;
+ }
+ }
+ seg = next_seg;
+ }
+
+ /* put the partly filled smsg in the queue */
+ if (smsg != NULL) {
+ StreamTcpStoreStreamChunk(ssn, smsg, p, 1);
+ smsg = NULL;
+ stream->ra_raw_base_seq = ra_base_seq;
+ }
+
+ /* see if we can clean up some segments */
+ left_edge = (ra_base_seq + 1) - chunk_size;
+ SCLogDebug("left_edge %"PRIu32", ra_base_seq %"PRIu32, left_edge, ra_base_seq);
+
+ /* loop through the segments to remove unneeded segments */
+ for (seg = stream->seg_list; seg != NULL && SEQ_LEQ((seg->seq + p->payload_len), left_edge); ) {
+ SCLogDebug("seg %p seq %"PRIu32", len %"PRIu16", sum %"PRIu32, seg, seg->seq, seg->payload_len, seg->seq+seg->payload_len);
+
+ /* only remove if app layer reassembly is ready too */
+ if (StreamTcpAppLayerSegmentProcessed(ssn, stream, seg)) {
+ TcpSegment *next_seg = seg->next;
+ StreamTcpRemoveSegmentFromStream(stream, seg);
+ StreamTcpSegmentReturntoPool(seg);
+ seg = next_seg;
+ } else {
+ break;
+ }
+ }
+ SCLogDebug("stream->ra_raw_base_seq %u", stream->ra_raw_base_seq);
+ SCReturnInt(0);
+}
+
+/** \brief Remove idle TcpSegments from TcpSession
+ *
+ * \param f flow
+ * \param flags direction flags
+ */
+void StreamTcpPruneSession(Flow *f, uint8_t flags)
+{
+ if (f == NULL || f->protoctx == NULL)
+ return;
+
+ TcpSession *ssn = f->protoctx;
+ TcpStream *stream = NULL;
+
+ if (flags & STREAM_TOSERVER) {
+ stream = &ssn->client;
+ } else if (flags & STREAM_TOCLIENT) {
+ stream = &ssn->server;
+ } else {
+ return;
+ }
+
+ /* loop through the segments and fill one or more msgs */
+ TcpSegment *seg = stream->seg_list;
+
+ for (; seg != NULL && SEQ_LT(seg->seq, stream->last_ack);)
+ {
+ SCLogDebug("seg %p, SEQ %"PRIu32", LEN %"PRIu16", SUM %"PRIu32", FLAGS %02x",
+ seg, seg->seq, seg->payload_len,
+ (uint32_t)(seg->seq + seg->payload_len), seg->flags);
+
+ if (StreamTcpReturnSegmentCheck(f, ssn, stream, seg) == 0) {
+ break;
+ }
+
+ TcpSegment *next_seg = seg->next;
+ StreamTcpRemoveSegmentFromStream(stream, seg);
+ StreamTcpSegmentReturntoPool(seg);
+ seg = next_seg;
+ continue;
+ }
+}
+
+#ifdef DEBUG
+static uint64_t GetStreamSize(TcpStream *stream)
+{
+ if (stream) {
+ uint64_t size = 0;
+ uint32_t cnt = 0;
+
+ TcpSegment *seg = stream->seg_list;
+ while (seg) {
+ cnt++;
+ size += (uint64_t)seg->payload_len;
+
+ seg = seg->next;
+ }
+
+ SCLogDebug("size %"PRIu64", cnt %"PRIu32, size, cnt);
+ return size;
+ }
+ return (uint64_t)0;
+}
+
+static void GetSessionSize(TcpSession *ssn, Packet *p)
+{
+ uint64_t size = 0;
+ if (ssn) {
+ size = GetStreamSize(&ssn->client);
+ size += GetStreamSize(&ssn->server);
+
+ //if (size > 900000)
+ // SCLogInfo("size %"PRIu64", packet %"PRIu64, size, p->pcap_cnt);
+ SCLogDebug("size %"PRIu64", packet %"PRIu64, size, p->pcap_cnt);
+ }
+}
+#endif
+
+typedef struct ReassembleData_ {
+ uint32_t ra_base_seq;
+ uint32_t data_len;
+ uint8_t data[4096];
+ int partial; /* last segment was processed only partially */
+ uint32_t data_sent; /* data passed on this run */
+} ReassembleData;
+
+/** \internal
+ * \brief test if segment follows a gap. If so, handle the gap
+ *
+ * If in inline mode, segment may be un-ack'd. In this case we
+ * consider it a gap, but it's not 'final' yet.
+ *
+ * \retval bool 1 gap 0 no gap
+ */
+int DoHandleGap(ThreadVars *tv, TcpReassemblyThreadCtx *ra_ctx,
+ TcpSession *ssn, TcpStream *stream, TcpSegment *seg, ReassembleData *rd,
+ Packet *p, uint32_t next_seq)
+{
+ if (unlikely(SEQ_GT(seg->seq, next_seq))) {
+ /* we've run into a sequence gap */
+
+ if (StreamTcpInlineMode()) {
+ /* don't conclude it's a gap until we see that the data
+ * that is missing was acked. */
+ if (SEQ_GT(seg->seq,stream->last_ack) && ssn->state != TCP_CLOSED)
+ return 1;
+ }
+
+ /* first, pass on data before the gap */
+ if (rd->data_len > 0) {
+ SCLogDebug("pre GAP data");
+
+ /* process what we have so far */
+ AppLayerHandleTCPData(tv, ra_ctx, p, p->flow, ssn, stream,
+ rd->data, rd->data_len,
+ StreamGetAppLayerFlags(ssn, stream, p));
+ AppLayerProfilingStore(ra_ctx->app_tctx, p);
+ rd->data_sent += rd->data_len;
+ rd->data_len = 0;
+ }
+
+#ifdef DEBUG
+ uint32_t gap_len = seg->seq - next_seq;
+ SCLogDebug("expected next_seq %" PRIu32 ", got %" PRIu32 " , "
+ "stream->last_ack %" PRIu32 ". Seq gap %" PRIu32"",
+ next_seq, seg->seq, stream->last_ack, gap_len);
+#endif
+ /* We have missed the packet and end host has ack'd it, so
+ * IDS should advance it's ra_base_seq and should not consider this
+ * packet any longer, even if it is retransmitted, as end host will
+ * drop it anyway */
+ rd->ra_base_seq = seg->seq - 1;
+
+ /* 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
+ return 1;
+ }
+ return 0;
+}
+
+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;
+#ifdef DEBUG
+ ra_ctx->fp1++;
+#endif
+ /* 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 >= stream_config.zero_copy_size)
+ {
+ /* 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;
+#ifdef DEBUG
+ ra_ctx->fp2++;
+#endif
+ /* 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;
+ }
+#ifdef DEBUG
+ ra_ctx->sp++;
+#endif
+ uint16_t payload_offset = 0;
+ uint16_t payload_len = 0;
+
+ /* start clean */
+ rd->partial = FALSE;
+
+ /* if the segment ends beyond ra_base_seq we need to consider it */
+ if (SEQ_GT((seg->seq + seg->payload_len), rd->ra_base_seq+1)) {
+ SCLogDebug("seg->seq %" PRIu32 ", seg->payload_len %" PRIu32 ", "
+ "ra_base_seq %" PRIu32 ", last_ack %"PRIu32, seg->seq,
+ seg->payload_len, rd->ra_base_seq, stream->last_ack);
+
+ if (StreamTcpInlineMode() == 0) {
+ /* handle segments partly before ra_base_seq */
+ if (SEQ_GT(rd->ra_base_seq, seg->seq)) {
+ payload_offset = (rd->ra_base_seq + 1) - seg->seq;
+ SCLogDebug("payload_offset %u", payload_offset);
+
+ if (SEQ_LT(stream->last_ack, (seg->seq + seg->payload_len))) {
+ if (SEQ_LT(stream->last_ack, (rd->ra_base_seq + 1))) {
+ payload_len = (stream->last_ack - seg->seq);
+ SCLogDebug("payload_len %u", payload_len);
+ } else {
+ payload_len = (stream->last_ack - seg->seq) - payload_offset;
+ SCLogDebug("payload_len %u", payload_len);
+ }
+ rd->partial = TRUE;
+ } else {
+ payload_len = seg->payload_len - payload_offset;
+ SCLogDebug("payload_len %u", payload_len);
+ }
+
+ if (SCLogDebugEnabled()) {
+ BUG_ON(payload_offset > seg->payload_len);
+ BUG_ON((payload_len + payload_offset) > seg->payload_len);
+ }
+ } else {
+ payload_offset = 0;
+
+ if (SEQ_LT(stream->last_ack, (seg->seq + seg->payload_len))) {
+ payload_len = stream->last_ack - seg->seq;
+ SCLogDebug("payload_len %u", payload_len);
+
+ rd->partial = TRUE;
+ } else {
+ payload_len = seg->payload_len;
+ SCLogDebug("payload_len %u", payload_len);
+ }
+ }
+ /* inline mode, don't consider last_ack as we process un-ACK'd segments */
+ } else {
+ /* handle segments partly before ra_base_seq */
+ if (SEQ_GT(rd->ra_base_seq, seg->seq)) {
+ payload_offset = rd->ra_base_seq - seg->seq - 1;
+ payload_len = seg->payload_len - payload_offset;
+
+ if (SCLogDebugEnabled()) {
+ BUG_ON(payload_offset > seg->payload_len);
+ BUG_ON((payload_len + payload_offset) > seg->payload_len);
+ }
+ } else {
+ payload_offset = 0;
+ payload_len = seg->payload_len;
+ }
+ }
+ SCLogDebug("payload_offset is %"PRIu16", payload_len is %"PRIu16""
+ " and stream->last_ack is %"PRIu32"", payload_offset,
+ payload_len, stream->last_ack);
+
+ if (payload_len == 0) {
+ SCLogDebug("no payload_len, so bail out");
+ return 0;
+ }
+
+ /* copy the data into the buffer */
+ uint16_t copy_size = sizeof(rd->data) - rd->data_len;
+ if (copy_size > payload_len) {
+ copy_size = payload_len;
+ }
+ if (SCLogDebugEnabled()) {
+ BUG_ON(copy_size > sizeof(rd->data));
+ }
+ SCLogDebug("copy_size is %"PRIu16"", copy_size);
+ memcpy(rd->data + rd->data_len, seg->payload + payload_offset, copy_size);
+ rd->data_len += copy_size;
+ rd->ra_base_seq += copy_size;
+ SCLogDebug("ra_base_seq %"PRIu32", data_len %"PRIu32, rd->ra_base_seq, rd->data_len);
+
+ /* queue the smsg if it's full */
+ if (rd->data_len == sizeof(rd->data)) {
+ /* process what we have so far */
+ AppLayerHandleTCPData(tv, ra_ctx, p, p->flow, ssn, stream,
+ rd->data, rd->data_len,
+ StreamGetAppLayerFlags(ssn, stream, p));
+ AppLayerProfilingStore(ra_ctx->app_tctx, p);
+ rd->data_sent += rd->data_len;
+ rd->data_len = 0;
+
+ /* 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;
+ }
+ }
+
+ /* if the payload len is bigger than what we copied, we handle the
+ * rest of the payload next... */
+ if (copy_size < payload_len) {
+ SCLogDebug("copy_size %" PRIu32 " < %" PRIu32 "", copy_size,
+ payload_len);
+
+ payload_offset += copy_size;
+ payload_len -= copy_size;
+ SCLogDebug("payload_offset is %"PRIu16", seg->payload_len is "
+ "%"PRIu16" and stream->last_ack is %"PRIu32"",
+ payload_offset, seg->payload_len, stream->last_ack);
+ if (SCLogDebugEnabled()) {
+ BUG_ON(payload_offset > seg->payload_len);
+ }
+
+ /* we need a while loop here as the packets theoretically can be
+ * 64k */
+ char segment_done = FALSE;
+ while (segment_done == FALSE) {
+ SCLogDebug("new msg at offset %" PRIu32 ", payload_len "
+ "%" PRIu32 "", payload_offset, payload_len);
+ rd->data_len = 0;
+
+ copy_size = sizeof(rd->data) - rd->data_len;
+ if (copy_size > (seg->payload_len - payload_offset)) {
+ copy_size = (seg->payload_len - payload_offset);
+ }
+ if (SCLogDebugEnabled()) {
+ BUG_ON(copy_size > sizeof(rd->data));
+ }
+
+ SCLogDebug("copy payload_offset %" PRIu32 ", data_len "
+ "%" PRIu32 ", copy_size %" PRIu32 "",
+ payload_offset, rd->data_len, copy_size);
+ memcpy(rd->data + rd->data_len, seg->payload +
+ payload_offset, copy_size);
+ rd->data_len += copy_size;
+ rd->ra_base_seq += copy_size;
+ SCLogDebug("ra_base_seq %"PRIu32, rd->ra_base_seq);
+ SCLogDebug("copied payload_offset %" PRIu32 ", "
+ "data_len %" PRIu32 ", copy_size %" PRIu32 "",
+ payload_offset, rd->data_len, copy_size);
+
+ if (rd->data_len == sizeof(rd->data)) {
+ /* process what we have so far */
+ AppLayerHandleTCPData(tv, ra_ctx, p, p->flow, ssn, stream,
+ rd->data, rd->data_len,
+ StreamGetAppLayerFlags(ssn, stream, p));
+ AppLayerProfilingStore(ra_ctx->app_tctx, p);
+ rd->data_sent += rd->data_len;
+ rd->data_len = 0;
+
+ /* 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;
+ }
+ }
+
+ /* see if we have segment payload left to process */
+ if ((copy_size + payload_offset) < seg->payload_len) {
+ payload_offset += copy_size;
+ payload_len -= copy_size;
+
+ if (SCLogDebugEnabled()) {
+ BUG_ON(payload_offset > seg->payload_len);
+ }
+ } else {
+ payload_offset = 0;
+ segment_done = TRUE;
+ }
+ }
+ }
+ }
+
+ return 1;
+}
+
+/**
+ * \brief Update the stream reassembly upon receiving an ACK packet.
+ *
+ * Stream is in the opposite direction of the packet, as the ACK-packet
+ * is ACK'ing the stream.
+ *
+ * One of the utilities call by this function AppLayerHandleTCPData(),
+ * has a feature where it will call this very same function for the
+ * stream opposing the stream it is called with. This shouldn't cause
+ * any issues, since processing of each stream is independent of the
+ * other stream.
+ *
+ * \todo this function is too long, we need to break it up. It needs it BAD
+ */
+int StreamTcpReassembleAppLayer (ThreadVars *tv, TcpReassemblyThreadCtx *ra_ctx,
+ TcpSession *ssn, TcpStream *stream,
+ Packet *p)
+{
+ SCEnter();
+
+ /* this function can be directly called by app layer protocol
+ * detection. */
+ if (stream->flags & STREAMTCP_STREAM_FLAG_NOREASSEMBLY) {
+ SCLogDebug("stream no reassembly flag set. Mostly called via "
+ "app proto detection.");
+ SCReturnInt(0);
+ }
+
+ SCLogDebug("stream->seg_list %p", stream->seg_list);
+#ifdef DEBUG
+ PrintList(stream->seg_list);
+ GetSessionSize(ssn, p);
+#endif
+
+ /* 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;
+ if (seg_tail == NULL ||
+ (seg_tail->flags & SEGMENTTCP_FLAG_APPLAYER_PROCESSED))
+ {
+ /* send an empty EOF msg if we have no segments but TCP state
+ * is beyond ESTABLISHED */
+ if (ssn->state >= TCP_CLOSING || (p->flags & PKT_PSEUDO_STREAM_END)) {
+ SCLogDebug("sending empty eof message");
+ /* send EOF to app layer */
+ AppLayerHandleTCPData(tv, ra_ctx, p, p->flow, ssn, stream,
+ NULL, 0,
+ StreamGetAppLayerFlags(ssn, stream, p));
+ AppLayerProfilingStore(ra_ctx->app_tctx, p);
+
+ SCReturnInt(0);
+ }
+ }
+
+ /* no segments, nothing to do */
+ if (stream->seg_list == NULL) {
+ SCLogDebug("no segments in the list to reassemble");
+ SCReturnInt(0);
+ }
+
+
+ if (stream->flags & STREAMTCP_STREAM_FLAG_GAP) {
+ SCReturnInt(0);
+ }
+
+ /* stream->ra_app_base_seq remains at stream->isn until protocol is
+ * detected. */
+ ReassembleData rd;
+ rd.ra_base_seq = stream->ra_app_base_seq;
+ rd.data_len = 0;
+ rd.data_sent = 0;
+ rd.partial = FALSE;
+ uint32_t next_seq = rd.ra_base_seq + 1;
+
+ SCLogDebug("ra_base_seq %"PRIu32", last_ack %"PRIu32", next_seq %"PRIu32,
+ rd.ra_base_seq, stream->last_ack, next_seq);
+
+ /* 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);
+ }
+ }
+
+ for (; seg != NULL; )
+ {
+ /* if in inline mode, we process all segments regardless of whether
+ * they are ack'd or not. In non-inline, we process only those that
+ * are at least partly ack'd. */
+ if (StreamTcpInlineMode() == 0 && SEQ_GEQ(seg->seq, stream->last_ack))
+ break;
+
+ SCLogDebug("seg %p, SEQ %"PRIu32", LEN %"PRIu16", SUM %"PRIu32,
+ seg, seg->seq, seg->payload_len,
+ (uint32_t)(seg->seq + seg->payload_len));
+
+ if (StreamTcpReturnSegmentCheck(p->flow, ssn, stream, seg) == 1) {
+ SCLogDebug("removing segment");
+ TcpSegment *next_seg = seg->next;
+ StreamTcpRemoveSegmentFromStream(stream, seg);
+ StreamTcpSegmentReturntoPool(seg);
+ seg = next_seg;
+ continue;
+ } else if (StreamTcpAppLayerSegmentProcessed(ssn, stream, seg)) {
+ TcpSegment *next_seg = seg->next;
+ seg = next_seg;
+ continue;
+ }
+
+ /* check if we have a sequence gap and if so, handle it */
+ if (DoHandleGap(tv, ra_ctx, ssn, stream, seg, &rd, p, next_seq) == 1)
+ break;
+
+ /* process this segment */
+ if (DoReassemble(tv, ra_ctx, ssn, stream, seg, &rd, p) == 0)
+ break;
+
+ /* done with this segment, return it to the pool */
+ TcpSegment *next_seg = seg->next;
+ next_seq = seg->seq + seg->payload_len;
+ if (rd.partial == FALSE) {
+ SCLogDebug("fully done with segment in app layer reassembly (seg %p seq %"PRIu32")",
+ seg, seg->seq);
+ seg->flags |= SEGMENTTCP_FLAG_APPLAYER_PROCESSED;
+ SCLogDebug("flags now %02x", seg->flags);
+ } else {
+ SCLogDebug("not yet fully done with segment in app layer reassembly");
+ }
+ seg = next_seg;
+ }
+
+ /* put the partly filled smsg in the queue to the l7 handler */
+ if (rd.data_len > 0) {
+ SCLogDebug("data_len > 0, %u", rd.data_len);
+ /* process what we have so far */
+ BUG_ON(rd.data_len > sizeof(rd.data));
+ AppLayerHandleTCPData(tv, ra_ctx, p, p->flow, ssn, stream,
+ rd.data, rd.data_len,
+ StreamGetAppLayerFlags(ssn, stream, p));
+ AppLayerProfilingStore(ra_ctx->app_tctx, p);
+ }
+
+ /* if no data was sent to the applayer, we send it a empty 'nudge'
+ * when in inline mode */
+ if (StreamTcpInlineMode() && rd.data_sent == 0 && ssn->state > TCP_ESTABLISHED) {
+ SCLogDebug("sending empty eof message");
+ /* send EOF to app layer */
+ AppLayerHandleTCPData(tv, ra_ctx, p, p->flow, ssn, stream,
+ NULL, 0, StreamGetAppLayerFlags(ssn, stream, p));
+ AppLayerProfilingStore(ra_ctx->app_tctx, p);
+ }
+
+ /* store ra_base_seq in the stream */
+ if (StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(stream)) {
+ stream->ra_app_base_seq = rd.ra_base_seq;
+ } else {
+ TcpSegment *tmp_seg = stream->seg_list;
+ while (tmp_seg != NULL) {
+ tmp_seg->flags &= ~SEGMENTTCP_FLAG_APPLAYER_PROCESSED;
+ tmp_seg = tmp_seg->next;
+ }
+ }
+ SCLogDebug("stream->ra_app_base_seq %u", stream->ra_app_base_seq);
+ SCReturnInt(0);
+}
+
+typedef struct ReassembleRawData_ {
+ uint32_t ra_base_seq;
+ int partial; /* last segment was processed only partially */
+ StreamMsg *smsg;
+ uint32_t smsg_offset; // TODO diff with smsg->data_len?
+} ReassembleRawData;
+
+static void DoHandleRawGap(TcpSession *ssn, TcpStream *stream, TcpSegment *seg, Packet *p,
+ ReassembleRawData *rd, uint32_t next_seq)
+{
+ /* we've run into a sequence gap */
+ if (SEQ_GT(seg->seq, next_seq)) {
+ /* pass on pre existing smsg (if any) */
+ if (rd->smsg != NULL && rd->smsg->data_len > 0) {
+ /* if app layer protocol has not been detected till yet,
+ then check did we have sent message to app layer already
+ or not. If not then sent the message and set flag that first
+ message has been sent. No more data till proto has not
+ been detected */
+ StreamTcpStoreStreamChunk(ssn, rd->smsg, p, 0);
+ stream->ra_raw_base_seq = rd->ra_base_seq;
+ rd->smsg = NULL;
+ }
+
+ /* see what the length of the gap is, gap length is seg->seq -
+ * (ra_base_seq +1) */
+#ifdef DEBUG
+ uint32_t gap_len = seg->seq - next_seq;
+ SCLogDebug("expected next_seq %" PRIu32 ", got %" PRIu32 " , "
+ "stream->last_ack %" PRIu32 ". Seq gap %" PRIu32"",
+ next_seq, seg->seq, stream->last_ack, gap_len);
+#endif
+ stream->ra_raw_base_seq = rd->ra_base_seq;
+
+ /* We have missed the packet and end host has ack'd it, so
+ * IDS should advance it's ra_base_seq and should not consider this
+ * packet any longer, even if it is retransmitted, as end host will
+ * drop it anyway */
+ rd->ra_base_seq = seg->seq - 1;
+ }
+}
+
+static int DoRawReassemble(TcpSession *ssn, TcpStream *stream, TcpSegment *seg, Packet *p,
+ ReassembleRawData *rd)
+{
+ uint16_t payload_offset = 0;
+ uint16_t payload_len = 0;
+
+ /* start clean */
+ rd->partial = FALSE;
+
+ /* if the segment ends beyond ra_base_seq we need to consider it */
+ if (SEQ_GT((seg->seq + seg->payload_len), rd->ra_base_seq+1)) {
+ SCLogDebug("seg->seq %" PRIu32 ", seg->payload_len %" PRIu32 ", "
+ "ra_base_seq %" PRIu32 "", seg->seq,
+ seg->payload_len, rd->ra_base_seq);
+
+ /* handle segments partly before ra_base_seq */
+ if (SEQ_GT(rd->ra_base_seq, seg->seq)) {
+ payload_offset = rd->ra_base_seq - seg->seq;
+
+ if (SEQ_LT(stream->last_ack, (seg->seq + seg->payload_len))) {
+
+ if (SEQ_LT(stream->last_ack, rd->ra_base_seq)) {
+ payload_len = (stream->last_ack - seg->seq);
+ } else {
+ payload_len = (stream->last_ack - seg->seq) - payload_offset;
+ }
+ rd->partial = TRUE;
+ } else {
+ payload_len = seg->payload_len - payload_offset;
+ }
+
+ if (SCLogDebugEnabled()) {
+ BUG_ON(payload_offset > seg->payload_len);
+ BUG_ON((payload_len + payload_offset) > seg->payload_len);
+ }
+ } else {
+ payload_offset = 0;
+
+ if (SEQ_LT(stream->last_ack, (seg->seq + seg->payload_len))) {
+ payload_len = stream->last_ack - seg->seq;
+ rd->partial = TRUE;
+ } else {
+ payload_len = seg->payload_len;
+ }
+ }
+ SCLogDebug("payload_offset is %"PRIu16", payload_len is %"PRIu16""
+ " and stream->last_ack is %"PRIu32"", payload_offset,
+ payload_len, stream->last_ack);
+
+ if (payload_len == 0) {
+ SCLogDebug("no payload_len, so bail out");
+ return 1; // TODO
+ }
+
+ if (rd->smsg == NULL) {
+ rd->smsg = StreamMsgGetFromPool();
+ if (rd->smsg == NULL) {
+ SCLogDebug("stream_msg_pool is empty");
+ return -1;
+ }
+
+ rd->smsg_offset = 0;
+
+ StreamTcpSetupMsg(ssn, stream, p, rd->smsg);
+ rd->smsg->seq = rd->ra_base_seq + 1;
+ SCLogDebug("smsg->seq %u", rd->smsg->seq);
+ }
+
+ /* copy the data into the smsg */
+ uint32_t copy_size = rd->smsg->data_size - rd->smsg_offset;
+ if (copy_size > payload_len) {
+ copy_size = payload_len;
+ }
+ if (SCLogDebugEnabled()) {
+ BUG_ON(copy_size > rd->smsg->data_size);
+ }
+ SCLogDebug("copy_size is %"PRIu16"", copy_size);
+ memcpy(rd->smsg->data + rd->smsg_offset, seg->payload + payload_offset,
+ copy_size);
+ rd->smsg_offset += copy_size;
+ rd->ra_base_seq += copy_size;
+ SCLogDebug("ra_base_seq %"PRIu32, rd->ra_base_seq);
+
+ rd->smsg->data_len += copy_size;
+
+ /* queue the smsg if it's full */
+ if (rd->smsg->data_len == rd->smsg->data_size) {
+ StreamTcpStoreStreamChunk(ssn, rd->smsg, p, 0);
+ stream->ra_raw_base_seq = rd->ra_base_seq;
+ rd->smsg = NULL;
+ }
+
+ /* if the payload len is bigger than what we copied, we handle the
+ * rest of the payload next... */
+ if (copy_size < payload_len) {
+ SCLogDebug("copy_size %" PRIu32 " < %" PRIu32 "", copy_size,
+ payload_len);
+
+ payload_offset += copy_size;
+ payload_len -= copy_size;
+ SCLogDebug("payload_offset is %"PRIu16", seg->payload_len is "
+ "%"PRIu16" and stream->last_ack is %"PRIu32"",
+ payload_offset, seg->payload_len, stream->last_ack);
+ if (SCLogDebugEnabled()) {
+ BUG_ON(payload_offset > seg->payload_len);
+ }
+
+ /* we need a while loop here as the packets theoretically can be
+ * 64k */
+ char segment_done = FALSE;
+ while (segment_done == FALSE) {
+ SCLogDebug("new msg at offset %" PRIu32 ", payload_len "
+ "%" PRIu32 "", payload_offset, payload_len);
+
+ /* get a new message
+ XXX we need a setup function */
+ rd->smsg = StreamMsgGetFromPool();
+ if (rd->smsg == NULL) {
+ SCLogDebug("stream_msg_pool is empty");
+ SCReturnInt(-1);
+ }
+ rd->smsg_offset = 0;
+
+ StreamTcpSetupMsg(ssn, stream, p, rd->smsg);
+ rd->smsg->seq = rd->ra_base_seq + 1;
+
+ copy_size = rd->smsg->data_size - rd->smsg_offset;
+ if (copy_size > payload_len) {
+ copy_size = payload_len;
+ }
+ if (SCLogDebugEnabled()) {
+ BUG_ON(copy_size > rd->smsg->data_size);
+ }
+
+ SCLogDebug("copy payload_offset %" PRIu32 ", smsg_offset "
+ "%" PRIu32 ", copy_size %" PRIu32 "",
+ payload_offset, rd->smsg_offset, copy_size);
+ memcpy(rd->smsg->data + rd->smsg_offset, seg->payload +
+ payload_offset, copy_size);
+ rd->smsg_offset += copy_size;
+ rd->ra_base_seq += copy_size;
+ SCLogDebug("ra_base_seq %"PRIu32, rd->ra_base_seq);
+ rd->smsg->data_len += copy_size;
+ SCLogDebug("copied payload_offset %" PRIu32 ", "
+ "smsg_offset %" PRIu32 ", copy_size %" PRIu32 "",
+ payload_offset, rd->smsg_offset, copy_size);
+ if (rd->smsg->data_len == rd->smsg->data_size) {
+ StreamTcpStoreStreamChunk(ssn, rd->smsg, p, 0);
+ stream->ra_raw_base_seq = rd->ra_base_seq;
+ rd->smsg = NULL;
+ }
+
+ /* see if we have segment payload left to process */
+ if (copy_size < payload_len) {
+ payload_offset += copy_size;
+ payload_len -= copy_size;
+
+ if (SCLogDebugEnabled()) {
+ BUG_ON(payload_offset > seg->payload_len);
+ }
+ } else {
+ payload_offset = 0;
+ segment_done = TRUE;
+ }
+ }
+ }
+ }
+ return 1;
+}
+
+/**
+ * \brief Update the stream reassembly upon receiving an ACK packet.
+ * \todo this function is too long, we need to break it up. It needs it BAD
+ */
+static int StreamTcpReassembleRaw (TcpReassemblyThreadCtx *ra_ctx,
+ TcpSession *ssn, TcpStream *stream, Packet *p)
+{
+ SCEnter();
+ SCLogDebug("start p %p", p);
+
+ if (ssn->flags & STREAMTCP_FLAG_DISABLE_RAW)
+ SCReturnInt(0);
+
+ if (stream->seg_list == NULL) {
+ SCLogDebug("no segments in the list to reassemble");
+ SCReturnInt(0);
+ }
+
+#if 0
+ if (ssn->state <= TCP_ESTABLISHED &&
+ !StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(stream)) {
+ SCLogDebug("only starting raw reassembly after app layer protocol "
+ "detection has completed.");
+ SCReturnInt(0);
+ }
+#endif
+ /* check if we have enough data */
+ if (StreamTcpReassembleRawCheckLimit(ssn,stream,p) == 0) {
+ SCLogDebug("not yet reassembling");
+ SCReturnInt(0);
+ }
+
+ TcpSegment *seg = stream->seg_list;
+ ReassembleRawData rd;
+ rd.smsg = NULL;
+ rd.ra_base_seq = stream->ra_raw_base_seq;
+ rd.smsg_offset = 0;
+ uint32_t next_seq = rd.ra_base_seq + 1;
+
+ SCLogDebug("ra_base_seq %"PRIu32", last_ack %"PRIu32", next_seq %"PRIu32,
+ rd.ra_base_seq, stream->last_ack, next_seq);
+
+ /* loop through the segments and fill one or more msgs */
+ for (; seg != NULL && SEQ_LT(seg->seq, stream->last_ack);)
+ {
+ SCLogDebug("seg %p, SEQ %"PRIu32", LEN %"PRIu16", SUM %"PRIu32", flags %02x",
+ seg, seg->seq, seg->payload_len,
+ (uint32_t)(seg->seq + seg->payload_len), seg->flags);
+
+ if (StreamTcpReturnSegmentCheck(p->flow, ssn, stream, seg) == 1) {
+ SCLogDebug("removing segment");
+ TcpSegment *next_seg = seg->next;
+ StreamTcpRemoveSegmentFromStream(stream, seg);
+ StreamTcpSegmentReturntoPool(seg);
+ seg = next_seg;
+ continue;
+ } else if(seg->flags & SEGMENTTCP_FLAG_RAW_PROCESSED) {
+ TcpSegment *next_seg = seg->next;
+ seg = next_seg;
+ continue;
+ }
+
+ DoHandleRawGap(ssn, stream, seg, p, &rd, next_seq);
+
+ if (DoRawReassemble(ssn, stream, seg, p, &rd) == 0)
+ break;
+
+ /* done with this segment, return it to the pool */
+ TcpSegment *next_seg = seg->next;
+ next_seq = seg->seq + seg->payload_len;
+ if (rd.partial == FALSE) {
+ SCLogDebug("fully done with segment in raw reassembly (seg %p seq %"PRIu32")",
+ seg, seg->seq);
+ seg->flags |= SEGMENTTCP_FLAG_RAW_PROCESSED;
+ SCLogDebug("flags now %02x", seg->flags);
+ } else {
+ SCLogDebug("not yet fully done with segment in raw reassembly");
+ }
+ seg = next_seg;
+ }
+
+ /* put the partly filled smsg in the queue to the l7 handler */
+ if (rd.smsg != NULL) {
+ StreamTcpStoreStreamChunk(ssn, rd.smsg, p, 0);
+ rd.smsg = NULL;
+ stream->ra_raw_base_seq = rd.ra_base_seq;
+ }
+
+ SCReturnInt(0);
+}
+
+/** \brief update app layer and raw reassembly
+ *
+ * \retval r 0 on success, -1 on error
+ */
+int StreamTcpReassembleHandleSegmentUpdateACK (ThreadVars *tv,
+ TcpReassemblyThreadCtx *ra_ctx, TcpSession *ssn, TcpStream *stream, Packet *p)
+{
+ SCEnter();
+
+ SCLogDebug("stream->seg_list %p", stream->seg_list);
+
+ int r = 0;
+ if (!(StreamTcpInlineMode())) {
+ if (StreamTcpReassembleAppLayer(tv, ra_ctx, ssn, stream, p) < 0)
+ r = -1;
+ if (StreamTcpReassembleRaw(ra_ctx, ssn, stream, p) < 0)
+ r = -1;
+ }
+
+ SCLogDebug("stream->seg_list %p", stream->seg_list);
+ SCReturnInt(r);
+}
+
+int StreamTcpReassembleHandleSegment(ThreadVars *tv, TcpReassemblyThreadCtx *ra_ctx,
+ TcpSession *ssn, TcpStream *stream,
+ Packet *p, PacketQueue *pq)
+{
+ SCEnter();
+ SCLogDebug("ssn %p, stream %p, p %p, p->payload_len %"PRIu16"",
+ ssn, stream, p, p->payload_len);
+
+ /* we need to update the opposing stream in
+ * StreamTcpReassembleHandleSegmentUpdateACK */
+ TcpStream *opposing_stream = NULL;
+ if (stream == &ssn->client) {
+ opposing_stream = &ssn->server;
+ } else {
+ opposing_stream = &ssn->client;
+ }
+
+ /* handle ack received */
+ if (StreamTcpReassembleHandleSegmentUpdateACK(tv, ra_ctx, ssn, opposing_stream, p) != 0)
+ {
+ SCLogDebug("StreamTcpReassembleHandleSegmentUpdateACK error");
+ SCReturnInt(-1);
+ }
+
+ /* If no stream reassembly/application layer protocol inspection, then
+ simple return */
+ if (p->payload_len > 0 && !(stream->flags & STREAMTCP_STREAM_FLAG_NOREASSEMBLY)) {
+ SCLogDebug("calling StreamTcpReassembleHandleSegmentHandleData");
+
+ if (StreamTcpReassembleHandleSegmentHandleData(tv, ra_ctx, ssn, stream, p) != 0) {
+ SCLogDebug("StreamTcpReassembleHandleSegmentHandleData error");
+ SCReturnInt(-1);
+ }
+
+ p->flags |= PKT_STREAM_ADD;
+ }
+
+ /* in stream inline mode even if we have no data we call the reassembly
+ * functions to handle EOF */
+ if (StreamTcpInlineMode()) {
+ int r = 0;
+ if (StreamTcpReassembleAppLayer(tv, ra_ctx, ssn, stream, p) < 0)
+ r = -1;
+ if (StreamTcpReassembleInlineRaw(ra_ctx, ssn, stream, p) < 0)
+ r = -1;
+
+ if (r < 0) {
+ SCReturnInt(-1);
+ }
+ }
+
+ SCReturnInt(0);
+}
+
+/**
+ * \brief Function to replace the data from a specific point up to given length.
+ *
+ * \param dst_seg Destination segment to replace the data
+ * \param src_seg Source segment of which data is to be written to destination
+ * \param start_point Starting point to replace the data onwards
+ * \param len Length up to which data is need to be replaced
+ *
+ * \todo VJ We can remove the abort()s later.
+ * \todo VJ Why not memcpy?
+ */
+void StreamTcpSegmentDataReplace(TcpSegment *dst_seg, TcpSegment *src_seg,
+ uint32_t start_point, uint16_t len)
+{
+ uint32_t seq;
+ uint16_t src_pos = 0;
+ uint16_t dst_pos = 0;
+
+ SCLogDebug("start_point %u", start_point);
+
+ if (SEQ_GT(start_point, dst_seg->seq)) {
+ dst_pos = start_point - dst_seg->seq;
+ } else if (SEQ_LT(start_point, dst_seg->seq)) {
+ dst_pos = dst_seg->seq - start_point;
+ }
+
+ if (SCLogDebugEnabled()) {
+ BUG_ON(((len + dst_pos) - 1) > dst_seg->payload_len);
+ } else {
+ if (((len + dst_pos) - 1) > dst_seg->payload_len)
+ return;
+ }
+
+ src_pos = (uint16_t)(start_point - src_seg->seq);
+
+ SCLogDebug("Replacing data from dst_pos %"PRIu16"", dst_pos);
+
+ for (seq = start_point; SEQ_LT(seq, (start_point + len)) &&
+ src_pos < src_seg->payload_len && dst_pos < dst_seg->payload_len;
+ seq++, dst_pos++, src_pos++)
+ {
+ dst_seg->payload[dst_pos] = src_seg->payload[src_pos];
+ }
+
+ SCLogDebug("Replaced data of size %"PRIu16" up to src_pos %"PRIu16
+ " dst_pos %"PRIu16, len, src_pos, dst_pos);
+}
+
+/**
+ * \brief Function to compare the data from a specific point up to given length.
+ *
+ * \param dst_seg Destination segment to compare the data
+ * \param src_seg Source segment of which data is to be compared to destination
+ * \param start_point Starting point to compare the data onwards
+ * \param len Length up to which data is need to be compared
+ *
+ * \retval 1 same
+ * \retval 0 different
+ */
+static int StreamTcpSegmentDataCompare(TcpSegment *dst_seg, TcpSegment *src_seg,
+ uint32_t start_point, uint16_t len)
+{
+ uint32_t seq;
+ uint16_t src_pos = 0;
+ uint16_t dst_pos = 0;
+
+ SCLogDebug("start_point %u dst_seg %u src_seg %u", start_point, dst_seg->seq, src_seg->seq);
+
+ if (SEQ_GT(start_point, dst_seg->seq)) {
+ SCLogDebug("start_point %u > dst %u", start_point, dst_seg->seq);
+ dst_pos = start_point - dst_seg->seq;
+ } else if (SEQ_LT(start_point, dst_seg->seq)) {
+ SCLogDebug("start_point %u < dst %u", start_point, dst_seg->seq);
+ dst_pos = dst_seg->seq - start_point;
+ }
+
+ if (SCLogDebugEnabled()) {
+ BUG_ON(((len + dst_pos) - 1) > dst_seg->payload_len);
+ } else {
+ if (((len + dst_pos) - 1) > dst_seg->payload_len)
+ return 1;
+ }
+
+ src_pos = (uint16_t)(start_point - src_seg->seq);
+
+ SCLogDebug("Comparing data from dst_pos %"PRIu16", src_pos %u", dst_pos, src_pos);
+
+ for (seq = start_point; SEQ_LT(seq, (start_point + len)) &&
+ src_pos < src_seg->payload_len && dst_pos < dst_seg->payload_len;
+ seq++, dst_pos++, src_pos++)
+ {
+ if (dst_seg->payload[dst_pos] != src_seg->payload[src_pos]) {
+ SCLogDebug("data is different %02x != %02x, dst_pos %u, src_pos %u", dst_seg->payload[dst_pos], src_seg->payload[src_pos], dst_pos, src_pos);
+ return 0;
+ }
+ }
+
+ SCLogDebug("Compared data of size %"PRIu16" up to src_pos %"PRIu16
+ " dst_pos %"PRIu16, len, src_pos, dst_pos);
+ return 1;
+}
+
+/**
+ * \brief Function to copy the data from src_seg to dst_seg.
+ *
+ * \param dst_seg Destination segment for copying the contents
+ * \param src_seg Source segment to copy its contents
+ *
+ * \todo VJ wouldn't a memcpy be more appropriate here?
+ *
+ * \warning Both segments need to be properly initialized.
+ */
+
+void StreamTcpSegmentDataCopy(TcpSegment *dst_seg, TcpSegment *src_seg)
+{
+ uint32_t u;
+ uint16_t dst_pos = 0;
+ uint16_t src_pos = 0;
+ uint32_t seq;
+
+ if (SEQ_GT(dst_seg->seq, src_seg->seq)) {
+ src_pos = dst_seg->seq - src_seg->seq;
+ seq = dst_seg->seq;
+ } else {
+ dst_pos = src_seg->seq - dst_seg->seq;
+ seq = src_seg->seq;
+ }
+
+ SCLogDebug("Copying data from seq %"PRIu32"", seq);
+ for (u = seq;
+ (SEQ_LT(u, (src_seg->seq + src_seg->payload_len)) &&
+ SEQ_LT(u, (dst_seg->seq + dst_seg->payload_len))); u++)
+ {
+ //SCLogDebug("u %"PRIu32, u);
+
+ dst_seg->payload[dst_pos] = src_seg->payload[src_pos];
+
+ dst_pos++;
+ src_pos++;
+ }
+ SCLogDebug("Copyied data of size %"PRIu16" up to dst_pos %"PRIu16"",
+ src_pos, dst_pos);
+}
+
+/**
+ * \brief Function to get the segment of required length from the pool.
+ *
+ * \param len Length which tells the required size of needed segment.
+ *
+ * \retval seg Segment from the pool or NULL
+ */
+TcpSegment* StreamTcpGetSegment(ThreadVars *tv, TcpReassemblyThreadCtx *ra_ctx, uint16_t len)
+{
+ uint16_t idx = segment_pool_idx[len];
+ SCLogDebug("segment_pool_idx %" PRIu32 " for payload_len %" PRIu32 "",
+ idx, len);
+
+ SCMutexLock(&segment_pool_mutex[idx]);
+ TcpSegment *seg = (TcpSegment *) PoolGet(segment_pool[idx]);
+
+ SCLogDebug("segment_pool[%u]->empty_stack_size %u, segment_pool[%u]->alloc_"
+ "list_size %u, alloc %u", idx, segment_pool[idx]->empty_stack_size,
+ idx, segment_pool[idx]->alloc_stack_size,
+ segment_pool[idx]->allocated);
+ SCMutexUnlock(&segment_pool_mutex[idx]);
+
+ SCLogDebug("seg we return is %p", seg);
+ if (seg == NULL) {
+ SCLogDebug("segment_pool[%u]->empty_stack_size %u, "
+ "alloc %u", idx, segment_pool[idx]->empty_stack_size,
+ segment_pool[idx]->allocated);
+ /* Increment the counter to show that we are not able to serve the
+ segment request due to memcap limit */
+ StatsIncr(tv, ra_ctx->counter_tcp_segment_memcap);
+ } else {
+ seg->flags = stream_config.segment_init_flags;
+ seg->next = NULL;
+ seg->prev = NULL;
+ }
+
+#ifdef DEBUG
+ SCMutexLock(&segment_pool_cnt_mutex);
+ segment_pool_cnt++;
+ SCMutexUnlock(&segment_pool_cnt_mutex);
+#endif
+
+ return seg;
+}
+
+/**
+ * \brief Trigger RAW stream reassembly
+ *
+ * Used by AppLayerTriggerRawStreamReassembly to trigger RAW stream
+ * reassembly from the applayer, for example upon completion of a
+ * HTTP request.
+ *
+ * Works by setting a flag in the TcpSession that is unset as soon
+ * as it's checked. Since everything happens when operating under
+ * a single lock period, no side effects are expected.
+ *
+ * \param ssn TcpSession
+ */
+void StreamTcpReassembleTriggerRawReassembly(TcpSession *ssn)
+{
+#ifdef DEBUG
+ BUG_ON(ssn == NULL);
+#endif
+
+ if (ssn != NULL) {
+ SCLogDebug("flagged ssn %p for immediate raw reassembly", ssn);
+ ssn->flags |= STREAMTCP_FLAG_TRIGGER_RAW_REASSEMBLY;
+ }
+}
+
+#ifdef UNITTESTS
+/** unit tests and it's support functions below */
+
+static int UtTestSmsg(StreamMsg *smsg, const uint8_t *buf, uint32_t buf_len)
+{
+ if (smsg == NULL)
+ return 0;
+
+ if (smsg->data_len != buf_len) {
+ return 0;
+ }
+
+ if (!(memcmp(buf, smsg->data, buf_len) == 0)) {
+ printf("data is not what we expected:\nExpected:\n");
+ PrintRawDataFp(stdout, (uint8_t *)buf, buf_len);
+ printf("Got:\n");
+ PrintRawDataFp(stdout, smsg->data, smsg->data_len);
+ return 0;
+ }
+ return 1;
+}
+
+static uint32_t UtSsnSmsgCnt(TcpSession *ssn, uint8_t direction)
+{
+ uint32_t cnt = 0;
+ StreamMsg *smsg = (direction == STREAM_TOSERVER) ?
+ ssn->toserver_smsg_head :
+ ssn->toclient_smsg_head;
+ while (smsg) {
+ cnt++;
+ smsg = smsg->next;
+ }
+ return cnt;
+}
+
+/** \brief The Function tests the reassembly engine working for different
+ * OSes supported. It includes all the OS cases and send
+ * crafted packets to test the reassembly.
+ *
+ * \param stream The stream which will contain the reassembled segments
+ */
+
+static int StreamTcpReassembleStreamTest(TcpStream *stream)
+{
+
+ TcpSession ssn;
+ Packet *p = PacketGetFromAlloc();
+ if (unlikely(p == NULL))
+ return 0;
+ Flow f;
+ uint8_t payload[4];
+ TCPHdr tcph;
+ TcpReassemblyThreadCtx *ra_ctx = StreamTcpReassembleInitThreadCtx(NULL);
+
+ /* prevent L7 from kicking in */
+ StreamMsgQueueSetMinChunkLen(FLOW_PKT_TOSERVER, 4096);
+ StreamMsgQueueSetMinChunkLen(FLOW_PKT_TOCLIENT, 4096);
+ PacketQueue pq;
+ memset(&pq,0,sizeof(PacketQueue));
+
+ memset(&ssn, 0, sizeof (TcpSession));
+ memset(&f, 0, sizeof (Flow));
+ memset(&tcph, 0, sizeof (TCPHdr));
+ ThreadVars tv;
+ memset(&tv, 0, sizeof (ThreadVars));
+ FLOW_INITIALIZE(&f);
+ f.protoctx = &ssn;
+ f.proto = IPPROTO_TCP;
+ p->src.family = AF_INET;
+ p->dst.family = AF_INET;
+ p->proto = IPPROTO_TCP;
+ p->flow = &f;
+ tcph.th_win = 5480;
+ tcph.th_flags = TH_PUSH | TH_ACK;
+ p->tcph = &tcph;
+ p->flowflags = FLOW_PKT_TOSERVER;
+
+ StreamTcpCreateTestPacket(payload, 0x41, 3, 4); /*AAA*/
+ p->tcph->th_seq = htonl(12);
+ p->tcph->th_ack = htonl(31);
+ p->payload = payload;
+ p->payload_len = 3;
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx,&ssn, stream, p, &pq) == -1) {
+ SCFree(p);
+ return 0;
+ }
+
+ StreamTcpCreateTestPacket(payload, 0x42, 2, 4); /*BB*/
+ p->tcph->th_seq = htonl(16);
+ p->tcph->th_ack = htonl(31);
+ p->payload = payload;
+ p->payload_len = 2;
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx,&ssn, stream, p, &pq) == -1) {
+ SCFree(p);
+ return 0;
+ }
+
+ StreamTcpCreateTestPacket(payload, 0x43, 3, 4); /*CCC*/
+ p->tcph->th_seq = htonl(18);
+ p->tcph->th_ack = htonl(31);
+ p->payload = payload;
+ p->payload_len = 3;
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx,&ssn, stream, p, &pq) == -1) {
+ SCFree(p);
+ return 0;
+ }
+
+ StreamTcpCreateTestPacket(payload, 0x44, 1, 4); /*D*/
+ p->tcph->th_seq = htonl(22);
+ p->tcph->th_ack = htonl(31);
+ p->payload = payload;
+ p->payload_len = 1;
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx,&ssn, stream, p, &pq) == -1) {
+ SCFree(p);
+ return 0;
+ }
+
+ StreamTcpCreateTestPacket(payload, 0x45, 2, 4); /*EE*/
+ p->tcph->th_seq = htonl(25);
+ p->tcph->th_ack = htonl(31);
+ p->payload = payload;
+ p->payload_len = 2;
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx,&ssn, stream, p, &pq) == -1) {
+ SCFree(p);
+ return 0;
+ }
+
+ StreamTcpCreateTestPacket(payload, 0x46, 3, 4); /*FFF*/
+ p->tcph->th_seq = htonl(27);
+ p->tcph->th_ack = htonl(31);
+ p->payload = payload;
+ p->payload_len = 3;
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx,&ssn, stream, p, &pq) == -1) {
+ SCFree(p);
+ return 0;
+ }
+
+ StreamTcpCreateTestPacket(payload, 0x47, 2, 4); /*GG*/
+ p->tcph->th_seq = htonl(30);
+ p->tcph->th_ack = htonl(31);
+ p->payload = payload;
+ p->payload_len = 2;
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx,&ssn, stream, p, &pq) == -1) {
+ SCFree(p);
+ return 0;
+ }
+
+ StreamTcpCreateTestPacket(payload, 0x48, 2, 4); /*HH*/
+ p->tcph->th_seq = htonl(32);
+ p->tcph->th_ack = htonl(31);
+ p->payload = payload;
+ p->payload_len = 2;
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx,&ssn, stream, p, &pq) == -1) {
+ SCFree(p);
+ return 0;
+ }
+
+ StreamTcpCreateTestPacket(payload, 0x49, 1, 4); /*I*/
+ p->tcph->th_seq = htonl(34);
+ p->tcph->th_ack = htonl(31);
+ p->payload = payload;
+ p->payload_len = 1;
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx,&ssn, stream, p, &pq) == -1) {
+ SCFree(p);
+ return 0;
+ }
+
+ StreamTcpCreateTestPacket(payload, 0x4a, 4, 4); /*JJJJ*/
+ p->tcph->th_seq = htonl(13);
+ p->tcph->th_ack = htonl(31);
+ p->payload = payload;
+ p->payload_len = 4;
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx,&ssn, stream, p, &pq) == -1) {
+ SCFree(p);
+ return 0;
+ }
+
+ StreamTcpCreateTestPacket(payload, 0x4b, 3, 4); /*KKK*/
+ p->tcph->th_seq = htonl(18);
+ p->tcph->th_ack = htonl(31);
+ p->payload = payload;
+ p->payload_len = 3;
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx,&ssn, stream, p, &pq) == -1) {
+ SCFree(p);
+ return 0;
+ }
+
+ StreamTcpCreateTestPacket(payload, 0x4c, 3, 4); /*LLL*/
+ p->tcph->th_seq = htonl(21);
+ p->tcph->th_ack = htonl(31);
+ p->payload = payload;
+ p->payload_len = 3;
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx,&ssn, stream, p, &pq) == -1) {
+ SCFree(p);
+ return 0;
+ }
+
+ StreamTcpCreateTestPacket(payload, 0x4d, 3, 4); /*MMM*/
+ p->tcph->th_seq = htonl(24);
+ p->tcph->th_ack = htonl(31);
+ p->payload = payload;
+ p->payload_len = 3;
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx,&ssn, stream, p, &pq) == -1) {
+ SCFree(p);
+ return 0;
+ }
+
+ StreamTcpCreateTestPacket(payload, 0x4e, 1, 4); /*N*/
+ p->tcph->th_seq = htonl(28);
+ p->tcph->th_ack = htonl(31);
+ p->payload = payload;
+ p->payload_len = 1;
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx,&ssn, stream, p, &pq) == -1) {
+ SCFree(p);
+ return 0;
+ }
+
+ StreamTcpCreateTestPacket(payload, 0x4f, 1, 4); /*O*/
+ p->tcph->th_seq = htonl(31);
+ p->tcph->th_ack = htonl(31);
+ p->payload = payload;
+ p->payload_len = 1;
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx,&ssn, stream, p, &pq) == -1) {
+ SCFree(p);
+ return 0;
+ }
+
+ StreamTcpCreateTestPacket(payload, 0x50, 1, 4); /*P*/
+ p->tcph->th_seq = htonl(32);
+ p->tcph->th_ack = htonl(31);
+ p->payload = payload;
+ p->payload_len = 1;
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx,&ssn, stream, p, &pq) == -1) {
+ SCFree(p);
+ return 0;
+ }
+
+ StreamTcpCreateTestPacket(payload, 0x51, 2, 4); /*QQ*/
+ p->tcph->th_seq = htonl(34);
+ p->tcph->th_ack = htonl(31);
+ p->payload = payload;
+ p->payload_len = 2;
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx,&ssn, stream, p, &pq) == -1) {
+ SCFree(p);
+ return 0;
+ }
+
+ StreamTcpCreateTestPacket(payload, 0x30, 1, 4); /*0*/
+ p->tcph->th_seq = htonl(11);
+ p->tcph->th_ack = htonl(31);
+ p->payload = payload;
+ p->payload_len = 1;
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx,&ssn, stream, p, &pq) == -1) {
+ SCFree(p);
+ return 0;
+ }
+
+ StreamTcpReassembleFreeThreadCtx(ra_ctx);
+
+ SCFree(p);
+ return 1;
+}
+
+/** \brief The Function to create the packet with given payload, which is used
+ * to test the reassembly of the engine.
+ *
+ * \param payload The variable used to store the payload contents of the
+ * current packet.
+ * \param value The value which current payload will have for this packet
+ * \param payload_len The length of the filed payload for current packet.
+ * \param len Length of the payload array
+ */
+
+void StreamTcpCreateTestPacket(uint8_t *payload, uint8_t value,
+ uint8_t payload_len, uint8_t len)
+{
+ uint8_t i;
+ for (i = 0; i < payload_len; i++)
+ payload[i] = value;
+ for (; i < len; i++)
+ payload = NULL;
+}
+
+/** \brief The Function Checks the reassembled stream contents against predefined
+ * stream contents according to OS policy used.
+ *
+ * \param stream_policy Predefined value of stream for different OS policies
+ * \param stream Reassembled stream returned from the reassembly functions
+ */
+
+int StreamTcpCheckStreamContents(uint8_t *stream_policy, uint16_t sp_size, TcpStream *stream)
+{
+ TcpSegment *temp;
+ uint16_t i = 0;
+ uint8_t j;
+
+#ifdef DEBUG
+ if (SCLogDebugEnabled()) {
+ TcpSegment *temp1;
+ for (temp1 = stream->seg_list; temp1 != NULL; temp1 = temp1->next)
+ PrintRawDataFp(stdout, temp1->payload, temp1->payload_len);
+
+ PrintRawDataFp(stdout, stream_policy, sp_size);
+ }
+#endif
+
+ for (temp = stream->seg_list; temp != NULL; temp = temp->next) {
+ j = 0;
+ for (; j < temp->payload_len; j++) {
+ SCLogDebug("i %"PRIu16", len %"PRIu32", stream %"PRIx32" and temp is %"PRIx8"",
+ i, temp->payload_len, stream_policy[i], temp->payload[j]);
+
+ if (stream_policy[i] == temp->payload[j]) {
+ i++;
+ continue;
+ } else
+ return 0;
+ }
+ }
+ return 1;
+}
+
+/** \brief The Function Checks the Stream Queue contents against predefined
+ * stream contents.
+ *
+ * \param stream_contents Predefined value of stream contents
+ * \param stream Queue which has the stream contents
+ *
+ * \retval On success the function returns 1, on failure 0.
+ */
+static int StreamTcpCheckChunks (TcpSession *ssn, uint8_t *stream_contents)
+{
+ SCEnter();
+
+ StreamMsg *msg;
+ uint16_t i = 0;
+ uint8_t j;
+ uint8_t cnt = 0;
+
+ if (ssn == NULL) {
+ printf("ssn == NULL, ");
+ SCReturnInt(0);
+ }
+
+ if (ssn->toserver_smsg_head == NULL) {
+ printf("ssn->toserver_smsg_head == NULL, ");
+ SCReturnInt(0);
+ }
+
+ msg = ssn->toserver_smsg_head;
+ while(msg != NULL) {
+ cnt++;
+ j = 0;
+ for (; j < msg->data_len; j++) {
+ SCLogDebug("i is %" PRIu32 " and len is %" PRIu32 " and temp is %" PRIx32 "", i, msg->data_len, msg->data[j]);
+
+ if (stream_contents[i] == msg->data[j]) {
+ i++;
+ continue;
+ } else {
+ SCReturnInt(0);
+ }
+ }
+ msg = msg->next;
+ }
+ SCReturnInt(1);
+}
+
+/* \brief The function craft packets to test the overlapping, where
+ * new segment stats before the list segment.
+ *
+ * \param stream The stream which will contain the reassembled segments and
+ * also tells the OS policy used for reassembling the segments.
+ */
+
+static int StreamTcpTestStartsBeforeListSegment(TcpStream *stream) {
+ TcpSession ssn;
+ Packet *p = PacketGetFromAlloc();
+ if (unlikely(p == NULL))
+ return 0;
+ Flow f;
+ uint8_t payload[4];
+ TCPHdr tcph;
+ TcpReassemblyThreadCtx *ra_ctx = StreamTcpReassembleInitThreadCtx(NULL);
+
+ /* prevent L7 from kicking in */
+ StreamMsgQueueSetMinChunkLen(FLOW_PKT_TOSERVER, 4096);
+ StreamMsgQueueSetMinChunkLen(FLOW_PKT_TOCLIENT, 4096);
+ PacketQueue pq;
+ memset(&pq,0,sizeof(PacketQueue));
+
+ memset(&ssn, 0, sizeof (TcpSession));
+ memset(&f, 0, sizeof (Flow));
+ memset(&tcph, 0, sizeof (TCPHdr));
+
+ ThreadVars tv;
+ memset(&tv, 0, sizeof (ThreadVars));
+ FLOW_INITIALIZE(&f);
+ f.protoctx = &ssn;
+ f.proto = IPPROTO_TCP;
+ p->src.family = AF_INET;
+ p->dst.family = AF_INET;
+ p->proto = IPPROTO_TCP;
+ p->flow = &f;
+ tcph.th_win = 5480;
+ tcph.th_flags = TH_PUSH | TH_ACK;
+ p->tcph = &tcph;
+ p->flowflags = FLOW_PKT_TOSERVER;
+
+ StreamTcpCreateTestPacket(payload, 0x42, 1, 4); /*B*/
+ p->tcph->th_seq = htonl(16);
+ p->tcph->th_ack = htonl(31);
+ p->payload = payload;
+ p->payload_len = 1;
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx,&ssn, stream, p, &pq) == -1) {
+ SCFree(p);
+ return 0;
+ }
+
+ StreamTcpCreateTestPacket(payload, 0x44, 1, 4); /*D*/
+ p->tcph->th_seq = htonl(22);
+ p->tcph->th_ack = htonl(31);
+ p->payload = payload;
+ p->payload_len = 1;
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx,&ssn, stream, p, &pq) == -1) {
+ SCFree(p);
+ return 0;
+ }
+
+ StreamTcpCreateTestPacket(payload, 0x45, 2, 4); /*EE*/
+ p->tcph->th_seq = htonl(25);
+ p->tcph->th_ack = htonl(31);
+ p->payload = payload;
+ p->payload_len = 2;
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx,&ssn, stream, p, &pq) == -1) {
+ SCFree(p);
+ return 0;
+ }
+
+ StreamTcpCreateTestPacket(payload, 0x41, 2, 4); /*AA*/
+ p->tcph->th_seq = htonl(15);
+ p->tcph->th_ack = htonl(31);
+ p->payload = payload;
+ p->payload_len = 2;
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx,&ssn, stream, p, &pq) == -1) {
+ SCFree(p);
+ return 0;
+ }
+
+ StreamTcpCreateTestPacket(payload, 0x4a, 4, 4); /*JJJJ*/
+ p->tcph->th_seq = htonl(14);
+ p->tcph->th_ack = htonl(31);
+ p->payload = payload;
+ p->payload_len = 4;
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx,&ssn, stream, p, &pq) == -1) {
+ SCFree(p);
+ return 0;
+ }
+
+ SCLogDebug("sending segment with SEQ 21, len 3");
+ StreamTcpCreateTestPacket(payload, 0x4c, 3, 4); /*LLL*/
+ p->tcph->th_seq = htonl(21);
+ p->tcph->th_ack = htonl(31);
+ p->payload = payload;
+ p->payload_len = 3;
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx,&ssn, stream, p, &pq) == -1) {
+ SCFree(p);
+ return 0;
+ }
+
+ StreamTcpCreateTestPacket(payload, 0x4d, 3, 4); /*MMM*/
+ p->tcph->th_seq = htonl(24);
+ p->tcph->th_ack = htonl(31);
+ p->payload = payload;
+ p->payload_len = 3;
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx,&ssn, stream, p, &pq) == -1) {
+ SCFree(p);
+ return 0;
+ }
+
+ SCFree(p);
+ return 1;
+}
+
+/* \brief The function craft packets to test the overlapping, where
+ * new segment stats at the same seq no. as the list segment.
+ *
+ * \param stream The stream which will contain the reassembled segments and
+ * also tells the OS policy used for reassembling the segments.
+ */
+
+static int StreamTcpTestStartsAtSameListSegment(TcpStream *stream)
+{
+ TcpSession ssn;
+ Packet *p = PacketGetFromAlloc();
+ if (unlikely(p == NULL))
+ return 0;
+ Flow f;
+ uint8_t payload[4];
+ TCPHdr tcph;
+ TcpReassemblyThreadCtx *ra_ctx = StreamTcpReassembleInitThreadCtx(NULL);
+ PacketQueue pq;
+ memset(&pq,0,sizeof(PacketQueue));
+
+ /* prevent L7 from kicking in */
+ StreamMsgQueueSetMinChunkLen(FLOW_PKT_TOSERVER, 4096);
+ StreamMsgQueueSetMinChunkLen(FLOW_PKT_TOCLIENT, 4096);
+
+ memset(&ssn, 0, sizeof (TcpSession));
+ memset(&f, 0, sizeof (Flow));
+ memset(&tcph, 0, sizeof (TCPHdr));
+ ThreadVars tv;
+ memset(&tv, 0, sizeof (ThreadVars));
+ FLOW_INITIALIZE(&f);
+ f.protoctx = &ssn;
+ f.proto = IPPROTO_TCP;
+ p->src.family = AF_INET;
+ p->dst.family = AF_INET;
+ p->proto = IPPROTO_TCP;
+ p->flow = &f;
+ tcph.th_win = 5480;
+ tcph.th_flags = TH_PUSH | TH_ACK;
+ p->tcph = &tcph;
+ p->flowflags = FLOW_PKT_TOSERVER;
+
+ StreamTcpCreateTestPacket(payload, 0x43, 3, 4); /*CCC*/
+ p->tcph->th_seq = htonl(18);
+ p->tcph->th_ack = htonl(31);
+ p->payload = payload;
+ p->payload_len = 3;
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx,&ssn, stream, p, &pq) == -1) {
+ SCFree(p);
+ return 0;
+ }
+
+ StreamTcpCreateTestPacket(payload, 0x48, 2, 4); /*HH*/
+ p->tcph->th_seq = htonl(32);
+ p->tcph->th_ack = htonl(31);
+ p->payload = payload;
+ p->payload_len = 2;
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx,&ssn, stream, p, &pq) == -1) {
+ SCFree(p);
+ return 0;
+ }
+
+ StreamTcpCreateTestPacket(payload, 0x49, 1, 4); /*I*/
+ p->tcph->th_seq = htonl(34);
+ p->tcph->th_ack = htonl(31);
+ p->payload = payload;
+ p->payload_len = 1;
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx,&ssn, stream, p, &pq) == -1) {
+ SCFree(p);
+ return 0;
+ }
+
+ StreamTcpCreateTestPacket(payload, 0x4b, 3, 4); /*KKK*/
+ p->tcph->th_seq = htonl(18);
+ p->tcph->th_ack = htonl(31);
+ p->payload = payload;
+ p->payload_len = 3;
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx,&ssn, stream, p, &pq) == -1) {
+ SCFree(p);
+ return 0;
+ }
+
+ StreamTcpCreateTestPacket(payload, 0x4c, 4, 4); /*LLLL*/
+ p->tcph->th_seq = htonl(18);
+ p->tcph->th_ack = htonl(31);
+ p->payload = payload;
+ p->payload_len = 4;
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx,&ssn, stream, p, &pq) == -1) {
+ SCFree(p);
+ return 0;
+ }
+
+ StreamTcpCreateTestPacket(payload, 0x50, 1, 4); /*P*/
+ p->tcph->th_seq = htonl(32);
+ p->tcph->th_ack = htonl(31);
+ p->payload = payload;
+ p->payload_len = 1;
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx,&ssn, stream, p, &pq) == -1) {
+ SCFree(p);
+ return 0;
+ }
+
+ StreamTcpCreateTestPacket(payload, 0x51, 2, 4); /*QQ*/
+ p->tcph->th_seq = htonl(34);
+ p->tcph->th_ack = htonl(31);
+ p->payload = payload;
+ p->payload_len = 2;
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx,&ssn, stream, p, &pq) == -1) {
+ SCFree(p);
+ return 0;
+ }
+
+ SCFree(p);
+ return 1;
+}
+
+/* \brief The function craft packets to test the overlapping, where
+ * new segment stats after the list segment.
+ *
+ * \param stream The stream which will contain the reassembled segments and
+ * also tells the OS policy used for reassembling the segments.
+ */
+
+
+static int StreamTcpTestStartsAfterListSegment(TcpStream *stream)
+{
+ TcpSession ssn;
+ Packet *p = PacketGetFromAlloc();
+ if (unlikely(p == NULL))
+ return 0;
+ Flow f;
+ uint8_t payload[4];
+ TCPHdr tcph;
+ TcpReassemblyThreadCtx *ra_ctx = StreamTcpReassembleInitThreadCtx(NULL);
+ PacketQueue pq;
+ memset(&pq,0,sizeof(PacketQueue));
+
+ /* prevent L7 from kicking in */
+ StreamMsgQueueSetMinChunkLen(FLOW_PKT_TOSERVER, 4096);
+ StreamMsgQueueSetMinChunkLen(FLOW_PKT_TOCLIENT, 4096);
+
+ memset(&ssn, 0, sizeof (TcpSession));
+ memset(&f, 0, sizeof (Flow));
+ memset(&tcph, 0, sizeof (TCPHdr));
+ ThreadVars tv;
+ memset(&tv, 0, sizeof (ThreadVars));
+ FLOW_INITIALIZE(&f);
+ f.protoctx = &ssn;
+ f.proto = IPPROTO_TCP;
+ p->src.family = AF_INET;
+ p->dst.family = AF_INET;
+ p->proto = IPPROTO_TCP;
+ p->flow = &f;
+ tcph.th_win = 5480;
+ tcph.th_flags = TH_PUSH | TH_ACK;
+ p->tcph = &tcph;
+ p->flowflags = FLOW_PKT_TOSERVER;
+
+ StreamTcpCreateTestPacket(payload, 0x41, 2, 4); /*AA*/
+ p->tcph->th_seq = htonl(12);
+ p->tcph->th_ack = htonl(31);
+ p->payload = payload;
+ p->payload_len = 2;
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx,&ssn, stream, p, &pq) == -1) {
+ SCFree(p);
+ return 0;
+ }
+
+ StreamTcpCreateTestPacket(payload, 0x46, 3, 4); /*FFF*/
+ p->tcph->th_seq = htonl(27);
+ p->tcph->th_ack = htonl(31);
+ p->payload = payload;
+ p->payload_len = 3;
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx,&ssn, stream, p, &pq) == -1) {
+ SCFree(p);
+ return 0;
+ }
+
+ StreamTcpCreateTestPacket(payload, 0x47, 2, 4); /*GG*/
+ p->tcph->th_seq = htonl(30);
+ p->tcph->th_ack = htonl(31);
+ p->payload = payload;
+ p->payload_len = 2;
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx,&ssn, stream, p, &pq) == -1) {
+ SCFree(p);
+ return 0;
+ }
+
+ StreamTcpCreateTestPacket(payload, 0x4a, 2, 4); /*JJ*/
+ p->tcph->th_seq = htonl(13);
+ p->tcph->th_ack = htonl(31);
+ p->payload = payload;
+ p->payload_len = 2;
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx,&ssn, stream, p, &pq) == -1) {
+ SCFree(p);
+ return 0;
+ }
+
+ StreamTcpCreateTestPacket(payload, 0x4f, 1, 4); /*O*/
+ p->tcph->th_seq = htonl(31);
+ p->tcph->th_ack = htonl(31);
+ p->payload = payload;
+ p->payload_len = 1;
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx,&ssn, stream, p, &pq) == -1) {
+ SCFree(p);
+ return 0;
+ }
+
+ StreamTcpCreateTestPacket(payload, 0x4e, 1, 4); /*N*/
+ p->tcph->th_seq = htonl(28);
+ p->tcph->th_ack = htonl(31);
+ p->payload = payload;
+ p->payload_len = 1;
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx,&ssn, stream, p, &pq) == -1) {
+ SCFree(p);
+ return 0;
+ }
+
+ SCFree(p);
+ return 1;
+}
+
+/** \brief The Function to test the reassembly when new segment starts
+ * before the list segment and BSD policy is used to reassemble
+ * segments.
+ */
+
+static int StreamTcpReassembleTest01(void)
+{
+ TcpStream stream;
+ uint8_t stream_before_bsd[10] = {0x4a, 0x4a, 0x4a, 0x4a, 0x4c, 0x4c,
+ 0x4c, 0x4d, 0x4d, 0x4d};
+ memset(&stream, 0, sizeof (TcpStream));
+ stream.os_policy = OS_POLICY_BSD;
+
+ StreamTcpInitConfig(TRUE);
+
+ if (StreamTcpTestStartsBeforeListSegment(&stream) == 0) {
+ printf("failed in segments reassembly!!\n");
+ return 0;
+ }
+ if (StreamTcpCheckStreamContents(stream_before_bsd,sizeof(stream_before_bsd), &stream) == 0) {
+ printf("failed in stream matching!!\n");
+ return 0;
+ }
+
+ StreamTcpFreeConfig(TRUE);
+ return 1;
+}
+
+/** \brief The Function to test the reassembly when new segment starts
+ * at the same seq no. as the list segment and BSD policy is used
+ * to reassemble segments.
+ */
+
+static int StreamTcpReassembleTest02(void)
+{
+ TcpStream stream;
+ uint8_t stream_same_bsd[8] = {0x43, 0x43, 0x43, 0x4c, 0x48, 0x48,
+ 0x49, 0x51};
+ memset(&stream, 0, sizeof (TcpStream));
+ stream.os_policy = OS_POLICY_BSD;
+
+ StreamTcpInitConfig(TRUE);
+
+ if (StreamTcpTestStartsAtSameListSegment(&stream) == 0) {
+ printf("failed in segments reassembly!!\n");
+ return 0;
+ }
+ if (StreamTcpCheckStreamContents(stream_same_bsd, sizeof(stream_same_bsd), &stream) == 0) {
+ printf("failed in stream matching!!\n");
+ return 0;
+ }
+ StreamTcpFreeConfig(TRUE);
+ return 1;
+}
+
+/** \brief The Function to test the reassembly when new segment starts
+ * after the list segment and BSD policy is used to reassemble
+ * segments.
+ */
+
+static int StreamTcpReassembleTest03(void)
+{
+ TcpStream stream;
+ uint8_t stream_after_bsd[8] = {0x41, 0x41, 0x4a, 0x46, 0x46, 0x46,
+ 0x47, 0x47};
+ memset(&stream, 0, sizeof (TcpStream));
+ stream.os_policy = OS_POLICY_BSD;
+
+ StreamTcpInitConfig(TRUE);
+
+ if (StreamTcpTestStartsAfterListSegment(&stream) == 0) {
+ printf("failed in segments reassembly!!\n");
+ return 0;
+ }
+ if (StreamTcpCheckStreamContents(stream_after_bsd, sizeof(stream_after_bsd), &stream) == 0) {
+ printf("failed in stream matching!!\n");
+ return 0;
+ }
+ StreamTcpFreeConfig(TRUE);
+ return 1;
+}
+
+/** \brief The Function to test the reassembly engine for all the case
+ * before, same and after overlapping and BSD policy is used to
+ * reassemble segments.
+ */
+
+static int StreamTcpReassembleTest04(void)
+{
+ TcpStream stream;
+ uint8_t stream_bsd[25] = {0x30, 0x41, 0x41, 0x41, 0x4a, 0x4a, 0x42, 0x43,
+ 0x43, 0x43, 0x4c, 0x4c, 0x4c, 0x4d, 0x4d, 0x4d,
+ 0x46, 0x46, 0x46, 0x47, 0x47, 0x48, 0x48, 0x49, 0x51};
+ memset(&stream, 0, sizeof (TcpStream));
+ stream.os_policy = OS_POLICY_BSD;
+ StreamTcpInitConfig(TRUE);
+ if (StreamTcpReassembleStreamTest(&stream) == 0) {
+ printf("failed in segments reassembly: ");
+ return 0;
+ }
+ if (StreamTcpCheckStreamContents(stream_bsd, sizeof(stream_bsd), &stream) == 0) {
+ printf("failed in stream matching: ");
+ return 0;
+ }
+ StreamTcpFreeConfig(TRUE);
+ return 1;
+}
+
+/** \brief The Function to test the reassembly when new segment starts
+ * before the list segment and VISTA policy is used to reassemble
+ * segments.
+ */
+
+static int StreamTcpReassembleTest05(void)
+{
+ TcpStream stream;
+ uint8_t stream_before_vista[10] = {0x4a, 0x41, 0x42, 0x4a, 0x4c, 0x44,
+ 0x4c, 0x4d, 0x45, 0x45};
+ memset(&stream, 0, sizeof (TcpStream));
+ stream.os_policy = OS_POLICY_VISTA;
+ StreamTcpInitConfig(TRUE);
+ if (StreamTcpTestStartsBeforeListSegment(&stream) == 0) {
+ printf("failed in segments reassembly!!\n");
+ return 0;
+ }
+ if (StreamTcpCheckStreamContents(stream_before_vista, sizeof(stream_before_vista), &stream) == 0) {
+ printf("failed in stream matching!!\n");
+ return 0;
+ }
+ StreamTcpFreeConfig(TRUE);
+ return 1;
+}
+
+/** \brief The Function to test the reassembly when new segment starts
+ * at the same seq no. as the list segment and VISTA policy is used
+ * to reassemble segments.
+ */
+
+static int StreamTcpReassembleTest06(void)
+{
+ TcpStream stream;
+ uint8_t stream_same_vista[8] = {0x43, 0x43, 0x43, 0x4c, 0x48, 0x48,
+ 0x49, 0x51};
+ memset(&stream, 0, sizeof (TcpStream));
+ stream.os_policy = OS_POLICY_VISTA;
+
+ StreamTcpInitConfig(TRUE);
+
+ if (StreamTcpTestStartsAtSameListSegment(&stream) == 0) {
+ printf("failed in segments reassembly!!\n");
+ return 0;
+ }
+ if (StreamTcpCheckStreamContents(stream_same_vista, sizeof(stream_same_vista), &stream) == 0) {
+ printf("failed in stream matching!!\n");
+ return 0;
+ }
+ StreamTcpFreeConfig(TRUE);
+ return 1;
+}
+
+/** \brief The Function to test the reassembly when new segment starts
+ * after the list segment and BSD policy is used to reassemble
+ * segments.
+ */
+
+static int StreamTcpReassembleTest07(void)
+{
+ TcpStream stream;
+ uint8_t stream_after_vista[8] = {0x41, 0x41, 0x4a, 0x46, 0x46, 0x46,
+ 0x47, 0x47};
+ memset(&stream, 0, sizeof (TcpStream));
+ stream.os_policy = OS_POLICY_VISTA;
+
+ StreamTcpInitConfig(TRUE);
+
+ if (StreamTcpTestStartsAfterListSegment(&stream) == 0) {
+ printf("failed in segments reassembly!!\n");
+ return 0;
+ }
+ if (StreamTcpCheckStreamContents(stream_after_vista, sizeof(stream_after_vista), &stream) == 0) {
+ printf("failed in stream matching!!\n");
+ return 0;
+ }
+ StreamTcpFreeConfig(TRUE);
+ return 1;
+}
+
+/** \brief The Function to test the reassembly engine for all the case
+ * before, same and after overlapping and VISTA policy is used to
+ * reassemble segments.
+ */
+
+static int StreamTcpReassembleTest08(void)
+{
+ TcpStream stream;
+ uint8_t stream_vista[25] = {0x30, 0x41, 0x41, 0x41, 0x4a, 0x42, 0x42, 0x43,
+ 0x43, 0x43, 0x4c, 0x44, 0x4c, 0x4d, 0x45, 0x45,
+ 0x46, 0x46, 0x46, 0x47, 0x47, 0x48, 0x48, 0x49, 0x51};
+ memset(&stream, 0, sizeof (TcpStream));
+ stream.os_policy = OS_POLICY_VISTA;
+ StreamTcpInitConfig(TRUE);
+ if (StreamTcpReassembleStreamTest(&stream) == 0) {
+ printf("failed in segments reassembly!!\n");
+ return 0;
+ }
+ if (StreamTcpCheckStreamContents(stream_vista, sizeof(stream_vista), &stream) == 0) {
+ printf("failed in stream matching!!\n");
+ return 0;
+ }
+ StreamTcpFreeConfig(TRUE);
+ return 1;
+}
+
+/** \brief The Function to test the reassembly when new segment starts
+ * before the list segment and LINUX policy is used to reassemble
+ * segments.
+ */
+
+static int StreamTcpReassembleTest09(void)
+{
+ TcpStream stream;
+ uint8_t stream_before_linux[10] = {0x4a, 0x4a, 0x4a, 0x4a, 0x4c, 0x4c,
+ 0x4c, 0x4d, 0x4d, 0x4d};
+ memset(&stream, 0, sizeof (TcpStream));
+ stream.os_policy = OS_POLICY_LINUX;
+ StreamTcpInitConfig(TRUE);
+ if (StreamTcpTestStartsBeforeListSegment(&stream) == 0) {
+ printf("failed in segments reassembly!!\n");
+ return 0;
+ }
+ if (StreamTcpCheckStreamContents(stream_before_linux, sizeof(stream_before_linux), &stream) == 0) {
+ printf("failed in stream matching!!\n");
+ return 0;
+ }
+ StreamTcpFreeConfig(TRUE);
+ return 1;
+}
+
+/** \brief The Function to test the reassembly when new segment starts
+ * at the same seq no. as the list segment and LINUX policy is used
+ * to reassemble segments.
+ */
+
+static int StreamTcpReassembleTest10(void)
+{
+ TcpStream stream;
+ uint8_t stream_same_linux[8] = {0x4c, 0x4c, 0x4c, 0x4c, 0x48, 0x48,
+ 0x51, 0x51};
+ memset(&stream, 0, sizeof (TcpStream));
+ stream.os_policy = OS_POLICY_LINUX;
+ StreamTcpInitConfig(TRUE);
+ if (StreamTcpTestStartsAtSameListSegment(&stream) == 0) {
+ printf("failed in segments reassembly!!\n");
+ return 0;
+ }
+ if (StreamTcpCheckStreamContents(stream_same_linux, sizeof(stream_same_linux), &stream) == 0) {
+ printf("failed in stream matching!!\n");
+ return 0;
+ }
+ StreamTcpFreeConfig(TRUE);
+ return 1;
+}
+
+/** \brief The Function to test the reassembly when new segment starts
+ * after the list segment and LINUX policy is used to reassemble
+ * segments.
+ */
+
+static int StreamTcpReassembleTest11(void)
+{
+ TcpStream stream;
+ uint8_t stream_after_linux[8] = {0x41, 0x41, 0x4a, 0x46, 0x46, 0x46,
+ 0x47, 0x47};
+ memset(&stream, 0, sizeof (TcpStream));
+ stream.os_policy = OS_POLICY_LINUX;
+ StreamTcpInitConfig(TRUE);
+ if (StreamTcpTestStartsAfterListSegment(&stream) == 0) {
+ printf("failed in segments reassembly!!\n");
+ return 0;
+ }
+ if (StreamTcpCheckStreamContents(stream_after_linux, sizeof(stream_after_linux), &stream) == 0) {
+ printf("failed in stream matching!!\n");
+ return 0;
+ }
+ StreamTcpFreeConfig(TRUE);
+ return 1;
+}
+
+/** \brief The Function to test the reassembly engine for all the case
+ * before, same and after overlapping and LINUX policy is used to
+ * reassemble segments.
+ */
+
+static int StreamTcpReassembleTest12(void)
+{
+ TcpStream stream;
+ uint8_t stream_linux[25] = {0x30, 0x41, 0x41, 0x41, 0x4a, 0x4a, 0x42, 0x43,
+ 0x43, 0x43, 0x4c, 0x4c, 0x4c, 0x4d, 0x4d, 0x4d,
+ 0x46, 0x46, 0x46, 0x47, 0x47, 0x48, 0x48, 0x51, 0x51};
+ memset(&stream, 0, sizeof (TcpStream));
+ stream.os_policy = OS_POLICY_LINUX;
+ StreamTcpInitConfig(TRUE);
+ if (StreamTcpReassembleStreamTest(&stream) == 0) {
+ printf("failed in segments reassembly!!\n");
+ return 0;
+ }
+ if (StreamTcpCheckStreamContents(stream_linux, sizeof(stream_linux), &stream) == 0) {
+ printf("failed in stream matching!!\n");
+ return 0;
+ }
+ StreamTcpFreeConfig(TRUE);
+ return 1;
+}
+
+/** \brief The Function to test the reassembly when new segment starts
+ * before the list segment and OLD_LINUX policy is used to reassemble
+ * segments.
+ */
+
+static int StreamTcpReassembleTest13(void)
+{
+ TcpStream stream;
+ uint8_t stream_before_old_linux[10] = {0x4a, 0x4a, 0x4a, 0x4a, 0x4c, 0x4c,
+ 0x4c, 0x4d, 0x4d, 0x4d};
+ memset(&stream, 0, sizeof (TcpStream));
+ stream.os_policy = OS_POLICY_OLD_LINUX;
+ StreamTcpInitConfig(TRUE);
+ if (StreamTcpTestStartsBeforeListSegment(&stream) == 0) {
+ printf("failed in segments reassembly!!\n");
+ return 0;
+ }
+ if (StreamTcpCheckStreamContents(stream_before_old_linux, sizeof(stream_before_old_linux), &stream) == 0) {
+ printf("failed in stream matching!!\n");
+ return 0;
+ }
+ StreamTcpFreeConfig(TRUE);
+ return 1;
+}
+
+/** \brief The Function to test the reassembly when new segment starts
+ * at the same seq no. as the list segment and OLD_LINUX policy is
+ * used to reassemble segments.
+ */
+
+static int StreamTcpReassembleTest14(void)
+{
+ TcpStream stream;
+ uint8_t stream_same_old_linux[8] = {0x4c, 0x4c, 0x4c, 0x4c, 0x48, 0x48,
+ 0x51, 0x51};
+ memset(&stream, 0, sizeof (TcpStream));
+ stream.os_policy = OS_POLICY_OLD_LINUX;
+ StreamTcpInitConfig(TRUE);
+ if (StreamTcpTestStartsAtSameListSegment(&stream) == 0) {
+ printf("failed in segments reassembly!!\n");
+ return 0;
+ }
+ if (StreamTcpCheckStreamContents(stream_same_old_linux, sizeof(stream_same_old_linux), &stream) == 0) {
+ printf("failed in stream matching!!\n");
+ return 0;
+ }
+ StreamTcpFreeConfig(TRUE);
+ return 1;
+}
+
+/** \brief The Function to test the reassembly when new segment starts
+ * after the list segment and OLD_LINUX policy is used to reassemble
+ * segments.
+ */
+
+static int StreamTcpReassembleTest15(void)
+{
+ TcpStream stream;
+ uint8_t stream_after_old_linux[8] = {0x41, 0x41, 0x4a, 0x46, 0x46, 0x46,
+ 0x47, 0x47};
+ memset(&stream, 0, sizeof (TcpStream));
+ stream.os_policy = OS_POLICY_OLD_LINUX;
+ StreamTcpInitConfig(TRUE);
+ if (StreamTcpTestStartsAfterListSegment(&stream) == 0) {
+ printf("failed in segments reassembly!!\n");
+ return 0;
+ }
+ if (StreamTcpCheckStreamContents(stream_after_old_linux, sizeof(stream_after_old_linux), &stream) == 0) {
+ printf("failed in stream matching!!\n");
+ return 0;
+ }
+ StreamTcpFreeConfig(TRUE);
+ return 1;
+}
+
+/** \brief The Function to test the reassembly engine for all the case
+ * before, same and after overlapping and OLD_LINUX policy is used to
+ * reassemble segments.
+ */
+
+static int StreamTcpReassembleTest16(void)
+{
+ TcpStream stream;
+ uint8_t stream_old_linux[25] = {0x30, 0x41, 0x41, 0x41, 0x4a, 0x4a, 0x42, 0x4b,
+ 0x4b, 0x4b, 0x4c, 0x4c, 0x4c, 0x4d, 0x4d, 0x4d,
+ 0x46, 0x46, 0x46, 0x47, 0x47, 0x48, 0x48, 0x51, 0x51};
+ memset(&stream, 0, sizeof (TcpStream));
+ stream.os_policy = OS_POLICY_OLD_LINUX;
+ StreamTcpInitConfig(TRUE);
+ if (StreamTcpReassembleStreamTest(&stream) == 0) {
+ printf("failed in segments reassembly!!\n");
+ return 0;
+ }
+ if (StreamTcpCheckStreamContents(stream_old_linux, sizeof(stream_old_linux), &stream) == 0) {
+ printf("failed in stream matching!!\n");
+ return 0;
+ }
+ StreamTcpFreeConfig(TRUE);
+ return 1;
+}
+
+/** \brief The Function to test the reassembly when new segment starts
+ * before the list segment and SOLARIS policy is used to reassemble
+ * segments.
+ */
+
+static int StreamTcpReassembleTest17(void)
+{
+ TcpStream stream;
+ uint8_t stream_before_solaris[10] = {0x4a, 0x4a, 0x4a, 0x4a, 0x4c, 0x4c,
+ 0x4c, 0x4d, 0x4d, 0x4d};
+ memset(&stream, 0, sizeof (TcpStream));
+ stream.os_policy = OS_POLICY_SOLARIS;
+ StreamTcpInitConfig(TRUE);
+ if (StreamTcpTestStartsBeforeListSegment(&stream) == 0) {
+ printf("failed in segments reassembly!!\n");
+ return 0;
+ }
+ if (StreamTcpCheckStreamContents(stream_before_solaris, sizeof(stream_before_solaris), &stream) == 0) {
+ printf("failed in stream matching!!\n");
+ return 0;
+ }
+ StreamTcpFreeConfig(TRUE);
+ return 1;
+}
+
+/** \brief The Function to test the reassembly when new segment starts
+ * at the same seq no. as the list segment and SOLARIS policy is used
+ * to reassemble segments.
+ */
+
+static int StreamTcpReassembleTest18(void)
+{
+ TcpStream stream;
+ uint8_t stream_same_solaris[8] = {0x4c, 0x4c, 0x4c, 0x4c, 0x48, 0x48,
+ 0x51, 0x51};
+ memset(&stream, 0, sizeof (TcpStream));
+ stream.os_policy = OS_POLICY_SOLARIS;
+ StreamTcpInitConfig(TRUE);
+ if (StreamTcpTestStartsAtSameListSegment(&stream) == 0) {
+ printf("failed in segments reassembly!!\n");
+ return 0;
+ }
+ if (StreamTcpCheckStreamContents(stream_same_solaris, sizeof(stream_same_solaris), &stream) == 0) {
+ printf("failed in stream matching!!\n");
+ return 0;
+ }
+ StreamTcpFreeConfig(TRUE);
+ return 1;
+}
+
+/** \brief The Function to test the reassembly when new segment starts
+ * after the list segment and SOLARIS policy is used to reassemble
+ * segments.
+ */
+
+static int StreamTcpReassembleTest19(void)
+{
+ TcpStream stream;
+ uint8_t stream_after_solaris[8] = {0x41, 0x4a, 0x4a, 0x46, 0x46, 0x46,
+ 0x47, 0x47};
+ memset(&stream, 0, sizeof (TcpStream));
+ stream.os_policy = OS_POLICY_SOLARIS;
+ StreamTcpInitConfig(TRUE);
+ if (StreamTcpTestStartsAfterListSegment(&stream) == 0) {
+ printf("failed in segments reassembly!!\n");
+ StreamTcpFreeConfig(TRUE);
+ return 0;
+ }
+ if (StreamTcpCheckStreamContents(stream_after_solaris, sizeof(stream_after_solaris), &stream) == 0) {
+ printf("failed in stream matching!!\n");
+ StreamTcpFreeConfig(TRUE);
+ return 0;
+ }
+ StreamTcpFreeConfig(TRUE);
+ return 1;
+}
+
+/** \brief The Function to test the reassembly engine for all the case
+ * before, same and after overlapping and SOLARIS policy is used to
+ * reassemble segments.
+ */
+
+static int StreamTcpReassembleTest20(void)
+{
+ TcpStream stream;
+ uint8_t stream_solaris[25] = {0x30, 0x41, 0x4a, 0x4a, 0x4a, 0x42, 0x42, 0x4b,
+ 0x4b, 0x4b, 0x4c, 0x4c, 0x4c, 0x4d, 0x4d, 0x4d,
+ 0x46, 0x46, 0x46, 0x47, 0x47, 0x48, 0x48, 0x51, 0x51};
+ memset(&stream, 0, sizeof (TcpStream));
+ stream.os_policy = OS_POLICY_SOLARIS;
+ StreamTcpInitConfig(TRUE);
+ if (StreamTcpReassembleStreamTest(&stream) == 0) {
+ printf("failed in segments reassembly!!\n");
+ StreamTcpFreeConfig(TRUE);
+ return 0;
+ }
+ if (StreamTcpCheckStreamContents(stream_solaris, sizeof(stream_solaris), &stream) == 0) {
+ printf("failed in stream matching!!\n");
+ StreamTcpFreeConfig(TRUE);
+ return 0;
+ }
+ StreamTcpFreeConfig(TRUE);
+ return 1;
+}
+
+/** \brief The Function to test the reassembly when new segment starts
+ * before the list segment and LAST policy is used to reassemble
+ * segments.
+ */
+
+static int StreamTcpReassembleTest21(void)
+{
+ TcpStream stream;
+ uint8_t stream_before_last[10] = {0x4a, 0x4a, 0x4a, 0x4a, 0x4c, 0x4c,
+ 0x4c, 0x4d, 0x4d, 0x4d};
+ memset(&stream, 0, sizeof (TcpStream));
+ stream.os_policy = OS_POLICY_LAST;
+ StreamTcpInitConfig(TRUE);
+ if (StreamTcpTestStartsBeforeListSegment(&stream) == 0) {
+ printf("failed in segments reassembly!!\n");
+ return 0;
+ }
+ if (StreamTcpCheckStreamContents(stream_before_last, sizeof(stream_before_last), &stream) == 0) {
+ printf("failed in stream matching!!\n");
+ return 0;
+ }
+ StreamTcpFreeConfig(TRUE);
+ return 1;
+}
+
+/** \brief The Function to test the reassembly when new segment starts
+ * at the same seq no. as the list segment and LAST policy is used
+ * to reassemble segments.
+ */
+
+static int StreamTcpReassembleTest22(void)
+{
+ TcpStream stream;
+ uint8_t stream_same_last[8] = {0x4c, 0x4c, 0x4c, 0x4c, 0x50, 0x48,
+ 0x51, 0x51};
+ memset(&stream, 0, sizeof (TcpStream));
+ stream.os_policy = OS_POLICY_LAST;
+ StreamTcpInitConfig(TRUE);
+ if (StreamTcpTestStartsAtSameListSegment(&stream) == 0) {
+ printf("failed in segments reassembly!!\n");
+ return 0;
+ }
+ if (StreamTcpCheckStreamContents(stream_same_last, sizeof(stream_same_last), &stream) == 0) {
+ printf("failed in stream matching!!\n");
+ return 0;
+ }
+ StreamTcpFreeConfig(TRUE);
+ return 1;
+}
+
+/** \brief The Function to test the reassembly when new segment starts
+ * after the list segment and LAST policy is used to reassemble
+ * segments.
+ */
+static int StreamTcpReassembleTest23(void)
+{
+ TcpStream stream;
+ uint8_t stream_after_last[8] = {0x41, 0x4a, 0x4a, 0x46, 0x4e, 0x46, 0x47, 0x4f};
+ memset(&stream, 0, sizeof (TcpStream));
+
+ stream.os_policy = OS_POLICY_LAST;
+ StreamTcpInitConfig(TRUE);
+
+ if (StreamTcpTestStartsAfterListSegment(&stream) == 0) {
+ printf("failed in segments reassembly!!\n");
+ return 0;
+ }
+ if (StreamTcpCheckStreamContents(stream_after_last, sizeof(stream_after_last), &stream) == 0) {
+ printf("failed in stream matching!!\n");
+ return 0;
+ }
+ StreamTcpFreeConfig(TRUE);
+ return 1;
+}
+
+/** \brief The Function to test the reassembly engine for all the case
+ * before, same and after overlapping and LAST policy is used to
+ * reassemble segments.
+ */
+
+static int StreamTcpReassembleTest24(void)
+{
+ int ret = 0;
+ TcpStream stream;
+ uint8_t stream_last[25] = {0x30, 0x41, 0x4a, 0x4a, 0x4a, 0x4a, 0x42, 0x4b,
+ 0x4b, 0x4b, 0x4c, 0x4c, 0x4c, 0x4d, 0x4d, 0x4d,
+ 0x46, 0x4e, 0x46, 0x47, 0x4f, 0x50, 0x48, 0x51, 0x51};
+ memset(&stream, 0, sizeof (TcpStream));
+
+ stream.os_policy = OS_POLICY_LAST;
+ StreamTcpInitConfig(TRUE);
+
+ if (StreamTcpReassembleStreamTest(&stream) == 0) {
+ printf("failed in segments reassembly: ");
+ goto end;
+ }
+ if (StreamTcpCheckStreamContents(stream_last, sizeof(stream_last), &stream) == 0) {
+ printf("failed in stream matching: ");
+ goto end;
+ }
+
+ ret = 1;
+end:
+ StreamTcpFreeConfig(TRUE);
+ return ret;
+}
+
+/** \brief The Function to test the missed packets handling with given payload,
+ * which is used to test the reassembly of the engine.
+ *
+ * \param stream Stream which contain the packets
+ * \param seq Sequence number of the packet
+ * \param ack Acknowledgment number of the packet
+ * \param payload The variable used to store the payload contents of the
+ * current packet.
+ * \param len The length of the payload for current packet.
+ * \param th_flag The TCP flags
+ * \param flowflags The packet flow direction
+ * \param state The TCP session state
+ *
+ * \retval On success it returns 0 and on failure it return -1.
+ */
+
+static int StreamTcpTestMissedPacket (TcpReassemblyThreadCtx *ra_ctx,
+ TcpSession *ssn, uint32_t seq, uint32_t ack, uint8_t *payload,
+ uint16_t len, uint8_t th_flags, uint8_t flowflags, uint8_t state)
+{
+ Packet *p = PacketGetFromAlloc();
+ if (unlikely(p == NULL))
+ return -1;
+ Flow f;
+ TCPHdr tcph;
+ Port sp;
+ Port dp;
+ struct in_addr in;
+ ThreadVars tv;
+ PacketQueue pq;
+
+ memset(&pq,0,sizeof(PacketQueue));
+ memset(&f, 0, sizeof (Flow));
+ memset(&tcph, 0, sizeof (TCPHdr));
+ memset(&tv, 0, sizeof (ThreadVars));
+
+ sp = 200;
+ dp = 220;
+
+ FLOW_INITIALIZE(&f);
+ if (inet_pton(AF_INET, "1.2.3.4", &in) != 1) {
+ SCFree(p);
+ return -1;
+ }
+ f.src.addr_data32[0] = in.s_addr;
+ if (inet_pton(AF_INET, "1.2.3.5", &in) != 1) {
+ SCFree(p);
+ return -1;
+ }
+ f.dst.addr_data32[0] = in.s_addr;
+ f.flags |= FLOW_IPV4;
+ f.sp = sp;
+ f.dp = dp;
+ f.protoctx = ssn;
+ f.proto = IPPROTO_TCP;
+ p->flow = &f;
+
+ tcph.th_win = htons(5480);
+ tcph.th_seq = htonl(seq);
+ tcph.th_ack = htonl(ack);
+ tcph.th_flags = th_flags;
+ p->tcph = &tcph;
+ p->flowflags = flowflags;
+
+ p->payload = payload;
+ p->payload_len = len;
+ ssn->state = state;
+
+ TcpStream *s = NULL;
+ if (flowflags & FLOW_PKT_TOSERVER) {
+ s = &ssn->server;
+ } else {
+ s = &ssn->client;
+ }
+
+ SCMutexLock(&f.m);
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx, ssn, s, p, &pq) == -1) {
+ SCMutexUnlock(&f.m);
+ SCFree(p);
+ return -1;
+ }
+
+ SCMutexUnlock(&f.m);
+ SCFree(p);
+ return 0;
+}
+
+/**
+ * \test Test the handling of packets missed by both IDS and the end host.
+ * The packet is missed in the starting of the stream.
+ *
+ * \retval On success it returns 1 and on failure 0.
+ */
+
+static int StreamTcpReassembleTest25 (void)
+{
+ int ret = 0;
+ uint8_t payload[4];
+ uint32_t seq;
+ uint32_t ack;
+ TcpSession ssn;
+ uint8_t th_flag;
+ uint8_t flowflags;
+ uint8_t check_contents[7] = {0x41, 0x41, 0x41, 0x42, 0x42, 0x43, 0x43};
+
+ TcpReassemblyThreadCtx *ra_ctx = StreamTcpReassembleInitThreadCtx(NULL);
+ memset(&ssn, 0, sizeof (TcpSession));
+
+ flowflags = FLOW_PKT_TOSERVER;
+ th_flag = TH_ACK|TH_PUSH;
+ ack = 20;
+ StreamTcpInitConfig(TRUE);
+
+ StreamTcpCreateTestPacket(payload, 0x42, 2, 4); /*BB*/
+ seq = 10;
+ if (StreamTcpTestMissedPacket (ra_ctx, &ssn, seq, ack, payload, 2, th_flag, flowflags, TCP_ESTABLISHED) == -1){
+ printf("failed in segments reassembly: ");
+ goto end;
+ }
+
+ StreamTcpCreateTestPacket(payload, 0x43, 2, 4); /*CC*/
+ seq = 12;
+ if (StreamTcpTestMissedPacket (ra_ctx, &ssn, seq, ack, payload, 2, th_flag, flowflags, TCP_ESTABLISHED) == -1){
+ printf("failed in segments reassembly: ");
+ goto end;
+ }
+ ssn.server.next_seq = 14;
+ StreamTcpCreateTestPacket(payload, 0x41, 3, 4); /*AAA*/
+ seq = 7;
+ if (StreamTcpTestMissedPacket (ra_ctx, &ssn, seq, ack, payload, 3, th_flag, flowflags, TCP_ESTABLISHED) == -1) {
+ printf("failed in segments reassembly: ");
+ goto end;
+ }
+
+ if (StreamTcpCheckStreamContents(check_contents, sizeof(check_contents), &ssn.server) == 0) {
+ printf("failed in stream matching: ");
+ goto end;
+ }
+
+ ret = 1;
+end:
+ StreamTcpReassembleFreeThreadCtx(ra_ctx);
+ StreamTcpFreeConfig(TRUE);
+ return ret;
+}
+
+/**
+ * \test Test the handling of packets missed by both IDS and the end host.
+ * The packet is missed in the middle of the stream.
+ *
+ * \retval On success it returns 1 and on failure 0.
+ */
+
+static int StreamTcpReassembleTest26 (void)
+{
+ int ret = 0;
+ uint8_t payload[4];
+ uint32_t seq;
+ uint32_t ack;
+ TcpSession ssn;
+ uint8_t th_flag;
+ uint8_t flowflags;
+ uint8_t check_contents[7] = {0x41, 0x41, 0x41, 0x42, 0x42, 0x43, 0x43};
+ memset(&ssn, 0, sizeof (TcpSession));
+ flowflags = FLOW_PKT_TOSERVER;
+ th_flag = TH_ACK|TH_PUSH;
+ ack = 20;
+ StreamTcpInitConfig(TRUE);
+
+ TcpReassemblyThreadCtx *ra_ctx = StreamTcpReassembleInitThreadCtx(NULL);
+
+ StreamTcpCreateTestPacket(payload, 0x41, 3, 4); /*AAA*/
+ seq = 10;
+ if (StreamTcpTestMissedPacket (ra_ctx, &ssn, seq, ack, payload, 3, th_flag, flowflags, TCP_ESTABLISHED) == -1){
+ printf("failed in segments reassembly: ");
+ goto end;
+ }
+
+ StreamTcpCreateTestPacket(payload, 0x43, 2, 4); /*CC*/
+ seq = 15;
+ if (StreamTcpTestMissedPacket (ra_ctx, &ssn, seq, ack, payload, 2, th_flag, flowflags, TCP_ESTABLISHED) == -1){
+ printf("failed in segments reassembly: ");
+ goto end;
+ }
+
+ StreamTcpCreateTestPacket(payload, 0x42, 2, 4); /*BB*/
+ seq = 13;
+ if (StreamTcpTestMissedPacket (ra_ctx, &ssn, seq, ack, payload, 2, th_flag, flowflags, TCP_ESTABLISHED) == -1) {
+ printf("failed in segments reassembly: ");
+ goto end;
+ }
+
+ if (StreamTcpCheckStreamContents(check_contents, sizeof(check_contents), &ssn.server) == 0) {
+ printf("failed in stream matching: ");
+ goto end;
+ }
+
+ ret = 1;
+end:
+ StreamTcpReassembleFreeThreadCtx(ra_ctx);
+ StreamTcpFreeConfig(TRUE);
+ return ret;
+}
+
+/**
+ * \test Test the handling of packets missed by both IDS and the end host.
+ * The packet is missed in the end of the stream.
+ *
+ * \retval On success it returns 1 and on failure 0.
+ */
+
+static int StreamTcpReassembleTest27 (void)
+{
+ int ret = 0;
+ uint8_t payload[4];
+ uint32_t seq;
+ uint32_t ack;
+ TcpSession ssn;
+ uint8_t th_flag;
+ uint8_t flowflags;
+ uint8_t check_contents[7] = {0x41, 0x41, 0x41, 0x42, 0x42, 0x43, 0x43};
+ memset(&ssn, 0, sizeof (TcpSession));
+ flowflags = FLOW_PKT_TOSERVER;
+ th_flag = TH_ACK|TH_PUSH;
+ ack = 20;
+ StreamTcpInitConfig(TRUE);
+
+ TcpReassemblyThreadCtx *ra_ctx = StreamTcpReassembleInitThreadCtx(NULL);
+
+ StreamTcpCreateTestPacket(payload, 0x41, 3, 4); /*AAA*/
+ seq = 10;
+ if (StreamTcpTestMissedPacket (ra_ctx, &ssn, seq, ack, payload, 3, th_flag, flowflags, TCP_ESTABLISHED) == -1){
+ printf("failed in segments reassembly: ");
+ goto end;
+ }
+
+ StreamTcpCreateTestPacket(payload, 0x42, 2, 4); /*BB*/
+ seq = 13;
+ if (StreamTcpTestMissedPacket (ra_ctx, &ssn, seq, ack, payload, 2, th_flag, flowflags, TCP_ESTABLISHED) == -1){
+ printf("failed in segments reassembly: ");
+ goto end;
+ }
+
+ StreamTcpCreateTestPacket(payload, 0x43, 2, 4); /*CC*/
+ seq = 15;
+ if (StreamTcpTestMissedPacket (ra_ctx, &ssn, seq, ack, payload, 2, th_flag, flowflags, TCP_ESTABLISHED) == -1) {
+ printf("failed in segments reassembly: ");
+ goto end;
+ }
+
+ if (StreamTcpCheckStreamContents(check_contents, sizeof(check_contents), &ssn.server) == 0) {
+ printf("failed in stream matching: ");
+ goto end;
+ }
+
+ ret = 1;
+end:
+ StreamTcpReassembleFreeThreadCtx(ra_ctx);
+ StreamTcpFreeConfig(TRUE);
+ return ret;
+}
+
+/**
+ * \test Test the handling of packets missed by IDS, but the end host has
+ * received it and send the acknowledgment of it. The packet is missed
+ * in the starting of the stream.
+ *
+ * \retval On success it returns 1 and on failure 0.
+ */
+
+static int StreamTcpReassembleTest28 (void)
+{
+ int ret = 0;
+ uint8_t payload[4];
+ uint32_t seq;
+ uint32_t ack;
+ uint8_t th_flag;
+ uint8_t th_flags;
+ uint8_t flowflags;
+ uint8_t check_contents[5] = {0x41, 0x41, 0x42, 0x42, 0x42};
+ TcpSession ssn;
+ memset(&ssn, 0, sizeof (TcpSession));
+ TcpReassemblyThreadCtx *ra_ctx = StreamTcpReassembleInitThreadCtx(NULL);
+
+ StreamTcpInitConfig(TRUE);
+ StreamMsgQueueSetMinChunkLen(FLOW_PKT_TOSERVER, 4096);
+ StreamMsgQueueSetMinChunkLen(FLOW_PKT_TOCLIENT, 4096);
+
+ flowflags = FLOW_PKT_TOSERVER;
+ th_flag = TH_ACK|TH_PUSH;
+ th_flags = TH_ACK;
+
+ ssn.server.last_ack = 22;
+ ssn.server.ra_raw_base_seq = ssn.server.ra_app_base_seq = 6;
+ ssn.server.isn = 6;
+
+ StreamTcpCreateTestPacket(payload, 0x41, 2, 4); /*AA*/
+ seq = 10;
+ ack = 20;
+ if (StreamTcpTestMissedPacket (ra_ctx, &ssn, seq, ack, payload, 2, th_flag, flowflags, TCP_ESTABLISHED) == -1) {
+ printf("failed in segments reassembly (1): ");
+ goto end;
+ }
+
+ flowflags = FLOW_PKT_TOCLIENT;
+ StreamTcpCreateTestPacket(payload, 0x00, 0, 4);
+ seq = 20;
+ ack = 12;
+ if (StreamTcpTestMissedPacket (ra_ctx, &ssn, seq, ack, payload, 0, th_flags, flowflags, TCP_ESTABLISHED) == -1) {
+ printf("failed in segments reassembly (2): ");
+ goto end;
+ }
+
+ flowflags = FLOW_PKT_TOSERVER;
+ StreamTcpCreateTestPacket(payload, 0x42, 3, 4); /*BBB*/
+ seq = 12;
+ ack = 20;
+ if (StreamTcpTestMissedPacket (ra_ctx, &ssn, seq, ack, payload, 3, th_flag, flowflags, TCP_ESTABLISHED) == -1) {
+ printf("failed in segments reassembly (4): ");
+ goto end;
+ }
+
+ flowflags = FLOW_PKT_TOCLIENT;
+ StreamTcpCreateTestPacket(payload, 0x00, 0, 4);
+ seq = 20;
+ ack = 15;
+ if (StreamTcpTestMissedPacket (ra_ctx, &ssn, seq, ack, payload, 0, th_flags, flowflags, TCP_TIME_WAIT) == -1) {
+ printf("failed in segments reassembly (5): ");
+ goto end;
+ }
+
+ if (StreamTcpCheckChunks(&ssn, check_contents) == 0) {
+ printf("failed in stream matching (6): ");
+ goto end;
+ }
+
+ ret = 1;
+end:
+ StreamTcpReassembleFreeThreadCtx(ra_ctx);
+ StreamTcpFreeConfig(TRUE);
+ return ret;
+}
+
+/**
+ * \test Test the handling of packets missed by IDS, but the end host has
+ * received it and send the acknowledgment of it. The packet is missed
+ * in the middle of the stream.
+ *
+ * \retval On success it returns 1 and on failure 0.
+ */
+
+static int StreamTcpReassembleTest29 (void)
+{
+ int ret = 0;
+ uint8_t payload[4];
+ uint32_t seq;
+ uint32_t ack;
+ uint8_t th_flag;
+ uint8_t th_flags;
+ uint8_t flowflags;
+ uint8_t check_contents[5] = {0x41, 0x41, 0x42, 0x42, 0x42};
+ TcpReassemblyThreadCtx *ra_ctx = StreamTcpReassembleInitThreadCtx(NULL);
+ TcpSession ssn;
+ memset(&ssn, 0, sizeof (TcpSession));
+
+ flowflags = FLOW_PKT_TOSERVER;
+ th_flag = TH_ACK|TH_PUSH;
+ th_flags = TH_ACK;
+
+ ssn.server.last_ack = 22;
+ ssn.server.ra_raw_base_seq = 9;
+ ssn.server.isn = 9;
+ StreamTcpInitConfig(TRUE);
+
+ StreamTcpCreateTestPacket(payload, 0x41, 2, 4); /*AA*/
+ seq = 10;
+ ack = 20;
+ if (StreamTcpTestMissedPacket (ra_ctx, &ssn, seq, ack, payload, 2, th_flag, flowflags, TCP_ESTABLISHED) == -1){
+ printf("failed in segments reassembly: ");
+ goto end;
+ }
+
+ flowflags = FLOW_PKT_TOCLIENT;
+ StreamTcpCreateTestPacket(payload, 0x00, 0, 4);
+ seq = 20;
+ ack = 15;
+ if (StreamTcpTestMissedPacket (ra_ctx, &ssn, seq, ack, payload, 0, th_flags, flowflags, TCP_ESTABLISHED) == -1){
+ printf("failed in segments reassembly: ");
+ goto end;
+ }
+
+ flowflags = FLOW_PKT_TOSERVER;
+ StreamTcpCreateTestPacket(payload, 0x42, 3, 4); /*BBB*/
+ seq = 15;
+ ack = 20;
+ if (StreamTcpTestMissedPacket (ra_ctx, &ssn, seq, ack, payload, 3, th_flag, flowflags, TCP_ESTABLISHED) == -1) {
+ printf("failed in segments reassembly: ");
+ goto end;
+ }
+
+ flowflags = FLOW_PKT_TOCLIENT;
+ StreamTcpCreateTestPacket(payload, 0x00, 0, 4);
+ seq = 20;
+ ack = 18;
+ if (StreamTcpTestMissedPacket (ra_ctx, &ssn, seq, ack, payload, 0, th_flags, flowflags, TCP_TIME_WAIT) == -1) {
+ printf("failed in segments reassembly: ");
+ goto end;
+ }
+
+ if (StreamTcpCheckChunks(&ssn, check_contents) == 0) {
+ printf("failed in stream matching: ");
+ goto end;
+ }
+
+ ret = 1;
+end:
+ StreamTcpReassembleFreeThreadCtx(ra_ctx);
+ StreamTcpFreeConfig(TRUE);
+ return ret;
+}
+
+/**
+ * \test Test the handling of packets missed by IDS, but the end host has
+ * received it and send the acknowledgment of it. The packet is missed
+ * at the end of the stream.
+ *
+ * \retval On success it returns 1 and on failure 0.
+ */
+
+static int StreamTcpReassembleTest30 (void)
+{
+ int ret = 0;
+ uint8_t payload[4];
+ uint32_t seq;
+ uint32_t ack;
+ uint8_t th_flag;
+ uint8_t th_flags;
+ uint8_t flowflags;
+ uint8_t check_contents[6] = {0x41, 0x41, 0x42, 0x42, 0x42, 0x00};
+ TcpSession ssn;
+ memset(&ssn, 0, sizeof (TcpSession));
+
+ TcpReassemblyThreadCtx *ra_ctx = StreamTcpReassembleInitThreadCtx(NULL);
+
+ flowflags = FLOW_PKT_TOSERVER;
+ th_flag = TH_ACK|TH_PUSH;
+ th_flags = TH_ACK;
+
+ ssn.server.last_ack = 22;
+ ssn.server.ra_raw_base_seq = ssn.server.ra_app_base_seq = 9;
+ ssn.server.isn = 9;
+
+ StreamTcpInitConfig(TRUE);
+ StreamTcpCreateTestPacket(payload, 0x41, 2, 4); /*AA*/
+ seq = 10;
+ ack = 20;
+ if (StreamTcpTestMissedPacket (ra_ctx, &ssn, seq, ack, payload, 2, th_flag, flowflags, TCP_ESTABLISHED) == -1){
+ printf("failed in segments reassembly: ");
+ goto end;
+ }
+
+ flowflags = FLOW_PKT_TOCLIENT;
+ StreamTcpCreateTestPacket(payload, 0x00, 0, 4);
+ seq = 20;
+ ack = 12;
+ if (StreamTcpTestMissedPacket (ra_ctx, &ssn, seq, ack, payload, 0, th_flags, flowflags, TCP_ESTABLISHED) == -1){
+ printf("failed in segments reassembly: ");
+ goto end;
+ }
+
+ flowflags = FLOW_PKT_TOSERVER;
+ StreamTcpCreateTestPacket(payload, 0x42, 3, 4); /*BBB*/
+ seq = 12;
+ ack = 20;
+ if (StreamTcpTestMissedPacket (ra_ctx, &ssn, seq, ack, payload, 3, th_flag, flowflags, TCP_ESTABLISHED) == -1) {
+ printf("failed in segments reassembly: ");
+ goto end;
+ }
+
+ flowflags = FLOW_PKT_TOCLIENT;
+ StreamTcpCreateTestPacket(payload, 0x00, 0, 4);
+ seq = 20;
+ ack = 18;
+ if (StreamTcpTestMissedPacket (ra_ctx, &ssn, seq, ack, payload, 0, th_flags, flowflags, TCP_ESTABLISHED) == -1) {
+ printf("failed in segments reassembly: ");
+ goto end;
+ }
+
+ th_flag = TH_FIN|TH_ACK;
+ seq = 18;
+ ack = 20;
+ flowflags = FLOW_PKT_TOSERVER;
+ StreamTcpCreateTestPacket(payload, 0x00, 1, 4);
+ if (StreamTcpTestMissedPacket (ra_ctx, &ssn, seq, ack, payload, 1, th_flag, flowflags, TCP_ESTABLISHED) == -1) {
+ printf("failed in segments reassembly: ");
+ goto end;
+ }
+
+ flowflags = FLOW_PKT_TOCLIENT;
+ StreamTcpCreateTestPacket(payload, 0x00, 0, 4);
+ seq = 20;
+ ack = 18;
+ if (StreamTcpTestMissedPacket (ra_ctx, &ssn, seq, ack, payload, 0, th_flag, flowflags, TCP_TIME_WAIT) == -1) {
+ printf("failed in segments reassembly: ");
+ goto end;
+ }
+
+ if (StreamTcpCheckChunks(&ssn, check_contents) == 0) {
+ printf("failed in stream matching: ");
+ goto end;
+ }
+
+ ret = 1;
+end:
+ StreamTcpReassembleFreeThreadCtx(ra_ctx);
+ StreamTcpFreeConfig(TRUE);
+ return ret;
+}
+
+/**
+ * \test Test to reassemble the packets using the fast track method, as most
+ * packets arrives in order.
+ *
+ * \retval On success it returns 1 and on failure 0.
+ */
+
+static int StreamTcpReassembleTest31 (void)
+{
+ int ret = 0;
+ uint8_t payload[4];
+ uint32_t seq;
+ uint32_t ack;
+ uint8_t th_flag;
+ uint8_t flowflags;
+ uint8_t check_contents[5] = {0x41, 0x41, 0x42, 0x42, 0x42};
+ TcpReassemblyThreadCtx *ra_ctx = StreamTcpReassembleInitThreadCtx(NULL);
+ TcpSession ssn;
+ memset(&ssn, 0, sizeof (TcpSession));
+
+ flowflags = FLOW_PKT_TOSERVER;
+ th_flag = TH_ACK|TH_PUSH;
+
+ ssn.server.ra_raw_base_seq = 9;
+ ssn.server.isn = 9;
+ StreamTcpInitConfig(TRUE);
+
+ StreamTcpCreateTestPacket(payload, 0x41, 2, 4); /*AA*/
+ seq = 10;
+ ack = 20;
+ if (StreamTcpTestMissedPacket (ra_ctx, &ssn, seq, ack, payload, 2, th_flag, flowflags, TCP_ESTABLISHED) == -1){
+ printf("failed in segments reassembly: ");
+ goto end;
+ }
+
+ flowflags = FLOW_PKT_TOSERVER;
+ StreamTcpCreateTestPacket(payload, 0x42, 1, 4); /*B*/
+ seq = 15;
+ ack = 20;
+ if (StreamTcpTestMissedPacket (ra_ctx, &ssn, seq, ack, payload, 1, th_flag, flowflags, TCP_ESTABLISHED) == -1) {
+ printf("failed in segments reassembly: ");
+ goto end;
+ }
+
+ flowflags = FLOW_PKT_TOSERVER;
+ StreamTcpCreateTestPacket(payload, 0x42, 1, 4); /*B*/
+ seq = 12;
+ ack = 20;
+ if (StreamTcpTestMissedPacket (ra_ctx, &ssn, seq, ack, payload, 1, th_flag, flowflags, TCP_ESTABLISHED) == -1) {
+ printf("failed in segments reassembly: ");
+ goto end;
+ }
+
+ flowflags = FLOW_PKT_TOSERVER;
+ StreamTcpCreateTestPacket(payload, 0x42, 1, 4); /*B*/
+ seq = 16;
+ ack = 20;
+ if (StreamTcpTestMissedPacket (ra_ctx, &ssn, seq, ack, payload, 1, th_flag, flowflags, TCP_ESTABLISHED) == -1) {
+ printf("failed in segments reassembly: ");
+ goto end;
+ }
+
+ if (StreamTcpCheckStreamContents(check_contents, 5, &ssn.server) == 0) {
+ printf("failed in stream matching: ");
+ goto end;
+ }
+
+ if (ssn.server.seg_list_tail->seq != 16) {
+ printf("failed in fast track handling: ");
+ goto end;
+ }
+
+ ret = 1;
+end:
+ StreamTcpReassembleFreeThreadCtx(ra_ctx);
+ StreamTcpFreeConfig(TRUE);
+ return ret;
+}
+
+static int StreamTcpReassembleTest32(void)
+{
+ TcpSession ssn;
+ Packet *p = PacketGetFromAlloc();
+ if (unlikely(p == NULL))
+ return 0;
+ Flow f;
+ TCPHdr tcph;
+ TcpReassemblyThreadCtx *ra_ctx = StreamTcpReassembleInitThreadCtx(NULL);
+ TcpStream stream;
+ uint8_t ret = 0;
+ uint8_t check_contents[35] = {0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x43,
+ 0x43, 0x43, 0x43};
+ memset(&stream, 0, sizeof (TcpStream));
+ stream.os_policy = OS_POLICY_BSD;
+ uint8_t payload[20] = "";
+
+ StreamTcpInitConfig(TRUE);
+
+ /* prevent L7 from kicking in */
+ StreamMsgQueueSetMinChunkLen(FLOW_PKT_TOSERVER, 4096);
+ StreamMsgQueueSetMinChunkLen(FLOW_PKT_TOCLIENT, 4096);
+
+ PacketQueue pq;
+ memset(&pq,0,sizeof(PacketQueue));
+ memset(&ssn, 0, sizeof (TcpSession));
+ memset(&f, 0, sizeof (Flow));
+ memset(&tcph, 0, sizeof (TCPHdr));
+ ThreadVars tv;
+ memset(&tv, 0, sizeof (ThreadVars));
+ FLOW_INITIALIZE(&f);
+ f.protoctx = &ssn;
+ f.proto = IPPROTO_TCP;
+ p->src.family = AF_INET;
+ p->dst.family = AF_INET;
+ p->proto = IPPROTO_TCP;
+ p->flow = &f;
+ tcph.th_win = 5480;
+ tcph.th_flags = TH_PUSH | TH_ACK;
+ p->tcph = &tcph;
+ p->flowflags = FLOW_PKT_TOSERVER;
+
+ p->tcph->th_seq = htonl(10);
+ p->tcph->th_ack = htonl(31);
+ p->payload_len = 10;
+ StreamTcpCreateTestPacket(payload, 0x41, 10, 20); /*AA*/
+ p->payload = payload;
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx,&ssn, &stream, p, &pq) == -1)
+ goto end;
+
+ p->tcph->th_seq = htonl(20);
+ p->tcph->th_ack = htonl(31);
+ p->payload_len = 10;
+ StreamTcpCreateTestPacket(payload, 0x42, 10, 20); /*BB*/
+ p->payload = payload;
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx,&ssn, &stream, p, &pq) == -1)
+ goto end;
+
+ p->tcph->th_seq = htonl(40);
+ p->tcph->th_ack = htonl(31);
+ p->payload_len = 10;
+ StreamTcpCreateTestPacket(payload, 0x43, 10, 20); /*CC*/
+ p->payload = payload;
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx,&ssn, &stream, p, &pq) == -1)
+ goto end;
+
+ p->tcph->th_seq = htonl(5);
+ p->tcph->th_ack = htonl(31);
+ p->payload_len = 20;
+ StreamTcpCreateTestPacket(payload, 0x41, 20, 20); /*AA*/
+ p->payload = payload;
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx,&ssn, &stream, p, &pq) == -1)
+ goto end;
+
+ if (StreamTcpCheckStreamContents(check_contents, 35, &stream) != 0) {
+ ret = 1;
+ } else {
+ printf("failed in stream matching: ");
+ }
+
+
+end:
+ StreamTcpFreeConfig(TRUE);
+ SCFree(p);
+ return ret;
+}
+
+static int StreamTcpReassembleTest33(void)
+{
+ TcpSession ssn;
+ Packet *p = PacketGetFromAlloc();
+ if (unlikely(p == NULL))
+ return 0;
+ Flow f;
+ TCPHdr tcph;
+ TcpReassemblyThreadCtx *ra_ctx = StreamTcpReassembleInitThreadCtx(NULL);
+ TcpStream stream;
+ memset(&stream, 0, sizeof (TcpStream));
+ stream.os_policy = OS_POLICY_BSD;
+ uint8_t packet[1460] = "";
+
+ StreamTcpInitConfig(TRUE);
+
+ /* prevent L7 from kicking in */
+ StreamMsgQueueSetMinChunkLen(FLOW_PKT_TOSERVER, 4096);
+ StreamMsgQueueSetMinChunkLen(FLOW_PKT_TOCLIENT, 4096);
+
+ PacketQueue pq;
+ memset(&pq,0,sizeof(PacketQueue));
+ memset(&ssn, 0, sizeof (TcpSession));
+ memset(&f, 0, sizeof (Flow));
+ memset(&tcph, 0, sizeof (TCPHdr));
+ ThreadVars tv;
+ memset(&tv, 0, sizeof (ThreadVars));
+ FLOW_INITIALIZE(&f);
+ f.protoctx = &ssn;
+ f.proto = IPPROTO_TCP;
+ p->src.family = AF_INET;
+ p->dst.family = AF_INET;
+ p->proto = IPPROTO_TCP;
+ p->flow = &f;
+ tcph.th_win = 5480;
+ tcph.th_flags = TH_PUSH | TH_ACK;
+ p->tcph = &tcph;
+ p->flowflags = FLOW_PKT_TOSERVER;
+ p->payload = packet;
+
+ p->tcph->th_seq = htonl(10);
+ p->tcph->th_ack = htonl(31);
+ p->payload_len = 10;
+
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx,&ssn, &stream, p, &pq) == -1) {
+ SCFree(p);
+ return 0;
+ }
+
+ p->tcph->th_seq = htonl(20);
+ p->tcph->th_ack = htonl(31);
+ p->payload_len = 10;
+
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx,&ssn, &stream, p, &pq) == -1) {
+ SCFree(p);
+ return 0;
+ }
+
+ p->tcph->th_seq = htonl(40);
+ p->tcph->th_ack = htonl(31);
+ p->payload_len = 10;
+
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx,&ssn, &stream, p, &pq) == -1) {
+ SCFree(p);
+ return 0;
+ }
+
+ p->tcph->th_seq = htonl(5);
+ p->tcph->th_ack = htonl(31);
+ p->payload_len = 30;
+
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx,&ssn, &stream, p, &pq) == -1) {
+ SCFree(p);
+ return 0;
+ }
+
+ StreamTcpFreeConfig(TRUE);
+ SCFree(p);
+ return 1;
+}
+
+static int StreamTcpReassembleTest34(void)
+{
+ TcpSession ssn;
+ Packet *p = PacketGetFromAlloc();
+ if (unlikely(p == NULL))
+ return 0;
+ Flow f;
+ TCPHdr tcph;
+ TcpReassemblyThreadCtx *ra_ctx = StreamTcpReassembleInitThreadCtx(NULL);
+ TcpStream stream;
+ memset(&stream, 0, sizeof (TcpStream));
+ stream.os_policy = OS_POLICY_BSD;
+ uint8_t packet[1460] = "";
+
+ StreamTcpInitConfig(TRUE);
+
+ /* prevent L7 from kicking in */
+ StreamMsgQueueSetMinChunkLen(FLOW_PKT_TOSERVER, 4096);
+ StreamMsgQueueSetMinChunkLen(FLOW_PKT_TOCLIENT, 4096);
+
+ PacketQueue pq;
+ memset(&pq,0,sizeof(PacketQueue));
+ memset(&ssn, 0, sizeof (TcpSession));
+ memset(&f, 0, sizeof (Flow));
+ memset(&tcph, 0, sizeof (TCPHdr));
+ ThreadVars tv;
+ memset(&tv, 0, sizeof (ThreadVars));
+ FLOW_INITIALIZE(&f);
+ f.protoctx = &ssn;
+ f.proto = IPPROTO_TCP;
+ p->src.family = AF_INET;
+ p->dst.family = AF_INET;
+ p->proto = IPPROTO_TCP;
+ p->flow = &f;
+ tcph.th_win = 5480;
+ tcph.th_flags = TH_PUSH | TH_ACK;
+ p->tcph = &tcph;
+ p->flowflags = FLOW_PKT_TOSERVER;
+ p->payload = packet;
+
+ p->tcph->th_seq = htonl(857961230);
+ p->tcph->th_ack = htonl(31);
+ p->payload_len = 304;
+
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx,&ssn, &stream, p, &pq) == -1) {
+ SCFree(p);
+ return 0;
+ }
+
+ p->tcph->th_seq = htonl(857961534);
+ p->tcph->th_ack = htonl(31);
+ p->payload_len = 1460;
+
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx,&ssn, &stream, p, &pq) == -1) {
+ SCFree(p);
+ return 0;
+ }
+
+ p->tcph->th_seq = htonl(857963582);
+ p->tcph->th_ack = htonl(31);
+ p->payload_len = 1460;
+
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx,&ssn, &stream, p, &pq) == -1) {
+ SCFree(p);
+ return 0;
+ }
+
+ p->tcph->th_seq = htonl(857960946);
+ p->tcph->th_ack = htonl(31);
+ p->payload_len = 1460;
+
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx,&ssn, &stream, p, &pq) == -1) {
+ SCFree(p);
+ return 0;
+ }
+
+ StreamTcpFreeConfig(TRUE);
+ SCFree(p);
+ return 1;
+}
+
+/** \test Test the bug 56 condition */
+static int StreamTcpReassembleTest35(void)
+{
+ TcpSession ssn;
+ Packet *p = PacketGetFromAlloc();
+ if (unlikely(p == NULL))
+ return 0;
+ Flow f;
+ TCPHdr tcph;
+ TcpReassemblyThreadCtx *ra_ctx = StreamTcpReassembleInitThreadCtx(NULL);
+ TcpStream stream;
+ memset(&stream, 0, sizeof (TcpStream));
+ stream.os_policy = OS_POLICY_BSD;
+ uint8_t packet[1460] = "";
+
+ StreamTcpInitConfig(TRUE);
+
+ /* prevent L7 from kicking in */
+ StreamMsgQueueSetMinChunkLen(FLOW_PKT_TOSERVER, 10);
+ StreamMsgQueueSetMinChunkLen(FLOW_PKT_TOCLIENT, 10);
+
+ PacketQueue pq;
+ memset(&pq,0,sizeof(PacketQueue));
+ memset(&ssn, 0, sizeof (TcpSession));
+ memset(&f, 0, sizeof (Flow));
+ memset(&tcph, 0, sizeof (TCPHdr));
+ ThreadVars tv;
+ memset(&tv, 0, sizeof (ThreadVars));
+ FLOW_INITIALIZE(&f);
+ f.protoctx = &ssn;
+ f.proto = IPPROTO_TCP;
+ p->src.family = AF_INET;
+ p->dst.family = AF_INET;
+ p->proto = IPPROTO_TCP;
+ p->flow = &f;
+ tcph.th_win = 5480;
+ tcph.th_flags = TH_PUSH | TH_ACK;
+ p->tcph = &tcph;
+ p->flowflags = FLOW_PKT_TOSERVER;
+ p->payload = packet;
+
+ p->tcph->th_seq = htonl(2257022155UL);
+ p->tcph->th_ack = htonl(1374943142);
+ p->payload_len = 142;
+ stream.last_ack = 2257022285UL;
+ stream.ra_raw_base_seq = 2257022172UL;
+ stream.ra_app_base_seq = 2257022172UL;
+
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx,&ssn, &stream, p, &pq) == -1) {
+ SCFree(p);
+ return 0;
+ }
+
+ p->tcph->th_seq = htonl(2257022285UL);
+ p->tcph->th_ack = htonl(1374943142);
+ p->payload_len = 34;
+ stream.last_ack = 2257022285UL;
+ stream.ra_raw_base_seq = 2257022172UL;
+
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx,&ssn, &stream, p, &pq) == -1) {
+ SCFree(p);
+ return 0;
+ }
+
+ StreamTcpFreeConfig(TRUE);
+ SCFree(p);
+ return 1;
+}
+
+/** \test Test the bug 57 condition */
+static int StreamTcpReassembleTest36(void)
+{
+ TcpSession ssn;
+ Packet *p = PacketGetFromAlloc();
+ if (unlikely(p == NULL))
+ return 0;
+ Flow f;
+ TCPHdr tcph;
+ TcpReassemblyThreadCtx *ra_ctx = StreamTcpReassembleInitThreadCtx(NULL);
+ TcpStream stream;
+ memset(&stream, 0, sizeof (TcpStream));
+ stream.os_policy = OS_POLICY_BSD;
+ uint8_t packet[1460] = "";
+
+ StreamTcpInitConfig(TRUE);
+
+ /* prevent L7 from kicking in */
+ StreamMsgQueueSetMinChunkLen(FLOW_PKT_TOSERVER, 10);
+ StreamMsgQueueSetMinChunkLen(FLOW_PKT_TOCLIENT, 10);
+
+ PacketQueue pq;
+ memset(&pq,0,sizeof(PacketQueue));
+ memset(&ssn, 0, sizeof (TcpSession));
+ memset(&f, 0, sizeof (Flow));
+ memset(&tcph, 0, sizeof (TCPHdr));
+ ThreadVars tv;
+ memset(&tv, 0, sizeof (ThreadVars));
+ FLOW_INITIALIZE(&f);
+ f.protoctx = &ssn;
+ f.proto = IPPROTO_TCP;
+ p->src.family = AF_INET;
+ p->dst.family = AF_INET;
+ p->proto = IPPROTO_TCP;
+ p->flow = &f;
+ tcph.th_win = 5480;
+ tcph.th_flags = TH_PUSH | TH_ACK;
+ p->tcph = &tcph;
+ p->flowflags = FLOW_PKT_TOSERVER;
+ p->payload = packet;
+
+ p->tcph->th_seq = htonl(1549588966);
+ p->tcph->th_ack = htonl(4162241372UL);
+ p->payload_len = 204;
+ stream.last_ack = 1549589007;
+ stream.ra_raw_base_seq = 1549589101;
+
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx,&ssn, &stream, p, &pq) == -1) {
+ SCFree(p);
+ return 0;
+ }
+
+ p->tcph->th_seq = htonl(1549589007);
+ p->tcph->th_ack = htonl(4162241372UL);
+ p->payload_len = 23;
+ stream.last_ack = 1549589007;
+ stream.ra_raw_base_seq = 1549589101;
+
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx,&ssn, &stream, p, &pq) == -1) {
+ SCFree(p);
+ return 0;
+ }
+
+ StreamTcpFreeConfig(TRUE);
+ SCFree(p);
+ return 1;
+}
+
+/** \test Test the bug 76 condition */
+static int StreamTcpReassembleTest37(void)
+{
+ TcpSession ssn;
+ Flow f;
+ TCPHdr tcph;
+ TcpReassemblyThreadCtx *ra_ctx = StreamTcpReassembleInitThreadCtx(NULL);
+ TcpStream stream;
+ uint8_t packet[1460] = "";
+ PacketQueue pq;
+ ThreadVars tv;
+
+ Packet *p = PacketGetFromAlloc();
+ if (unlikely(p == NULL))
+ return 0;
+
+ StreamTcpInitConfig(TRUE);
+
+ /* prevent L7 from kicking in */
+ StreamMsgQueueSetMinChunkLen(FLOW_PKT_TOSERVER, 10);
+ StreamMsgQueueSetMinChunkLen(FLOW_PKT_TOCLIENT, 10);
+
+ memset(&stream, 0, sizeof (TcpStream));
+ memset(&pq,0,sizeof(PacketQueue));
+ memset(&ssn, 0, sizeof (TcpSession));
+ memset(&f, 0, sizeof (Flow));
+ memset(&tcph, 0, sizeof (TCPHdr));
+ memset(&tv, 0, sizeof (ThreadVars));
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = &ssn;
+ f.proto = IPPROTO_TCP;
+ p->src.family = AF_INET;
+ p->dst.family = AF_INET;
+ p->proto = IPPROTO_TCP;
+ p->flow = &f;
+ tcph.th_win = 5480;
+ tcph.th_flags = TH_PUSH | TH_ACK;
+ p->tcph = &tcph;
+ p->flowflags = FLOW_PKT_TOSERVER;
+ p->payload = packet;
+ stream.os_policy = OS_POLICY_BSD;
+
+ p->tcph->th_seq = htonl(3061088537UL);
+ p->tcph->th_ack = htonl(1729548549UL);
+ p->payload_len = 1391;
+ stream.last_ack = 3061091137UL;
+ stream.ra_raw_base_seq = 3061091309UL;
+ stream.ra_app_base_seq = 3061091309UL;
+
+ /* pre base_seq, so should be rejected */
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx,&ssn, &stream, p, &pq) != -1) {
+ SCFree(p);
+ return 0;
+ }
+
+ p->tcph->th_seq = htonl(3061089928UL);
+ p->tcph->th_ack = htonl(1729548549UL);
+ p->payload_len = 1391;
+ stream.last_ack = 3061091137UL;
+ stream.ra_raw_base_seq = 3061091309UL;
+ stream.ra_app_base_seq = 3061091309UL;
+
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx,&ssn, &stream, p, &pq) == -1) {
+ SCFree(p);
+ return 0;
+ }
+
+ p->tcph->th_seq = htonl(3061091319UL);
+ p->tcph->th_ack = htonl(1729548549UL);
+ p->payload_len = 1391;
+ stream.last_ack = 3061091137UL;
+ stream.ra_raw_base_seq = 3061091309UL;
+ stream.ra_app_base_seq = 3061091309UL;
+
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx,&ssn, &stream, p, &pq) == -1) {
+ SCFree(p);
+ return 0;
+ }
+
+ StreamTcpFreeConfig(TRUE);
+ SCFree(p);
+ return 1;
+}
+
+/**
+ * \test Test to make sure we don't send the smsg from toclient to app layer
+ * until the app layer protocol has been detected and one smsg from
+ * toserver side has been sent to app layer.
+ *
+ * Unittest modified by commit -
+ *
+ * commit bab1636377bb4f1b7b889f4e3fd594795085eaa4
+ * Author: Anoop Saldanha <anoopsaldanha@gmail.com>
+ * Date: Fri Feb 15 18:58:33 2013 +0530
+ *
+ * Improved app protocol detection.
+ *
+ * \retval On success it returns 1 and on failure 0.
+ */
+static int StreamTcpReassembleTest38 (void)
+{
+ int ret = 0;
+ Packet *p = PacketGetFromAlloc();
+ if (unlikely(p == NULL))
+ return 0;
+ Flow f;
+ TCPHdr tcph;
+ Port sp;
+ Port dp;
+ struct in_addr in;
+ TcpSession ssn;
+ PacketQueue pq;
+ memset(&pq,0,sizeof(PacketQueue));
+ memset(&f, 0, sizeof (Flow));
+ memset(&tcph, 0, sizeof (TCPHdr));
+ memset(&ssn, 0, sizeof(TcpSession));
+ ThreadVars tv;
+ memset(&tv, 0, sizeof (ThreadVars));
+
+ StreamTcpInitConfig(TRUE);
+ TcpReassemblyThreadCtx *ra_ctx = StreamTcpReassembleInitThreadCtx(NULL);
+
+ uint8_t httpbuf2[] = "POST / HTTP/1.0\r\nUser-Agent: Victor/1.0\r\n\r\n";
+ uint32_t httplen2 = sizeof(httpbuf2) - 1; /* minus the \0 */
+
+ uint8_t httpbuf1[] = "HTTP/1.0 200 OK\r\nServer: VictorServer/1.0\r\n\r\n";
+ uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
+
+ FLOW_INITIALIZE(&f);
+ if (inet_pton(AF_INET, "1.2.3.4", &in) != 1)
+ goto end;
+ f.src.addr_data32[0] = in.s_addr;
+ if (inet_pton(AF_INET, "1.2.3.5", &in) != 1)
+ goto end;
+ f.dst.addr_data32[0] = in.s_addr;
+ sp = 200;
+ dp = 220;
+
+ ssn.server.ra_raw_base_seq = ssn.server.ra_app_base_seq = 9;
+ ssn.server.isn = 9;
+ 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;
+ f.alproto = ALPROTO_UNKNOWN;
+
+ f.flags |= FLOW_IPV4;
+ f.sp = sp;
+ f.dp = dp;
+ f.protoctx = &ssn;
+ f.proto = IPPROTO_TCP;
+ p->flow = &f;
+
+ tcph.th_win = htons(5480);
+ tcph.th_seq = htonl(10);
+ tcph.th_ack = htonl(20);
+ tcph.th_flags = TH_ACK|TH_PUSH;
+ p->tcph = &tcph;
+ p->flowflags = FLOW_PKT_TOSERVER;
+
+ p->payload = httpbuf2;
+ p->payload_len = httplen2;
+ ssn.state = TCP_ESTABLISHED;
+
+ TcpStream *s = NULL;
+ s = &ssn.server;
+
+ SCMutexLock(&f.m);
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx, &ssn, s, p, &pq) == -1) {
+ printf("failed in segments reassembly, while processing toserver packet (1): ");
+ goto end;
+ }
+
+ /* Check if we have stream smsgs in queue */
+ if (UtSsnSmsgCnt(&ssn, STREAM_TOSERVER) > 0) {
+ printf("there shouldn't be any stream smsgs in the queue (2): ");
+ goto end;
+ }
+
+ p->flowflags = FLOW_PKT_TOCLIENT;
+ p->payload = httpbuf1;
+ p->payload_len = httplen1;
+ tcph.th_seq = htonl(10);
+ tcph.th_ack = htonl(55);
+ s = &ssn.client;
+
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx, &ssn, s, p, &pq) == -1) {
+ printf("failed in segments reassembly, while processing toserver packet (3): ");
+ goto end;
+ }
+
+ /* Check if we have stream smsgs in queue */
+ if (UtSsnSmsgCnt(&ssn, STREAM_TOSERVER) != 1) {
+ printf("there should one stream smsg in the queue (6): ");
+ goto end;
+ }
+
+ ret = 1;
+end:
+ StreamTcpReassembleFreeThreadCtx(ra_ctx);
+ StreamTcpFreeConfig(TRUE);
+ SCMutexUnlock(&f.m);
+ SCFree(p);
+ return ret;
+}
+
+/**
+ * \test Test to make sure that we don't return the segments until the app
+ * layer proto has been detected and after that remove the processed
+ * segments.
+ *
+ * \retval On success it returns 1 and on failure 0.
+ */
+
+static int StreamTcpReassembleTest39 (void)
+{
+ Packet *p = PacketGetFromAlloc();
+ if (unlikely(p == NULL))
+ return 0;
+ Flow f;
+ ThreadVars tv;
+ StreamTcpThread *stt = NULL;
+ TCPHdr tcph;
+ PacketQueue pq;
+ memset(&pq,0,sizeof(PacketQueue));
+ memset (&f, 0, sizeof(Flow));
+ memset(&tv, 0, sizeof (ThreadVars));
+ StreamTcpThreadInit(&tv, NULL, (void **)&stt);
+ memset(&tcph, 0, sizeof (TCPHdr));
+
+ FLOW_INITIALIZE(&f);
+ f.flags = FLOW_IPV4;
+ f.proto = IPPROTO_TCP;
+ p->flow = &f;
+ p->tcph = &tcph;
+
+ SCMutexLock(&f.m);
+ int ret = 0;
+
+ StreamTcpInitConfig(TRUE);
+
+ /* handshake */
+ tcph.th_win = htons(5480);
+ tcph.th_flags = TH_SYN;
+ p->flowflags = FLOW_PKT_TOSERVER;
+ p->payload_len = 0;
+ p->payload = NULL;
+ if (StreamTcpPacket(&tv, p, stt, &pq) == -1)
+ goto end;
+
+ TcpSession *ssn = (TcpSession *)f.protoctx;
+
+ if (StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->server) ||
+ StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->client) ||
+ f.alproto != ALPROTO_UNKNOWN ||
+ f.alproto_ts != ALPROTO_UNKNOWN ||
+ f.alproto_tc != ALPROTO_UNKNOWN ||
+ f.data_al_so_far[0] != 0 ||
+ f.data_al_so_far[1] != 0 ||
+ ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED ||
+ FLOW_IS_PM_DONE(&f, STREAM_TOSERVER) || FLOW_IS_PP_DONE(&f, STREAM_TOSERVER) ||
+ FLOW_IS_PM_DONE(&f, STREAM_TOCLIENT) || FLOW_IS_PP_DONE(&f, STREAM_TOCLIENT) ||
+ ssn->client.seg_list != NULL ||
+ ssn->server.seg_list != NULL ||
+ ssn->toserver_smsg_head != NULL ||
+ ssn->toclient_smsg_head != NULL ||
+ ssn->data_first_seen_dir != 0) {
+ printf("failure 1\n");
+ goto end;
+ }
+
+ /* handshake */
+ p->tcph->th_ack = htonl(1);
+ p->tcph->th_flags = TH_SYN | TH_ACK;
+ p->flowflags = FLOW_PKT_TOCLIENT;
+ p->payload_len = 0;
+ p->payload = NULL;
+ if (StreamTcpPacket(&tv, p, stt, &pq) == -1)
+ goto end;
+ if (StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->server) ||
+ StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->client) ||
+ f.alproto != ALPROTO_UNKNOWN ||
+ f.alproto_ts != ALPROTO_UNKNOWN ||
+ f.alproto_tc != ALPROTO_UNKNOWN ||
+ f.data_al_so_far[0] != 0 ||
+ f.data_al_so_far[1] != 0 ||
+ ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED ||
+ FLOW_IS_PM_DONE(&f, STREAM_TOSERVER) || FLOW_IS_PP_DONE(&f, STREAM_TOSERVER) ||
+ FLOW_IS_PM_DONE(&f, STREAM_TOCLIENT) || FLOW_IS_PP_DONE(&f, STREAM_TOCLIENT) ||
+ ssn->client.seg_list != NULL ||
+ ssn->server.seg_list != NULL ||
+ ssn->toserver_smsg_head != NULL ||
+ ssn->toclient_smsg_head != NULL ||
+ ssn->data_first_seen_dir != 0) {
+ printf("failure 2\n");
+ goto end;
+ }
+
+ /* handshake */
+ p->tcph->th_ack = htonl(1);
+ p->tcph->th_seq = htonl(1);
+ p->tcph->th_flags = TH_ACK;
+ p->flowflags = FLOW_PKT_TOSERVER;
+ p->payload_len = 0;
+ p->payload = NULL;
+ if (StreamTcpPacket(&tv, p, stt, &pq) == -1)
+ goto end;
+ if (StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->server) ||
+ StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->client) ||
+ f.alproto != ALPROTO_UNKNOWN ||
+ f.alproto_ts != ALPROTO_UNKNOWN ||
+ f.alproto_tc != ALPROTO_UNKNOWN ||
+ f.data_al_so_far[0] != 0 ||
+ f.data_al_so_far[1] != 0 ||
+ ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED ||
+ FLOW_IS_PM_DONE(&f, STREAM_TOSERVER) || FLOW_IS_PP_DONE(&f, STREAM_TOSERVER) ||
+ FLOW_IS_PM_DONE(&f, STREAM_TOCLIENT) || FLOW_IS_PP_DONE(&f, STREAM_TOCLIENT) ||
+ ssn->client.seg_list != NULL ||
+ ssn->server.seg_list != NULL ||
+ ssn->toserver_smsg_head != NULL ||
+ ssn->toclient_smsg_head != NULL ||
+ ssn->data_first_seen_dir != 0) {
+ printf("failure 3\n");
+ goto end;
+ }
+
+ /* partial request */
+ uint8_t request1[] = { 0x47, 0x45, };
+ p->tcph->th_ack = htonl(1);
+ p->tcph->th_seq = htonl(1);
+ p->tcph->th_flags = TH_PUSH | TH_ACK;
+ p->flowflags = FLOW_PKT_TOSERVER;
+ p->payload_len = sizeof(request1);
+ p->payload = request1;
+ if (StreamTcpPacket(&tv, p, stt, &pq) == -1)
+ goto end;
+ if (StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->server) ||
+ StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->client) ||
+ f.alproto != ALPROTO_UNKNOWN ||
+ f.alproto_ts != ALPROTO_UNKNOWN ||
+ f.alproto_tc != ALPROTO_UNKNOWN ||
+ f.data_al_so_far[0] != 0 ||
+ f.data_al_so_far[1] != 0 ||
+ ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED ||
+ FLOW_IS_PM_DONE(&f, STREAM_TOSERVER) || FLOW_IS_PP_DONE(&f, STREAM_TOSERVER) ||
+ FLOW_IS_PM_DONE(&f, STREAM_TOCLIENT) || FLOW_IS_PP_DONE(&f, STREAM_TOCLIENT) ||
+ ssn->client.seg_list == NULL ||
+ ssn->client.seg_list->next != NULL ||
+ ssn->server.seg_list != NULL ||
+ ssn->toserver_smsg_head != NULL ||
+ ssn->toclient_smsg_head != NULL ||
+ ssn->data_first_seen_dir != STREAM_TOSERVER) {
+ printf("failure 4\n");
+ goto end;
+ }
+
+
+ /* response ack against partial request */
+ p->tcph->th_ack = htonl(3);
+ p->tcph->th_seq = htonl(1);
+ p->tcph->th_flags = TH_ACK;
+ p->flowflags = FLOW_PKT_TOCLIENT;
+ p->payload_len = 0;
+ p->payload = NULL;
+ if (StreamTcpPacket(&tv, p, stt, &pq) == -1)
+ goto end;
+ if (StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->server) ||
+ StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->client) ||
+ f.alproto != ALPROTO_UNKNOWN ||
+ f.alproto_ts != ALPROTO_UNKNOWN ||
+ f.alproto_tc != ALPROTO_UNKNOWN ||
+ f.data_al_so_far[0] != 0 ||
+ f.data_al_so_far[1] != 0 ||
+ ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED ||
+ FLOW_IS_PM_DONE(&f, STREAM_TOSERVER) || !FLOW_IS_PP_DONE(&f, STREAM_TOSERVER) ||
+ FLOW_IS_PM_DONE(&f, STREAM_TOCLIENT) || FLOW_IS_PP_DONE(&f, STREAM_TOCLIENT) ||
+ ssn->client.seg_list == NULL ||
+ ssn->client.seg_list->next != NULL ||
+ ssn->server.seg_list != NULL ||
+ ssn->toserver_smsg_head != NULL ||
+ ssn->toclient_smsg_head != NULL ||
+ ssn->data_first_seen_dir != STREAM_TOSERVER) {
+ printf("failure 5\n");
+ goto end;
+ }
+
+ /* complete partial request */
+ uint8_t request2[] = {
+ 0x54, 0x20, 0x2f, 0x69, 0x6e, 0x64,
+ 0x65, 0x78, 0x2e, 0x68, 0x74, 0x6d, 0x6c, 0x20,
+ 0x48, 0x54, 0x54, 0x50, 0x2f, 0x31, 0x2e, 0x30,
+ 0x0d, 0x0a, 0x48, 0x6f, 0x73, 0x74, 0x3a, 0x20,
+ 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x68, 0x6f, 0x73,
+ 0x74, 0x0d, 0x0a, 0x55, 0x73, 0x65, 0x72, 0x2d,
+ 0x41, 0x67, 0x65, 0x6e, 0x74, 0x3a, 0x20, 0x41,
+ 0x70, 0x61, 0x63, 0x68, 0x65, 0x42, 0x65, 0x6e,
+ 0x63, 0x68, 0x2f, 0x32, 0x2e, 0x33, 0x0d, 0x0a,
+ 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x3a, 0x20,
+ 0x2a, 0x2f, 0x2a, 0x0d, 0x0a, 0x0d, 0x0a };
+ p->tcph->th_ack = htonl(1);
+ p->tcph->th_seq = htonl(3);
+ p->tcph->th_flags = TH_PUSH | TH_ACK;
+ p->flowflags = FLOW_PKT_TOSERVER;
+ p->payload_len = sizeof(request2);
+ p->payload = request2;
+ if (StreamTcpPacket(&tv, p, stt, &pq) == -1)
+ goto end;
+ if (StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->server) ||
+ StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->client) ||
+ f.alproto != ALPROTO_UNKNOWN ||
+ f.alproto_ts != ALPROTO_UNKNOWN ||
+ f.alproto_tc != ALPROTO_UNKNOWN ||
+ f.data_al_so_far[0] != 0 ||
+ f.data_al_so_far[1] != 0 ||
+ ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED ||
+ FLOW_IS_PM_DONE(&f, STREAM_TOSERVER) || !FLOW_IS_PP_DONE(&f, STREAM_TOSERVER) ||
+ FLOW_IS_PM_DONE(&f, STREAM_TOCLIENT) || FLOW_IS_PP_DONE(&f, STREAM_TOCLIENT) ||
+ ssn->client.seg_list == NULL ||
+ ssn->client.seg_list->next == NULL ||
+ ssn->client.seg_list->next->next != NULL ||
+ ssn->server.seg_list != NULL ||
+ ssn->toserver_smsg_head != NULL ||
+ ssn->toclient_smsg_head != NULL ||
+ ssn->data_first_seen_dir != STREAM_TOSERVER) {
+ printf("failure 6\n");
+ goto end;
+ }
+
+ /* response - request ack */
+ uint8_t response[] = {
+ 0x48, 0x54, 0x54, 0x50, 0x2f, 0x31, 0x2e, 0x31,
+ 0x20, 0x32, 0x30, 0x30, 0x20, 0x4f, 0x4b, 0x0d,
+ 0x0a, 0x44, 0x61, 0x74, 0x65, 0x3a, 0x20, 0x46,
+ 0x72, 0x69, 0x2c, 0x20, 0x32, 0x33, 0x20, 0x53,
+ 0x65, 0x70, 0x20, 0x32, 0x30, 0x31, 0x31, 0x20,
+ 0x30, 0x36, 0x3a, 0x32, 0x39, 0x3a, 0x33, 0x39,
+ 0x20, 0x47, 0x4d, 0x54, 0x0d, 0x0a, 0x53, 0x65,
+ 0x72, 0x76, 0x65, 0x72, 0x3a, 0x20, 0x41, 0x70,
+ 0x61, 0x63, 0x68, 0x65, 0x2f, 0x32, 0x2e, 0x32,
+ 0x2e, 0x31, 0x35, 0x20, 0x28, 0x55, 0x6e, 0x69,
+ 0x78, 0x29, 0x20, 0x44, 0x41, 0x56, 0x2f, 0x32,
+ 0x0d, 0x0a, 0x4c, 0x61, 0x73, 0x74, 0x2d, 0x4d,
+ 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65, 0x64, 0x3a,
+ 0x20, 0x54, 0x68, 0x75, 0x2c, 0x20, 0x30, 0x34,
+ 0x20, 0x4e, 0x6f, 0x76, 0x20, 0x32, 0x30, 0x31,
+ 0x30, 0x20, 0x31, 0x35, 0x3a, 0x30, 0x34, 0x3a,
+ 0x34, 0x36, 0x20, 0x47, 0x4d, 0x54, 0x0d, 0x0a,
+ 0x45, 0x54, 0x61, 0x67, 0x3a, 0x20, 0x22, 0x61,
+ 0x62, 0x38, 0x39, 0x36, 0x35, 0x2d, 0x32, 0x63,
+ 0x2d, 0x34, 0x39, 0x34, 0x33, 0x62, 0x37, 0x61,
+ 0x37, 0x66, 0x37, 0x66, 0x38, 0x30, 0x22, 0x0d,
+ 0x0a, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x2d,
+ 0x52, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x3a, 0x20,
+ 0x62, 0x79, 0x74, 0x65, 0x73, 0x0d, 0x0a, 0x43,
+ 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x4c,
+ 0x65, 0x6e, 0x67, 0x74, 0x68, 0x3a, 0x20, 0x34,
+ 0x34, 0x0d, 0x0a, 0x43, 0x6f, 0x6e, 0x6e, 0x65,
+ 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x3a, 0x20, 0x63,
+ 0x6c, 0x6f, 0x73, 0x65, 0x0d, 0x0a, 0x43, 0x6f,
+ 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x54, 0x79,
+ 0x70, 0x65, 0x3a, 0x20, 0x74, 0x65, 0x78, 0x74,
+ 0x2f, 0x68, 0x74, 0x6d, 0x6c, 0x0d, 0x0a, 0x58,
+ 0x2d, 0x50, 0x61, 0x64, 0x3a, 0x20, 0x61, 0x76,
+ 0x6f, 0x69, 0x64, 0x20, 0x62, 0x72, 0x6f, 0x77,
+ 0x73, 0x65, 0x72, 0x20, 0x62, 0x75, 0x67, 0x0d,
+ 0x0a, 0x0d, 0x0a, 0x3c, 0x68, 0x74, 0x6d, 0x6c,
+ 0x3e, 0x3c, 0x62, 0x6f, 0x64, 0x79, 0x3e, 0x3c,
+ 0x68, 0x31, 0x3e, 0x49, 0x74, 0x20, 0x77, 0x6f,
+ 0x72, 0x6b, 0x73, 0x21, 0x3c, 0x2f, 0x68, 0x31,
+ 0x3e, 0x3c, 0x2f, 0x62, 0x6f, 0x64, 0x79, 0x3e,
+ 0x3c, 0x2f, 0x68, 0x74, 0x6d, 0x6c, 0x3e };
+ p->tcph->th_ack = htonl(88);
+ p->tcph->th_seq = htonl(1);
+ p->tcph->th_flags = TH_PUSH | TH_ACK;
+ p->flowflags = FLOW_PKT_TOCLIENT;
+ p->payload_len = sizeof(response);
+ p->payload = response;
+ if (StreamTcpPacket(&tv, p, stt, &pq) == -1)
+ goto end;
+ if (StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->server) ||
+ !StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->client) ||
+ f.alproto != ALPROTO_HTTP ||
+ f.alproto_ts != ALPROTO_HTTP ||
+ f.alproto_tc != ALPROTO_UNKNOWN ||
+ f.data_al_so_far[0] != 0 ||
+ f.data_al_so_far[1] != 0 ||
+ ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED ||
+ !FLOW_IS_PM_DONE(&f, STREAM_TOSERVER) || !FLOW_IS_PP_DONE(&f, STREAM_TOSERVER) ||
+ FLOW_IS_PM_DONE(&f, STREAM_TOCLIENT) || FLOW_IS_PP_DONE(&f, STREAM_TOCLIENT) ||
+ ssn->client.seg_list == NULL ||
+ ssn->client.seg_list->next == NULL ||
+ ssn->client.seg_list->next->next != NULL ||
+ ssn->server.seg_list == NULL ||
+ ssn->server.seg_list->next != NULL ||
+ ssn->data_first_seen_dir != APP_LAYER_DATA_ALREADY_SENT_TO_APP_LAYER) {
+ printf("failure 7\n");
+ goto end;
+ }
+
+ /* response ack from request */
+ p->tcph->th_ack = htonl(328);
+ p->tcph->th_seq = htonl(88);
+ p->tcph->th_flags = TH_ACK;
+ p->flowflags = FLOW_PKT_TOSERVER;
+ p->payload_len = 0;
+ p->payload = NULL;
+ if (StreamTcpPacket(&tv, p, stt, &pq) == -1)
+ goto end;
+ if (!StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->server) ||
+ !StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->client) ||
+ f.alproto != ALPROTO_HTTP ||
+ f.alproto_ts != ALPROTO_HTTP ||
+ f.alproto_tc != ALPROTO_HTTP ||
+ f.data_al_so_far[0] != 0 ||
+ f.data_al_so_far[1] != 0 ||
+ ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED ||
+ !FLOW_IS_PM_DONE(&f, STREAM_TOSERVER) || !FLOW_IS_PP_DONE(&f, STREAM_TOSERVER) ||
+ !FLOW_IS_PM_DONE(&f, STREAM_TOCLIENT) || FLOW_IS_PP_DONE(&f, STREAM_TOCLIENT) ||
+ ssn->client.seg_list == NULL ||
+ ssn->client.seg_list->next == NULL ||
+ ssn->client.seg_list->next->next != NULL ||
+ ssn->server.seg_list == NULL ||
+ ssn->server.seg_list->next != NULL ||
+ ssn->data_first_seen_dir != APP_LAYER_DATA_ALREADY_SENT_TO_APP_LAYER) {
+ printf("failure 8\n");
+ goto end;
+ }
+
+ /* response - acking */
+ p->tcph->th_ack = htonl(88);
+ p->tcph->th_seq = htonl(328);
+ p->tcph->th_flags = TH_PUSH | TH_ACK;
+ p->flowflags = FLOW_PKT_TOCLIENT;
+ p->payload_len = 0;
+ p->payload = NULL;
+ if (StreamTcpPacket(&tv, p, stt, &pq) == -1)
+ goto end;
+ if (!StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->server) ||
+ !StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->client) ||
+ f.alproto != ALPROTO_HTTP ||
+ f.alproto_ts != ALPROTO_HTTP ||
+ f.alproto_tc != ALPROTO_HTTP ||
+ f.data_al_so_far[0] != 0 ||
+ f.data_al_so_far[1] != 0 ||
+ ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED ||
+ !FLOW_IS_PM_DONE(&f, STREAM_TOSERVER) || !FLOW_IS_PP_DONE(&f, STREAM_TOSERVER) ||
+ !FLOW_IS_PM_DONE(&f, STREAM_TOCLIENT) || FLOW_IS_PP_DONE(&f, STREAM_TOCLIENT) ||
+ ssn->client.seg_list == NULL ||
+ ssn->client.seg_list->next == NULL ||
+ ssn->server.seg_list == NULL ||
+ ssn->server.seg_list->next != NULL ||
+ ssn->data_first_seen_dir != APP_LAYER_DATA_ALREADY_SENT_TO_APP_LAYER) {
+ printf("failure 9\n");
+ goto end;
+ }
+
+ /* response ack from request */
+ p->tcph->th_ack = htonl(328);
+ p->tcph->th_seq = htonl(88);
+ p->tcph->th_flags = TH_ACK;
+ p->flowflags = FLOW_PKT_TOSERVER;
+ p->payload_len = 0;
+ p->payload = NULL;
+ if (StreamTcpPacket(&tv, p, stt, &pq) == -1)
+ goto end;
+ if (!StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->server) ||
+ !StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->client) ||
+ f.alproto != ALPROTO_HTTP ||
+ f.alproto_ts != ALPROTO_HTTP ||
+ f.alproto_tc != ALPROTO_HTTP ||
+ f.data_al_so_far[0] != 0 ||
+ f.data_al_so_far[1] != 0 ||
+ ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED ||
+ !FLOW_IS_PM_DONE(&f, STREAM_TOSERVER) || !FLOW_IS_PP_DONE(&f, STREAM_TOSERVER) ||
+ !FLOW_IS_PM_DONE(&f, STREAM_TOCLIENT) || FLOW_IS_PP_DONE(&f, STREAM_TOCLIENT) ||
+ ssn->client.seg_list == NULL ||
+ ssn->client.seg_list->next == NULL ||
+ ssn->server.seg_list == NULL ||
+ ssn->server.seg_list->next != NULL ||
+ ssn->data_first_seen_dir != APP_LAYER_DATA_ALREADY_SENT_TO_APP_LAYER) {
+ printf("failure 10\n");
+ goto end;
+ }
+
+ /* response - acking the request again*/
+ p->tcph->th_ack = htonl(88);
+ p->tcph->th_seq = htonl(328);
+ p->tcph->th_flags = TH_PUSH | TH_ACK;
+ p->flowflags = FLOW_PKT_TOCLIENT;
+ p->payload_len = 0;
+ p->payload = NULL;
+ if (StreamTcpPacket(&tv, p, stt, &pq) == -1)
+ goto end;
+ if (!StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->server) ||
+ !StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->client) ||
+ f.alproto != ALPROTO_HTTP ||
+ f.alproto_ts != ALPROTO_HTTP ||
+ f.alproto_tc != ALPROTO_HTTP ||
+ f.data_al_so_far[0] != 0 ||
+ f.data_al_so_far[1] != 0 ||
+ ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED ||
+ !FLOW_IS_PM_DONE(&f, STREAM_TOSERVER) || !FLOW_IS_PP_DONE(&f, STREAM_TOSERVER) ||
+ !FLOW_IS_PM_DONE(&f, STREAM_TOCLIENT) || FLOW_IS_PP_DONE(&f, STREAM_TOCLIENT) ||
+ ssn->client.seg_list == NULL ||
+ ssn->client.seg_list->next == NULL ||
+ ssn->server.seg_list == NULL ||
+ ssn->server.seg_list->next != NULL ||
+ ssn->data_first_seen_dir != APP_LAYER_DATA_ALREADY_SENT_TO_APP_LAYER) {
+ printf("failure 11\n");
+ goto end;
+ }
+
+ /*** New Request ***/
+
+ /* partial request */
+ p->tcph->th_ack = htonl(328);
+ p->tcph->th_seq = htonl(88);
+ p->tcph->th_flags = TH_PUSH | TH_ACK;
+ p->flowflags = FLOW_PKT_TOSERVER;
+ p->payload_len = sizeof(request1);
+ p->payload = request1;
+ if (StreamTcpPacket(&tv, p, stt, &pq) == -1)
+ goto end;
+ if (!StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->server) ||
+ !StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->client) ||
+ f.alproto != ALPROTO_HTTP ||
+ f.alproto_ts != ALPROTO_HTTP ||
+ f.alproto_tc != ALPROTO_HTTP ||
+ f.data_al_so_far[0] != 0 ||
+ f.data_al_so_far[1] != 0 ||
+ ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED ||
+ !FLOW_IS_PM_DONE(&f, STREAM_TOSERVER) || !FLOW_IS_PP_DONE(&f, STREAM_TOSERVER) ||
+ !FLOW_IS_PM_DONE(&f, STREAM_TOCLIENT) || FLOW_IS_PP_DONE(&f, STREAM_TOCLIENT) ||
+ ssn->client.seg_list == NULL ||
+ ssn->client.seg_list->next == NULL ||
+ ssn->client.seg_list->next->next == NULL ||
+ ssn->server.seg_list == NULL ||
+ ssn->server.seg_list->next != NULL ||
+ ssn->data_first_seen_dir != APP_LAYER_DATA_ALREADY_SENT_TO_APP_LAYER) {
+ printf("failure 12\n");
+ goto end;
+ }
+
+
+ /* response ack against partial request */
+ p->tcph->th_ack = htonl(90);
+ p->tcph->th_seq = htonl(328);
+ p->tcph->th_flags = TH_ACK;
+ p->flowflags = FLOW_PKT_TOCLIENT;
+ p->payload_len = 0;
+ p->payload = NULL;
+ if (StreamTcpPacket(&tv, p, stt, &pq) == -1)
+ goto end;
+ if (!StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->server) ||
+ !StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->client) ||
+ f.alproto != ALPROTO_HTTP ||
+ f.alproto_ts != ALPROTO_HTTP ||
+ f.alproto_tc != ALPROTO_HTTP ||
+ f.data_al_so_far[0] != 0 ||
+ f.data_al_so_far[1] != 0 ||
+ ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED ||
+ !FLOW_IS_PM_DONE(&f, STREAM_TOSERVER) || !FLOW_IS_PP_DONE(&f, STREAM_TOSERVER) ||
+ !FLOW_IS_PM_DONE(&f, STREAM_TOCLIENT) || FLOW_IS_PP_DONE(&f, STREAM_TOCLIENT) ||
+ ssn->client.seg_list == NULL ||
+ ssn->client.seg_list->next == NULL ||
+ ssn->client.seg_list->next->next == NULL ||
+ ssn->server.seg_list == NULL ||
+ ssn->server.seg_list->next != NULL ||
+ ssn->data_first_seen_dir != APP_LAYER_DATA_ALREADY_SENT_TO_APP_LAYER) {
+ printf("failure 13\n");
+ goto end;
+ }
+
+ /* complete request */
+ p->tcph->th_ack = htonl(328);
+ p->tcph->th_seq = htonl(90);
+ p->tcph->th_flags = TH_PUSH | TH_ACK;
+ p->flowflags = FLOW_PKT_TOSERVER;
+ p->payload_len = sizeof(request2);
+ p->payload = request2;
+ if (StreamTcpPacket(&tv, p, stt, &pq) == -1)
+ goto end;
+ if (!StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->server) ||
+ !StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->client) ||
+ f.alproto != ALPROTO_HTTP ||
+ f.alproto_ts != ALPROTO_HTTP ||
+ f.alproto_tc != ALPROTO_HTTP ||
+ f.data_al_so_far[0] != 0 ||
+ f.data_al_so_far[1] != 0 ||
+ ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED ||
+ !FLOW_IS_PM_DONE(&f, STREAM_TOSERVER) || !FLOW_IS_PP_DONE(&f, STREAM_TOSERVER) ||
+ !FLOW_IS_PM_DONE(&f, STREAM_TOCLIENT) || FLOW_IS_PP_DONE(&f, STREAM_TOCLIENT) ||
+ ssn->client.seg_list == NULL ||
+ ssn->client.seg_list->next == NULL ||
+ ssn->client.seg_list->next->next == NULL ||
+ ssn->client.seg_list->next->next->next == NULL ||
+ ssn->server.seg_list == NULL ||
+ ssn->server.seg_list->next != NULL ||
+ ssn->data_first_seen_dir != APP_LAYER_DATA_ALREADY_SENT_TO_APP_LAYER) {
+ printf("failure 14\n");
+ goto end;
+ }
+
+ /* response ack against second partial request */
+ p->tcph->th_ack = htonl(175);
+ p->tcph->th_seq = htonl(328);
+ p->tcph->th_flags = TH_ACK;
+ p->flowflags = FLOW_PKT_TOCLIENT;
+ p->payload_len = 0;
+ p->payload = NULL;
+ if (StreamTcpPacket(&tv, p, stt, &pq) == -1)
+ goto end;
+ if (!StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->server) ||
+ !StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->client) ||
+ f.alproto != ALPROTO_HTTP ||
+ f.alproto_ts != ALPROTO_HTTP ||
+ f.alproto_tc != ALPROTO_HTTP ||
+ f.data_al_so_far[0] != 0 ||
+ f.data_al_so_far[1] != 0 ||
+ ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED ||
+ !FLOW_IS_PM_DONE(&f, STREAM_TOSERVER) || !FLOW_IS_PP_DONE(&f, STREAM_TOSERVER) ||
+ !FLOW_IS_PM_DONE(&f, STREAM_TOCLIENT) || FLOW_IS_PP_DONE(&f, STREAM_TOCLIENT) ||
+ ssn->client.seg_list->next == NULL ||
+ ssn->client.seg_list->next->next == NULL ||
+ ssn->client.seg_list->next->next->next == NULL ||
+ ssn->server.seg_list == NULL ||
+ ssn->server.seg_list->next != NULL ||
+ ssn->data_first_seen_dir != APP_LAYER_DATA_ALREADY_SENT_TO_APP_LAYER) {
+ printf("failure 15\n");
+ goto end;
+ }
+
+ if (ssn->toserver_smsg_head == NULL ||
+ ssn->toserver_smsg_head->next == NULL ||
+ ssn->toserver_smsg_head->next->next != NULL ||
+ ssn->toclient_smsg_head == NULL ||
+ ssn->toclient_smsg_head->next != NULL) {
+ printf("failure 16\n");
+ goto end;
+ }
+
+ StreamMsgReturnListToPool(ssn->toserver_smsg_head);
+ ssn->toserver_smsg_head = ssn->toserver_smsg_tail = NULL;
+ StreamMsgReturnListToPool(ssn->toclient_smsg_head);
+ ssn->toclient_smsg_head = ssn->toclient_smsg_tail = NULL;
+
+ /* response acking a request */
+ p->tcph->th_ack = htonl(175);
+ p->tcph->th_seq = htonl(328);
+ p->tcph->th_flags = TH_ACK;
+ p->flowflags = FLOW_PKT_TOCLIENT;
+ p->payload_len = 0;
+ p->payload = NULL;
+ if (StreamTcpPacket(&tv, p, stt, &pq) == -1)
+ goto end;
+ if (!StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->server) ||
+ !StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->client) ||
+ f.alproto != ALPROTO_HTTP ||
+ f.alproto_ts != ALPROTO_HTTP ||
+ f.alproto_tc != ALPROTO_HTTP ||
+ f.data_al_so_far[0] != 0 ||
+ f.data_al_so_far[1] != 0 ||
+ ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED ||
+ !FLOW_IS_PM_DONE(&f, STREAM_TOSERVER) || !FLOW_IS_PP_DONE(&f, STREAM_TOSERVER) ||
+ !FLOW_IS_PM_DONE(&f, STREAM_TOCLIENT) || FLOW_IS_PP_DONE(&f, STREAM_TOCLIENT) ||
+ ssn->client.seg_list != NULL ||
+ ssn->server.seg_list == NULL ||
+ ssn->server.seg_list->next != NULL ||
+ ssn->data_first_seen_dir != APP_LAYER_DATA_ALREADY_SENT_TO_APP_LAYER) {
+ printf("failure 15\n");
+ goto end;
+ }
+
+ /* request acking a response */
+ p->tcph->th_ack = htonl(328);
+ p->tcph->th_seq = htonl(175);
+ p->tcph->th_flags = TH_ACK;
+ p->flowflags = FLOW_PKT_TOSERVER;
+ p->payload_len = 0;
+ p->payload = NULL;
+ if (StreamTcpPacket(&tv, p, stt, &pq) == -1)
+ goto end;
+ if (!StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->server) ||
+ !StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->client) ||
+ f.alproto != ALPROTO_HTTP ||
+ f.alproto_ts != ALPROTO_HTTP ||
+ f.alproto_tc != ALPROTO_HTTP ||
+ f.data_al_so_far[0] != 0 ||
+ f.data_al_so_far[1] != 0 ||
+ ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED ||
+ !FLOW_IS_PM_DONE(&f, STREAM_TOSERVER) || !FLOW_IS_PP_DONE(&f, STREAM_TOSERVER) ||
+ !FLOW_IS_PM_DONE(&f, STREAM_TOCLIENT) || FLOW_IS_PP_DONE(&f, STREAM_TOCLIENT) ||
+ ssn->client.seg_list != NULL ||
+ ssn->server.seg_list != NULL ||
+ ssn->data_first_seen_dir != APP_LAYER_DATA_ALREADY_SENT_TO_APP_LAYER) {
+ printf("failure 15\n");
+ goto end;
+ }
+
+
+ ret = 1;
+end:
+ StreamTcpThreadDeinit(&tv, (void *)stt);
+ StreamTcpSessionClear(p->flow->protoctx);
+ StreamTcpFreeConfig(TRUE);
+ SCFree(p);
+ SCMutexUnlock(&f.m);
+ return ret;
+}
+
+/**
+ * \test Test to make sure that we sent all the segments from the initial
+ * segments to app layer until we have detected the app layer proto.
+ *
+ * \retval On success it returns 1 and on failure 0.
+ */
+
+static int StreamTcpReassembleTest40 (void)
+{
+ int ret = 0;
+ Packet *p = PacketGetFromAlloc();
+ if (unlikely(p == NULL))
+ return 0;
+ Flow *f = NULL;
+ TCPHdr tcph;
+ TcpSession ssn;
+ PacketQueue pq;
+ memset(&pq,0,sizeof(PacketQueue));
+ memset(&tcph, 0, sizeof (TCPHdr));
+ memset(&ssn, 0, sizeof(TcpSession));
+ ThreadVars tv;
+ memset(&tv, 0, sizeof (ThreadVars));
+
+ StreamTcpInitConfig(TRUE);
+ StreamMsgQueueSetMinChunkLen(FLOW_PKT_TOSERVER, 130);
+
+ TcpReassemblyThreadCtx *ra_ctx = StreamTcpReassembleInitThreadCtx(NULL);
+
+ uint8_t httpbuf1[] = "P";
+ uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
+ uint8_t httpbuf3[] = "O";
+ uint32_t httplen3 = sizeof(httpbuf3) - 1; /* minus the \0 */
+ uint8_t httpbuf4[] = "S";
+ uint32_t httplen4 = sizeof(httpbuf4) - 1; /* minus the \0 */
+ uint8_t httpbuf5[] = "T \r\n";
+ uint32_t httplen5 = sizeof(httpbuf5) - 1; /* minus the \0 */
+
+ uint8_t httpbuf2[] = "HTTP/1.0 200 OK\r\nServer: VictorServer/1.0\r\n\r\n";
+ uint32_t httplen2 = sizeof(httpbuf2) - 1; /* minus the \0 */
+
+ ssn.server.ra_raw_base_seq = ssn.server.ra_app_base_seq = 9;
+ ssn.server.isn = 9;
+ ssn.server.last_ack = 10;
+ ssn.client.ra_raw_base_seq = ssn.client.ra_app_base_seq = 9;
+ ssn.client.isn = 9;
+ ssn.client.last_ack = 10;
+
+ f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 200, 220);
+ if (f == NULL)
+ goto end;
+ f->protoctx = &ssn;
+ f->proto = IPPROTO_TCP;
+ p->flow = f;
+
+ tcph.th_win = htons(5480);
+ tcph.th_seq = htonl(10);
+ tcph.th_ack = htonl(10);
+ tcph.th_flags = TH_ACK|TH_PUSH;
+ p->tcph = &tcph;
+ p->flowflags = FLOW_PKT_TOSERVER;
+
+ p->payload = httpbuf1;
+ p->payload_len = httplen1;
+ ssn.state = TCP_ESTABLISHED;
+
+ TcpStream *s = NULL;
+ s = &ssn.client;
+
+ SCMutexLock(&f->m);
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx, &ssn, s, p, &pq) == -1) {
+ printf("failed in segments reassembly, while processing toserver packet (1): ");
+ goto end;
+ }
+
+ /* Check if we have stream smsgs in queue */
+ if (UtSsnSmsgCnt(&ssn, STREAM_TOCLIENT) > 0) {
+ printf("there shouldn't be any stream smsgs in the queue, as we didn't"
+ " processed any smsg from toserver side till yet (2): ");
+ goto end;
+ }
+
+ p->flowflags = FLOW_PKT_TOCLIENT;
+ p->payload = httpbuf2;
+ p->payload_len = httplen2;
+ tcph.th_seq = htonl(10);
+ tcph.th_ack = htonl(11);
+ s = &ssn.server;
+ ssn.server.last_ack = 11;
+
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx, &ssn, s, p, &pq) == -1) {
+ printf("failed in segments reassembly, while processing toserver packet (3): ");
+ goto end;
+ }
+
+ p->flowflags = FLOW_PKT_TOSERVER;
+ p->payload = httpbuf3;
+ p->payload_len = httplen3;
+ tcph.th_seq = htonl(11);
+ tcph.th_ack = htonl(55);
+ s = &ssn.client;
+ ssn.client.last_ack = 55;
+
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx, &ssn, s, p, &pq) == -1) {
+ printf("failed in segments reassembly, while processing toserver packet (5): ");
+ goto end;
+ }
+
+ p->flowflags = FLOW_PKT_TOCLIENT;
+ p->payload = httpbuf2;
+ p->payload_len = httplen2;
+ tcph.th_seq = htonl(55);
+ tcph.th_ack = htonl(12);
+ s = &ssn.server;
+ ssn.server.last_ack = 12;
+
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx, &ssn, s, p, &pq) == -1) {
+ printf("failed in segments reassembly, while processing toserver packet (6): ");
+ goto end;
+ }
+
+ /* check is have the segment in the list and flagged or not */
+ if (ssn.client.seg_list == NULL ||
+ (ssn.client.seg_list->flags & SEGMENTTCP_FLAG_APPLAYER_PROCESSED))
+ {
+ printf("the list is NULL or the processed segment has not been flaged (7): ");
+ goto end;
+ }
+
+ p->flowflags = FLOW_PKT_TOSERVER;
+ p->payload = httpbuf4;
+ p->payload_len = httplen4;
+ tcph.th_seq = htonl(12);
+ tcph.th_ack = htonl(100);
+ s = &ssn.client;
+ ssn.client.last_ack = 100;
+
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx, &ssn, s, p, &pq) == -1) {
+ printf("failed in segments reassembly, while processing toserver packet (10): ");
+ goto end;
+ }
+
+ p->flowflags = FLOW_PKT_TOCLIENT;
+ p->payload = httpbuf2;
+ p->payload_len = httplen2;
+ tcph.th_seq = htonl(100);
+ tcph.th_ack = htonl(13);
+ s = &ssn.server;
+ ssn.server.last_ack = 13;
+
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx, &ssn, s, p, &pq) == -1) {
+ printf("failed in segments reassembly, while processing toserver packet (11): ");
+ goto end;
+ }
+
+ p->flowflags = FLOW_PKT_TOSERVER;
+ p->payload = httpbuf5;
+ p->payload_len = httplen5;
+ tcph.th_seq = htonl(13);
+ tcph.th_ack = htonl(145);
+ s = &ssn.client;
+ ssn.client.last_ack = 145;
+
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx, &ssn, s, p, &pq) == -1) {
+ printf("failed in segments reassembly, while processing toserver packet (14): ");
+ goto end;
+ }
+
+ p->flowflags = FLOW_PKT_TOCLIENT;
+ p->payload = httpbuf2;
+ p->payload_len = httplen2;
+ tcph.th_seq = htonl(145);
+ tcph.th_ack = htonl(16);
+ s = &ssn.server;
+ ssn.server.last_ack = 16;
+
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx, &ssn, s, p, &pq) == -1) {
+ printf("failed in segments reassembly, while processing toserver packet (15): ");
+ goto end;
+ }
+
+ /* Check if we have stream smsgs in queue */
+ if (UtSsnSmsgCnt(&ssn, STREAM_TOSERVER) == 0) {
+ printf("there should be a stream smsgs in the queue, as we have detected"
+ " the app layer protocol and one smsg from toserver side has "
+ "been sent (16): ");
+ goto end;
+ }
+
+ if (f->alproto != ALPROTO_HTTP) {
+ printf("app layer proto has not been detected (18): ");
+ goto end;
+ }
+
+ ret = 1;
+end:
+ StreamTcpReassembleFreeThreadCtx(ra_ctx);
+ StreamTcpFreeConfig(TRUE);
+ SCFree(p);
+ SCMutexUnlock(&f->m);
+ UTHFreeFlow(f);
+ return ret;
+}
+
+/**
+ * \retval On success it returns 1 and on failure 0.
+ */
+
+static int StreamTcpReassembleTest43 (void)
+{
+ int ret = 0;
+ Packet *p = PacketGetFromAlloc();
+ if (unlikely(p == NULL))
+ return 0;
+ Flow *f = NULL;
+ TCPHdr tcph;
+ TcpSession ssn;
+ PacketQueue pq;
+ memset(&pq,0,sizeof(PacketQueue));
+ memset(&tcph, 0, sizeof (TCPHdr));
+ memset(&ssn, 0, sizeof(TcpSession));
+ ThreadVars tv;
+ memset(&tv, 0, sizeof (ThreadVars));
+
+ StreamTcpInitConfig(TRUE);
+ TcpReassemblyThreadCtx *ra_ctx = StreamTcpReassembleInitThreadCtx(NULL);
+
+ uint8_t httpbuf1[] = "/ HTTP/1.0\r\nUser-Agent: Victor/1.0";
+
+ uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
+
+ uint8_t httpbuf2[] = "HTTP/1.0 200 OK\r\nServer: VictorServer/1.0\r\n\r\n";
+ uint32_t httplen2 = sizeof(httpbuf2) - 1; /* minus the \0 */
+
+ uint8_t httpbuf3[] = "W2dyb3VwMV0NCnBob25lMT1wMDB3ODgyMTMxMzAyMTINCmxvZ2lu"
+ "MT0NCnBhc3N3b3JkMT0NCnBob25lMj1wMDB3ODgyMTMxMzAyMTIN"
+ "CmxvZ2luMj0NCnBhc3N3b3JkMj0NCnBob25lMz0NCmxvZ2luMz0N"
+ "CnBhc3N3b3JkMz0NCnBob25lND0NCmxvZ2luND0NCnBhc3N3b3Jk"
+ "ND0NCnBob25lNT0NCmxvZ2luNT0NCnBhc3N3b3JkNT0NCnBob25l"
+ "Nj0NCmxvZ2luNj0NCnBhc3N3b3JkNj0NCmNhbGxfdGltZTE9MzIN"
+ "CmNhbGxfdGltZTI9MjMyDQpkYXlfbGltaXQ9NQ0KbW9udGhfbGlt"
+ "aXQ9MTUNCltncm91cDJdDQpwaG9uZTE9DQpsb2dpbjE9DQpwYXNz"
+ "d29yZDE9DQpwaG9uZTI9DQpsb2dpbjI9DQpwYXNzd29yZDI9DQpw"
+ "aG9uZT\r\n\r\n";
+ uint32_t httplen3 = sizeof(httpbuf3) - 1; /* minus the \0 */
+
+ ssn.server.ra_raw_base_seq = ssn.server.ra_app_base_seq = 9;
+ ssn.server.isn = 9;
+ ssn.server.last_ack = 600;
+ ssn.client.ra_raw_base_seq = ssn.client.ra_app_base_seq = 9;
+ ssn.client.isn = 9;
+ ssn.client.last_ack = 600;
+
+ f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 200, 220);
+ if (f == NULL)
+ goto end;
+ f->protoctx = &ssn;
+ f->proto = IPPROTO_TCP;
+ p->flow = f;
+
+ tcph.th_win = htons(5480);
+ tcph.th_seq = htonl(10);
+ tcph.th_ack = htonl(10);
+ tcph.th_flags = TH_ACK|TH_PUSH;
+ p->tcph = &tcph;
+ p->flowflags = FLOW_PKT_TOCLIENT;
+
+ p->payload = httpbuf2;
+ p->payload_len = httplen2;
+ ssn.state = TCP_ESTABLISHED;
+
+ TcpStream *s = NULL;
+ s = &ssn.server;
+
+ SCMutexLock(&f->m);
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx, &ssn, s, p, &pq) == -1) {
+ printf("failed in segments reassembly, while processing toserver packet (1): ");
+ goto end;
+ }
+
+ /* Check if we have stream smsgs in queue */
+ if (UtSsnSmsgCnt(&ssn, STREAM_TOSERVER) > 0) {
+ printf("there shouldn't be any stream smsgs in the queue (2): ");
+ goto end;
+ }
+
+ p->flowflags = FLOW_PKT_TOSERVER;
+ p->payload = httpbuf1;
+ p->payload_len = httplen1;
+ tcph.th_seq = htonl(10);
+ tcph.th_ack = htonl(55);
+ s = &ssn.client;
+
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx, &ssn, s, p, &pq) == -1) {
+ printf("failed in segments reassembly, while processing toserver packet (3): ");
+ goto end;
+ }
+
+ /* Check if we have stream smsgs in queue */
+ if (UtSsnSmsgCnt(&ssn, STREAM_TOCLIENT) > 0) {
+ printf("there shouldn't be any stream smsgs in the queue, as we didn't"
+ " processed any smsg from toserver side till yet (4): ");
+ goto end;
+ }
+
+ p->flowflags = FLOW_PKT_TOCLIENT;
+ p->payload = httpbuf2;
+ p->payload_len = httplen2;
+ tcph.th_seq = htonl(55);
+ tcph.th_ack = htonl(44);
+ s = &ssn.server;
+
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx, &ssn, s, p, &pq) == -1) {
+ printf("failed in segments reassembly, while processing toserver packet (5): ");
+ goto end;
+ }
+ if (!StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn.client)) {
+ printf("app layer detected flag isn't set, it should be (8): ");
+ goto end;
+ }
+
+ /* This packets induces a packet gap and also shows why we need to
+ process the current segment completely, even if it results in sending more
+ than one smsg to the app layer. If we don't send more than one smsg in
+ this case, then the first segment of lentgh 34 bytes will be sent to
+ app layer and protocol can not be detected in that message and moreover
+ the segment lentgh is less than the max. signature size for protocol
+ detection, so this will keep looping !! */
+ p->flowflags = FLOW_PKT_TOSERVER;
+ p->payload = httpbuf3;
+ p->payload_len = httplen3;
+ tcph.th_seq = htonl(54);
+ tcph.th_ack = htonl(100);
+ s = &ssn.client;
+
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx, &ssn, s, p, &pq) == -1) {
+ printf("failed in segments reassembly, while processing toserver packet (9): ");
+ goto end;
+ }
+
+ /* Check if we have stream smsgs in queue */
+ if (UtSsnSmsgCnt(&ssn, STREAM_TOCLIENT) > 0) {
+ printf("there shouldn't be any stream smsgs in the queue, as we didn't"
+ " detected the app layer protocol till yet (10): ");
+ goto end;
+ }
+
+ p->flowflags = FLOW_PKT_TOCLIENT;
+ p->payload = httpbuf2;
+ p->payload_len = httplen2;
+ tcph.th_seq = htonl(100);
+ tcph.th_ack = htonl(53);
+ s = &ssn.server;
+
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx, &ssn, s, p, &pq) == -1) {
+ printf("failed in segments reassembly, while processing toserver packet (11): ");
+ goto end;
+ }
+ /* the flag should be set, as the smsg scanned size has crossed the max.
+ signature size for app proto detection */
+ if (!StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn.client)) {
+ printf("app layer detected flag is not set, it should be (14): ");
+ goto end;
+ }
+
+ ret = 1;
+end:
+ StreamTcpReassembleFreeThreadCtx(ra_ctx);
+ StreamTcpFreeConfig(TRUE);
+ SCFree(p);
+ SCMutexUnlock(&f->m);
+ UTHFreeFlow(f);
+ return ret;
+}
+
+/** \test Test the memcap incrementing/decrementing and memcap check */
+static int StreamTcpReassembleTest44(void)
+{
+ uint8_t ret = 0;
+ StreamTcpInitConfig(TRUE);
+ uint32_t memuse = SC_ATOMIC_GET(ra_memuse);
+
+ StreamTcpReassembleIncrMemuse(500);
+ if (SC_ATOMIC_GET(ra_memuse) != (memuse+500)) {
+ printf("failed in incrementing the memory");
+ goto end;
+ }
+
+ StreamTcpReassembleDecrMemuse(500);
+ if (SC_ATOMIC_GET(ra_memuse) != memuse) {
+ printf("failed in decrementing the memory");
+ goto end;
+ }
+
+ if (StreamTcpReassembleCheckMemcap(500) != 1) {
+ printf("failed in validating the memcap");
+ goto end;
+ }
+
+ if (StreamTcpReassembleCheckMemcap((memuse + stream_config.reassembly_memcap)) != 0) {
+ printf("failed in validating the memcap");
+ goto end;
+ }
+
+ StreamTcpFreeConfig(TRUE);
+
+ if (SC_ATOMIC_GET(ra_memuse) != 0) {
+ printf("failed in clearing the memory");
+ goto end;
+ }
+
+ ret = 1;
+ return ret;
+end:
+ StreamTcpFreeConfig(TRUE);
+ return ret;
+}
+
+/**
+ * \test Test to make sure that reassembly_depth is enforced.
+ *
+ * \retval On success it returns 1 and on failure 0.
+ */
+
+static int StreamTcpReassembleTest45 (void)
+{
+ int ret = 0;
+ Packet *p = PacketGetFromAlloc();
+ if (unlikely(p == NULL))
+ return 0;
+ Flow *f = NULL;
+ TCPHdr tcph;
+ TcpSession ssn;
+ PacketQueue pq;
+ memset(&pq,0,sizeof(PacketQueue));
+ memset(&tcph, 0, sizeof (TCPHdr));
+ memset(&ssn, 0, sizeof(TcpSession));
+ ThreadVars tv;
+ memset(&tv, 0, sizeof (ThreadVars));
+
+ uint8_t httpbuf1[] = "/ HTTP/1.0\r\nUser-Agent: Victor/1.0";
+
+ uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
+
+ StreamTcpInitConfig(TRUE);
+ TcpReassemblyThreadCtx *ra_ctx = StreamTcpReassembleInitThreadCtx(NULL);
+
+ STREAMTCP_SET_RA_BASE_SEQ(&ssn.server, 9);
+ ssn.server.isn = 9;
+ ssn.server.last_ack = 60;
+ STREAMTCP_SET_RA_BASE_SEQ(&ssn.client, 9);
+ ssn.client.isn = 9;
+ ssn.client.last_ack = 60;
+
+ f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 200, 220);
+ if (f == NULL)
+ goto end;
+ f->protoctx = &ssn;
+ f->proto = IPPROTO_TCP;
+ p->flow = f;
+
+ tcph.th_win = htons(5480);
+ tcph.th_seq = htonl(10);
+ tcph.th_ack = htonl(20);
+ tcph.th_flags = TH_ACK|TH_PUSH;
+ p->tcph = &tcph;
+ p->flowflags = FLOW_PKT_TOCLIENT;
+
+ p->payload = httpbuf1;
+ p->payload_len = httplen1;
+ ssn.state = TCP_ESTABLISHED;
+
+ /* set the default value of reassembly depth, as there is no config file */
+ stream_config.reassembly_depth = httplen1 + 1;
+
+ TcpStream *s = NULL;
+ s = &ssn.server;
+
+ SCMutexLock(&f->m);
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx, &ssn, s, p, &pq) == -1) {
+ printf("failed in segments reassembly, while processing toclient packet: ");
+ goto end;
+ }
+
+ /* Check if we have flags set or not */
+ if (s->flags & STREAMTCP_STREAM_FLAG_NOREASSEMBLY) {
+ printf("there shouldn't be a noreassembly flag be set: ");
+ goto end;
+ }
+ STREAMTCP_SET_RA_BASE_SEQ(&ssn.server, ssn.server.isn + httplen1);
+
+ p->flowflags = FLOW_PKT_TOSERVER;
+ p->payload_len = httplen1;
+ s = &ssn.client;
+
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx, &ssn, s, p, &pq) == -1) {
+ printf("failed in segments reassembly, while processing toserver packet: ");
+ goto end;
+ }
+
+ /* Check if we have flags set or not */
+ if (s->flags & STREAMTCP_STREAM_FLAG_NOREASSEMBLY) {
+ printf("there shouldn't be a noreassembly flag be set: ");
+ goto end;
+ }
+ STREAMTCP_SET_RA_BASE_SEQ(&ssn.client, ssn.client.isn + httplen1);
+
+ p->flowflags = FLOW_PKT_TOCLIENT;
+ p->payload_len = httplen1;
+ s = &ssn.server;
+
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx, &ssn, s, p, &pq) == -1) {
+ printf("failed in segments reassembly, while processing toserver packet: ");
+ goto end;
+ }
+
+ /* Check if we have flags set or not */
+ if (!(s->flags & STREAMTCP_STREAM_FLAG_NOREASSEMBLY)) {
+ printf("the noreassembly flags should be set, "
+ "p.payload_len %"PRIu16" stream_config.reassembly_"
+ "depth %"PRIu32": ", p->payload_len,
+ stream_config.reassembly_depth);
+ goto end;
+ }
+
+ ret = 1;
+end:
+ StreamTcpReassembleFreeThreadCtx(ra_ctx);
+ StreamTcpFreeConfig(TRUE);
+ SCFree(p);
+ SCMutexUnlock(&f->m);
+ UTHFreeFlow(f);
+ return ret;
+}
+
+/**
+ * \test Test the undefined config value of reassembly depth.
+ * the default value of 0 will be loaded and stream will be reassembled
+ * until the session ended
+ *
+ * \retval On success it returns 1 and on failure 0.
+ */
+
+static int StreamTcpReassembleTest46 (void)
+{
+ int ret = 0;
+ Packet *p = PacketGetFromAlloc();
+ if (unlikely(p == NULL))
+ return 0;
+ Flow *f = NULL;
+ TCPHdr tcph;
+ TcpSession ssn;
+ ThreadVars tv;
+ PacketQueue pq;
+ memset(&pq,0,sizeof(PacketQueue));
+ memset(&tcph, 0, sizeof (TCPHdr));
+ memset(&ssn, 0, sizeof(TcpSession));
+ memset(&tv, 0, sizeof (ThreadVars));
+
+ uint8_t httpbuf1[] = "/ HTTP/1.0\r\nUser-Agent: Victor/1.0";
+ uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
+
+ StreamTcpInitConfig(TRUE);
+ TcpReassemblyThreadCtx *ra_ctx = StreamTcpReassembleInitThreadCtx(NULL);
+
+ STREAMTCP_SET_RA_BASE_SEQ(&ssn.server, 9);
+ ssn.server.isn = 9;
+ ssn.server.last_ack = 60;
+ 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.next_seq = ssn.client.isn;
+
+ f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 200, 220);
+ if (f == NULL)
+ goto end;
+ f->protoctx = &ssn;
+ f->proto = IPPROTO_TCP;
+ p->flow = f;
+
+ tcph.th_win = htons(5480);
+ tcph.th_seq = htonl(10);
+ tcph.th_ack = htonl(20);
+ tcph.th_flags = TH_ACK|TH_PUSH;
+ p->tcph = &tcph;
+ p->flowflags = FLOW_PKT_TOCLIENT;
+
+ p->payload = httpbuf1;
+ p->payload_len = httplen1;
+ ssn.state = TCP_ESTABLISHED;
+
+ stream_config.reassembly_depth = 0;
+
+ TcpStream *s = NULL;
+ s = &ssn.server;
+
+ SCMutexLock(&f->m);
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx, &ssn, s, p, &pq) == -1) {
+ printf("failed in segments reassembly, while processing toclient packet\n");
+ goto end;
+ }
+
+ /* Check if we have flags set or not */
+ if ((ssn.client.flags & STREAMTCP_STREAM_FLAG_NOREASSEMBLY) ||
+ (ssn.server.flags & STREAMTCP_STREAM_FLAG_NOREASSEMBLY)) {
+ printf("there shouldn't be any no reassembly flag be set \n");
+ goto end;
+ }
+ STREAMTCP_SET_RA_BASE_SEQ(&ssn.server, ssn.server.isn + httplen1);
+
+ p->flowflags = FLOW_PKT_TOSERVER;
+ p->payload_len = httplen1;
+ s = &ssn.client;
+
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx, &ssn, s, p, &pq) == -1) {
+ printf("failed in segments reassembly, while processing toserver packet\n");
+ goto end;
+ }
+
+ /* Check if we have flags set or not */
+ if ((ssn.client.flags & STREAMTCP_STREAM_FLAG_NOREASSEMBLY) ||
+ (ssn.server.flags & STREAMTCP_STREAM_FLAG_NOREASSEMBLY)) {
+ printf("there shouldn't be any no reassembly flag be set \n");
+ goto end;
+ }
+ STREAMTCP_SET_RA_BASE_SEQ(&ssn.client, ssn.client.isn + httplen1);
+
+ p->flowflags = FLOW_PKT_TOCLIENT;
+ p->payload_len = httplen1;
+ tcph.th_seq = htonl(10 + httplen1);
+ tcph.th_ack = htonl(20 + httplen1);
+ s = &ssn.server;
+
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx, &ssn, s, p, &pq) == -1) {
+ printf("failed in segments reassembly, while processing toserver packet\n");
+ goto end;
+ }
+
+ /* Check if we have flags set or not */
+ if ((ssn.client.flags & STREAMTCP_STREAM_FLAG_NOREASSEMBLY) ||
+ (ssn.server.flags & STREAMTCP_STREAM_FLAG_NOREASSEMBLY)) {
+ printf("the no_reassembly flags should not be set, "
+ "p->payload_len %"PRIu16" stream_config.reassembly_"
+ "depth %"PRIu32": ", p->payload_len,
+ stream_config.reassembly_depth);
+ goto end;
+ }
+
+ ret = 1;
+end:
+ StreamTcpReassembleFreeThreadCtx(ra_ctx);
+ StreamTcpFreeConfig(TRUE);
+ SCFree(p);
+ SCMutexUnlock(&f->m);
+ UTHFreeFlow(f);
+ return ret;
+}
+
+/**
+ * \test Test to make sure we detect the sequence wrap around and continue
+ * stream reassembly properly.
+ *
+ * \retval On success it returns 1 and on failure 0.
+ */
+
+static int StreamTcpReassembleTest47 (void)
+{
+ int ret = 0;
+ Packet *p = PacketGetFromAlloc();
+ if (unlikely(p == NULL))
+ return 0;
+ Flow *f = NULL;
+ TCPHdr tcph;
+ TcpSession ssn;
+ ThreadVars tv;
+ PacketQueue pq;
+ memset(&pq,0,sizeof(PacketQueue));
+ memset(&tcph, 0, sizeof (TCPHdr));
+ memset(&ssn, 0, sizeof(TcpSession));
+ memset(&tv, 0, sizeof (ThreadVars));
+
+ /* prevent L7 from kicking in */
+ StreamMsgQueueSetMinChunkLen(FLOW_PKT_TOSERVER, 0);
+ StreamMsgQueueSetMinChunkLen(FLOW_PKT_TOCLIENT, 0);
+
+ StreamTcpInitConfig(TRUE);
+ TcpReassemblyThreadCtx *ra_ctx = StreamTcpReassembleInitThreadCtx(NULL);
+
+ uint8_t httpbuf1[] = "GET /EVILSUFF HTTP/1.1\r\n\r\n";
+ uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
+
+ ssn.server.ra_raw_base_seq = ssn.server.ra_app_base_seq = 572799781UL;
+ ssn.server.isn = 572799781UL;
+ ssn.server.last_ack = 572799782UL;
+ ssn.client.ra_raw_base_seq = ssn.client.ra_app_base_seq = 4294967289UL;
+ ssn.client.isn = 4294967289UL;
+ ssn.client.last_ack = 21;
+
+ f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 200, 220);
+ if (f == NULL)
+ goto end;
+ f->protoctx = &ssn;
+ f->proto = IPPROTO_TCP;
+ p->flow = f;
+
+ tcph.th_win = htons(5480);
+ ssn.state = TCP_ESTABLISHED;
+ TcpStream *s = NULL;
+ uint8_t cnt = 0;
+
+ SCMutexLock(&f->m);
+ for (cnt=0; cnt < httplen1; cnt++) {
+ tcph.th_seq = htonl(ssn.client.isn + 1 + cnt);
+ tcph.th_ack = htonl(572799782UL);
+ tcph.th_flags = TH_ACK|TH_PUSH;
+ p->tcph = &tcph;
+ p->flowflags = FLOW_PKT_TOSERVER;
+ p->payload = &httpbuf1[cnt];
+ p->payload_len = 1;
+ s = &ssn.client;
+
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx, &ssn, s, p, &pq) == -1) {
+ printf("failed in segments reassembly, while processing toserver "
+ "packet\n");
+ goto end;
+ }
+
+ p->flowflags = FLOW_PKT_TOCLIENT;
+ p->payload = NULL;
+ p->payload_len = 0;
+ tcph.th_seq = htonl(572799782UL);
+ tcph.th_ack = htonl(ssn.client.isn + 1 + cnt);
+ tcph.th_flags = TH_ACK;
+ p->tcph = &tcph;
+ s = &ssn.server;
+
+ if (StreamTcpReassembleHandleSegment(&tv, ra_ctx, &ssn, s, p, &pq) == -1) {
+ printf("failed in segments reassembly, while processing toserver "
+ "packet\n");
+ goto end;
+ }
+ }
+
+ if (f->alproto != ALPROTO_HTTP) {
+ printf("App layer protocol (HTTP) should have been detected\n");
+ goto end;
+ }
+
+ ret = 1;
+end:
+ StreamTcpReassembleFreeThreadCtx(ra_ctx);
+ StreamTcpFreeConfig(TRUE);
+ SCFree(p);
+ SCMutexUnlock(&f->m);
+ UTHFreeFlow(f);
+ return ret;
+}
+
+/** \test 3 in order segments in inline reassembly */
+static int StreamTcpReassembleInlineTest01(void)
+{
+ int ret = 0;
+ TcpReassemblyThreadCtx *ra_ctx = NULL;
+ ThreadVars tv;
+ TcpSession ssn;
+ Flow f;
+
+ memset(&tv, 0x00, sizeof(tv));
+
+ StreamTcpUTInit(&ra_ctx);
+ StreamTcpUTInitInline();
+ StreamTcpUTSetupSession(&ssn);
+ StreamTcpUTSetupStream(&ssn.client, 1);
+ FLOW_INITIALIZE(&f);
+
+ uint8_t stream_payload[] = "AAAAABBBBBCCCCC";
+ uint8_t payload[] = { 'C', 'C', 'C', 'C', 'C' };
+ Packet *p = UTHBuildPacketReal(payload, 5, IPPROTO_TCP, "1.1.1.1", "2.2.2.2", 1024, 80);
+ if (p == NULL) {
+ printf("couldn't get a packet: ");
+ goto end;
+ }
+ p->tcph->th_seq = htonl(12);
+ p->flow = &f;
+
+ SCMutexLock(&f.m);
+ if (StreamTcpUTAddSegmentWithByte(&tv, ra_ctx, &ssn.client, 2, 'A', 5) == -1) {
+ printf("failed to add segment 1: ");
+ goto end;
+ }
+ if (StreamTcpUTAddSegmentWithByte(&tv, ra_ctx, &ssn.client, 7, 'B', 5) == -1) {
+ printf("failed to add segment 2: ");
+ goto end;
+ }
+ if (StreamTcpUTAddSegmentWithByte(&tv, ra_ctx, &ssn.client, 12, 'C', 5) == -1) {
+ printf("failed to add segment 3: ");
+ goto end;
+ }
+ ssn.client.next_seq = 17;
+
+ int r = StreamTcpReassembleInlineRaw(ra_ctx, &ssn, &ssn.client, p);
+ if (r < 0) {
+ printf("StreamTcpReassembleInlineRaw failed: ");
+ goto end;
+ }
+
+ if (UtSsnSmsgCnt(&ssn, STREAM_TOSERVER) != 1) {
+ printf("expected a single stream message: ");
+ goto end;
+ }
+
+ StreamMsg *smsg = ssn.toserver_smsg_head;
+ if (UtTestSmsg(smsg, stream_payload, 15) == 0)
+ goto end;
+
+ ret = 1;
+end:
+ SCMutexUnlock(&f.m);
+ FLOW_DESTROY(&f);
+ UTHFreePacket(p);
+ StreamTcpUTClearSession(&ssn);
+ StreamTcpUTDeinit(ra_ctx);
+ return ret;
+}
+
+/** \test 3 in order segments, then reassemble, add one more and reassemble again.
+ * test the sliding window reassembly.
+ */
+static int StreamTcpReassembleInlineTest02(void)
+{
+ int ret = 0;
+ TcpReassemblyThreadCtx *ra_ctx = NULL;
+ ThreadVars tv;
+ TcpSession ssn;
+ Flow f;
+
+ memset(&tv, 0x00, sizeof(tv));
+
+ StreamTcpUTInit(&ra_ctx);
+ StreamTcpUTInitInline();
+ StreamTcpUTSetupSession(&ssn);
+ StreamTcpUTSetupStream(&ssn.client, 1);
+ FLOW_INITIALIZE(&f);
+
+ uint8_t stream_payload1[] = "AAAAABBBBBCCCCC";
+ uint8_t stream_payload2[] = "AAAAABBBBBCCCCCDDDDD";
+ uint8_t payload[] = { 'C', 'C', 'C', 'C', 'C' };
+ Packet *p = UTHBuildPacketReal(payload, 5, IPPROTO_TCP, "1.1.1.1", "2.2.2.2", 1024, 80);
+ if (p == NULL) {
+ printf("couldn't get a packet: ");
+ goto end;
+ }
+ p->tcph->th_seq = htonl(12);
+ p->flow = &f;
+
+ SCMutexLock(&f.m);
+ if (StreamTcpUTAddSegmentWithByte(&tv, ra_ctx, &ssn.client, 2, 'A', 5) == -1) {
+ printf("failed to add segment 1: ");
+ goto end;
+ }
+ if (StreamTcpUTAddSegmentWithByte(&tv, ra_ctx, &ssn.client, 7, 'B', 5) == -1) {
+ printf("failed to add segment 2: ");
+ goto end;
+ }
+ if (StreamTcpUTAddSegmentWithByte(&tv, ra_ctx, &ssn.client, 12, 'C', 5) == -1) {
+ printf("failed to add segment 3: ");
+ goto end;
+ }
+ ssn.client.next_seq = 17;
+
+ int r = StreamTcpReassembleInlineRaw(ra_ctx, &ssn, &ssn.client, p);
+ if (r < 0) {
+ printf("StreamTcpReassembleInlineRaw failed: ");
+ goto end;
+ }
+
+ if (UtSsnSmsgCnt(&ssn, STREAM_TOSERVER) != 1) {
+ printf("expected a single stream message: ");
+ goto end;
+ }
+
+ StreamMsg *smsg = ssn.toserver_smsg_head;
+ if (UtTestSmsg(smsg, stream_payload1, 15) == 0)
+ goto end;
+
+ if (StreamTcpUTAddSegmentWithByte(&tv, ra_ctx, &ssn.client, 17, 'D', 5) == -1) {
+ printf("failed to add segment 4: ");
+ goto end;
+ }
+ ssn.client.next_seq = 22;
+
+ r = StreamTcpReassembleInlineRaw(ra_ctx, &ssn, &ssn.client, p);
+ if (r < 0) {
+ printf("StreamTcpReassembleInlineRaw failed 2: ");
+ goto end;
+ }
+
+ if (UtSsnSmsgCnt(&ssn, STREAM_TOSERVER) != 2) {
+ printf("expected a single stream message: ");
+ goto end;
+ }
+
+ smsg = ssn.toserver_smsg_head->next;
+ if (UtTestSmsg(smsg, stream_payload2, 20) == 0)
+ goto end;
+
+ ret = 1;
+end:
+ SCMutexUnlock(&f.m);
+ FLOW_DESTROY(&f);
+ UTHFreePacket(p);
+ StreamTcpUTClearSession(&ssn);
+ StreamTcpUTDeinit(ra_ctx);
+ return ret;
+}
+
+/** \test 3 in order segments, then reassemble, add one more and reassemble again.
+ * test the sliding window reassembly with a small window size so that we
+ * cutting off at the start (left edge)
+ */
+static int StreamTcpReassembleInlineTest03(void)
+{
+ int ret = 0;
+ TcpReassemblyThreadCtx *ra_ctx = NULL;
+ ThreadVars tv;
+ TcpSession ssn;
+ Flow f;
+
+ memset(&tv, 0x00, sizeof(tv));
+
+ StreamTcpUTInit(&ra_ctx);
+ StreamTcpUTInitInline();
+ StreamTcpUTSetupSession(&ssn);
+ StreamTcpUTSetupStream(&ssn.client, 1);
+ FLOW_INITIALIZE(&f);
+
+ stream_config.reassembly_toserver_chunk_size = 15;
+
+ uint8_t stream_payload1[] = "AAAAABBBBBCCCCC";
+ uint8_t stream_payload2[] = "BBBBBCCCCCDDDDD";
+ uint8_t payload[] = { 'C', 'C', 'C', 'C', 'C' };
+ Packet *p = UTHBuildPacketReal(payload, 5, IPPROTO_TCP, "1.1.1.1", "2.2.2.2", 1024, 80);
+ if (p == NULL) {
+ printf("couldn't get a packet: ");
+ goto end;
+ }
+ p->tcph->th_seq = htonl(12);
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+
+ SCMutexLock(&f.m);
+ if (StreamTcpUTAddSegmentWithByte(&tv, ra_ctx, &ssn.client, 2, 'A', 5) == -1) {
+ printf("failed to add segment 1: ");
+ goto end;
+ }
+ if (StreamTcpUTAddSegmentWithByte(&tv, ra_ctx, &ssn.client, 7, 'B', 5) == -1) {
+ printf("failed to add segment 2: ");
+ goto end;
+ }
+ if (StreamTcpUTAddSegmentWithByte(&tv, ra_ctx, &ssn.client, 12, 'C', 5) == -1) {
+ printf("failed to add segment 3: ");
+ goto end;
+ }
+ ssn.client.next_seq = 17;
+
+ int r = StreamTcpReassembleInlineRaw(ra_ctx, &ssn, &ssn.client, p);
+ if (r < 0) {
+ printf("StreamTcpReassembleInlineRaw failed: ");
+ goto end;
+ }
+
+ if (UtSsnSmsgCnt(&ssn, STREAM_TOSERVER) != 1) {
+ printf("expected a single stream message 1: ");
+ goto end;
+ }
+
+ StreamMsg *smsg = ssn.toserver_smsg_head;
+ if (UtTestSmsg(smsg, stream_payload1, 15) == 0)
+ goto end;
+
+ if (StreamTcpUTAddSegmentWithByte(&tv, ra_ctx, &ssn.client, 17, 'D', 5) == -1) {
+ printf("failed to add segment 4: ");
+ goto end;
+ }
+ ssn.client.next_seq = 22;
+
+ p->tcph->th_seq = htonl(17);
+
+ r = StreamTcpReassembleInlineRaw(ra_ctx, &ssn, &ssn.client, p);
+ if (r < 0) {
+ printf("StreamTcpReassembleInlineRaw failed 2: ");
+ goto end;
+ }
+
+ if (UtSsnSmsgCnt(&ssn, STREAM_TOSERVER) != 2) {
+ printf("expected two stream messages: ");
+ goto end;
+ }
+
+ smsg = ssn.toserver_smsg_head->next;
+ if (UtTestSmsg(smsg, stream_payload2, 15) == 0)
+ goto end;
+
+ ret = 1;
+end:
+ SCMutexUnlock(&f.m);
+ FLOW_DESTROY(&f);
+ UTHFreePacket(p);
+ StreamTcpUTClearSession(&ssn);
+ StreamTcpUTDeinit(ra_ctx);
+ return ret;
+}
+
+/** \test 3 in order segments, then reassemble, add one more and reassemble again.
+ * test the sliding window reassembly with a small window size so that we
+ * cutting off at the start (left edge) with small packet overlap.
+ */
+static int StreamTcpReassembleInlineTest04(void)
+{
+ int ret = 0;
+ TcpReassemblyThreadCtx *ra_ctx = NULL;
+ ThreadVars tv;
+ TcpSession ssn;
+ Flow f;
+
+ memset(&tv, 0x00, sizeof(tv));
+
+ StreamTcpUTInit(&ra_ctx);
+ StreamTcpUTInitInline();
+ StreamTcpUTSetupSession(&ssn);
+ StreamTcpUTSetupStream(&ssn.client, 1);
+ FLOW_INITIALIZE(&f);
+
+ stream_config.reassembly_toserver_chunk_size = 16;
+
+ uint8_t stream_payload1[] = "AAAAABBBBBCCCCC";
+ uint8_t stream_payload2[] = "ABBBBBCCCCCDDDDD";
+ uint8_t payload[] = { 'C', 'C', 'C', 'C', 'C' };
+ Packet *p = UTHBuildPacketReal(payload, 5, IPPROTO_TCP, "1.1.1.1", "2.2.2.2", 1024, 80);
+ if (p == NULL) {
+ printf("couldn't get a packet: ");
+ goto end;
+ }
+ p->tcph->th_seq = htonl(12);
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+
+ SCMutexLock(&f.m);
+ if (StreamTcpUTAddSegmentWithByte(&tv, ra_ctx, &ssn.client, 2, 'A', 5) == -1) {
+ printf("failed to add segment 1: ");
+ goto end;
+ }
+ if (StreamTcpUTAddSegmentWithByte(&tv, ra_ctx, &ssn.client, 7, 'B', 5) == -1) {
+ printf("failed to add segment 2: ");
+ goto end;
+ }
+ if (StreamTcpUTAddSegmentWithByte(&tv, ra_ctx, &ssn.client, 12, 'C', 5) == -1) {
+ printf("failed to add segment 3: ");
+ goto end;
+ }
+ ssn.client.next_seq = 17;
+
+ int r = StreamTcpReassembleInlineRaw(ra_ctx, &ssn, &ssn.client, p);
+ if (r < 0) {
+ printf("StreamTcpReassembleInlineRaw failed: ");
+ goto end;
+ }
+
+ if (UtSsnSmsgCnt(&ssn, STREAM_TOSERVER) != 1) {
+ printf("expected a single stream message: ");
+ goto end;
+ }
+
+ StreamMsg *smsg = ssn.toserver_smsg_head;
+ if (UtTestSmsg(smsg, stream_payload1, 15) == 0)
+ goto end;
+
+ if (StreamTcpUTAddSegmentWithByte(&tv, ra_ctx, &ssn.client, 17, 'D', 5) == -1) {
+ printf("failed to add segment 4: ");
+ goto end;
+ }
+ ssn.client.next_seq = 22;
+
+ p->tcph->th_seq = htonl(17);
+
+ r = StreamTcpReassembleInlineRaw(ra_ctx, &ssn, &ssn.client, p);
+ if (r < 0) {
+ printf("StreamTcpReassembleInlineRaw failed 2: ");
+ goto end;
+ }
+
+ if (UtSsnSmsgCnt(&ssn, STREAM_TOSERVER) != 2) {
+ printf("expected a single stream message: ");
+ goto end;
+ }
+
+ smsg = ssn.toserver_smsg_head->next;
+ if (UtTestSmsg(smsg, stream_payload2, 16) == 0)
+ goto end;
+
+ ret = 1;
+end:
+ SCMutexUnlock(&f.m);
+ FLOW_DESTROY(&f);
+ UTHFreePacket(p);
+ StreamTcpUTClearSession(&ssn);
+ StreamTcpUTDeinit(ra_ctx);
+ return ret;
+}
+
+/** \test with a GAP we should have 2 smsgs */
+static int StreamTcpReassembleInlineTest05(void)
+{
+ int ret = 0;
+ TcpReassemblyThreadCtx *ra_ctx = NULL;
+ ThreadVars tv;
+ TcpSession ssn;
+ Flow f;
+
+ memset(&tv, 0x00, sizeof(tv));
+
+ StreamTcpUTInit(&ra_ctx);
+ StreamTcpUTInitInline();
+ StreamTcpUTSetupSession(&ssn);
+ StreamTcpUTSetupStream(&ssn.client, 1);
+ FLOW_INITIALIZE(&f);
+
+ uint8_t stream_payload1[] = "AAAAABBBBB";
+ uint8_t stream_payload2[] = "DDDDD";
+ uint8_t payload[] = { 'C', 'C', 'C', 'C', 'C' };
+ Packet *p = UTHBuildPacketReal(payload, 5, IPPROTO_TCP, "1.1.1.1", "2.2.2.2", 1024, 80);
+ if (p == NULL) {
+ printf("couldn't get a packet: ");
+ goto end;
+ }
+ p->tcph->th_seq = htonl(12);
+ p->flow = &f;
+
+ SCMutexLock(&f.m);
+ if (StreamTcpUTAddSegmentWithByte(&tv, ra_ctx, &ssn.client, 2, 'A', 5) == -1) {
+ printf("failed to add segment 1: ");
+ goto end;
+ }
+ if (StreamTcpUTAddSegmentWithByte(&tv, ra_ctx, &ssn.client, 7, 'B', 5) == -1) {
+ printf("failed to add segment 2: ");
+ goto end;
+ }
+ ssn.client.next_seq = 12;
+
+ if (StreamTcpUTAddSegmentWithByte(&tv, ra_ctx, &ssn.client, 17, 'D', 5) == -1) {
+ printf("failed to add segment 4: ");
+ goto end;
+ }
+
+ p->tcph->th_seq = htonl(17);
+
+ int r = StreamTcpReassembleInlineRaw(ra_ctx, &ssn, &ssn.client, p);
+ if (r < 0) {
+ printf("StreamTcpReassembleInlineRaw failed: ");
+ goto end;
+ }
+
+ if (UtSsnSmsgCnt(&ssn, STREAM_TOSERVER) != 2) {
+ printf("expected a single stream message: ");
+ goto end;
+ }
+
+ StreamMsg *smsg = ssn.toserver_smsg_head;
+ if (UtTestSmsg(smsg, stream_payload1, 10) == 0)
+ goto end;
+
+ smsg = ssn.toserver_smsg_head->next;
+ if (UtTestSmsg(smsg, stream_payload2, 5) == 0)
+ goto end;
+
+ ret = 1;
+end:
+ SCMutexUnlock(&f.m);
+ FLOW_DESTROY(&f);
+ UTHFreePacket(p);
+ StreamTcpUTClearSession(&ssn);
+ StreamTcpUTDeinit(ra_ctx);
+ return ret;
+}
+
+/** \test with a GAP we should have 2 smsgs, with filling the GAP later */
+static int StreamTcpReassembleInlineTest06(void)
+{
+ int ret = 0;
+ TcpReassemblyThreadCtx *ra_ctx = NULL;
+ ThreadVars tv;
+ TcpSession ssn;
+ Flow f;
+
+ memset(&tv, 0x00, sizeof(tv));
+
+ StreamTcpUTInit(&ra_ctx);
+ StreamTcpUTInitInline();
+ StreamTcpUTSetupSession(&ssn);
+ StreamTcpUTSetupStream(&ssn.client, 1);
+ FLOW_INITIALIZE(&f);
+
+ uint8_t stream_payload1[] = "AAAAABBBBB";
+ uint8_t stream_payload2[] = "DDDDD";
+ uint8_t stream_payload3[] = "AAAAABBBBBCCCCCDDDDD";
+ uint8_t payload[] = { 'C', 'C', 'C', 'C', 'C' };
+ Packet *p = UTHBuildPacketReal(payload, 5, IPPROTO_TCP, "1.1.1.1", "2.2.2.2", 1024, 80);
+ if (p == NULL) {
+ printf("couldn't get a packet: ");
+ goto end;
+ }
+ p->tcph->th_seq = htonl(12);
+ p->flow = &f;
+
+ SCMutexLock(&f.m);
+ if (StreamTcpUTAddSegmentWithByte(&tv, ra_ctx, &ssn.client, 2, 'A', 5) == -1) {
+ printf("failed to add segment 1: ");
+ goto end;
+ }
+ if (StreamTcpUTAddSegmentWithByte(&tv, ra_ctx, &ssn.client, 7, 'B', 5) == -1) {
+ printf("failed to add segment 2: ");
+ goto end;
+ }
+ ssn.client.next_seq = 12;
+
+ if (StreamTcpUTAddSegmentWithByte(&tv, ra_ctx, &ssn.client, 17, 'D', 5) == -1) {
+ printf("failed to add segment 4: ");
+ goto end;
+ }
+
+ p->tcph->th_seq = htonl(17);
+
+ int r = StreamTcpReassembleInlineRaw(ra_ctx, &ssn, &ssn.client, p);
+ if (r < 0) {
+ printf("StreamTcpReassembleInlineRaw failed: ");
+ goto end;
+ }
+
+ if (UtSsnSmsgCnt(&ssn, STREAM_TOSERVER) != 2) {
+ printf("expected two stream messages: ");
+ goto end;
+ }
+
+ StreamMsg *smsg = ssn.toserver_smsg_head;
+ if (UtTestSmsg(smsg, stream_payload1, 10) == 0)
+ goto end;
+
+ smsg = ssn.toserver_smsg_head->next;
+ if (UtTestSmsg(smsg, stream_payload2, 5) == 0)
+ goto end;
+
+ if (StreamTcpUTAddSegmentWithByte(&tv, ra_ctx, &ssn.client, 12, 'C', 5) == -1) {
+ printf("failed to add segment 3: ");
+ goto end;
+ }
+ ssn.client.next_seq = 22;
+
+ p->tcph->th_seq = htonl(12);
+
+ r = StreamTcpReassembleInlineRaw(ra_ctx, &ssn, &ssn.client, p);
+ if (r < 0) {
+ printf("StreamTcpReassembleInlineRaw failed: ");
+ goto end;
+ }
+
+ if (UtSsnSmsgCnt(&ssn, STREAM_TOSERVER) != 3) {
+ printf("expected a single stream message, got %u: ", UtSsnSmsgCnt(&ssn, STREAM_TOSERVER));
+ goto end;
+ }
+
+ smsg = ssn.toserver_smsg_head->next->next;
+ if (UtTestSmsg(smsg, stream_payload3, 20) == 0)
+ goto end;
+
+ ret = 1;
+end:
+ SCMutexUnlock(&f.m);
+ FLOW_DESTROY(&f);
+ UTHFreePacket(p);
+ StreamTcpUTClearSession(&ssn);
+ StreamTcpUTDeinit(ra_ctx);
+ return ret;
+}
+
+/** \test with a GAP we should have 2 smsgs, with filling the GAP later, small
+ * window */
+static int StreamTcpReassembleInlineTest07(void)
+{
+ int ret = 0;
+ TcpReassemblyThreadCtx *ra_ctx = NULL;
+ ThreadVars tv;
+ TcpSession ssn;
+ Flow f;
+
+ memset(&tv, 0x00, sizeof(tv));
+
+ StreamTcpUTInit(&ra_ctx);
+ StreamTcpUTInitInline();
+ StreamTcpUTSetupSession(&ssn);
+ StreamTcpUTSetupStream(&ssn.client, 1);
+ FLOW_INITIALIZE(&f);
+
+ stream_config.reassembly_toserver_chunk_size = 16;
+
+ uint8_t stream_payload1[] = "ABBBBB";
+ uint8_t stream_payload2[] = "DDDDD";
+ uint8_t stream_payload3[] = "AAAAABBBBBCCCCCD";
+ uint8_t payload[] = { 'C', 'C', 'C', 'C', 'C' };
+ Packet *p = UTHBuildPacketReal(payload, 5, IPPROTO_TCP, "1.1.1.1", "2.2.2.2", 1024, 80);
+ if (p == NULL) {
+ printf("couldn't get a packet: ");
+ goto end;
+ }
+ p->tcph->th_seq = htonl(12);
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+
+ SCMutexLock(&f.m);
+ if (StreamTcpUTAddSegmentWithByte(&tv, ra_ctx, &ssn.client, 2, 'A', 5) == -1) {
+ printf("failed to add segment 1: ");
+ goto end;
+ }
+ if (StreamTcpUTAddSegmentWithByte(&tv, ra_ctx, &ssn.client, 7, 'B', 5) == -1) {
+ printf("failed to add segment 2: ");
+ goto end;
+ }
+ ssn.client.next_seq = 12;
+
+ if (StreamTcpUTAddSegmentWithByte(&tv, ra_ctx, &ssn.client, 17, 'D', 5) == -1) {
+ printf("failed to add segment 4: ");
+ goto end;
+ }
+
+ p->tcph->th_seq = htonl(17);
+
+ int r = StreamTcpReassembleInlineRaw(ra_ctx, &ssn, &ssn.client, p);
+ if (r < 0) {
+ printf("StreamTcpReassembleInlineRaw failed: ");
+ goto end;
+ }
+
+ if (UtSsnSmsgCnt(&ssn, STREAM_TOSERVER) != 2) {
+ printf("expected a single stream message, got %u: ", UtSsnSmsgCnt(&ssn, STREAM_TOSERVER));
+ goto end;
+ }
+
+ StreamMsg *smsg = ssn.toserver_smsg_head;
+ if (UtTestSmsg(smsg, stream_payload1, 6) == 0)
+ goto end;
+
+ smsg = ssn.toserver_smsg_head->next;
+ if (UtTestSmsg(smsg, stream_payload2, 5) == 0)
+ goto end;
+
+ if (StreamTcpUTAddSegmentWithByte(&tv, ra_ctx, &ssn.client, 12, 'C', 5) == -1) {
+ printf("failed to add segment 3: ");
+ goto end;
+ }
+ ssn.client.next_seq = 22;
+
+ p->tcph->th_seq = htonl(12);
+
+ r = StreamTcpReassembleInlineRaw(ra_ctx, &ssn, &ssn.client, p);
+ if (r < 0) {
+ printf("StreamTcpReassembleInlineRaw failed: ");
+ goto end;
+ }
+
+ if (UtSsnSmsgCnt(&ssn, STREAM_TOSERVER) != 3) {
+ printf("expected a single stream message, got %u: ", UtSsnSmsgCnt(&ssn, STREAM_TOSERVER));
+ goto end;
+ }
+
+ smsg = ssn.toserver_smsg_head->next->next;
+ if (UtTestSmsg(smsg, stream_payload3, 16) == 0)
+ goto end;
+
+ ret = 1;
+end:
+ SCMutexUnlock(&f.m);
+ FLOW_DESTROY(&f);
+ UTHFreePacket(p);
+ StreamTcpUTClearSession(&ssn);
+ StreamTcpUTDeinit(ra_ctx);
+ return ret;
+}
+
+/** \test 3 in order segments, then reassemble, add one more and reassemble again.
+ * test the sliding window reassembly with a small window size so that we
+ * cutting off at the start (left edge). Test if the first segment is
+ * removed from the list.
+ */
+static int StreamTcpReassembleInlineTest08(void)
+{
+ int ret = 0;
+ TcpReassemblyThreadCtx *ra_ctx = NULL;
+ ThreadVars tv;
+ TcpSession ssn;
+ Flow f;
+
+ memset(&tv, 0x00, sizeof(tv));
+
+ StreamTcpUTInit(&ra_ctx);
+ StreamTcpUTInitInline();
+ StreamTcpUTSetupSession(&ssn);
+ StreamTcpUTSetupStream(&ssn.client, 1);
+ FLOW_INITIALIZE(&f);
+
+ stream_config.reassembly_toserver_chunk_size = 15;
+ ssn.client.flags |= STREAMTCP_STREAM_FLAG_GAP;
+
+ uint8_t stream_payload1[] = "AAAAABBBBBCCCCC";
+ uint8_t stream_payload2[] = "BBBBBCCCCCDDDDD";
+ uint8_t payload[] = { 'C', 'C', 'C', 'C', 'C' };
+ Packet *p = UTHBuildPacketReal(payload, 5, IPPROTO_TCP, "1.1.1.1", "2.2.2.2", 1024, 80);
+ if (p == NULL) {
+ printf("couldn't get a packet: ");
+ goto end;
+ }
+ p->tcph->th_seq = htonl(12);
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+
+ SCMutexLock(&f.m);
+ if (StreamTcpUTAddSegmentWithByte(&tv, ra_ctx, &ssn.client, 2, 'A', 5) == -1) {
+ printf("failed to add segment 1: ");
+ goto end;
+ }
+ if (StreamTcpUTAddSegmentWithByte(&tv, ra_ctx, &ssn.client, 7, 'B', 5) == -1) {
+ printf("failed to add segment 2: ");
+ goto end;
+ }
+ if (StreamTcpUTAddSegmentWithByte(&tv, ra_ctx, &ssn.client, 12, 'C', 5) == -1) {
+ printf("failed to add segment 3: ");
+ goto end;
+ }
+ ssn.client.next_seq = 17;
+
+ int r = StreamTcpReassembleInlineRaw(ra_ctx, &ssn, &ssn.client, p);
+ if (r < 0) {
+ printf("StreamTcpReassembleInlineRaw failed: ");
+ goto end;
+ }
+
+ if (UtSsnSmsgCnt(&ssn, STREAM_TOSERVER) != 1) {
+ printf("expected a single stream message: ");
+ goto end;
+ }
+
+ StreamMsg *smsg = ssn.toserver_smsg_head;
+ if (UtTestSmsg(smsg, stream_payload1, 15) == 0)
+ goto end;
+
+ if (ssn.client.ra_raw_base_seq != 16) {
+ printf("ra_raw_base_seq %"PRIu32", expected 16: ", ssn.client.ra_raw_base_seq);
+ goto end;
+ }
+
+ if (StreamTcpUTAddSegmentWithByte(&tv, ra_ctx, &ssn.client, 17, 'D', 5) == -1) {
+ printf("failed to add segment 4: ");
+ goto end;
+ }
+ ssn.client.next_seq = 22;
+
+ p->tcph->th_seq = htonl(17);
+
+ r = StreamTcpReassembleInlineRaw(ra_ctx, &ssn, &ssn.client, p);
+ if (r < 0) {
+ printf("StreamTcpReassembleInlineRaw failed 2: ");
+ goto end;
+ }
+
+ if (UtSsnSmsgCnt(&ssn, STREAM_TOSERVER) != 2) {
+ printf("expected a single stream message, got %u: ", UtSsnSmsgCnt(&ssn, STREAM_TOSERVER));
+ goto end;
+ }
+
+ smsg = ssn.toserver_smsg_head->next;
+ if (UtTestSmsg(smsg, stream_payload2, 15) == 0)
+ goto end;
+
+ if (ssn.client.ra_raw_base_seq != 21) {
+ printf("ra_raw_base_seq %"PRIu32", expected 21: ", ssn.client.ra_raw_base_seq);
+ goto end;
+ }
+
+ if (ssn.client.seg_list->seq != 7) {
+ printf("expected segment 2 (seq 7) to be first in the list, got seq %"PRIu32": ", ssn.client.seg_list->seq);
+ goto end;
+ }
+
+ ret = 1;
+end:
+ SCMutexUnlock(&f.m);
+ FLOW_DESTROY(&f);
+ UTHFreePacket(p);
+ StreamTcpUTClearSession(&ssn);
+ StreamTcpUTDeinit(ra_ctx);
+ return ret;
+}
+
+/** \test 3 in order segments, then reassemble, add one more and reassemble again.
+ * test the sliding window reassembly with a small window size so that we
+ * cutting off at the start (left edge). Test if the first segment is
+ * removed from the list.
+ */
+static int StreamTcpReassembleInlineTest09(void)
+{
+ int ret = 0;
+ TcpReassemblyThreadCtx *ra_ctx = NULL;
+ ThreadVars tv;
+ TcpSession ssn;
+ Flow f;
+
+ memset(&tv, 0x00, sizeof(tv));
+
+ StreamTcpUTInit(&ra_ctx);
+ StreamTcpUTInitInline();
+ StreamTcpUTSetupSession(&ssn);
+ StreamTcpUTSetupStream(&ssn.client, 1);
+ FLOW_INITIALIZE(&f);
+
+ stream_config.reassembly_toserver_chunk_size = 20;
+ ssn.client.flags |= STREAMTCP_STREAM_FLAG_GAP;
+
+ uint8_t stream_payload1[] = "AAAAABBBBBCCCCC";
+ uint8_t stream_payload2[] = "DDDDD";
+ uint8_t stream_payload3[] = "AAAAABBBBBCCCCCDDDDD";
+ uint8_t payload[] = { 'C', 'C', 'C', 'C', 'C' };
+ Packet *p = UTHBuildPacketReal(payload, 5, IPPROTO_TCP, "1.1.1.1", "2.2.2.2", 1024, 80);
+ if (p == NULL) {
+ printf("couldn't get a packet: ");
+ goto end;
+ }
+ p->tcph->th_seq = htonl(17);
+ p->flow = &f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+
+ SCMutexLock(&f.m);
+ if (StreamTcpUTAddSegmentWithByte(&tv, ra_ctx, &ssn.client, 2, 'A', 5) == -1) {
+ printf("failed to add segment 1: ");
+ goto end;
+ }
+ if (StreamTcpUTAddSegmentWithByte(&tv, ra_ctx, &ssn.client, 7, 'B', 5) == -1) {
+ printf("failed to add segment 2: ");
+ goto end;
+ }
+ if (StreamTcpUTAddSegmentWithByte(&tv, ra_ctx, &ssn.client, 17, 'D', 5) == -1) {
+ printf("failed to add segment 3: ");
+ goto end;
+ }
+ ssn.client.next_seq = 12;
+
+ int r = StreamTcpReassembleInlineRaw(ra_ctx, &ssn, &ssn.client, p);
+ if (r < 0) {
+ printf("StreamTcpReassembleInlineRaw failed: ");
+ goto end;
+ }
+
+ if (UtSsnSmsgCnt(&ssn, STREAM_TOSERVER) != 2) {
+ printf("expected 2 stream message2, got %u: ", UtSsnSmsgCnt(&ssn, STREAM_TOSERVER));
+ goto end;
+ }
+
+ StreamMsg *smsg = ssn.toserver_smsg_head;
+ if (UtTestSmsg(smsg, stream_payload1, 10) == 0)
+ goto end;
+
+ smsg = ssn.toserver_smsg_head->next;
+ if (UtTestSmsg(smsg, stream_payload2, 5) == 0)
+ goto end;
+
+ if (ssn.client.ra_raw_base_seq != 11) {
+ printf("ra_raw_base_seq %"PRIu32", expected 11: ", ssn.client.ra_raw_base_seq);
+ goto end;
+ }
+
+ /* close the GAP and see if we properly reassemble and update ra_base_seq */
+ if (StreamTcpUTAddSegmentWithByte(&tv, ra_ctx, &ssn.client, 12, 'C', 5) == -1) {
+ printf("failed to add segment 4: ");
+ goto end;
+ }
+ ssn.client.next_seq = 22;
+
+ p->tcph->th_seq = htonl(12);
+
+ r = StreamTcpReassembleInlineRaw(ra_ctx, &ssn, &ssn.client, p);
+ if (r < 0) {
+ printf("StreamTcpReassembleInlineRaw failed 2: ");
+ goto end;
+ }
+
+ if (UtSsnSmsgCnt(&ssn, STREAM_TOSERVER) != 3) {
+ printf("expected 3 stream messages: ");
+ goto end;
+ }
+
+ smsg = ssn.toserver_smsg_head->next->next;
+ if (UtTestSmsg(smsg, stream_payload3, 20) == 0)
+ goto end;
+
+ if (ssn.client.ra_raw_base_seq != 21) {
+ printf("ra_raw_base_seq %"PRIu32", expected 21: ", ssn.client.ra_raw_base_seq);
+ goto end;
+ }
+
+ if (ssn.client.seg_list->seq != 2) {
+ printf("expected segment 1 (seq 2) to be first in the list, got seq %"PRIu32": ", ssn.client.seg_list->seq);
+ goto end;
+ }
+
+ ret = 1;
+end:
+ SCMutexUnlock(&f.m);
+ FLOW_DESTROY(&f);
+ UTHFreePacket(p);
+ StreamTcpUTClearSession(&ssn);
+ StreamTcpUTDeinit(ra_ctx);
+ return ret;
+}
+
+/** \test App Layer reassembly.
+ */
+static int StreamTcpReassembleInlineTest10(void)
+{
+ int ret = 0;
+ TcpReassemblyThreadCtx *ra_ctx = NULL;
+ ThreadVars tv;
+ TcpSession ssn;
+ Flow *f = NULL;
+ Packet *p = NULL;
+
+ memset(&tv, 0x00, sizeof(tv));
+
+ StreamTcpUTInit(&ra_ctx);
+ StreamTcpUTInitInline();
+ StreamTcpUTSetupSession(&ssn);
+ StreamTcpUTSetupStream(&ssn.server, 1);
+
+ f = UTHBuildFlow(AF_INET, "1.1.1.1", "2.2.2.2", 1024, 80);
+ if (f == NULL)
+ goto end;
+ f->protoctx = &ssn;
+ f->proto = IPPROTO_TCP;
+
+ uint8_t stream_payload1[] = "GE";
+ uint8_t stream_payload2[] = "T /";
+ uint8_t stream_payload3[] = "HTTP/1.0\r\n\r\n";
+
+ p = UTHBuildPacketReal(stream_payload3, 12, IPPROTO_TCP, "1.1.1.1", "2.2.2.2", 1024, 80);
+ if (p == NULL) {
+ printf("couldn't get a packet: ");
+ goto end;
+ }
+ p->tcph->th_seq = htonl(7);
+ p->flow = f;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+
+ SCMutexLock(&f->m);
+ if (StreamTcpUTAddSegmentWithPayload(&tv, ra_ctx, &ssn.server, 2, stream_payload1, 2) == -1) {
+ printf("failed to add segment 1: ");
+ goto end;
+ }
+ ssn.server.next_seq = 4;
+
+ int r = StreamTcpReassembleAppLayer(&tv, ra_ctx, &ssn, &ssn.server, p);
+ if (r < 0) {
+ printf("StreamTcpReassembleAppLayer failed: ");
+ goto end;
+ }
+
+ /* ssn.server.ra_app_base_seq should be isn here. */
+ if (ssn.server.ra_app_base_seq != 1 || ssn.server.ra_app_base_seq != ssn.server.isn) {
+ printf("expected ra_app_base_seq 1, got %u: ", ssn.server.ra_app_base_seq);
+ goto end;
+ }
+
+ if (StreamTcpUTAddSegmentWithPayload(&tv, ra_ctx, &ssn.server, 4, stream_payload2, 3) == -1) {
+ printf("failed to add segment 2: ");
+ goto end;
+ }
+ if (StreamTcpUTAddSegmentWithPayload(&tv, ra_ctx, &ssn.server, 7, stream_payload3, 12) == -1) {
+ printf("failed to add segment 3: ");
+ goto end;
+ }
+ ssn.server.next_seq = 19;
+
+ r = StreamTcpReassembleAppLayer(&tv, ra_ctx, &ssn, &ssn.server, p);
+ if (r < 0) {
+ printf("StreamTcpReassembleAppLayer failed: ");
+ goto end;
+ }
+
+ if (ssn.server.ra_app_base_seq != 18) {
+ printf("expected ra_app_base_seq 18, got %u: ", ssn.server.ra_app_base_seq);
+ goto end;
+ }
+
+ ret = 1;
+end:
+ UTHFreePacket(p);
+ StreamTcpUTClearSession(&ssn);
+ StreamTcpUTDeinit(ra_ctx);
+ SCMutexUnlock(&f->m);
+ UTHFreeFlow(f);
+ return ret;
+}
+
+/** \test test insert with overlap
+ */
+static int StreamTcpReassembleInsertTest01(void)
+{
+ int ret = 0;
+ TcpReassemblyThreadCtx *ra_ctx = NULL;
+ ThreadVars tv;
+ TcpSession ssn;
+ Flow f;
+
+ memset(&tv, 0x00, sizeof(tv));
+
+ StreamTcpUTInit(&ra_ctx);
+ StreamTcpUTSetupSession(&ssn);
+ StreamTcpUTSetupStream(&ssn.client, 1);
+ FLOW_INITIALIZE(&f);
+
+ uint8_t stream_payload1[] = "AAAAABBBBBCCCCCDDDDD";
+ uint8_t payload[] = { 'C', 'C', 'C', 'C', 'C' };
+ Packet *p = UTHBuildPacketReal(payload, 5, IPPROTO_TCP, "1.1.1.1", "2.2.2.2", 1024, 80);
+ if (p == NULL) {
+ printf("couldn't get a packet: ");
+ goto end;
+ }
+ p->tcph->th_seq = htonl(12);
+ p->flow = &f;
+
+ SCMutexLock(&f.m);
+ if (StreamTcpUTAddSegmentWithByte(&tv, ra_ctx, &ssn.client, 2, 'A', 5) == -1) {
+ printf("failed to add segment 1: ");
+ goto end;
+ }
+ if (StreamTcpUTAddSegmentWithByte(&tv, ra_ctx, &ssn.client, 7, 'B', 5) == -1) {
+ printf("failed to add segment 2: ");
+ goto end;
+ }
+ if (StreamTcpUTAddSegmentWithByte(&tv, ra_ctx, &ssn.client, 14, 'D', 2) == -1) {
+ printf("failed to add segment 3: ");
+ goto end;
+ }
+ if (StreamTcpUTAddSegmentWithByte(&tv, ra_ctx, &ssn.client, 16, 'D', 6) == -1) {
+ printf("failed to add segment 4: ");
+ goto end;
+ }
+ if (StreamTcpUTAddSegmentWithByte(&tv, ra_ctx, &ssn.client, 12, 'C', 5) == -1) {
+ printf("failed to add segment 5: ");
+ goto end;
+ }
+ ssn.client.next_seq = 21;
+
+ int r = StreamTcpReassembleInlineRaw(ra_ctx, &ssn, &ssn.client, p);
+ if (r < 0) {
+ printf("StreamTcpReassembleInlineRaw failed: ");
+ goto end;
+ }
+
+ if (UtSsnSmsgCnt(&ssn, STREAM_TOSERVER) != 1) {
+ printf("expected a single stream message: ");
+ goto end;
+ }
+
+ StreamMsg *smsg = ssn.toserver_smsg_head;
+ if (UtTestSmsg(smsg, stream_payload1, 20) == 0)
+ goto end;
+
+ if (ssn.client.ra_raw_base_seq != 21) {
+ printf("ra_raw_base_seq %"PRIu32", expected 21: ", ssn.client.ra_raw_base_seq);
+ goto end;
+ }
+ ret = 1;
+end:
+ SCMutexUnlock(&f.m);
+ FLOW_DESTROY(&f);
+ UTHFreePacket(p);
+ StreamTcpUTClearSession(&ssn);
+ StreamTcpUTDeinit(ra_ctx);
+ return ret;
+}
+
+/** \test test insert with overlaps
+ */
+static int StreamTcpReassembleInsertTest02(void)
+{
+ int ret = 0;
+ TcpReassemblyThreadCtx *ra_ctx = NULL;
+ ThreadVars tv;
+ TcpSession ssn;
+
+ memset(&tv, 0x00, sizeof(tv));
+
+ StreamTcpUTInit(&ra_ctx);
+ StreamTcpUTSetupSession(&ssn);
+ StreamTcpUTSetupStream(&ssn.client, 1);
+
+ int i;
+ for (i = 2; i < 10; i++) {
+ int len;
+ len = i % 2;
+ if (len == 0)
+ len = 1;
+ int seq;
+ seq = i * 10;
+ if (seq < 2)
+ seq = 2;
+
+ if (StreamTcpUTAddSegmentWithByte(&tv, ra_ctx, &ssn.client, seq, 'A', len) == -1) {
+ printf("failed to add segment 1: ");
+ goto end;
+ }
+ }
+ if (StreamTcpUTAddSegmentWithByte(&tv, ra_ctx, &ssn.client, 2, 'B', 1024) == -1) {
+ printf("failed to add segment 2: ");
+ goto end;
+ }
+
+ ret = 1;
+end:
+ StreamTcpUTClearSession(&ssn);
+ StreamTcpUTDeinit(ra_ctx);
+ return ret;
+}
+
+/** \test test insert with overlaps
+ */
+static int StreamTcpReassembleInsertTest03(void)
+{
+ int ret = 0;
+ TcpReassemblyThreadCtx *ra_ctx = NULL;
+ ThreadVars tv;
+ TcpSession ssn;
+
+ memset(&tv, 0x00, sizeof(tv));
+
+ StreamTcpUTInit(&ra_ctx);
+ StreamTcpUTSetupSession(&ssn);
+ StreamTcpUTSetupStream(&ssn.client, 1);
+
+ if (StreamTcpUTAddSegmentWithByte(&tv, ra_ctx, &ssn.client, 2, 'A', 1024) == -1) {
+ printf("failed to add segment 2: ");
+ goto end;
+ }
+
+ int i;
+ for (i = 2; i < 10; i++) {
+ int len;
+ len = i % 2;
+ if (len == 0)
+ len = 1;
+ int seq;
+ seq = i * 10;
+ if (seq < 2)
+ seq = 2;
+
+ if (StreamTcpUTAddSegmentWithByte(&tv, ra_ctx, &ssn.client, seq, 'B', len) == -1) {
+ printf("failed to add segment 2: ");
+ goto end;
+ }
+ }
+ ret = 1;
+end:
+ StreamTcpUTClearSession(&ssn);
+ StreamTcpUTDeinit(ra_ctx);
+ return ret;
+}
+
+#endif /* UNITTESTS */
+
+/** \brief The Function Register the Unit tests to test the reassembly engine
+ * for various OS policies.
+ */
+
+void StreamTcpReassembleRegisterTests(void)
+{
+#ifdef UNITTESTS
+ UtRegisterTest("StreamTcpReassembleTest01 -- BSD OS Before Reassembly Test", StreamTcpReassembleTest01, 1);
+ UtRegisterTest("StreamTcpReassembleTest02 -- BSD OS At Same Reassembly Test", StreamTcpReassembleTest02, 1);
+ UtRegisterTest("StreamTcpReassembleTest03 -- BSD OS After Reassembly Test", StreamTcpReassembleTest03, 1);
+ UtRegisterTest("StreamTcpReassembleTest04 -- BSD OS Complete Reassembly Test", StreamTcpReassembleTest04, 1);
+ UtRegisterTest("StreamTcpReassembleTest05 -- VISTA OS Before Reassembly Test", StreamTcpReassembleTest05, 1);
+ UtRegisterTest("StreamTcpReassembleTest06 -- VISTA OS At Same Reassembly Test", StreamTcpReassembleTest06, 1);
+ UtRegisterTest("StreamTcpReassembleTest07 -- VISTA OS After Reassembly Test", StreamTcpReassembleTest07, 1);
+ UtRegisterTest("StreamTcpReassembleTest08 -- VISTA OS Complete Reassembly Test", StreamTcpReassembleTest08, 1);
+ UtRegisterTest("StreamTcpReassembleTest09 -- LINUX OS Before Reassembly Test", StreamTcpReassembleTest09, 1);
+ UtRegisterTest("StreamTcpReassembleTest10 -- LINUX OS At Same Reassembly Test", StreamTcpReassembleTest10, 1);
+ UtRegisterTest("StreamTcpReassembleTest11 -- LINUX OS After Reassembly Test", StreamTcpReassembleTest11, 1);
+ UtRegisterTest("StreamTcpReassembleTest12 -- LINUX OS Complete Reassembly Test", StreamTcpReassembleTest12, 1);
+ UtRegisterTest("StreamTcpReassembleTest13 -- LINUX_OLD OS Before Reassembly Test", StreamTcpReassembleTest13, 1);
+ UtRegisterTest("StreamTcpReassembleTest14 -- LINUX_OLD At Same Reassembly Test", StreamTcpReassembleTest14, 1);
+ UtRegisterTest("StreamTcpReassembleTest15 -- LINUX_OLD OS After Reassembly Test", StreamTcpReassembleTest15, 1);
+ UtRegisterTest("StreamTcpReassembleTest16 -- LINUX_OLD OS Complete Reassembly Test", StreamTcpReassembleTest16, 1);
+ UtRegisterTest("StreamTcpReassembleTest17 -- SOLARIS OS Before Reassembly Test", StreamTcpReassembleTest17, 1);
+ UtRegisterTest("StreamTcpReassembleTest18 -- SOLARIS At Same Reassembly Test", StreamTcpReassembleTest18, 1);
+ UtRegisterTest("StreamTcpReassembleTest19 -- SOLARIS OS After Reassembly Test", StreamTcpReassembleTest19, 1);
+ UtRegisterTest("StreamTcpReassembleTest20 -- SOLARIS OS Complete Reassembly Test", StreamTcpReassembleTest20, 1);
+ UtRegisterTest("StreamTcpReassembleTest21 -- LAST OS Before Reassembly Test", StreamTcpReassembleTest21, 1);
+ UtRegisterTest("StreamTcpReassembleTest22 -- LAST OS At Same Reassembly Test", StreamTcpReassembleTest22, 1);
+ UtRegisterTest("StreamTcpReassembleTest23 -- LAST OS After Reassembly Test", StreamTcpReassembleTest23, 1);
+ UtRegisterTest("StreamTcpReassembleTest24 -- LAST OS Complete Reassembly Test", StreamTcpReassembleTest24, 1);
+ UtRegisterTest("StreamTcpReassembleTest25 -- Gap at Start Reassembly Test", StreamTcpReassembleTest25, 1);
+ UtRegisterTest("StreamTcpReassembleTest26 -- Gap at middle Reassembly Test", StreamTcpReassembleTest26, 1);
+ UtRegisterTest("StreamTcpReassembleTest27 -- Gap at after Reassembly Test", StreamTcpReassembleTest27, 1);
+ UtRegisterTest("StreamTcpReassembleTest28 -- Gap at Start IDS missed packet Reassembly Test", StreamTcpReassembleTest28, 1);
+ UtRegisterTest("StreamTcpReassembleTest29 -- Gap at Middle IDS missed packet Reassembly Test", StreamTcpReassembleTest29, 1);
+ UtRegisterTest("StreamTcpReassembleTest30 -- Gap at End IDS missed packet Reassembly Test", StreamTcpReassembleTest30, 1);
+ UtRegisterTest("StreamTcpReassembleTest31 -- Fast Track Reassembly Test", StreamTcpReassembleTest31, 1);
+ UtRegisterTest("StreamTcpReassembleTest32 -- Bug test", StreamTcpReassembleTest32, 1);
+ UtRegisterTest("StreamTcpReassembleTest33 -- Bug test", StreamTcpReassembleTest33, 1);
+ UtRegisterTest("StreamTcpReassembleTest34 -- Bug test", StreamTcpReassembleTest34, 1);
+ UtRegisterTest("StreamTcpReassembleTest35 -- Bug56 test", StreamTcpReassembleTest35, 1);
+ UtRegisterTest("StreamTcpReassembleTest36 -- Bug57 test", StreamTcpReassembleTest36, 1);
+ UtRegisterTest("StreamTcpReassembleTest37 -- Bug76 test", StreamTcpReassembleTest37, 1);
+ UtRegisterTest("StreamTcpReassembleTest38 -- app proto test", StreamTcpReassembleTest38, 1);
+ UtRegisterTest("StreamTcpReassembleTest39 -- app proto test", StreamTcpReassembleTest39, 1);
+ UtRegisterTest("StreamTcpReassembleTest40 -- app proto test", StreamTcpReassembleTest40, 1);
+ UtRegisterTest("StreamTcpReassembleTest43 -- min smsg size test", StreamTcpReassembleTest43, 1);
+ UtRegisterTest("StreamTcpReassembleTest44 -- Memcap Test", StreamTcpReassembleTest44, 1);
+ UtRegisterTest("StreamTcpReassembleTest45 -- Depth Test", StreamTcpReassembleTest45, 1);
+ UtRegisterTest("StreamTcpReassembleTest46 -- Depth Test", StreamTcpReassembleTest46, 1);
+ UtRegisterTest("StreamTcpReassembleTest47 -- TCP Sequence Wraparound Test", StreamTcpReassembleTest47, 1);
+
+ UtRegisterTest("StreamTcpReassembleInlineTest01 -- inline RAW ra", StreamTcpReassembleInlineTest01, 1);
+ UtRegisterTest("StreamTcpReassembleInlineTest02 -- inline RAW ra 2", StreamTcpReassembleInlineTest02, 1);
+ UtRegisterTest("StreamTcpReassembleInlineTest03 -- inline RAW ra 3", StreamTcpReassembleInlineTest03, 1);
+ UtRegisterTest("StreamTcpReassembleInlineTest04 -- inline RAW ra 4", StreamTcpReassembleInlineTest04, 1);
+ UtRegisterTest("StreamTcpReassembleInlineTest05 -- inline RAW ra 5 GAP", StreamTcpReassembleInlineTest05, 1);
+ UtRegisterTest("StreamTcpReassembleInlineTest06 -- inline RAW ra 6 GAP", StreamTcpReassembleInlineTest06, 1);
+ UtRegisterTest("StreamTcpReassembleInlineTest07 -- inline RAW ra 7 GAP", StreamTcpReassembleInlineTest07, 1);
+ UtRegisterTest("StreamTcpReassembleInlineTest08 -- inline RAW ra 8 cleanup", StreamTcpReassembleInlineTest08, 1);
+ UtRegisterTest("StreamTcpReassembleInlineTest09 -- inline RAW ra 9 GAP cleanup", StreamTcpReassembleInlineTest09, 1);
+
+ UtRegisterTest("StreamTcpReassembleInlineTest10 -- inline APP ra 10", StreamTcpReassembleInlineTest10, 1);
+
+ UtRegisterTest("StreamTcpReassembleInsertTest01 -- insert with overlap", StreamTcpReassembleInsertTest01, 1);
+ UtRegisterTest("StreamTcpReassembleInsertTest02 -- insert with overlap", StreamTcpReassembleInsertTest02, 1);
+ UtRegisterTest("StreamTcpReassembleInsertTest03 -- insert with overlap", StreamTcpReassembleInsertTest03, 1);
+
+ StreamTcpInlineRegisterTests();
+ StreamTcpUtilRegisterTests();
+#endif /* UNITTESTS */
+}