]> git.ipfire.org Git - thirdparty/suricata.git/commitdiff
stream/segments: turn linked list into rbtree
authorVictor Julien <victor@inliniac.net>
Thu, 23 Aug 2018 15:27:08 +0000 (17:27 +0200)
committerVictor Julien <victor@inliniac.net>
Mon, 17 Sep 2018 06:27:24 +0000 (08:27 +0200)
To improve worst case performance turn the segments list into a rbtree.
This greatly improves inserts, lookups and removals if the number of
segments gets very large.

The tree is sorted by the segment sequence number as its primary key.
If 2 segments have the same seq, the payload_len (segment length) is
used. Then the larger segment will be places after the smaller segment.
Exact matches are not added to the tree.

src/flow-manager.c
src/stream-tcp-inline.c
src/stream-tcp-inline.h
src/stream-tcp-list.c
src/stream-tcp-list.h
src/stream-tcp-private.h
src/stream-tcp-reassemble.c
src/stream-tcp-util.c
src/stream-tcp.c

index 7c12f62564fdb920ae3dfea2486d8555a8857429..6f378dd3a1d935c88541e4c9dd0ce27ddf03d79f 100644 (file)
@@ -1204,9 +1204,7 @@ static int FlowMgrTest02 (void)
 
     TimeGet(&ts);
     TCP_SEG_LEN(&seg) = 3;
-    seg.next = NULL;
-    seg.prev = NULL;
-    client.seg_list = &seg;
+    TCPSEG_RB_INSERT(&client.seg_tree, &seg);
     ssn.client = client;
     ssn.server = client;
     ssn.state = TCP_ESTABLISHED;
@@ -1310,9 +1308,7 @@ static int FlowMgrTest04 (void)
 
     TimeGet(&ts);
     TCP_SEG_LEN(&seg) = 3;
-    seg.next = NULL;
-    seg.prev = NULL;
-    client.seg_list = &seg;
+    TCPSEG_RB_INSERT(&client.seg_tree, &seg);
     ssn.client = client;
     ssn.server = client;
     ssn.state = TCP_ESTABLISHED;
index d79723dd5141631330623e41678415c199a4b2ef..e7fbb42db6faa3b5e23ee2bb2096663b60f5fd92 100644 (file)
@@ -44,7 +44,8 @@
  *  \retval 0 shared data is the same (or no data is shared)
  *  \retval 1 shared data is different
  */
