From: Victor Julien Date: Thu, 23 Aug 2018 15:27:08 +0000 (+0200) Subject: stream/segments: turn linked list into rbtree X-Git-Tag: suricata-4.1.0-rc2~76 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=26b5e1ed13ae3a88b275d51d668d461a8e172080;p=thirdparty%2Fsuricata.git stream/segments: turn linked list into rbtree 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. --- diff --git a/src/flow-manager.c b/src/flow-manager.c index 7c12f62564..6f378dd3a1 100644 --- a/src/flow-manager.c +++ b/src/flow-manager.c @@ -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; diff --git a/src/stream-tcp-inline.c b/src/stream-tcp-inline.c index d79723dd51..e7fbb42db6 100644 --- a/src/stream-tcp-inline.c +++ b/src/stream-tcp-inline.c @@ -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(); diff --git a/src/stream-tcp-inline.h b/src/stream-tcp-inline.h index 0f4e7fddac..515c6fd650 100644 --- a/src/stream-tcp-inline.h +++ b/src/stream-tcp-inline.h @@ -26,8 +26,10 @@ #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); diff --git a/src/stream-tcp-list.c b/src/stream-tcp-list.c index c5856f7629..ea8c4f788a 100644 --- a/src/stream-tcp-list.c +++ b/src/stream-tcp-list.c @@ -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 diff --git a/src/stream-tcp-list.h b/src/stream-tcp-list.h index 28c79475cd..a910bb95ec 100644 --- a/src/stream-tcp-list.h +++ b/src/stream-tcp-list.h @@ -26,8 +26,6 @@ #include "stream-tcp-private.h" -void PrintList(TcpSegment *); - #ifdef UNITTESTS void StreamTcpListRegisterTests(void); #endif diff --git a/src/stream-tcp-private.h b/src/stream-tcp-private.h index aa954f9eaa..554e0f57d9 100644 --- a/src/stream-tcp-private.h +++ b/src/stream-tcp-private.h @@ -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 */ diff --git a/src/stream-tcp-reassemble.c b/src/stream-tcp-reassemble.c index e5e47e2157..2ed65348eb 100644 --- a/src/stream-tcp-reassemble.c +++ b/src/stream-tcp-reassemble.c @@ -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: diff --git a/src/stream-tcp-util.c b/src/stream-tcp-util.c index 26e5ead5c7..d594958869 100644 --- a/src/stream-tcp-util.c +++ b/src/stream-tcp-util.c @@ -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 diff --git a/src/stream-tcp.c b/src/stream-tcp.c index dcf6ad02c0..ccd8e71d80 100644 --- a/src/stream-tcp.c +++ b/src/stream-tcp.c @@ -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);