stream/sack: turn SACK record list into rbtree

Convert to rbtree from linked list. These ranges, of which there can
be multiple per packet, are fully controlled by an attacked. The
attacker could craft a stream of packet in such a way that the list
would grow very large. This would make inserts/removals very expensive,
as well as the list walk that is done and size calculation and pruning
operations.

The RBTREE makes inserts/removals much cheaper, at a slight overhead
for 'normal' operations and slightly higher per record memory use.
pull/3479/head
Victor Julien 7 years ago
parent 51ce03e76a
commit 7ec7d234cc

@ -46,12 +46,18 @@ typedef struct TcpStateQueue_ {
struct TcpStateQueue_ *next; struct TcpStateQueue_ *next;
} TcpStateQueue; } TcpStateQueue;
typedef struct StreamTcpSackRecord_ { typedef struct StreamTcpSackRecord {
uint32_t le; /**< left edge, host order */ uint32_t le; /**< left edge, host order */
uint32_t re; /**< right edge, host order */ uint32_t re; /**< right edge, host order */
struct StreamTcpSackRecord_ *next; RB_ENTRY(StreamTcpSackRecord) rb;
} StreamTcpSackRecord; } StreamTcpSackRecord;
int TcpSackCompare(struct StreamTcpSackRecord *a, struct StreamTcpSackRecord *b);
/* red-black tree prototype for SACK records */
RB_HEAD(TCPSACK, StreamTcpSackRecord);
RB_PROTOTYPE(TCPSACK, StreamTcpSackRecord, rb, TcpSackCompare);
typedef struct TcpSegment { typedef struct TcpSegment {
PoolThreadReserved res; PoolThreadReserved res;
uint16_t payload_len; /**< actual size of the payload */ uint16_t payload_len; /**< actual size of the payload */
@ -105,8 +111,7 @@ typedef struct TcpStream_ {
StreamingBuffer sb; StreamingBuffer sb;
struct TCPSEG seg_tree; /**< red black tree of TCP segments. Data is stored in TcpStream::sb */ 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 */ struct TCPSACK sack_tree; /**< red back tree of TCP SACK records. */
StreamTcpSackRecord *sack_tail; /**< tail of list of SACK records */
} TcpStream; } TcpStream;
#define STREAM_BASE_OFFSET(stream) ((stream)->sb.stream_offset) #define STREAM_BASE_OFFSET(stream) ((stream)->sb.stream_offset)

@ -29,17 +29,34 @@
#include "stream-tcp-sack.h" #include "stream-tcp-sack.h"
#include "util-unittest.h" #include "util-unittest.h"
RB_GENERATE(TCPSACK, StreamTcpSackRecord, rb, TcpSackCompare);
int TcpSackCompare(struct StreamTcpSackRecord *a, struct StreamTcpSackRecord *b)
{
if (SEQ_GT(a->le, b->le))
return 1;
else if (SEQ_LT(a->le, b->le))
return -1;
else {
if (SEQ_EQ(a->re, b->re))
return 0;
else if (SEQ_GT(a->re, b->re))
return 1;
else
return -1;
}
}
#ifdef DEBUG #ifdef DEBUG
static void StreamTcpSackPrintList(TcpStream *stream) static void StreamTcpSackPrintList(TcpStream *stream)
{ {
StreamTcpSackRecord *rec = stream->sack_head; StreamTcpSackRecord *rec = NULL;
for (; rec != NULL; rec = rec->next) { RB_FOREACH(rec, TCPSACK, &stream->sack_tree) {
SCLogDebug("record %8u - %8u", rec->le, rec->re); SCLogDebug("record %8u - %8u", rec->le, rec->re);
} }
} }
#endif /* DEBUG */ #endif /* DEBUG */
static StreamTcpSackRecord *StreamTcpSackRecordAlloc(void) static inline StreamTcpSackRecord *StreamTcpSackRecordAlloc(void)
{ {
if (StreamTcpCheckMemcap((uint32_t)sizeof(StreamTcpSackRecord)) == 0) if (StreamTcpCheckMemcap((uint32_t)sizeof(StreamTcpSackRecord)) == 0)
return NULL; return NULL;
@ -52,12 +69,124 @@ static StreamTcpSackRecord *StreamTcpSackRecordAlloc(void)
return rec; return rec;
} }
static void StreamTcpSackRecordFree(StreamTcpSackRecord *rec) static inline void StreamTcpSackRecordFree(StreamTcpSackRecord *rec)
{ {
SCFree(rec); SCFree(rec);
StreamTcpDecrMemuse((uint64_t)sizeof(*rec)); StreamTcpDecrMemuse((uint64_t)sizeof(*rec));
} }
static inline void ConsolidateFwd(struct TCPSACK *tree, struct StreamTcpSackRecord *sa)
{
struct StreamTcpSackRecord *tr, *s = sa;
RB_FOREACH_FROM(tr, TCPSACK, s) {
if (sa == tr)
continue;
SCLogDebug("-> (fwd) tr %p %u/%u", tr, tr->le, tr->re);
if (SEQ_LT(sa->re, tr->le))
break; // entirely before
if (SEQ_GEQ(sa->le, tr->le) && SEQ_LEQ(sa->re, tr->re)) {
sa->re = tr->re;
sa->le = tr->le;
SCLogDebug("-> (fwd) tr %p %u/%u REMOVED ECLIPSED2", tr, tr->le, tr->re);
TCPSACK_RB_REMOVE(tree, tr);
StreamTcpSackRecordFree(tr);
/*
sa: [ ]
tr: [ ]
sa: [ ]
tr: [ ]
sa: [ ]
tr: [ ]
*/
} else if (SEQ_LEQ(sa->le, tr->le) && SEQ_GEQ(sa->re, tr->re)) {
SCLogDebug("-> (fwd) tr %p %u/%u REMOVED ECLIPSED", tr, tr->le, tr->re);
TCPSACK_RB_REMOVE(tree, tr);
StreamTcpSackRecordFree(tr);
/*
sa: [ ]
tr: [ ]
sa: [ ]
tr: [ ]
*/
} else if (SEQ_LT(sa->le, tr->le) && // starts before
SEQ_GEQ(sa->re, tr->le) && SEQ_LT(sa->re, tr->re) // ends inside
) {
// merge
sa->re = tr->re;
SCLogDebug("-> (fwd) tr %p %u/%u REMOVED MERGED", tr, tr->le, tr->re);
TCPSACK_RB_REMOVE(tree, tr);
StreamTcpSackRecordFree(tr);
}
}
}
static inline void ConsolidateBackward(struct TCPSACK *tree, struct StreamTcpSackRecord *sa)
{
struct StreamTcpSackRecord *tr, *s = sa;
RB_FOREACH_REVERSE_FROM(tr, TCPSACK, s) {
if (sa == tr)
continue;
SCLogDebug("-> (bwd) tr %p %u/%u", tr, tr->le, tr->re);
if (SEQ_GT(sa->le, tr->re))
break; // entirely after
if (SEQ_GEQ(sa->le, tr->le) && SEQ_LEQ(sa->re, tr->re)) {
sa->re = tr->re;
sa->le = tr->le;
SCLogDebug("-> (bwd) tr %p %u/%u REMOVED ECLIPSED2", tr, tr->le, tr->re);
TCPSACK_RB_REMOVE(tree, tr);
StreamTcpSackRecordFree(tr);
/*
sa: [ ]
tr: [ ]
sa: [ ]
tr: [ ]
sa: [ ]
tr: [ ]
*/
} else if (SEQ_LEQ(sa->le, tr->le) && SEQ_GEQ(sa->re, tr->re)) {
SCLogDebug("-> (bwd) tr %p %u/%u REMOVED ECLIPSED", tr, tr->le, tr->re);
TCPSACK_RB_REMOVE(tree, tr);
StreamTcpSackRecordFree(tr);
/*
sa: [ ]
tr: [ ]
sa: [ ]
tr: [ ]
*/
} else if (SEQ_GT(sa->le, tr->le) && SEQ_GT(sa->re, tr->re) && SEQ_LEQ(sa->le,tr->re)) {
// merge
sa->le = tr->le;
SCLogDebug("-> (bwd) tr %p %u/%u REMOVED MERGED", tr, tr->le, tr->re);
TCPSACK_RB_REMOVE(tree, tr);
StreamTcpSackRecordFree(tr);
}
}
}
static int Insert(struct TCPSACK *tree, uint32_t le, uint32_t re)
{
SCLogDebug("* inserting: %u/%u\n", le, re);
struct StreamTcpSackRecord *sa = StreamTcpSackRecordAlloc();
if (unlikely(sa == NULL))
return -1;
sa->le = le;
sa->re = re;
struct StreamTcpSackRecord *res = TCPSACK_RB_INSERT(tree, sa);
if (res) {
// exact overlap
SCLogDebug("* insert failed: exact match in tree with %p %u/%u", res, res->le, res->re);
StreamTcpSackRecordFree(sa);
return 0;
}
ConsolidateBackward(tree, sa);
ConsolidateFwd(tree, sa);
return 0;
}
/** /**
* \brief insert a SACK range * \brief insert a SACK range
* *
@ -77,167 +206,17 @@ static int StreamTcpSackInsertRange(TcpStream *stream, uint32_t le, uint32_t re)
/* if to the left of last_ack then ignore */ /* if to the left of last_ack then ignore */
if (SEQ_LT(re, stream->last_ack)) { if (SEQ_LT(re, stream->last_ack)) {
SCLogDebug("too far left. discarding"); SCLogDebug("too far left. discarding");
goto end; SCReturnInt(0);
} }
/* if to the right of the tcp window then ignore */ /* if to the right of the tcp window then ignore */
if (SEQ_GT(le, (stream->last_ack + stream->window))) { if (SEQ_GT(le, (stream->last_ack + stream->window))) {
SCLogDebug("too far right. discarding"); SCLogDebug("too far right. discarding");
goto end; SCReturnInt(0);
} }
if (stream->sack_head != NULL) {
StreamTcpSackRecord *rec;
for (rec = stream->sack_head; rec != NULL; rec = rec->next) {
SCLogDebug("rec %p, le %u, re %u", rec, rec->le, rec->re);
if (SEQ_LT(le, rec->le)) {
SCLogDebug("SEQ_LT(le, rec->le)");
if (SEQ_LT(re, rec->le)) {
SCLogDebug("SEQ_LT(re, rec->le)");
// entirely before, prepend
StreamTcpSackRecord *stsr = StreamTcpSackRecordAlloc();
if (unlikely(stsr == NULL)) {
SCReturnInt(-1);
}
stsr->le = le;
stsr->re = re;
stsr->next = stream->sack_head;
stream->sack_head = stsr;
goto end;
} else if (SEQ_EQ(re, rec->le)) {
SCLogDebug("SEQ_EQ(re, rec->le)");
// starts before, ends on rec->le, expand
rec->le = le;
} else if (SEQ_GT(re, rec->le)) {
SCLogDebug("SEQ_GT(re, rec->le)");
// starts before, ends beyond rec->le
if (SEQ_LEQ(re, rec->re)) {
SCLogDebug("SEQ_LEQ(re, rec->re)");
// ends before rec->re, expand
rec->le = le;
} else { // implied if (re > rec->re)
SCLogDebug("implied if (re > rec->re), le set to %u", rec->re);
le = rec->re;
continue;
}
}
} else if (SEQ_EQ(le, rec->le)) {
SCLogDebug("SEQ_EQ(le, rec->le)");
if (SEQ_LEQ(re, rec->re)) {
SCLogDebug("SEQ_LEQ(re, rec->re)");
// new record fully overlapped
SCReturnInt(0);
} else { // implied re > rec->re
SCLogDebug("implied re > rec->re");
if (rec->next != NULL) {
if (SEQ_LEQ(re, rec->next->le)) {
rec->re = re;
goto end;
} else {
rec->re = rec->next->le;
le = rec->next->le;
SCLogDebug("le is now %u", le);
continue;
}
} else {
rec->re = re;
goto end;
}
}
} else { // implied (le > rec->le)
SCLogDebug("implied (le > rec->le)");
if (SEQ_LT(le, rec->re)) {
SCLogDebug("SEQ_LT(le, rec->re))");
// new record fully overlapped
if (SEQ_GT(re, rec->re)) {
SCLogDebug("SEQ_GT(re, rec->re)");
if (rec->next != NULL) {
if (SEQ_LEQ(re, rec->next->le)) {
rec->re = re;
goto end;
} else {
rec->re = rec->next->le;
le = rec->next->le;
continue;
}
} else {
rec->re = re;
goto end;
}
}
SCLogDebug("new range fully overlapped");
SCReturnInt(0);
} else if (SEQ_EQ(le, rec->re)) {
SCLogDebug("here");
// new record fully overlapped
//int r = StreamTcpSackInsertRange(stream, rec->re+1, re);
//SCReturnInt(r);
le = rec->re;
continue;
} else { /* implied le > rec->re */
SCLogDebug("implied le > rec->re");
if (rec->next == NULL) {
SCLogDebug("rec->next == NULL");
StreamTcpSackRecord *stsr = StreamTcpSackRecordAlloc();
if (unlikely(stsr == NULL)) {
SCReturnInt(-1);
}
stsr->le = le;
stsr->re = re;
stsr->next = NULL;
stream->sack_tail->next = stsr;
stream->sack_tail = stsr;
goto end;
} else {
SCLogDebug("implied rec->next != NULL");
if (SEQ_LT(le, rec->next->le) && SEQ_LT(re, rec->next->le)) {
SCLogDebug("SEQ_LT(le, rec->next->le) && SEQ_LT(re, rec->next->le)");
StreamTcpSackRecord *stsr = StreamTcpSackRecordAlloc();
if (unlikely(stsr == NULL)) {
SCReturnInt(-1);
}
stsr->le = le;
stsr->re = re;
stsr->next = rec->next;
rec->next = stsr;
} else if (SEQ_LT(le, rec->next->le) && SEQ_GEQ(re, rec->next->le)) {
SCLogDebug("SEQ_LT(le, rec->next->le) && SEQ_GEQ(re, rec->next->le)");
StreamTcpSackRecord *stsr = StreamTcpSackRecordAlloc();
if (unlikely(stsr == NULL)) {
SCReturnInt(-1);
}
stsr->le = le;
stsr->re = rec->next->le;
stsr->next = rec->next;
rec->next = stsr;
le = rec->next->le;
}
}
}
}
}
} else {
SCLogDebug("implied empty list");
StreamTcpSackRecord *stsr = StreamTcpSackRecordAlloc();
if (unlikely(stsr == NULL)) {
SCReturnInt(-1);
}
stsr->le = le;
stsr->re = re;
stsr->next = NULL;
stream->sack_head = stsr; if (Insert(&stream->sack_tree, le, re) < 0)
stream->sack_tail = stsr; SCReturnInt(-1);
}
StreamTcpSackPruneList(stream);
end:
SCReturnInt(0); SCReturnInt(0);
} }
@ -252,8 +231,7 @@ end:
*/ */
int StreamTcpSackUpdatePacket(TcpStream *stream, Packet *p) int StreamTcpSackUpdatePacket(TcpStream *stream, Packet *p)
{ {
int records = TCP_GET_SACK_CNT(p); const int records = TCP_GET_SACK_CNT(p);
int record = 0;
const uint8_t *data = TCP_GET_SACK_PTR(p); const uint8_t *data = TCP_GET_SACK_PTR(p);
if (records == 0 || data == NULL) if (records == 0 || data == NULL)
@ -262,35 +240,37 @@ int StreamTcpSackUpdatePacket(TcpStream *stream, Packet *p)
TCPOptSackRecord rec[records], *sack_rec = rec; TCPOptSackRecord rec[records], *sack_rec = rec;
memcpy(&rec, data, sizeof(TCPOptSackRecord) * records); memcpy(&rec, data, sizeof(TCPOptSackRecord) * records);
for (record = 0; record < records; record++) { for (int record = 0; record < records; record++) {
const uint32_t le = SCNtohl(sack_rec->le);
const uint32_t re = SCNtohl(sack_rec->re);
SCLogDebug("%p last_ack %u, left edge %u, right edge %u", sack_rec, SCLogDebug("%p last_ack %u, left edge %u, right edge %u", sack_rec,
stream->last_ack, SCNtohl(sack_rec->le), SCNtohl(sack_rec->re)); stream->last_ack, le, re);
if (SEQ_LEQ(SCNtohl(sack_rec->re), stream->last_ack)) { if (SEQ_LEQ(re, stream->last_ack)) {
SCLogDebug("record before last_ack"); SCLogDebug("record before last_ack");
goto next; goto next;
} }
if (SEQ_GT(SCNtohl(sack_rec->re), stream->next_win)) { if (SEQ_GT(re, stream->next_win)) {
SCLogDebug("record %u:%u beyond next_win %u", SCLogDebug("record %u:%u beyond next_win %u",
SCNtohl(sack_rec->le), SCNtohl(sack_rec->re), stream->next_win); le, re, stream->next_win);
goto next; goto next;
} }
if (SEQ_GEQ(SCNtohl(sack_rec->le), SCNtohl(sack_rec->re))) { if (SEQ_GEQ(le, re)) {
SCLogDebug("invalid record: le >= re"); SCLogDebug("invalid record: le >= re");
goto next; goto next;
} }
if (StreamTcpSackInsertRange(stream, SCNtohl(sack_rec->le), if (StreamTcpSackInsertRange(stream, le, re) == -1) {
SCNtohl(sack_rec->re)) == -1)
{
SCReturnInt(-1); SCReturnInt(-1);
} }
next: next:
sack_rec++; sack_rec++;
} }
StreamTcpSackPruneList(stream);
#ifdef DEBUG #ifdef DEBUG
StreamTcpSackPrintList(stream); StreamTcpSackPrintList(stream);
#endif #endif
@ -301,23 +281,13 @@ void StreamTcpSackPruneList(TcpStream *stream)
{ {
SCEnter(); SCEnter();
StreamTcpSackRecord *rec = stream->sack_head; StreamTcpSackRecord *rec = NULL, *safe = NULL;
RB_FOREACH_SAFE(rec, TCPSACK, &stream->sack_tree, safe) {
while (rec != NULL) {
if (SEQ_LT(rec->re, stream->last_ack)) { if (SEQ_LT(rec->re, stream->last_ack)) {
SCLogDebug("removing le %u re %u", rec->le, rec->re); SCLogDebug("removing le %u re %u", rec->le, rec->re);
TCPSACK_RB_REMOVE(&stream->sack_tree, rec);
StreamTcpSackRecordFree(rec);
if (rec->next != NULL) {
stream->sack_head = rec->next;
StreamTcpSackRecordFree(rec);
rec = stream->sack_head;
continue;
} else {
stream->sack_head = NULL;
stream->sack_tail = NULL;
StreamTcpSackRecordFree(rec);
break;
}
} else if (SEQ_LT(rec->le, stream->last_ack)) { } else if (SEQ_LT(rec->le, stream->last_ack)) {
SCLogDebug("adjusting record to le %u re %u", rec->le, rec->re); SCLogDebug("adjusting record to le %u re %u", rec->le, rec->re);
/* last ack inside this record, update */ /* last ack inside this record, update */
@ -335,7 +305,7 @@ void StreamTcpSackPruneList(TcpStream *stream)
} }
/** /**
* \brief Free SACK list from a stream * \brief Free SACK tree from a stream
* *
* \param stream Stream to cleanup * \param stream Stream to cleanup
*/ */
@ -343,17 +313,12 @@ void StreamTcpSackFreeList(TcpStream *stream)
{ {
SCEnter(); SCEnter();
StreamTcpSackRecord *rec = stream->sack_head; StreamTcpSackRecord *rec = NULL, *safe = NULL;
StreamTcpSackRecord *next = NULL; RB_FOREACH_SAFE(rec, TCPSACK, &stream->sack_tree, safe) {
TCPSACK_RB_REMOVE(&stream->sack_tree, rec);
while (rec != NULL) {
next = rec->next;
StreamTcpSackRecordFree(rec); StreamTcpSackRecordFree(rec);
rec = next;
} }
stream->sack_head = NULL;
stream->sack_tail = NULL;
SCReturn; SCReturn;
} }
@ -369,8 +334,6 @@ void StreamTcpSackFreeList(TcpStream *stream)
static int StreamTcpSackTest01 (void) static int StreamTcpSackTest01 (void)
{ {
TcpStream stream; TcpStream stream;
int retval = 0;
memset(&stream, 0, sizeof(stream)); memset(&stream, 0, sizeof(stream));
stream.window = 100; stream.window = 100;
@ -382,21 +345,15 @@ static int StreamTcpSackTest01 (void)
StreamTcpSackPrintList(&stream); StreamTcpSackPrintList(&stream);
#endif /* DEBUG */ #endif /* DEBUG */
if (stream.sack_head->le != 1 || stream.sack_head->re != 20) { StreamTcpSackRecord *rec = RB_MIN(TCPSACK, &stream.sack_tree);
printf("list in weird state, head le %u, re %u: ", FAIL_IF_NULL(rec);
stream.sack_head->le, stream.sack_head->re);
goto end;
}
if (StreamTcpSackedSize(&stream) != 19) { FAIL_IF(rec->le != 1);
printf("size should be 19, is %u: ", StreamTcpSackedSize(&stream)); FAIL_IF(rec->re != 20);
goto end;
}
retval = 1; FAIL_IF(StreamTcpSackedSize(&stream) != 19);
end:
StreamTcpSackFreeList(&stream); StreamTcpSackFreeList(&stream);
SCReturnInt(retval); PASS;
} }
/** /**
@ -408,8 +365,6 @@ end:
static int StreamTcpSackTest02 (void) static int StreamTcpSackTest02 (void)
{ {
TcpStream stream; TcpStream stream;
int retval = 0;
memset(&stream, 0, sizeof(stream)); memset(&stream, 0, sizeof(stream));
stream.window = 100; stream.window = 100;
@ -419,21 +374,15 @@ static int StreamTcpSackTest02 (void)
StreamTcpSackPrintList(&stream); StreamTcpSackPrintList(&stream);
#endif /* DEBUG */ #endif /* DEBUG */
if (stream.sack_head->le != 1 || stream.sack_head->re != 20) { StreamTcpSackRecord *rec = RB_MIN(TCPSACK, &stream.sack_tree);
printf("list in weird state, head le %u, re %u: ", FAIL_IF_NULL(rec);
stream.sack_head->le, stream.sack_head->re);
goto end;
}
if (StreamTcpSackedSize(&stream) != 19) { FAIL_IF(rec->le != 1);
printf("size should be 19, is %u: ", StreamTcpSackedSize(&stream)); FAIL_IF(rec->re != 20);
goto end;
}
retval = 1; FAIL_IF(StreamTcpSackedSize(&stream) != 19);
end:
StreamTcpSackFreeList(&stream); StreamTcpSackFreeList(&stream);
SCReturnInt(retval); PASS;
} }
/** /**
@ -445,8 +394,6 @@ end:
static int StreamTcpSackTest03 (void) static int StreamTcpSackTest03 (void)
{ {
TcpStream stream; TcpStream stream;
int retval = 0;
memset(&stream, 0, sizeof(stream)); memset(&stream, 0, sizeof(stream));
stream.window = 100; stream.window = 100;
@ -460,19 +407,15 @@ static int StreamTcpSackTest03 (void)
StreamTcpSackPrintList(&stream); StreamTcpSackPrintList(&stream);
#endif /* DEBUG */ #endif /* DEBUG */
if (stream.sack_head->le != 5) { StreamTcpSackRecord *rec = RB_MIN(TCPSACK, &stream.sack_tree);
goto end; FAIL_IF_NULL(rec);
}
if (StreamTcpSackedSize(&stream) != 20) { FAIL_IF(rec->le != 5);
printf("size should be 20, is %u: ", StreamTcpSackedSize(&stream)); FAIL_IF(rec->re != 25);
goto end;
}
retval = 1; FAIL_IF(StreamTcpSackedSize(&stream) != 20);
end:
StreamTcpSackFreeList(&stream); StreamTcpSackFreeList(&stream);
SCReturnInt(retval); PASS;
} }
/** /**
@ -484,8 +427,6 @@ end:
static int StreamTcpSackTest04 (void) static int StreamTcpSackTest04 (void)
{ {
TcpStream stream; TcpStream stream;
int retval = 0;
memset(&stream, 0, sizeof(stream)); memset(&stream, 0, sizeof(stream));
stream.window = 100; stream.window = 100;
@ -496,19 +437,15 @@ static int StreamTcpSackTest04 (void)
StreamTcpSackPrintList(&stream); StreamTcpSackPrintList(&stream);
#endif /* DEBUG */ #endif /* DEBUG */
if (stream.sack_head->le != 0) { StreamTcpSackRecord *rec = RB_MIN(TCPSACK, &stream.sack_tree);
goto end; FAIL_IF_NULL(rec);
}
if (StreamTcpSackedSize(&stream) != 45) { FAIL_IF(rec->le != 0);
printf("size should be 45, is %u: ", StreamTcpSackedSize(&stream)); FAIL_IF(rec->re != 25);
goto end;
}
retval = 1; FAIL_IF(StreamTcpSackedSize(&stream) != 45);
end:
StreamTcpSackFreeList(&stream); StreamTcpSackFreeList(&stream);
SCReturnInt(retval); PASS;
} }
/** /**
@ -520,8 +457,6 @@ end:
static int StreamTcpSackTest05 (void) static int StreamTcpSackTest05 (void)
{ {
TcpStream stream; TcpStream stream;
int retval = 0;
memset(&stream, 0, sizeof(stream)); memset(&stream, 0, sizeof(stream));
stream.window = 100; stream.window = 100;
@ -532,19 +467,15 @@ static int StreamTcpSackTest05 (void)
StreamTcpSackPrintList(&stream); StreamTcpSackPrintList(&stream);
#endif /* DEBUG */ #endif /* DEBUG */
if (stream.sack_head->le != 0) { StreamTcpSackRecord *rec = RB_MIN(TCPSACK, &stream.sack_tree);
goto end; FAIL_IF_NULL(rec);
}
if (StreamTcpSackedSize(&stream) != 50) { FAIL_IF(rec->le != 0);
printf("size should be 50, is %u: ", StreamTcpSackedSize(&stream)); FAIL_IF(rec->re != 50);
goto end;
}
retval = 1; FAIL_IF(StreamTcpSackedSize(&stream) != 50);
end:
StreamTcpSackFreeList(&stream); StreamTcpSackFreeList(&stream);
SCReturnInt(retval); PASS;
} }
/** /**
@ -556,8 +487,6 @@ end:
static int StreamTcpSackTest06 (void) static int StreamTcpSackTest06 (void)
{ {
TcpStream stream; TcpStream stream;
int retval = 0;
memset(&stream, 0, sizeof(stream)); memset(&stream, 0, sizeof(stream));
stream.window = 100; stream.window = 100;
@ -570,19 +499,15 @@ static int StreamTcpSackTest06 (void)
StreamTcpSackPrintList(&stream); StreamTcpSackPrintList(&stream);
#endif /* DEBUG */ #endif /* DEBUG */
if (stream.sack_head->le != 0) { StreamTcpSackRecord *rec = RB_MIN(TCPSACK, &stream.sack_tree);
goto end; FAIL_IF_NULL(rec);
}
if (StreamTcpSackedSize(&stream) != 40) { FAIL_IF(rec->le != 0);
printf("size should be 40, is %u: ", StreamTcpSackedSize(&stream)); FAIL_IF(rec->re != 40);
goto end;
}
retval = 1; FAIL_IF(StreamTcpSackedSize(&stream) != 40);
end:
StreamTcpSackFreeList(&stream); StreamTcpSackFreeList(&stream);
SCReturnInt(retval); PASS;
} }
/** /**
@ -594,8 +519,6 @@ end:
static int StreamTcpSackTest07 (void) static int StreamTcpSackTest07 (void)
{ {
TcpStream stream; TcpStream stream;
int retval = 0;
memset(&stream, 0, sizeof(stream)); memset(&stream, 0, sizeof(stream));
stream.window = 100; stream.window = 100;
@ -608,28 +531,18 @@ static int StreamTcpSackTest07 (void)
StreamTcpSackPrintList(&stream); StreamTcpSackPrintList(&stream);
#endif /* DEBUG */ #endif /* DEBUG */
if (stream.sack_head->le != 0) { StreamTcpSackRecord *rec = RB_MIN(TCPSACK, &stream.sack_tree);
goto end; FAIL_IF_NULL(rec);
} FAIL_IF(rec->le != 0);
FAIL_IF(rec->re != 40);
if (StreamTcpSackedSize(&stream) != 40) { FAIL_IF(StreamTcpSackedSize(&stream) != 40);
printf("size should be 40, is %u: ", StreamTcpSackedSize(&stream));
goto end;
}
stream.last_ack = 10; stream.last_ack = 10;
StreamTcpSackPruneList(&stream); StreamTcpSackPruneList(&stream);
FAIL_IF(StreamTcpSackedSize(&stream) != 30);
if (StreamTcpSackedSize(&stream) != 30) {
printf("size should be 30, is %u: ", StreamTcpSackedSize(&stream));
goto end;
}
retval = 1;
end:
StreamTcpSackFreeList(&stream); StreamTcpSackFreeList(&stream);
SCReturnInt(retval); PASS;
} }
/** /**
@ -641,8 +554,6 @@ end:
static int StreamTcpSackTest08 (void) static int StreamTcpSackTest08 (void)
{ {
TcpStream stream; TcpStream stream;
int retval = 0;
memset(&stream, 0, sizeof(stream)); memset(&stream, 0, sizeof(stream));
stream.window = 100; stream.window = 100;
@ -655,28 +566,18 @@ static int StreamTcpSackTest08 (void)
StreamTcpSackPrintList(&stream); StreamTcpSackPrintList(&stream);
#endif /* DEBUG */ #endif /* DEBUG */
if (stream.sack_head->le != 0) { StreamTcpSackRecord *rec = RB_MIN(TCPSACK, &stream.sack_tree);
goto end; FAIL_IF_NULL(rec);
} FAIL_IF(rec->le != 0);
FAIL_IF(rec->re != 40);
if (StreamTcpSackedSize(&stream) != 40) { FAIL_IF(StreamTcpSackedSize(&stream) != 40);
printf("size should be 40, is %u: ", StreamTcpSackedSize(&stream));
goto end;
}
stream.last_ack = 41; stream.last_ack = 41;
StreamTcpSackPruneList(&stream); StreamTcpSackPruneList(&stream);
FAIL_IF(StreamTcpSackedSize(&stream) != 0);
if (StreamTcpSackedSize(&stream) != 0) {
printf("size should be 0, is %u: ", StreamTcpSackedSize(&stream));
goto end;
}
retval = 1;
end:
StreamTcpSackFreeList(&stream); StreamTcpSackFreeList(&stream);
SCReturnInt(retval); PASS;
} }
/** /**
@ -688,8 +589,6 @@ end:
static int StreamTcpSackTest09 (void) static int StreamTcpSackTest09 (void)
{ {
TcpStream stream; TcpStream stream;
int retval = 0;
memset(&stream, 0, sizeof(stream)); memset(&stream, 0, sizeof(stream));
stream.window = 100; stream.window = 100;
@ -703,28 +602,18 @@ static int StreamTcpSackTest09 (void)
StreamTcpSackPrintList(&stream); StreamTcpSackPrintList(&stream);
#endif /* DEBUG */ #endif /* DEBUG */
if (stream.sack_head->le != 0) { StreamTcpSackRecord *rec = RB_MIN(TCPSACK, &stream.sack_tree);
goto end; FAIL_IF_NULL(rec);
} FAIL_IF(rec->le != 0);
FAIL_IF(rec->re != 40);
if (StreamTcpSackedSize(&stream) != 40) { FAIL_IF(StreamTcpSackedSize(&stream) != 40);
printf("size should be 40, is %u: ", StreamTcpSackedSize(&stream));
goto end;
}
stream.last_ack = 39; stream.last_ack = 39;
StreamTcpSackPruneList(&stream); StreamTcpSackPruneList(&stream);
FAIL_IF(StreamTcpSackedSize(&stream) != 1);
if (StreamTcpSackedSize(&stream) != 1) {
printf("size should be 1, is %u: ", StreamTcpSackedSize(&stream));
goto end;
}
retval = 1;
end:
StreamTcpSackFreeList(&stream); StreamTcpSackFreeList(&stream);
SCReturnInt(retval); PASS;
} }
/** /**
@ -736,8 +625,6 @@ end:
static int StreamTcpSackTest10 (void) static int StreamTcpSackTest10 (void)
{ {
TcpStream stream; TcpStream stream;
int retval = 0;
memset(&stream, 0, sizeof(stream)); memset(&stream, 0, sizeof(stream));
stream.window = 1000; stream.window = 1000;
@ -750,28 +637,18 @@ static int StreamTcpSackTest10 (void)
StreamTcpSackPrintList(&stream); StreamTcpSackPrintList(&stream);
#endif /* DEBUG */ #endif /* DEBUG */
if (stream.sack_head->le != 100) { StreamTcpSackRecord *rec = RB_MIN(TCPSACK, &stream.sack_tree);
goto end; FAIL_IF_NULL(rec);
} FAIL_IF(rec->le != 100);
FAIL_IF(rec->re != 140);
if (StreamTcpSackedSize(&stream) != 40) { FAIL_IF(StreamTcpSackedSize(&stream) != 40);
printf("size should be 40, is %u: ", StreamTcpSackedSize(&stream));
goto end;
}
stream.last_ack = 99; stream.last_ack = 99;
StreamTcpSackPruneList(&stream); StreamTcpSackPruneList(&stream);
FAIL_IF(StreamTcpSackedSize(&stream) != 40);
if (StreamTcpSackedSize(&stream) != 40) {
printf("size should be 40, is %u: ", StreamTcpSackedSize(&stream));
goto end;
}
retval = 1;
end:
StreamTcpSackFreeList(&stream); StreamTcpSackFreeList(&stream);
SCReturnInt(retval); PASS;
} }
/** /**
@ -783,8 +660,6 @@ end:
static int StreamTcpSackTest11 (void) static int StreamTcpSackTest11 (void)
{ {
TcpStream stream; TcpStream stream;
int retval = 0;
memset(&stream, 0, sizeof(stream)); memset(&stream, 0, sizeof(stream));
stream.window = 1000; stream.window = 1000;
@ -797,28 +672,18 @@ static int StreamTcpSackTest11 (void)
StreamTcpSackPrintList(&stream); StreamTcpSackPrintList(&stream);
#endif /* DEBUG */ #endif /* DEBUG */
if (stream.sack_head->le != 100) { StreamTcpSackRecord *rec = RB_MIN(TCPSACK, &stream.sack_tree);
goto end; FAIL_IF_NULL(rec);
} FAIL_IF(rec->le != 100);
FAIL_IF(rec->re != 140);
if (StreamTcpSackedSize(&stream) != 40) { FAIL_IF(StreamTcpSackedSize(&stream) != 40);
printf("size should be 40, is %u: ", StreamTcpSackedSize(&stream));
goto end;
}
stream.last_ack = 99; stream.last_ack = 99;
StreamTcpSackPruneList(&stream); StreamTcpSackPruneList(&stream);
FAIL_IF(StreamTcpSackedSize(&stream) != 40);
if (StreamTcpSackedSize(&stream) != 40) {
printf("size should be 40, is %u: ", StreamTcpSackedSize(&stream));
goto end;
}
retval = 1;
end:
StreamTcpSackFreeList(&stream); StreamTcpSackFreeList(&stream);
SCReturnInt(retval); PASS;
} }
/** /**
@ -830,8 +695,6 @@ end:
static int StreamTcpSackTest12 (void) static int StreamTcpSackTest12 (void)
{ {
TcpStream stream; TcpStream stream;
int retval = 0;
memset(&stream, 0, sizeof(stream)); memset(&stream, 0, sizeof(stream));
stream.window = 2000; stream.window = 2000;
@ -844,35 +707,21 @@ static int StreamTcpSackTest12 (void)
StreamTcpSackPrintList(&stream); StreamTcpSackPrintList(&stream);
#endif /* DEBUG */ #endif /* DEBUG */
if (stream.sack_head->le != 100) { StreamTcpSackRecord *rec = RB_MIN(TCPSACK, &stream.sack_tree);
goto end; FAIL_IF_NULL(rec);
} FAIL_IF(rec->le != 100);
FAIL_IF(rec->re != 1000);
if (StreamTcpSackedSize(&stream) != 900) { FAIL_IF(StreamTcpSackedSize(&stream) != 900);
printf("size should be 900, is %u: ", StreamTcpSackedSize(&stream));
goto end;
}
StreamTcpSackInsertRange(&stream, 0, 1000); StreamTcpSackInsertRange(&stream, 0, 1000);
FAIL_IF(StreamTcpSackedSize(&stream) != 1000);
if (StreamTcpSackedSize(&stream) != 1000) {
printf("size should be 1000, is %u: ", StreamTcpSackedSize(&stream));
goto end;
}
stream.last_ack = 500; stream.last_ack = 500;
StreamTcpSackPruneList(&stream); StreamTcpSackPruneList(&stream);
FAIL_IF(StreamTcpSackedSize(&stream) != 500);
if (StreamTcpSackedSize(&stream) != 500) {
printf("size should be 500, is %u: ", StreamTcpSackedSize(&stream));
goto end;
}
retval = 1;
end:
StreamTcpSackFreeList(&stream); StreamTcpSackFreeList(&stream);
SCReturnInt(retval); PASS;
} }
/** /**
@ -883,29 +732,21 @@ end:
static int StreamTcpSackTest13 (void) { static int StreamTcpSackTest13 (void) {
TcpStream stream; TcpStream stream;
int retval = 0;
int i;
memset(&stream, 0, sizeof(stream)); memset(&stream, 0, sizeof(stream));
stream.last_ack = 10000; stream.last_ack = 10000;
stream.window = 2000; stream.window = 2000;
for (i = 0; i < 10; i++) { for (int i = 0; i < 10; i++) {
StreamTcpSackInsertRange(&stream, 100+(20*i), 110+(20*i)); StreamTcpSackInsertRange(&stream, 100+(20*i), 110+(20*i));
} }
#ifdef DEBUG #ifdef DEBUG
StreamTcpSackPrintList(&stream); StreamTcpSackPrintList(&stream);
#endif /* DEBUG */ #endif /* DEBUG */
if (StreamTcpSackedSize(&stream) != 0) { FAIL_IF(StreamTcpSackedSize(&stream) != 0);
printf("Sacked size is %u: ", StreamTcpSackedSize(&stream));
goto end;
}
retval = 1;
end:
StreamTcpSackFreeList(&stream); StreamTcpSackFreeList(&stream);
SCReturnInt(retval); PASS;
} }
/** /**
@ -916,29 +757,21 @@ end:
static int StreamTcpSackTest14 (void) { static int StreamTcpSackTest14 (void) {
TcpStream stream; TcpStream stream;
int retval = 0;
int i;
memset(&stream, 0, sizeof(stream)); memset(&stream, 0, sizeof(stream));
stream.last_ack = 1000; stream.last_ack = 1000;
stream.window = 2000; stream.window = 2000;
for (i = 0; i < 10; i++) { for (int i = 0; i < 10; i++) {
StreamTcpSackInsertRange(&stream, 4000+(20*i), 4010+(20*i)); StreamTcpSackInsertRange(&stream, 4000+(20*i), 4010+(20*i));
} }
#ifdef DEBUG #ifdef DEBUG
StreamTcpSackPrintList(&stream); StreamTcpSackPrintList(&stream);
#endif /* DEBUG */ #endif /* DEBUG */
if (StreamTcpSackedSize(&stream) != 0) { FAIL_IF(StreamTcpSackedSize(&stream) != 0);
printf("Sacked size is %u: ", StreamTcpSackedSize(&stream));
goto end;
}
retval = 1;
end:
StreamTcpSackFreeList(&stream); StreamTcpSackFreeList(&stream);
SCReturnInt(retval); PASS;
} }
#endif /* UNITTESTS */ #endif /* UNITTESTS */

@ -40,14 +40,13 @@
*/ */
static inline uint32_t StreamTcpSackedSize(TcpStream *stream) static inline uint32_t StreamTcpSackedSize(TcpStream *stream)
{ {
if (likely(stream->sack_head == NULL)) { if (likely(RB_EMPTY(&stream->sack_tree))) {
SCReturnUInt(0U); SCReturnUInt(0U);
} else { } else {
uint32_t size = 0; uint32_t size = 0;
StreamTcpSackRecord *rec = NULL; StreamTcpSackRecord *rec = NULL;
RB_FOREACH(rec, TCPSACK, &stream->sack_tree) {
for (rec = stream->sack_head; rec != NULL; rec = rec->next) {
size += (rec->re - rec->le); size += (rec->re - rec->le);
} }

Loading…
Cancel
Save