-int StreamTcpInlineSegmentCompare(TcpStream *stream, Packet *p, TcpSegment *seg)
+int StreamTcpInlineSegmentCompare(const TcpStream *stream,
+        const Packet *p, const TcpSegment *seg)
 {
     SCEnter();
 
@@ -108,7 +109,8 @@ int StreamTcpInlineSegmentCompare(TcpStream *stream, Packet *p, TcpSegment *seg)
  *  \todo What about reassembled fragments?
  *  \todo What about unwrapped tunnel packets?
  */
-void StreamTcpInlineSegmentReplacePacket(TcpStream *stream, Packet *p, TcpSegment *seg)
+void StreamTcpInlineSegmentReplacePacket(const TcpStream *stream,
+        Packet *p, const TcpSegment *seg)
 {
     SCEnter();
 
index 0f4e7fddacc48e6268312a6355f423250d0c2133..515c6fd6503bd3ff4a9d44a17fa1730c84f43ea9 100644 (file)
 
 #include "stream-tcp-private.h"
 
-int StreamTcpInlineSegmentCompare(TcpStream *, Packet *, TcpSegment *);
-void StreamTcpInlineSegmentReplacePacket(TcpStream *, Packet *, TcpSegment *);
+int StreamTcpInlineSegmentCompare(const TcpStream *,
+        const Packet *, const TcpSegment *);
+void StreamTcpInlineSegmentReplacePacket(const TcpStream *,
+        Packet *, const TcpSegment *);
 
 void StreamTcpInlineRegisterTests(void);
 
index c5856f7629fff3469bb049da6326b31d4b757d29..ea8c4f788a5f2c2c819cf13b68095952a6b6315c 100644 (file)
@@ -31,8 +31,6 @@
 #include "util-print.h"
 #include "util-validate.h"
 
-//static void PrintList2(TcpSegment *seg);
-
 static void StreamTcpRemoveSegmentFromStream(TcpStream *stream, TcpSegment *seg);
 
 static int check_overlap_different_data = 0;
@@ -46,6 +44,23 @@ void StreamTcpReassembleConfigEnableOverlapCheck(void)
  *  Inserts and overlap handling
  */
 
+RB_GENERATE(TCPSEG, TcpSegment, rb, TcpSegmentCompare);
+
+int TcpSegmentCompare(struct TcpSegment *a, struct TcpSegment *b)
+{
+    if (SEQ_GT(a->seq, b->seq))
+        return 1;
+    else if (SEQ_LT(a->seq, b->seq))
+        return -1;
+    else {
+        if (a->payload_len == b->payload_len)
+            return 0;
+        else if (a->payload_len > b->payload_len)
+            return 1;
+        else
+            return -1;
+    }
+}
 
 /** \internal
  *  \brief insert segment data into the streaming buffer
@@ -98,22 +113,55 @@ static inline int InsertSegmentDataCustom(TcpStream *stream, TcpSegment *seg, ui
 }
 
 /** \internal
- *  \brief insert the segment into the proper place in the list
+ *  \brief check if this segments overlaps with an in-tree seg.
+ *  \retval true
+ *  \retval false
+ */
+static inline bool CheckOverlap(struct TCPSEG *tree, TcpSegment *seg)
+{
+    const uint32_t re = SEG_SEQ_RIGHT_EDGE(seg);
+    SCLogDebug("start. SEQ %u payload_len %u. Right edge: %u. Seg %p",
+            seg->seq, seg->payload_len, re, seg);
+
+    /* check forward */
+    TcpSegment *next = TCPSEG_RB_NEXT(seg);
+    if (next) {
+        // next has same seq, so data must overlap
+        if (SEQ_EQ(next->seq, seg->seq))
+            return true;
+        // our right edge is beyond next seq, overlap
+        if (SEQ_GT(re, next->seq))
+            return true;
+    }
+    /* check backwards */
+    TcpSegment *prev = TCPSEG_RB_PREV(seg);
+    if (prev) {
+        // prev has same seq, so data must overlap
+        if (SEQ_EQ(prev->seq, seg->seq))
+            return true;
+        // prev's right edge is beyond our seq, overlap
+        const uint32_t prev_re = SEG_SEQ_RIGHT_EDGE(prev);
+        if (SEQ_GT(prev_re, prev->seq))
+            return true;
+    }
+
+    SCLogDebug("no overlap");
+    return false;
+}
+
+/** \internal
+ *  \brief insert the segment into the proper place in the tree
  *         don't worry about the data or overlaps
  *
- *         If seq is equal to list seq, keep sorted by insert time.
- *         1. seg 123 len 12
- *         2. seg 123 len 14
- *         3. seg 124 len 1
- *
+ *  \retval 2 not inserted, data overlap
  *  \retval 1 inserted with overlap detected
  *  \retval 0 inserted, no overlap
  *  \retval -1 error
  */
-static int DoInsertSegment (TcpStream *stream, TcpSegment *seg, Packet *p)
+static int DoInsertSegment (TcpStream *stream, TcpSegment *seg, TcpSegment **dup_seg, Packet *p)
 {
     /* before our base_seq we don't insert it in our list */
-    if (SEQ_LEQ((seg->seq + TCP_SEG_LEN(seg)), stream->base_seq))
+    if (SEQ_LEQ(SEG_SEQ_RIGHT_EDGE(seg), stream->base_seq))
     {
         SCLogDebug("not inserting: SEQ+payload %"PRIu32", last_ack %"PRIu32", "
                 "base_seq %"PRIu32, (seg->seq + TCP_SEG_LEN(seg)),
@@ -123,74 +171,39 @@ static int DoInsertSegment (TcpStream *stream, TcpSegment *seg, Packet *p)
     }
 
     /* fast track */
-    if (stream->seg_list == NULL) {
-        SCLogDebug("empty list, inserting seg %p seq %" PRIu32 ", "
+    if (RB_EMPTY(&stream->seg_tree)) {
+        SCLogDebug("empty tree, inserting seg %p seq %" PRIu32 ", "
                    "len %" PRIu32 "", seg, seg->seq, TCP_SEG_LEN(seg));
-        stream->seg_list = seg;
-        seg->prev = NULL;
-        stream->seg_list_tail = seg;
+        TCPSEG_RB_INSERT(&stream->seg_tree, seg);
         return 0;
     }
 
-    /* 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 +
-                    TCP_SEG_LEN(stream->seg_list_tail))))
+    /* insert the segment in the stream tree using this fast track, if seg->seq
+       is equal or higher than last segments tail. */
+    TcpSegment *last = RB_MAX(TCPSEG, &stream->seg_tree);
+    if (last && SEQ_GEQ(seg->seq, (uint32_t)SEG_SEQ_RIGHT_EDGE(last)))
     {
-        SCLogDebug("seg beyond list tail, append");
-        stream->seg_list_tail->next = seg;
-        seg->prev = stream->seg_list_tail;
-        stream->seg_list_tail = seg;
+        SCLogDebug("seg beyond tree tail, append");
+        TCPSEG_RB_INSERT(&stream->seg_tree, seg);
         return 0;
     }
 
-    /* walk the list to see where we can insert the segment.
-     * Check if a segment overlaps with us, if so we return 1 to indicate
-     * to the caller that we need to handle overlaps. */
-    TcpSegment *list_seg;
-    for (list_seg = stream->seg_list; list_seg != NULL; list_seg = list_seg->next)
-    {
-        if (SEQ_LT(seg->seq, list_seg->seq)) {
-            if (list_seg->prev != NULL) {
-                list_seg->prev->next = seg;
-            } else {
-                stream->seg_list = seg;
-            }
-            seg->prev = list_seg->prev;
-            seg->next = list_seg;
-            list_seg->prev = seg;
-
-            SCLogDebug("inserted %u before %p seq %u", seg->seq, list_seg, list_seg->seq);
-
-            if (seg->prev != NULL) {
-                SCLogDebug("previous %u", seg->prev->seq);
-            }
-            if (seg->next != NULL) {
-                SCLogDebug("next %u", seg->next->seq);
-            }
-            if (seg->prev != NULL && SEQ_GT(SEG_SEQ_RIGHT_EDGE(seg->prev), seg->seq)) {
-                SCLogDebug("seg inserted with overlap (before)");
-                return 1;
-            }
-            else if (SEQ_GT(SEG_SEQ_RIGHT_EDGE(seg), seg->next->seq)) {
-                SCLogDebug("seg inserted with overlap (after)");
-                return 1;
-            }
-
-            return 0;
+    /* insert and then check if there was any overlap with other segments */
+    TcpSegment *res = TCPSEG_RB_INSERT(&stream->seg_tree, seg);
+    if (res) {
+        SCLogDebug("seg has a duplicate in the tree seq %u/%u",
+                res->seq, res->payload_len);
+        /* exact duplicate SEQ + payload_len */
+        *dup_seg = res;
+        return 2; // duplicate has overlap by definition.
+    } else {
+        /* insert succeeded, now check if we overlap with someone */
+        if (CheckOverlap(&stream->seg_tree, seg) == true) {
+            SCLogDebug("seg %u has overlap in the tree", seg->seq);
+            return 1;
         }
     }
-    /* if we got here we didn't insert. Append */
-    seg->prev = stream->seg_list_tail;
-    stream->seg_list_tail->next = seg;
-    stream->seg_list_tail = seg;
-
-    if (seg->prev != NULL && SEQ_GT(SEG_SEQ_RIGHT_EDGE(seg->prev), seg->seq)) {
-        SCLogDebug("seg inserted with overlap (before)");
-        return 1;
-    }
-
-    SCLogDebug("default: append");
+    SCLogDebug("seg %u: no overlap", seg->seq);
     return 0;
 }
 
@@ -212,10 +225,12 @@ static int DoInsertSegment (TcpStream *stream, TcpSegment *seg, Packet *p)
  *  \retval 1 if data was different
  *  \retval 0 data was the same or we didn't check for differences
  */
-static int DoHandleDataOverlap(TcpStream *stream, TcpSegment *list, TcpSegment *seg, uint8_t *buf, Packet *p)
+static int DoHandleDataOverlap(TcpStream *stream, const TcpSegment *list,
+        const TcpSegment *seg, uint8_t *buf, Packet *p)
 {
     SCLogDebug("handle overlap for segment %p seq %u len %u re %u, "
-            "list segment %p seq %u len %u re %u", seg, seg->seq, p->payload_len, SEG_SEQ_RIGHT_EDGE(seg),
+            "list segment %p seq %u len %u re %u", seg, seg->seq,
+            p->payload_len, SEG_SEQ_RIGHT_EDGE(seg),
             list, list->seq, TCP_SEG_LEN(list), SEG_SEQ_RIGHT_EDGE(list));
 
     int data_is_different = 0;
@@ -402,51 +417,55 @@ static int DoHandleDataOverlap(TcpStream *stream, TcpSegment *list, TcpSegment *
 #define MAX_IP_DATA (uint32_t)(65536 - 40) // min ip header and min tcp header
 
 /** \internal
- *  \brief walk segment list backwards to see if there are overlaps
+ *  \brief walk segment tree backwards to see if there are overlaps
  *
- *  Walk back from the current segment which is already in the list.
+ *  Walk back from the current segment which is already in the tree.
  *  We walk until we can't possibly overlap anymore.
  */
-static int DoHandleDataCheckBackwards(TcpStream *stream, TcpSegment *seg, uint8_t *buf, Packet *p)
+static int DoHandleDataCheckBackwards(TcpStream *stream,
+        TcpSegment *seg, uint8_t *buf, Packet *p)
 {
     int retval = 0;
 
-    SCLogDebug("check list backwards: insert data for segment %p seq %u len %u re %u",
+    SCLogDebug("check tree backwards: insert data for segment %p seq %u len %u re %u",
             seg, seg->seq, TCP_SEG_LEN(seg), SEG_SEQ_RIGHT_EDGE(seg));
 
-    TcpSegment *list = seg->prev;
-    do {
+    /* check backwards */
+    TcpSegment *tree_seg = NULL, *s = seg;
+    RB_FOREACH_REVERSE_FROM(tree_seg, TCPSEG, s) {
+        if (tree_seg == seg)
+            continue;
+
         int overlap = 0;
-        if (SEQ_LEQ(SEG_SEQ_RIGHT_EDGE(list), stream->base_seq)) {
+        if (SEQ_LEQ(SEG_SEQ_RIGHT_EDGE(tree_seg), stream->base_seq)) {
             // segment entirely before base_seq
             ;
-        } else if (SEQ_LEQ(list->seq + MAX_IP_DATA, seg->seq)) {
+        } else if (SEQ_LEQ(tree_seg->seq + MAX_IP_DATA, seg->seq)) {
             SCLogDebug("list segment too far to the left, no more overlap will be found");
             break;
-        } else if (SEQ_GT(SEG_SEQ_RIGHT_EDGE(list), seg->seq)) {
+        } else if (SEQ_GT(SEG_SEQ_RIGHT_EDGE(tree_seg), seg->seq)) {
             overlap = 1;
         }
 
-        SCLogDebug("(back) list seg %u len %u re %u overlap? %s", list->seq, TCP_SEG_LEN(list),
-                SEG_SEQ_RIGHT_EDGE(list), overlap ? "yes" : "no");
+        SCLogDebug("(back) tree seg %u len %u re %u overlap? %s",
+                tree_seg->seq, TCP_SEG_LEN(tree_seg),
+                SEG_SEQ_RIGHT_EDGE(tree_seg), overlap ? "yes" : "no");
 
         if (overlap) {
-            retval |= DoHandleDataOverlap(stream, list, seg, buf, p);
+            retval |= DoHandleDataOverlap(stream, tree_seg, seg, buf, p);
         }
-
-        list = list->prev;
-    } while (list != NULL);
-
+    }
     return retval;
 }
 
 /** \internal
- *  \brief walk segment list in forward direction to see if there are overlaps
+ *  \brief walk segment tree in forward direction to see if there are overlaps
  *
- *  Walk forward from the current segment which is already in the list.
+ *  Walk forward from the current segment which is already in the tree.
  *  We walk until the next segs start with a SEQ beyond our right edge.
  */
-static int DoHandleDataCheckForward(TcpStream *stream, TcpSegment *seg, uint8_t *buf, Packet *p)
+static int DoHandleDataCheckForward(TcpStream *stream,
+        TcpSegment *seg, uint8_t *buf, Packet *p)
 {
     int retval = 0;
 
@@ -455,34 +474,39 @@ static int DoHandleDataCheckForward(TcpStream *stream, TcpSegment *seg, uint8_t
     SCLogDebug("check list forward: insert data for segment %p seq %u len %u re %u",
             seg, seg->seq, TCP_SEG_LEN(seg), seg_re);
 
-    TcpSegment *list = seg->next;
-    do {
+    TcpSegment *tree_seg = NULL, *s = seg;
+    RB_FOREACH_FROM(tree_seg, TCPSEG, s) {
+        if (tree_seg == seg)
+            continue;
+
         int overlap = 0;
-        if (SEQ_GT(seg_re, list->seq))
+        if (SEQ_GT(seg_re, tree_seg->seq))
             overlap = 1;
-        else if (SEQ_LEQ(seg_re, list->seq)) {
-            SCLogDebug("list segment %u too far ahead, "
-                    "no more overlaps can happen", list->seq);
+        else if (SEQ_LEQ(seg_re, tree_seg->seq)) {
+            SCLogDebug("tree segment %u too far ahead, "
+                    "no more overlaps can happen", tree_seg->seq);
             break;
         }
 
-        SCLogDebug("(fwd) list seg %u len %u re %u overlap? %s", list->seq,
-                TCP_SEG_LEN(list), SEG_SEQ_RIGHT_EDGE(list), overlap ? "yes" : "no");
+        SCLogDebug("(fwd) in-tree seg %u len %u re %u overlap? %s",
+                tree_seg->seq, TCP_SEG_LEN(tree_seg),
+                SEG_SEQ_RIGHT_EDGE(tree_seg), overlap ? "yes" : "no");
 
         if (overlap) {
-            retval |= DoHandleDataOverlap(stream, list, seg, buf, p);
+            retval |= DoHandleDataOverlap(stream, tree_seg, seg, buf, p);
         }
-
-        list = list->next;
-    } while (list != NULL);
-
+    }
     return retval;
 }
 
+/**
+ *  \param dup_seg in-tree duplicate of `seg`
+ */
 static int DoHandleData(ThreadVars *tv, TcpReassemblyThreadCtx *ra_ctx,
-        TcpStream *stream, TcpSegment *seg, Packet *p)
+        TcpStream *stream, TcpSegment *seg, TcpSegment *tree_seg, Packet *p)
 {
     int result = 0;
+    TcpSegment *handle = seg;
 
     SCLogDebug("insert data for segment %p seq %u len %u re %u",
             seg, seg->seq, TCP_SEG_LEN(seg), SEG_SEQ_RIGHT_EDGE(seg));
@@ -493,18 +517,24 @@ static int DoHandleData(ThreadVars *tv, TcpReassemblyThreadCtx *ra_ctx,
     uint8_t buf[p->payload_len];
     memcpy(buf, p->payload, p->payload_len);
 
+    /* if tree_seg is set, we have an exact duplicate that we need to check */
+    if (tree_seg) {
+        DoHandleDataOverlap(stream, tree_seg, seg, buf, p);
+        handle = tree_seg;
+    }
+
     /* new list head  */
-    if (seg->next != NULL && seg->prev == NULL) {
-        result = DoHandleDataCheckForward(stream, seg, buf, p);
+    if (handle == RB_MIN(TCPSEG, &stream->seg_tree) && TCPSEG_RB_NEXT(handle)) {
+        result = DoHandleDataCheckForward(stream, handle, buf, p);
 
     /* new list tail */
-    } else if (seg->next == NULL && seg->prev != NULL) {
-        result = DoHandleDataCheckBackwards(stream, seg, buf, p);
+    } else if (handle == RB_MAX(TCPSEG, &stream->seg_tree) && TCPSEG_RB_PREV(handle)) {
+        result = DoHandleDataCheckBackwards(stream, handle, buf, p);
 
     /* middle of the list */
-    } else if (seg->next != NULL && seg->prev != NULL) {
-        result = DoHandleDataCheckBackwards(stream, seg, buf, p);
-        result |= DoHandleDataCheckForward(stream, seg, buf, p);
+    } else if (TCPSEG_RB_NEXT(handle) && TCPSEG_RB_PREV(handle)) {
+        result = DoHandleDataCheckBackwards(stream, handle, buf, p);
+        result |= DoHandleDataCheckForward(stream, handle, buf, p);
     }
 
     /* we had an overlap with different data */
@@ -515,7 +545,7 @@ static int DoHandleData(ThreadVars *tv, TcpReassemblyThreadCtx *ra_ctx,
 
     /* insert the temp buffer now that we've (possibly) updated
      * it to account for the overlap policies */
-    if (InsertSegmentDataCustom(stream, seg, buf, p->payload_len) < 0) {
+    if (InsertSegmentDataCustom(stream, handle, buf, p->payload_len) < 0) {
         return -1;
     }
 
@@ -530,26 +560,22 @@ static int DoHandleData(ThreadVars *tv, TcpReassemblyThreadCtx *ra_ctx,
  *  In case of error, this function returns the segment to the pool
  */
 int StreamTcpReassembleInsertSegment(ThreadVars *tv, TcpReassemblyThreadCtx *ra_ctx,
-        TcpStream *stream, TcpSegment *seg, Packet *p, uint32_t pkt_seq, uint8_t *pkt_data, uint16_t pkt_datalen)
+        TcpStream *stream, TcpSegment *seg, Packet *p,
+        uint32_t pkt_seq, uint8_t *pkt_data, uint16_t pkt_datalen)
 {
-#ifdef DEBUG
-    SCLogDebug("pre insert");
-    PrintList(stream->seg_list);
-#endif
+    SCEnter();
+
+    TcpSegment *dup_seg = NULL;
 
     /* insert segment into list. Note: doesn't handle the data */
-    int r = DoInsertSegment (stream, seg, p);
+    int r = DoInsertSegment (stream, seg, &dup_seg, p);
+    SCLogDebug("DoInsertSegment returned %d", r);
     if (r < 0) {
         StatsIncr(tv, ra_ctx->counter_tcp_reass_list_fail);
         StreamTcpSegmentReturntoPool(seg);
         SCReturnInt(-1);
     }
 
-#ifdef DEBUG
-    SCLogDebug("post insert");
-    PrintList(stream->seg_list);
-#endif
-
     if (likely(r == 0)) {
         /* no overlap, straight data insert */
         int res = InsertSegmentDataCustom(stream, seg, pkt_data, pkt_datalen);
@@ -560,18 +586,47 @@ int StreamTcpReassembleInsertSegment(ThreadVars *tv, TcpReassemblyThreadCtx *ra_
             SCReturnInt(-1);
         }
 
-    } else if (r == 1) {
+    } else if (r == 1 || r == 2) {
+        SCLogDebug("overlap (%s%s)", r == 1 ? "normal" : "", r == 2 ? "duplicate" : "");
+
+        if (r == 2) {
+            SCLogDebug("dup_seg %p", dup_seg);
+        }
+
         /* XXX should we exclude 'retransmissions' here? */
         StatsIncr(tv, ra_ctx->counter_tcp_reass_overlap);
 
         /* now let's consider the data in the overlap case */
-        int res = DoHandleData(tv, ra_ctx, stream, seg, p);
+        int res = DoHandleData(tv, ra_ctx, stream, seg, dup_seg, p);
         if (res < 0) {
             StatsIncr(tv, ra_ctx->counter_tcp_reass_data_overlap_fail);
-            StreamTcpRemoveSegmentFromStream(stream, seg);
+
+            if (r == 1) // r == 2 mean seg wasn't added to stream
+                StreamTcpRemoveSegmentFromStream(stream, seg);
+
             StreamTcpSegmentReturntoPool(seg);
             SCReturnInt(-1);
         }
+        if (r == 2) {
+            SCLogDebug("duplicate segment %u/%u, discard it",
+                    seg->seq, seg->payload_len);
+
+            StreamTcpSegmentReturntoPool(seg);
+#ifdef DEBUG
+            if (SCLogDebugEnabled()) {
+                TcpSegment *s = NULL, *safe = NULL;
+                RB_FOREACH_SAFE(s, TCPSEG, &stream->seg_tree, safe)
+                {
+                    SCLogDebug("tree: seg %p, SEQ %"PRIu32", LEN %"PRIu16", SUM %"PRIu32"%s%s%s",
+                            s, s->seq, TCP_SEG_LEN(s),
+                            (uint32_t)(s->seq + TCP_SEG_LEN(s)),
+                            s->seq == seg->seq ? " DUPLICATE" : "",
+                            TCPSEG_RB_PREV(s) == NULL ? " HEAD" : "",
+                            TCPSEG_RB_NEXT(s) == NULL ? " TAIL" : "");
+                }
+            }
+#endif
+        }
     }
 
     SCReturnInt(0);
@@ -703,9 +758,8 @@ static inline uint64_t GetLeftEdge(TcpSession *ssn, TcpStream *stream)
         /* we know left edge based on the progress values now,
          * lets adjust it to make sure in-use segments still have
          * data */
-        const TcpSegment *seg;
-        for (seg = stream->seg_list; seg != NULL; seg = seg->next)
-        {
+        TcpSegment *seg = NULL;
+        RB_FOREACH(seg, TCPSEG, &stream->seg_tree) {
             if (TCP_SEG_OFFSET(seg) > left_edge) {
                 SCLogDebug("seg beyond left_edge, we're done");
                 break;
@@ -724,18 +778,7 @@ static inline uint64_t GetLeftEdge(TcpSession *ssn, TcpStream *stream)
 
 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;
+    RB_REMOVE(TCPSEG, &stream->seg_tree, seg);
 }
 
 /** \brief Remove idle TcpSegments from TcpSession
@@ -817,9 +860,9 @@ void StreamTcpPruneSession(Flow *f, uint8_t flags)
                 stream->base_seq, STREAM_BASE_OFFSET(stream));
     }
 
-    /* loop through the segments and fill one or more msgs */
-    TcpSegment *seg = stream->seg_list;
-    while (seg != NULL)
+    /* loop through the segments and remove all not in use */
+    TcpSegment *seg = NULL, *safe = NULL;
+    RB_FOREACH_SAFE(seg, TCPSEG, &stream->seg_tree, safe)
     {
         SCLogDebug("seg %p, SEQ %"PRIu32", LEN %"PRIu16", SUM %"PRIu32,
                 seg, seg->seq, TCP_SEG_LEN(seg),
@@ -830,147 +873,15 @@ void StreamTcpPruneSession(Flow *f, uint8_t flags)
             break;
         }
 
-        TcpSegment *next_seg = seg->next;
         StreamTcpRemoveSegmentFromStream(stream, seg);
         StreamTcpSegmentReturntoPool(seg);
-        seg = next_seg;
         SCLogDebug("removed segment");
         continue;
     }
-#ifdef DEBUG
-    PrintList(stream->seg_list);
-#endif
-    SCReturn;
-}
-
-/*
- *  Utils
- */
-
-#if 0
-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, TCP_SEG_LEN(seg), 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 + TCP_SEG_LEN(seg);
-        SCLogDebug("next_seq is now %"PRIu32"", next_seq);
-        prev_seg = seg;
-        seg = seg->next;
-    }
-}
-#endif
-
-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",
-                    seg->seq, TCP_SEG_LEN(seg), 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);
-//                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 + TCP_SEG_LEN(seg);
-        SCLogDebug("next_seq is now %"PRIu32"", next_seq);
-        prev_seg = seg;
-        seg = seg->next;
-    }
+    SCReturn;
 }
 
-#if 0
-void ValidateList(const TcpStream *stream)
-{
-    TcpSegment *seg = stream->seg_list;
-    TcpSegment *prev_seg = NULL;
-
-    BUG_ON(seg && seg->next == NULL && stream->seg_list != stream->seg_list_tail);
-    BUG_ON(stream->seg_list != stream->seg_list_tail && stream->seg_list_tail->prev == NULL);
-
-    while (seg != NULL) {
-        prev_seg = seg;
-        seg = seg->next;
-        BUG_ON(seg && seg->prev != prev_seg);
-
-        // equal is possible
-        BUG_ON(seg && SEQ_LT(seg->seq, prev_seg->seq));
-    }
-}
-#endif
 
 /*
  *  unittests
index 28c79475cd583e31882ef223eddfbf445c662770..a910bb95ecabdf9436faff80216a805871a0c57e 100644 (file)
@@ -26,8 +26,6 @@
 
 #include "stream-tcp-private.h"
 
-void PrintList(TcpSegment *);
-
 #ifdef UNITTESTS
 void StreamTcpListRegisterTests(void);
 #endif
index aa954f9eaa79ecb305dac24402b4968d5c7ef00d..554e0f57d9ba28847f7431333d7c282bbdd3f077 100644 (file)
@@ -24,6 +24,7 @@
 #ifndef __STREAM_TCP_PRIVATE_H__
 #define __STREAM_TCP_PRIVATE_H__
 
+#include "tree.h"
 #include "decode.h"
 #include "util-pool.h"
 #include "util-pool-thread.h"
@@ -51,15 +52,27 @@ typedef struct StreamTcpSackRecord_ {
     struct StreamTcpSackRecord_ *next;
 } StreamTcpSackRecord;
 
-typedef struct TcpSegment_ {
+typedef struct TcpSegment {
     PoolThreadReserved res;
     uint16_t payload_len;       /**< actual size of the payload */
     uint32_t seq;
     StreamingBufferSegment sbseg;
-    struct TcpSegment_ *next;
-    struct TcpSegment_ *prev;
+    RB_ENTRY(TcpSegment) rb;
 } TcpSegment;
 
+/** \brief compare function for the Segment tree
+ *
+ *  Main sort point is the sequence number. When sequence numbers
+ *  are equal compare payload_len as well. This way the tree is
+ *  sorted by seq, and in case of duplicate seqs we are sorted
+ *  small to large.
+ */
+int TcpSegmentCompare(struct TcpSegment *a, struct TcpSegment *b);
+
+/* red-black tree prototype for TcpSegment */
+RB_HEAD(TCPSEG, TcpSegment);
+RB_PROTOTYPE(TCPSEG, TcpSegment, rb, TcpSegmentCompare);
+
 #define TCP_SEG_LEN(seg)        (seg)->payload_len
 #define TCP_SEG_OFFSET(seg)     (seg)->sbseg.stream_offset
 
@@ -90,9 +103,7 @@ typedef struct TcpStream_ {
     uint32_t log_progress_rel;      /**< streaming logger progress relative to STREAM_BASE_OFFSET */
 
     StreamingBuffer sb;
-
-    TcpSegment *seg_list;           /**< list of TCP segments that are not yet (fully) used in reassembly */
-    TcpSegment *seg_list_tail;      /**< Last segment in the reassembled stream seg list*/
+    struct TCPSEG seg_tree;         /**< red black tree of TCP segments. Data is stored in TcpStream::sb */
 
     StreamTcpSackRecord *sack_head; /**< head of list of SACK records */
     StreamTcpSackRecord *sack_tail; /**< tail of list of SACK records */
index e5e47e2157558a37d8a9dadca122d841b2628468..2ed65348eb5366235aa393f271a0bfadb9cdb933 100644 (file)
@@ -304,8 +304,6 @@ void StreamTcpSegmentReturntoPool(TcpSegment *seg)
     if (seg == NULL)
         return;
 
-    seg->next = NULL;
-    seg->prev = NULL;
     PoolThreadReturn(segment_thread_pool, seg);
 }
 
@@ -316,20 +314,12 @@ void StreamTcpSegmentReturntoPool(TcpSegment *seg)
  */
 void StreamTcpReturnStreamSegments (TcpStream *stream)
 {
-    TcpSegment *seg = stream->seg_list;
-    TcpSegment *next_seg;
-
-    if (seg == NULL)
-        return;
-
-    while (seg != NULL) {
-        next_seg = seg->next;
+    TcpSegment *seg = NULL, *safe = NULL;
+    RB_FOREACH_SAFE(seg, TCPSEG, &stream->seg_tree, safe)
+    {
+        RB_REMOVE(TCPSEG, &stream->seg_tree, seg);
         StreamTcpSegmentReturntoPool(seg);
-        seg = next_seg;
     }
-
-    stream->seg_list = NULL;
-    stream->seg_list_tail = NULL;
 }
 
 /** \internal
@@ -656,7 +646,7 @@ int StreamTcpReassembleHandleSegmentHandleData(ThreadVars *tv, TcpReassemblyThre
     seg->seq = TCP_GET_SEQ(p);
 
     /* proto detection skipped, but now we do get data. Set event. */
-    if (stream->seg_list == NULL &&
+    if (RB_EMPTY(&stream->seg_tree) &&
         stream->flags & STREAMTCP_STREAM_FLAG_APPPROTO_DETECTION_SKIPPED) {
 
         AppLayerDecoderEventsSetEventRaw(&p->app_layer_events,
@@ -832,9 +822,8 @@ int StreamNeedsReassembly(const TcpSession *ssn, uint8_t direction)
 
     uint64_t right_edge = STREAM_BASE_OFFSET(stream) + stream->sb.buf_offset;
 
-    SCLogDebug("%s: list %p app %"PRIu64" (use: %s), raw %"PRIu64" (use: %s). Stream right edge: %"PRIu64,
+    SCLogDebug("%s: app %"PRIu64" (use: %s), raw %"PRIu64" (use: %s). Stream right edge: %"PRIu64,
             dirstr,
-            stream->seg_list,
             STREAM_APP_PROGRESS(stream), use_app ? "yes" : "no",
             STREAM_RAW_PROGRESS(stream), use_raw ? "yes" : "no",
             right_edge);
@@ -862,12 +851,10 @@ static uint64_t GetStreamSize(TcpStream *stream)
         uint64_t size = 0;
         uint32_t cnt = 0;
 
-        TcpSegment *seg = stream->seg_list;
-        while (seg) {
+        TcpSegment *seg;
+        RB_FOREACH(seg, TCPSEG, &stream->seg_tree) {
             cnt++;
             size += (uint64_t)TCP_SEG_LEN(seg);
-
-            seg = seg->next;
         }
 
         SCLogDebug("size %"PRIu64", cnt %"PRIu32, size, cnt);
@@ -1123,14 +1110,13 @@ int StreamTcpReassembleAppLayer (ThreadVars *tv, TcpReassemblyThreadCtx *ra_ctx,
         SCReturnInt(0);
     }
 
-    SCLogDebug("stream->seg_list %p", stream->seg_list);
 #ifdef DEBUG
-    PrintList(stream->seg_list);
+    SCLogDebug("stream->seg_tree RB_MIN %p", RB_MIN(TCPSEG, &stream->seg_tree));
     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;
+    TcpSegment *seg_tail = RB_MAX(TCPSEG, &stream->seg_tree);
     if (seg_tail == NULL ||
             SEGMENT_BEFORE_OFFSET(stream, seg_tail, STREAM_APP_PROGRESS(stream)))
     {
@@ -1237,7 +1223,7 @@ bool StreamReassembleRawHasDataReady(TcpSession *ssn, Packet *p)
         stream = &ssn->server;
     }
 
-    if (stream->seg_list == NULL) {
+    if (RB_EMPTY(&stream->seg_tree)) {
         return false;
     }
 
@@ -1695,14 +1681,11 @@ static 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 (StreamTcpReassembleAppLayer(tv, ra_ctx, ssn, stream, p, UPDATE_DIR_OPPOSING) < 0)
-        r = -1;
+        SCReturnInt(-1);
 
-    SCLogDebug("stream->seg_list %p", stream->seg_list);
-    SCReturnInt(r);
+    SCReturnInt(0);
 }
 
 int StreamTcpReassembleHandleSegment(ThreadVars *tv, TcpReassemblyThreadCtx *ra_ctx,
@@ -1809,9 +1792,6 @@ TcpSegment *StreamTcpGetSegment(ThreadVars *tv, TcpReassemblyThreadCtx *ra_ctx)
            segment request due to memcap limit */
         StatsIncr(tv, ra_ctx->counter_tcp_segment_memcap);
     } else {
-        seg->next = NULL;
-        seg->prev = NULL;
-
         memset(&seg->sbseg, 0, sizeof(seg->sbseg));
     }
 
@@ -2221,7 +2201,6 @@ static int StreamTcpReassembleTest39 (void)
     memset(&tv, 0, sizeof (ThreadVars));
     memset(&stt, 0, sizeof (stt));
     memset(&tcph, 0, sizeof (TCPHdr));
-    TcpSession *ssn = NULL;
 
     FLOW_INITIALIZE(&f);
     f.flags = FLOW_IPV4;
@@ -2229,9 +2208,6 @@ static int StreamTcpReassembleTest39 (void)
     p->flow = &f;
     p->tcph = &tcph;
 
-    FLOWLOCK_WRLOCK(&f);
-    int ret = 0;
-
     StreamTcpUTInit(&stt.ra_ctx);
 
     /* handshake */
@@ -2240,25 +2216,24 @@ static int StreamTcpReassembleTest39 (void)
     p->flowflags = FLOW_PKT_TOSERVER;
     p->payload_len = 0;
     p->payload = NULL;
-    if (StreamTcpPacket(&tv, p, &stt, &pq) == -1)
-        goto end;
-
-    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 ||
-        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 != 0) {
-        printf("failure 1\n");
-        goto end;
-    }
+    FAIL_IF(StreamTcpPacket(&tv, p, &stt, &pq) == -1);
+
+    TcpSession *ssn = (TcpSession *)f.protoctx;
+    FAIL_IF_NULL(ssn);
+
+    FAIL_IF(StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->server));
+    FAIL_IF(StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->client));
+    FAIL_IF(f.alproto != ALPROTO_UNKNOWN);
+    FAIL_IF(f.alproto_ts != ALPROTO_UNKNOWN);
+    FAIL_IF(f.alproto_tc != ALPROTO_UNKNOWN);
+    FAIL_IF(ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED);
+    FAIL_IF(FLOW_IS_PM_DONE(&f, STREAM_TOSERVER));
+    FAIL_IF(FLOW_IS_PP_DONE(&f, STREAM_TOSERVER));
+    FAIL_IF(FLOW_IS_PM_DONE(&f, STREAM_TOCLIENT));
+    FAIL_IF(FLOW_IS_PP_DONE(&f, STREAM_TOCLIENT));
+    FAIL_IF(!RB_EMPTY(&ssn->client.seg_tree));
+    FAIL_IF(!RB_EMPTY(&ssn->server.seg_tree));
+    FAIL_IF(ssn->data_first_seen_dir != 0);
 
     /* handshake */
     p->tcph->th_ack = htonl(1);
@@ -2266,22 +2241,20 @@ static int StreamTcpReassembleTest39 (void)
     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 ||
-        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 != 0) {
-        printf("failure 2\n");
-        goto end;
-    }
+    FAIL_IF(StreamTcpPacket(&tv, p, &stt, &pq) == -1);
+    FAIL_IF(StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->server));
+    FAIL_IF(StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->client));
+    FAIL_IF(f.alproto != ALPROTO_UNKNOWN);
+    FAIL_IF(f.alproto_ts != ALPROTO_UNKNOWN);
+    FAIL_IF(f.alproto_tc != ALPROTO_UNKNOWN);
+    FAIL_IF(ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED);
+    FAIL_IF(FLOW_IS_PM_DONE(&f, STREAM_TOSERVER));
+    FAIL_IF(FLOW_IS_PP_DONE(&f, STREAM_TOSERVER));
+    FAIL_IF(FLOW_IS_PM_DONE(&f, STREAM_TOCLIENT));
+    FAIL_IF(FLOW_IS_PP_DONE(&f, STREAM_TOCLIENT));
+    FAIL_IF(!RB_EMPTY(&ssn->client.seg_tree));
+    FAIL_IF(!RB_EMPTY(&ssn->server.seg_tree));
+    FAIL_IF(ssn->data_first_seen_dir != 0);
 
     /* handshake */
     p->tcph->th_ack = htonl(1);
@@ -2290,22 +2263,20 @@ static int StreamTcpReassembleTest39 (void)
     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 ||
-        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 != 0) {
-        printf("failure 3\n");
-        goto end;
-    }
+    FAIL_IF(StreamTcpPacket(&tv, p, &stt, &pq) == -1);
+    FAIL_IF(StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->server));
+    FAIL_IF(StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->client));
+    FAIL_IF(f.alproto != ALPROTO_UNKNOWN);
+    FAIL_IF(f.alproto_ts != ALPROTO_UNKNOWN);
+    FAIL_IF(f.alproto_tc != ALPROTO_UNKNOWN);
+    FAIL_IF(ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED);
+    FAIL_IF(FLOW_IS_PM_DONE(&f, STREAM_TOSERVER));
+    FAIL_IF(FLOW_IS_PP_DONE(&f, STREAM_TOSERVER));
+    FAIL_IF(FLOW_IS_PM_DONE(&f, STREAM_TOCLIENT));
+    FAIL_IF(FLOW_IS_PP_DONE(&f, STREAM_TOCLIENT));
+    FAIL_IF(!RB_EMPTY(&ssn->client.seg_tree));
+    FAIL_IF(!RB_EMPTY(&ssn->server.seg_tree));
+    FAIL_IF(ssn->data_first_seen_dir != 0);
 
     /* partial request */
     uint8_t request1[] = { 0x47, 0x45, };
@@ -2315,24 +2286,21 @@ static int StreamTcpReassembleTest39 (void)
     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 ||
-        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->data_first_seen_dir != STREAM_TOSERVER) {
-        printf("failure 4\n");
-        goto end;
-    }
-
+    FAIL_IF(StreamTcpPacket(&tv, p, &stt, &pq) == -1);
+    FAIL_IF(StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->server));
+    FAIL_IF(StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->client));
+    FAIL_IF(f.alproto != ALPROTO_UNKNOWN);
+    FAIL_IF(f.alproto_ts != ALPROTO_UNKNOWN);
+    FAIL_IF(f.alproto_tc != ALPROTO_UNKNOWN);
+    FAIL_IF(ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED);
+    FAIL_IF(FLOW_IS_PM_DONE(&f, STREAM_TOSERVER));
+    FAIL_IF(FLOW_IS_PP_DONE(&f, STREAM_TOSERVER));
+    FAIL_IF(FLOW_IS_PM_DONE(&f, STREAM_TOCLIENT));
+    FAIL_IF(FLOW_IS_PP_DONE(&f, STREAM_TOCLIENT));
+    FAIL_IF(RB_EMPTY(&ssn->client.seg_tree));
+    FAIL_IF(TCPSEG_RB_NEXT(RB_MIN(TCPSEG, &ssn->client.seg_tree)));
+    FAIL_IF(!RB_EMPTY(&ssn->server.seg_tree));
+    FAIL_IF(ssn->data_first_seen_dir != STREAM_TOSERVER);
 
     /* response ack against partial request */
     p->tcph->th_ack = htonl(3);
@@ -2341,23 +2309,21 @@ static int StreamTcpReassembleTest39 (void)
     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 ||
-        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->data_first_seen_dir != STREAM_TOSERVER) {
-        printf("failure 5\n");
-        goto end;
-    }
+    FAIL_IF(StreamTcpPacket(&tv, p, &stt, &pq) == -1);
+    FAIL_IF(StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->server));
+    FAIL_IF(StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->client));
+    FAIL_IF(f.alproto != ALPROTO_UNKNOWN);
+    FAIL_IF(f.alproto_ts != ALPROTO_UNKNOWN);
+    FAIL_IF(f.alproto_tc != ALPROTO_UNKNOWN);
+    FAIL_IF(ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED);
+    FAIL_IF(FLOW_IS_PM_DONE(&f, STREAM_TOSERVER));
+    FAIL_IF(!FLOW_IS_PP_DONE(&f, STREAM_TOSERVER));
+    FAIL_IF(FLOW_IS_PM_DONE(&f, STREAM_TOCLIENT));
+    FAIL_IF(FLOW_IS_PP_DONE(&f, STREAM_TOCLIENT));
+    FAIL_IF(RB_EMPTY(&ssn->client.seg_tree));
+    FAIL_IF(TCPSEG_RB_NEXT(RB_MIN(TCPSEG, &ssn->client.seg_tree)));
+    FAIL_IF(!RB_EMPTY(&ssn->server.seg_tree));
+    FAIL_IF(ssn->data_first_seen_dir != STREAM_TOSERVER);
 
     /* complete partial request */
     uint8_t request2[] = {
@@ -2378,24 +2344,22 @@ static int StreamTcpReassembleTest39 (void)
     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 ||
-        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->data_first_seen_dir != STREAM_TOSERVER) {
-        printf("failure 6\n");
-        goto end;
-    }
+    FAIL_IF(StreamTcpPacket(&tv, p, &stt, &pq) == -1);
+    FAIL_IF(StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->server));
+    FAIL_IF(StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->client));
+    FAIL_IF(f.alproto != ALPROTO_UNKNOWN);
+    FAIL_IF(f.alproto_ts != ALPROTO_UNKNOWN);
+    FAIL_IF(f.alproto_tc != ALPROTO_UNKNOWN);
+    FAIL_IF(ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED);
+    FAIL_IF(FLOW_IS_PM_DONE(&f, STREAM_TOSERVER));
+    FAIL_IF(!FLOW_IS_PP_DONE(&f, STREAM_TOSERVER));
+    FAIL_IF(FLOW_IS_PM_DONE(&f, STREAM_TOCLIENT));
+    FAIL_IF(FLOW_IS_PP_DONE(&f, STREAM_TOCLIENT));
+    FAIL_IF(RB_EMPTY(&ssn->client.seg_tree));
+    FAIL_IF(!TCPSEG_RB_NEXT(RB_MIN(TCPSEG, &ssn->client.seg_tree)));
+    FAIL_IF(TCPSEG_RB_NEXT(TCPSEG_RB_NEXT(RB_MIN(TCPSEG, &ssn->client.seg_tree))));
+    FAIL_IF(!RB_EMPTY(&ssn->server.seg_tree));
+    FAIL_IF(ssn->data_first_seen_dir != STREAM_TOSERVER);
 
     /* response - request ack */
     uint8_t response[] = {
@@ -2447,25 +2411,22 @@ static int StreamTcpReassembleTest39 (void)
     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 ||
-        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;
-    }
+    FAIL_IF(StreamTcpPacket(&tv, p, &stt, &pq) == -1);
+    FAIL_IF(StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->server));
+    FAIL_IF(!StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->client));
+    FAIL_IF(f.alproto != ALPROTO_HTTP);
+    FAIL_IF(f.alproto_ts != ALPROTO_HTTP);
+    FAIL_IF(f.alproto_tc != ALPROTO_UNKNOWN);
+    FAIL_IF(ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED);
+    FAIL_IF(!FLOW_IS_PM_DONE(&f, STREAM_TOSERVER));
+    FAIL_IF(!FLOW_IS_PP_DONE(&f, STREAM_TOSERVER));
+    FAIL_IF(FLOW_IS_PM_DONE(&f, STREAM_TOCLIENT));
+    FAIL_IF(FLOW_IS_PP_DONE(&f, STREAM_TOCLIENT));
+    FAIL_IF(ssn->data_first_seen_dir != APP_LAYER_DATA_ALREADY_SENT_TO_APP_LAYER);
+    FAIL_IF(RB_EMPTY(&ssn->client.seg_tree));
+    FAIL_IF(RB_EMPTY(&ssn->server.seg_tree));
+    FAIL_IF(!TCPSEG_RB_NEXT(RB_MIN(TCPSEG, &ssn->client.seg_tree)));
+    FAIL_IF(TCPSEG_RB_NEXT(TCPSEG_RB_NEXT(RB_MIN(TCPSEG, &ssn->client.seg_tree))));
 
     /* response ack from request */
     p->tcph->th_ack = htonl(328);
@@ -2474,25 +2435,23 @@ static int StreamTcpReassembleTest39 (void)
     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 ||
-        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;
-    }
+    FAIL_IF(StreamTcpPacket(&tv, p, &stt, &pq) == -1);
+    FAIL_IF(!StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->server));
+    FAIL_IF(!StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->client));
+    FAIL_IF(f.alproto != ALPROTO_HTTP);
+    FAIL_IF(f.alproto_ts != ALPROTO_HTTP);
+    FAIL_IF(f.alproto_tc != ALPROTO_HTTP);
+    FAIL_IF(ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED);
+    FAIL_IF(!FLOW_IS_PM_DONE(&f, STREAM_TOSERVER));
+    FAIL_IF(!FLOW_IS_PP_DONE(&f, STREAM_TOSERVER));
+    FAIL_IF(!FLOW_IS_PM_DONE(&f, STREAM_TOCLIENT));
+    FAIL_IF(FLOW_IS_PP_DONE(&f, STREAM_TOCLIENT));
+    FAIL_IF(ssn->data_first_seen_dir != APP_LAYER_DATA_ALREADY_SENT_TO_APP_LAYER);
+    FAIL_IF(RB_EMPTY(&ssn->client.seg_tree));
+    FAIL_IF(RB_EMPTY(&ssn->server.seg_tree));
+    FAIL_IF(!TCPSEG_RB_NEXT(RB_MIN(TCPSEG, &ssn->client.seg_tree)));
+    FAIL_IF(TCPSEG_RB_NEXT(TCPSEG_RB_NEXT(RB_MIN(TCPSEG, &ssn->client.seg_tree))));
+    FAIL_IF(TCPSEG_RB_NEXT(RB_MIN(TCPSEG, &ssn->server.seg_tree)));
 
     /* response - acking */
     p->tcph->th_ack = htonl(88);
@@ -2501,24 +2460,23 @@ static int StreamTcpReassembleTest39 (void)
     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 ||
-        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;
-    }
+    FAIL_IF(StreamTcpPacket(&tv, p, &stt, &pq) == -1);
+    FAIL_IF(!StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->server));
+    FAIL_IF(!StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->client));
+    FAIL_IF(f.alproto != ALPROTO_HTTP);
+    FAIL_IF(f.alproto_ts != ALPROTO_HTTP);
+    FAIL_IF(f.alproto_tc != ALPROTO_HTTP);
+    FAIL_IF(ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED);
+    FAIL_IF(!FLOW_IS_PM_DONE(&f, STREAM_TOSERVER));
+    FAIL_IF(!FLOW_IS_PP_DONE(&f, STREAM_TOSERVER));
+    FAIL_IF(!FLOW_IS_PM_DONE(&f, STREAM_TOCLIENT));
+    FAIL_IF(FLOW_IS_PP_DONE(&f, STREAM_TOCLIENT));
+    FAIL_IF(ssn->data_first_seen_dir != APP_LAYER_DATA_ALREADY_SENT_TO_APP_LAYER);
+    FAIL_IF(RB_EMPTY(&ssn->client.seg_tree));
+    FAIL_IF(RB_EMPTY(&ssn->server.seg_tree));
+    FAIL_IF(!TCPSEG_RB_NEXT(RB_MIN(TCPSEG, &ssn->client.seg_tree)));
+    FAIL_IF(TCPSEG_RB_NEXT(TCPSEG_RB_NEXT(RB_MIN(TCPSEG, &ssn->client.seg_tree))));
+    FAIL_IF(TCPSEG_RB_NEXT(RB_MIN(TCPSEG, &ssn->server.seg_tree)));
 
     /* response ack from request */
     p->tcph->th_ack = htonl(328);
@@ -2527,24 +2485,22 @@ static int StreamTcpReassembleTest39 (void)
     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 ||
-        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;
-    }
+    FAIL_IF(StreamTcpPacket(&tv, p, &stt, &pq) == -1);
+    FAIL_IF(!StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->server));
+    FAIL_IF(!StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->client));
+    FAIL_IF(f.alproto != ALPROTO_HTTP);
+    FAIL_IF(f.alproto_ts != ALPROTO_HTTP);
+    FAIL_IF(f.alproto_tc != ALPROTO_HTTP);
+    FAIL_IF(ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED);
+    FAIL_IF(!FLOW_IS_PM_DONE(&f, STREAM_TOSERVER));
+    FAIL_IF(!FLOW_IS_PP_DONE(&f, STREAM_TOSERVER));
+    FAIL_IF(!FLOW_IS_PM_DONE(&f, STREAM_TOCLIENT));
+    FAIL_IF(FLOW_IS_PP_DONE(&f, STREAM_TOCLIENT));
+    FAIL_IF(ssn->data_first_seen_dir != APP_LAYER_DATA_ALREADY_SENT_TO_APP_LAYER);
+    FAIL_IF(RB_EMPTY(&ssn->client.seg_tree));
+    FAIL_IF(!TCPSEG_RB_NEXT(RB_MIN(TCPSEG, &ssn->client.seg_tree)));
+    FAIL_IF(RB_EMPTY(&ssn->server.seg_tree));
+    FAIL_IF(TCPSEG_RB_NEXT(RB_MIN(TCPSEG, &ssn->server.seg_tree)));
 
     /* response - acking the request again*/
     p->tcph->th_ack = htonl(88);
@@ -2553,24 +2509,22 @@ static int StreamTcpReassembleTest39 (void)
     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 ||
-        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;
-    }
+    FAIL_IF(StreamTcpPacket(&tv, p, &stt, &pq) == -1);
+    FAIL_IF(!StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->server));
+    FAIL_IF(!StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->client));
+    FAIL_IF(f.alproto != ALPROTO_HTTP);
+    FAIL_IF(f.alproto_ts != ALPROTO_HTTP);
+    FAIL_IF(f.alproto_tc != ALPROTO_HTTP);
+    FAIL_IF(ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED);
+    FAIL_IF(!FLOW_IS_PM_DONE(&f, STREAM_TOSERVER));
+    FAIL_IF(!FLOW_IS_PP_DONE(&f, STREAM_TOSERVER));
+    FAIL_IF(!FLOW_IS_PM_DONE(&f, STREAM_TOCLIENT));
+    FAIL_IF(FLOW_IS_PP_DONE(&f, STREAM_TOCLIENT));
+    FAIL_IF(ssn->data_first_seen_dir != APP_LAYER_DATA_ALREADY_SENT_TO_APP_LAYER);
+    FAIL_IF(RB_EMPTY(&ssn->client.seg_tree));
+    FAIL_IF(!TCPSEG_RB_NEXT(RB_MIN(TCPSEG, &ssn->client.seg_tree)));
+    FAIL_IF(RB_EMPTY(&ssn->server.seg_tree));
+    FAIL_IF(TCPSEG_RB_NEXT(RB_MIN(TCPSEG, &ssn->server.seg_tree)));
 
     /*** New Request ***/
 
@@ -2581,26 +2535,23 @@ static int StreamTcpReassembleTest39 (void)
     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 ||
-        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;
-    }
-
+    FAIL_IF(StreamTcpPacket(&tv, p, &stt, &pq) == -1);
+    FAIL_IF(!StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->server));
+    FAIL_IF(!StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->client));
+    FAIL_IF(f.alproto != ALPROTO_HTTP);
+    FAIL_IF(f.alproto_ts != ALPROTO_HTTP);
+    FAIL_IF(f.alproto_tc != ALPROTO_HTTP);
+    FAIL_IF(ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED);
+    FAIL_IF(!FLOW_IS_PM_DONE(&f, STREAM_TOSERVER));
+    FAIL_IF(!FLOW_IS_PP_DONE(&f, STREAM_TOSERVER));
+    FAIL_IF(!FLOW_IS_PM_DONE(&f, STREAM_TOCLIENT));
+    FAIL_IF(FLOW_IS_PP_DONE(&f, STREAM_TOCLIENT));
+    FAIL_IF(ssn->data_first_seen_dir != APP_LAYER_DATA_ALREADY_SENT_TO_APP_LAYER);
+    FAIL_IF(RB_EMPTY(&ssn->client.seg_tree));
+    FAIL_IF(!TCPSEG_RB_NEXT(RB_MIN(TCPSEG, &ssn->client.seg_tree)));
+    FAIL_IF(!TCPSEG_RB_NEXT(TCPSEG_RB_NEXT(RB_MIN(TCPSEG, &ssn->client.seg_tree))));
+    FAIL_IF(RB_EMPTY(&ssn->server.seg_tree));
+    FAIL_IF(TCPSEG_RB_NEXT(RB_MIN(TCPSEG, &ssn->server.seg_tree)));
 
     /* response ack against partial request */
     p->tcph->th_ack = htonl(90);
@@ -2609,25 +2560,24 @@ static int StreamTcpReassembleTest39 (void)
     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 ||
-        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;
-    }
+    FAIL_IF(StreamTcpPacket(&tv, p, &stt, &pq) == -1);
+    FAIL_IF(StreamTcpPacket(&tv, p, &stt, &pq) == -1);
+    FAIL_IF(!StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->server));
+    FAIL_IF(!StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->client));
+    FAIL_IF(f.alproto != ALPROTO_HTTP);
+    FAIL_IF(f.alproto_ts != ALPROTO_HTTP);
+    FAIL_IF(f.alproto_tc != ALPROTO_HTTP);
+    FAIL_IF(ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED);
+    FAIL_IF(!FLOW_IS_PM_DONE(&f, STREAM_TOSERVER));
+    FAIL_IF(!FLOW_IS_PP_DONE(&f, STREAM_TOSERVER));
+    FAIL_IF(!FLOW_IS_PM_DONE(&f, STREAM_TOCLIENT));
+    FAIL_IF(FLOW_IS_PP_DONE(&f, STREAM_TOCLIENT));
+    FAIL_IF(ssn->data_first_seen_dir != APP_LAYER_DATA_ALREADY_SENT_TO_APP_LAYER);
+    FAIL_IF(RB_EMPTY(&ssn->client.seg_tree));
+    FAIL_IF(!TCPSEG_RB_NEXT(RB_MIN(TCPSEG, &ssn->client.seg_tree)));
+    FAIL_IF(!TCPSEG_RB_NEXT(TCPSEG_RB_NEXT(RB_MIN(TCPSEG, &ssn->client.seg_tree))));
+    FAIL_IF(RB_EMPTY(&ssn->server.seg_tree));
+    FAIL_IF(TCPSEG_RB_NEXT(RB_MIN(TCPSEG, &ssn->server.seg_tree)));
 
     /* complete request */
     p->tcph->th_ack = htonl(328);
@@ -2636,26 +2586,24 @@ static int StreamTcpReassembleTest39 (void)
     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 ||
-        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;
-    }
+    FAIL_IF(StreamTcpPacket(&tv, p, &stt, &pq) == -1);
+    FAIL_IF(!StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->server));
+    FAIL_IF(!StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->client));
+    FAIL_IF(f.alproto != ALPROTO_HTTP);
+    FAIL_IF(f.alproto_ts != ALPROTO_HTTP);
+    FAIL_IF(f.alproto_tc != ALPROTO_HTTP);
+    FAIL_IF(ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED);
+    FAIL_IF(!FLOW_IS_PM_DONE(&f, STREAM_TOSERVER));
+    FAIL_IF(!FLOW_IS_PP_DONE(&f, STREAM_TOSERVER));
+    FAIL_IF(!FLOW_IS_PM_DONE(&f, STREAM_TOCLIENT));
+    FAIL_IF(FLOW_IS_PP_DONE(&f, STREAM_TOCLIENT));
+    FAIL_IF(ssn->data_first_seen_dir != APP_LAYER_DATA_ALREADY_SENT_TO_APP_LAYER);
+    FAIL_IF(RB_EMPTY(&ssn->client.seg_tree));
+    FAIL_IF(!TCPSEG_RB_NEXT(RB_MIN(TCPSEG, &ssn->client.seg_tree)));
+    FAIL_IF(!TCPSEG_RB_NEXT(TCPSEG_RB_NEXT(RB_MIN(TCPSEG, &ssn->client.seg_tree))));
+    FAIL_IF(!TCPSEG_RB_NEXT(TCPSEG_RB_NEXT(TCPSEG_RB_NEXT(RB_MIN(TCPSEG, &ssn->client.seg_tree)))));
+    FAIL_IF(RB_EMPTY(&ssn->server.seg_tree));
+    FAIL_IF(TCPSEG_RB_NEXT(RB_MIN(TCPSEG, &ssn->server.seg_tree)));
 
     /* response ack against second partial request */
     p->tcph->th_ack = htonl(175);
@@ -2664,25 +2612,26 @@ static int StreamTcpReassembleTest39 (void)
     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 ||
-        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;
-    }
+
+    FAIL_IF(StreamTcpPacket(&tv, p, &stt, &pq) == -1);
+    FAIL_IF(!StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->server));
+    FAIL_IF(!StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->client));
+    FAIL_IF(f.alproto != ALPROTO_HTTP);
+    FAIL_IF(f.alproto_ts != ALPROTO_HTTP);
+    FAIL_IF(f.alproto_tc != ALPROTO_HTTP);
+    FAIL_IF(ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED);
+    FAIL_IF(!FLOW_IS_PM_DONE(&f, STREAM_TOSERVER));
+    FAIL_IF(!FLOW_IS_PP_DONE(&f, STREAM_TOSERVER));
+    FAIL_IF(!FLOW_IS_PM_DONE(&f, STREAM_TOCLIENT));
+    FAIL_IF(FLOW_IS_PP_DONE(&f, STREAM_TOCLIENT));
+    FAIL_IF(ssn->data_first_seen_dir != APP_LAYER_DATA_ALREADY_SENT_TO_APP_LAYER);
+    FAIL_IF(RB_EMPTY(&ssn->client.seg_tree));
+    FAIL_IF(!TCPSEG_RB_NEXT(RB_MIN(TCPSEG, &ssn->client.seg_tree)));
+    FAIL_IF(!TCPSEG_RB_NEXT(TCPSEG_RB_NEXT(RB_MIN(TCPSEG, &ssn->client.seg_tree))));
+    FAIL_IF(!TCPSEG_RB_NEXT(TCPSEG_RB_NEXT(TCPSEG_RB_NEXT(RB_MIN(TCPSEG, &ssn->client.seg_tree)))));
+    FAIL_IF(RB_EMPTY(&ssn->server.seg_tree));
+    FAIL_IF(TCPSEG_RB_NEXT(RB_MIN(TCPSEG, &ssn->server.seg_tree)));
+
     /* response acking a request */
     p->tcph->th_ack = htonl(175);
     p->tcph->th_seq = htonl(328);
@@ -2690,32 +2639,12 @@ static int StreamTcpReassembleTest39 (void)
     p->flowflags = FLOW_PKT_TOCLIENT;
     p->payload_len = 0;
     p->payload = NULL;
-    if (StreamTcpPacket(&tv, p, &stt, &pq) == -1)
-        goto end;
-
-    SCLogDebug("StreamTcpIsSetStreamFlagAppProtoDetectionCompleted %s, "
-            "StreamTcpIsSetStreamFlagAppProtoDetectionCompleted %s, "
-            "f.alproto %u f.alproto_ts %u f.alproto_tc %u",
-            StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->server) ? "true" : "false",
-            StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->client) ? "true" : "false",
-            f.alproto, f.alproto_ts, f.alproto_tc);
-
-    if (!StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->server) ||
-        !StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->client) ||
-        f.alproto != ALPROTO_HTTP ||
-        f.alproto_ts != ALPROTO_HTTP ||
-        f.alproto_tc != ALPROTO_HTTP)// ||
-        //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;
-    }
+    FAIL_IF(StreamTcpPacket(&tv, p, &stt, &pq) == -1);
+    FAIL_IF(!StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->server));
+    FAIL_IF(!StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->client));
+    FAIL_IF(f.alproto != ALPROTO_HTTP);
+    FAIL_IF(f.alproto_ts != ALPROTO_HTTP);
+    FAIL_IF(f.alproto_tc != ALPROTO_HTTP);
 
     StreamTcpPruneSession(&f, STREAM_TOSERVER);
     StreamTcpPruneSession(&f, STREAM_TOCLIENT);
@@ -2727,34 +2656,12 @@ static int StreamTcpReassembleTest39 (void)
     p->flowflags = FLOW_PKT_TOSERVER;
     p->payload_len = 0;
     p->payload = NULL;
-    if (StreamTcpPacket(&tv, p, &stt, &pq) == -1)
-        goto end;
-#if 0
-    if (//!StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->server) ||
-        //!StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(&ssn->client) ||
-        //f.alproto != ALPROTO_HTTP ||
-        //f.alproto_ts != ALPROTO_HTTP ||
-        //f.alproto_tc != ALPROTO_HTTP ||
-        //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 16\n");
-        abort();
-        goto end;
-    }
-#endif
+    FAIL_IF(StreamTcpPacket(&tv, p, &stt, &pq) == -1);
 
-    ret = 1;
-end:
     StreamTcpSessionClear(ssn);
     StreamTcpUTDeinit(stt.ra_ctx);
     SCFree(p);
-    FLOWLOCK_UNLOCK(&f);
-    return ret;
+    PASS;
 }
 
 /**
@@ -2867,12 +2774,9 @@ static int StreamTcpReassembleTest40 (void)
     }
 
     /* check is have the segment in the list and flagged or not */
-    if (ssn.client.seg_list == NULL ||
-        SEGMENT_BEFORE_OFFSET(&ssn.client, ssn.client.seg_list, STREAM_APP_PROGRESS(&ssn.client)))
-    {
-        printf("the list is NULL or the processed segment has not been flaged (7): ");
-        goto end;
-    }
+    TcpSegment *seg = RB_MIN(TCPSEG, &ssn.client.seg_tree);
+    FAIL_IF_NULL(seg);
+    FAIL_IF(SEGMENT_BEFORE_OFFSET(&ssn.client, seg, STREAM_APP_PROGRESS(&ssn.client)));
 
     p->flowflags = FLOW_PKT_TOSERVER;
     p->payload = httpbuf4;
@@ -3391,7 +3295,9 @@ static int StreamTcpReassembleInlineTest08(void)
     p->tcph->th_seq = htonl(17);
     StreamTcpPruneSession(&f, STREAM_TOSERVER);
 
-    FAIL_IF (ssn.client.seg_list->seq != 2);
+    TcpSegment *seg = RB_MIN(TCPSEG, &ssn.client.seg_tree);
+    FAIL_IF_NULL(seg);
+    FAIL_IF_NOT(seg->seq == 2);
 
     FLOW_DESTROY(&f);
     UTHFreePacket(p);
@@ -3459,10 +3365,9 @@ static int StreamTcpReassembleInlineTest09(void)
 
     p->tcph->th_seq = htonl(12);
 
-    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;
-    }
+    TcpSegment *seg = RB_MIN(TCPSEG, &ssn.client.seg_tree);
+    FAIL_IF_NULL(seg);
+    FAIL_IF_NOT(seg->seq == 2);
 
     ret = 1;
 end:
index 26e5ead5c718199b5bfac21061bffdb3c8a7e4fb..d59495886921cb616e132e2022cf93af12403021 100644 (file)
@@ -180,102 +180,64 @@ end:
 
 static int StreamTcpUtilStreamTest01(void)
 {
-    int ret = 0;
     TcpReassemblyThreadCtx *ra_ctx = NULL;
-    ThreadVars tv;
     TcpStream stream;
-
+    ThreadVars tv;
     memset(&tv, 0x00, sizeof(tv));
 
     StreamTcpUTInit(&ra_ctx);
     StreamTcpUTSetupStream(&stream, 1);
 
-    if (StreamTcpUTAddSegmentWithByte(&tv, ra_ctx, &stream,  2, 'A', 5) == -1) {
-        printf("failed to add segment 1: ");
-        goto end;
-    }
-    if (StreamTcpUTAddSegmentWithByte(&tv, ra_ctx, &stream,  7, 'B', 5) == -1) {
-        printf("failed to add segment 2: ");
-        goto end;
-    }
-    if (StreamTcpUTAddSegmentWithByte(&tv, ra_ctx, &stream, 12, 'C', 5) == -1) {
-        printf("failed to add segment 3: ");
-        goto end;
-    }
+    FAIL_IF(StreamTcpUTAddSegmentWithByte(&tv, ra_ctx, &stream,  2, 'A', 5) == -1);
+    FAIL_IF(StreamTcpUTAddSegmentWithByte(&tv, ra_ctx, &stream,  7, 'B', 5) == -1);
+    FAIL_IF(StreamTcpUTAddSegmentWithByte(&tv, ra_ctx, &stream, 12, 'C', 5) == -1);
 
-    TcpSegment *seg = stream.seg_list;
-    if (seg->seq != 2) {
-        printf("first seg in the list should have seq 2: ");
-        goto end;
-    }
+    TcpSegment *seg = RB_MIN(TCPSEG, &stream.seg_tree);
+    FAIL_IF_NULL(seg);
+    FAIL_IF(seg->seq != 2);
 
-    seg = seg->next;
-    if (seg->seq != 7) {
-        printf("first seg in the list should have seq 7: ");
-        goto end;
-    }
+    seg = TCPSEG_RB_NEXT(seg);
+    FAIL_IF_NULL(seg);
+    FAIL_IF(seg->seq != 7);
 
-    seg = seg->next;
-    if (seg->seq != 12) {
-        printf("first seg in the list should have seq 12: ");
-        goto end;
-    }
+    seg = TCPSEG_RB_NEXT(seg);
+    FAIL_IF_NULL(seg);
+    FAIL_IF(seg->seq != 12);
 
-    ret = 1;
-end:
     StreamTcpUTClearStream(&stream);
     StreamTcpUTDeinit(ra_ctx);
-    return ret;
+    PASS;
 }
 
 static int StreamTcpUtilStreamTest02(void)
 {
-    int ret = 0;
     TcpReassemblyThreadCtx *ra_ctx = NULL;
-    ThreadVars tv;
     TcpStream stream;
-
+    ThreadVars tv;
     memset(&tv, 0x00, sizeof(tv));
 
     StreamTcpUTInit(&ra_ctx);
     StreamTcpUTSetupStream(&stream, 1);
 
-    if (StreamTcpUTAddSegmentWithByte(&tv, ra_ctx, &stream,  7, 'B', 5) == -1) {
-        printf("failed to add segment 2: ");
-        goto end;
-    }
-    if (StreamTcpUTAddSegmentWithByte(&tv, ra_ctx, &stream, 12, 'C', 5) == -1) {
-        printf("failed to add segment 3: ");
-        goto end;
-    }
-    if (StreamTcpUTAddSegmentWithByte(&tv, ra_ctx, &stream,  2, 'A', 5) == -1) {
-        printf("failed to add segment 1: ");
-        goto end;
-    }
+    FAIL_IF(StreamTcpUTAddSegmentWithByte(&tv, ra_ctx, &stream,  7, 'B', 5) == -1);
+    FAIL_IF(StreamTcpUTAddSegmentWithByte(&tv, ra_ctx, &stream, 12, 'C', 5) == -1);
+    FAIL_IF(StreamTcpUTAddSegmentWithByte(&tv, ra_ctx, &stream,  2, 'A', 5) == -1);
 
-    TcpSegment *seg = stream.seg_list;
-    if (seg->seq != 2) {
-        printf("first seg in the list should have seq 2: ");
-        goto end;
-    }
+    TcpSegment *seg = RB_MIN(TCPSEG, &stream.seg_tree);
+    FAIL_IF_NULL(seg);
+    FAIL_IF(seg->seq != 2);
 
-    seg = seg->next;
-    if (seg->seq != 7) {
-        printf("first seg in the list should have seq 7: ");
-        goto end;
-    }
+    seg = TCPSEG_RB_NEXT(seg);
+    FAIL_IF_NULL(seg);
+    FAIL_IF(seg->seq != 7);
 
-    seg = seg->next;
-    if (seg->seq != 12) {
-        printf("first seg in the list should have seq 12: ");
-        goto end;
-    }
+    seg = TCPSEG_RB_NEXT(seg);
+    FAIL_IF_NULL(seg);
+    FAIL_IF(seg->seq != 12);
 
-    ret = 1;
-end:
     StreamTcpUTClearStream(&stream);
     StreamTcpUTDeinit(ra_ctx);
-    return ret;
+    PASS;
 }
 
 #endif
index dcf6ad02c00e1963450ec41215a90a32b272840c..ccd8e71d8080a9c0f6287023d55ad56e5b78f9e0 100644 (file)
@@ -2399,10 +2399,11 @@ static inline uint32_t StreamTcpResetGetMaxAck(TcpStream *stream, uint32_t seq)
 {
     uint32_t ack = seq;
 
-    if (stream->seg_list_tail != NULL) {
-        if (SEQ_GT((stream->seg_list_tail->seq + TCP_SEG_LEN(stream->seg_list_tail)), ack))
-        {
-            ack = stream->seg_list_tail->seq + TCP_SEG_LEN(stream->seg_list_tail);
+    const TcpSegment *seg = RB_MAX(TCPSEG, &stream->seg_tree);
+    if (seg != NULL) {
+        const uint32_t tail_seq = seg->seq + TCP_SEG_LEN(seg);
+        if (SEQ_GT(tail_seq, ack)) {
+            ack = tail_seq;
         }
     }
 
@@ -4824,7 +4825,6 @@ int StreamTcpPacket (ThreadVars *tv, Packet *p, StreamTcpThread *stt,
         if (p->flags & PKT_STREAM_MODIFIED) {
             ReCalculateChecksum(p);
         }
-
         /* check for conditions that may make us not want to log this packet */
 
         /* streams that hit depth */
@@ -5942,7 +5942,7 @@ void StreamTcpPseudoPacketCreateStreamEndPacket(ThreadVars *tv, StreamTcpThread
     }
 
     /* no need for a pseudo packet if there is nothing left to reassemble */
-    if (ssn->server.seg_list == NULL && ssn->client.seg_list == NULL) {
+    if (RB_EMPTY(&ssn->server.seg_tree) && RB_EMPTY(&ssn->client.seg_tree)) {
         SCReturn;
     }
 
@@ -6234,11 +6234,12 @@ int StreamTcpSegmentForEach(const Packet *p, uint8_t flag, StreamSegmentCallback
     }
 
     /* for IDS, return ack'd segments. For IPS all. */
-    TcpSegment *seg = stream->seg_list;
-    for (; seg != NULL &&
-            ((stream_config.flags & STREAMTCP_INIT_FLAG_INLINE)
-             || SEQ_LT(seg->seq, stream->last_ack));)
-    {
+    TcpSegment *seg;
+    RB_FOREACH(seg, TCPSEG, &stream->seg_tree) {
+        if (!((stream_config.flags & STREAMTCP_INIT_FLAG_INLINE)
+                    || SEQ_LT(seg->seq, stream->last_ack)))
+            break;
+
         const uint8_t *seg_data;
         uint32_t seg_datalen;
         StreamingBufferSegmentGetData(&stream->sb, &seg->sbseg, &seg_data, &seg_datalen);
@@ -6248,7 +6249,7 @@ int StreamTcpSegmentForEach(const Packet *p, uint8_t flag, StreamSegmentCallback
             SCLogDebug("Callback function has failed");
             return -1;
         }
-        seg = seg->next;
+
         cnt++;
     }
     return cnt;
@@ -6914,7 +6915,11 @@ static int StreamTcpTest09 (void)
 
     FAIL_IF(StreamTcpPacket(&tv, p, &stt, &pq) == -1);
 
-    FAIL_IF(((TcpSession *) (p->flow->protoctx))->client.seg_list->next != NULL);
+    TcpSession *ssn = p->flow->protoctx;
+    FAIL_IF_NULL(ssn);
+    TcpSegment *seg = RB_MIN(TCPSEG, &ssn->client.seg_tree);
+    FAIL_IF_NULL(seg);
+    FAIL_IF(TCPSEG_RB_NEXT(seg) != NULL);
 
     StreamTcpSessionClear(p->flow->protoctx);
     SCFree(p);
@@ -8596,8 +8601,9 @@ static int StreamTcpTest23(void)
 
     FAIL_IF(StreamTcpReassembleHandleSegment(&tv, stt.ra_ctx, &ssn, &ssn.client, p, &pq) == -1);
 
-    FAIL_IF(ssn.client.seg_list_tail == NULL);
-    FAIL_IF(TCP_SEG_LEN(ssn.client.seg_list_tail) != 2);
+    TcpSegment *seg = RB_MAX(TCPSEG, &ssn.client.seg_tree);
+    FAIL_IF_NULL(seg);
+    FAIL_IF(TCP_SEG_LEN(seg) != 2);
 
     StreamTcpUTClearSession(&ssn);
     SCFree(p);
@@ -8661,8 +8667,9 @@ static int StreamTcpTest24(void)
 
     FAIL_IF(StreamTcpReassembleHandleSegment(&tv, stt.ra_ctx, &ssn, &ssn.client, p, &pq) == -1);
 
-    FAIL_IF(ssn.client.seg_list_tail == NULL);
-    FAIL_IF(TCP_SEG_LEN(ssn.client.seg_list_tail) != 4);
+    TcpSegment *seg = RB_MAX(TCPSEG, &ssn.client.seg_tree);
+    FAIL_IF_NULL(seg);
+    FAIL_IF(TCP_SEG_LEN(seg) != 4);
 
     StreamTcpUTClearSession(&ssn);
     SCFree(p);