aboutsummaryrefslogtreecommitdiffstats
path: root/framework/src/suricata/src/stream-tcp-sack.c
diff options
context:
space:
mode:
authorAshlee Young <ashlee@onosfw.com>2015-09-09 22:21:41 -0700
committerAshlee Young <ashlee@onosfw.com>2015-09-09 22:21:41 -0700
commit8879b125d26e8db1a5633de5a9c692eb2d1c4f83 (patch)
treec7259d85a991b83dfa85ab2e339360669fc1f58e /framework/src/suricata/src/stream-tcp-sack.c
parent13d05bc8458758ee39cb829098241e89616717ee (diff)
suricata checkin based on commit id a4bce14770beee46a537eda3c3f6e8e8565d5d0a
Change-Id: I9a214fa0ee95e58fc640e50bd604dac7f42db48f
Diffstat (limited to 'framework/src/suricata/src/stream-tcp-sack.c')
-rw-r--r--framework/src/suricata/src/stream-tcp-sack.c960
1 files changed, 960 insertions, 0 deletions
diff --git a/framework/src/suricata/src/stream-tcp-sack.c b/framework/src/suricata/src/stream-tcp-sack.c
new file mode 100644
index 00000000..9e5503f7
--- /dev/null
+++ b/framework/src/suricata/src/stream-tcp-sack.c
@@ -0,0 +1,960 @@
+/* Copyright (C) 2007-2011 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ * Stream engine TCP SACK handling.
+ */
+
+#include "suricata-common.h"
+#include "stream-tcp.h"
+#include "stream-tcp-private.h"
+#include "stream-tcp-sack.h"
+#include "util-unittest.h"
+
+#ifdef DEBUG
+void StreamTcpSackPrintList(TcpStream *stream)
+{
+ StreamTcpSackRecord *rec = stream->sack_head;
+ for (; rec != NULL; rec = rec->next) {
+ SCLogDebug("record %8u - %8u", rec->le, rec->re);
+ }
+}
+#endif /* DEBUG */
+
+static StreamTcpSackRecord *StreamTcpSackRecordAlloc(void)
+{
+ if (StreamTcpCheckMemcap((uint32_t)sizeof(StreamTcpSackRecord)) == 0)
+ return NULL;
+
+ StreamTcpSackRecord *rec = SCMalloc(sizeof(*rec));
+ if (unlikely(rec == NULL))
+ return NULL;
+
+ StreamTcpIncrMemuse((uint64_t)sizeof(*rec));
+ return rec;
+}
+
+static void StreamTcpSackRecordFree(StreamTcpSackRecord *rec)
+{
+ SCFree(rec);
+ StreamTcpDecrMemuse((uint64_t)sizeof(*rec));
+}
+
+/**
+ * \brief insert a SACK range
+ *
+ * \param le left edge in host order
+ * \param re right edge in host order
+ *
+ * \retval 0 all is good
+ * \retval -1 error
+ */
+static int StreamTcpSackInsertRange(TcpStream *stream, uint32_t le, uint32_t re)
+{
+ SCLogDebug("le %u, re %u", le, re);
+#ifdef DEBUG
+ StreamTcpSackPrintList(stream);
+#endif
+
+ /* if to the left of last_ack then ignore */
+ if (SEQ_LT(re, stream->last_ack)) {
+ SCLogDebug("too far left. discarding");
+ goto end;
+ }
+ /* if to the right of the tcp window then ignore */
+ if (SEQ_GT(le, (stream->last_ack + stream->window))) {
+ SCLogDebug("too far right. discarding");
+ goto end;
+ }
+ 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;
+ stream->sack_tail = stsr;
+ }
+
+ StreamTcpSackPruneList(stream);
+end:
+ SCReturnInt(0);
+}
+
+/**
+ * \brief Update stream with SACK records from a TCP packet.
+ *
+ * \param stream The stream to update.
+ * \param p packet to get the SACK records from
+ *
+ * \retval -1 error
+ * \retval 0 ok
+ */
+int StreamTcpSackUpdatePacket(TcpStream *stream, Packet *p)
+{
+ int records = TCP_GET_SACK_CNT(p);
+ int record = 0;
+
+ TCPOptSackRecord *sack_rec = (TCPOptSackRecord *)(TCP_GET_SACK_PTR(p));
+
+ for (record = 0; record < records; record++) {
+ SCLogDebug("%p last_ack %u, left edge %u, right edge %u", sack_rec,
+ stream->last_ack, ntohl(sack_rec->le), ntohl(sack_rec->re));
+
+ if (SEQ_LEQ(ntohl(sack_rec->re), stream->last_ack)) {
+ SCLogDebug("record before last_ack");
+ goto next;
+ }
+
+ /** \todo need a metric to a check for a right edge limit */
+/*
+ if (SEQ_GT(ntohl(sack_rec->re), stream->next_seq)) {
+ SCLogDebug("record beyond next_seq %u", stream->next_seq);
+ goto next;
+ }
+*/
+ if (SEQ_GEQ(ntohl(sack_rec->le), ntohl(sack_rec->re))) {
+ SCLogDebug("invalid record: le >= re");
+ goto next;
+ }
+
+ if (StreamTcpSackInsertRange(stream, ntohl(sack_rec->le),
+ ntohl(sack_rec->re)) == -1)
+ {
+ SCReturnInt(-1);
+ }
+
+ next:
+ sack_rec++;
+ }
+#ifdef DEBUG
+ StreamTcpSackPrintList(stream);
+#endif
+ SCReturnInt(0);
+}
+
+void StreamTcpSackPruneList(TcpStream *stream)
+{
+ SCEnter();
+
+ StreamTcpSackRecord *rec = stream->sack_head;
+
+ while (rec != NULL) {
+ if (SEQ_LT(rec->re, stream->last_ack)) {
+ SCLogDebug("removing le %u re %u", rec->le, rec->re);
+
+ 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)) {
+ SCLogDebug("adjusting record to le %u re %u", rec->le, rec->re);
+ /* last ack inside this record, update */
+ rec->le = stream->last_ack;
+ break;
+ } else {
+ SCLogDebug("record beyond last_ack, nothing to do. Bailing out.");
+ break;
+ }
+ }
+#ifdef DEBUG
+ StreamTcpSackPrintList(stream);
+#endif
+ SCReturn;
+}
+
+/**
+ * \brief Free SACK list from a stream
+ *
+ * \param stream Stream to cleanup
+ */
+void StreamTcpSackFreeList(TcpStream *stream)
+{
+ SCEnter();
+
+ StreamTcpSackRecord *rec = stream->sack_head;
+ StreamTcpSackRecord *next = NULL;
+
+ while (rec != NULL) {
+ next = rec->next;
+ StreamTcpSackRecordFree(rec);
+ rec = next;
+ }
+
+ stream->sack_head = NULL;
+ stream->sack_tail = NULL;
+ SCReturn;
+}
+
+
+#ifdef UNITTESTS
+
+/**
+ * \test Test the insertion of SACK ranges.
+ *
+ * \retval On success it returns 1 and on failure 0.
+ */
+
+static int StreamTcpSackTest01 (void)
+{
+ TcpStream stream;
+ int retval = 0;
+
+ memset(&stream, 0, sizeof(stream));
+ stream.window = 100;
+
+ StreamTcpSackInsertRange(&stream, 1, 10);
+ StreamTcpSackInsertRange(&stream, 10, 20);
+ StreamTcpSackInsertRange(&stream, 10, 20);
+ StreamTcpSackInsertRange(&stream, 1, 20);
+#ifdef DEBUG
+ StreamTcpSackPrintList(&stream);
+#endif /* DEBUG */
+
+ if (stream.sack_head->le != 1 || stream.sack_head->re != 20) {
+ printf("list in weird state, head le %u, re %u: ",
+ stream.sack_head->le, stream.sack_head->re);
+ goto end;
+ }
+
+ if (StreamTcpSackedSize(&stream) != 19) {
+ printf("size should be 19, is %u: ", StreamTcpSackedSize(&stream));
+ goto end;
+ }
+
+ retval = 1;
+end:
+ SCReturnInt(retval);
+}
+
+/**
+ * \test Test the insertion of SACK ranges.
+ *
+ * \retval On success it returns 1 and on failure 0.
+ */
+
+static int StreamTcpSackTest02 (void)
+{
+ TcpStream stream;
+ int retval = 0;
+
+ memset(&stream, 0, sizeof(stream));
+ stream.window = 100;
+
+ StreamTcpSackInsertRange(&stream, 10, 20);
+ StreamTcpSackInsertRange(&stream, 1, 20);
+#ifdef DEBUG
+ StreamTcpSackPrintList(&stream);
+#endif /* DEBUG */
+
+ if (stream.sack_head->le != 1 || stream.sack_head->re != 20) {
+ printf("list in weird state, head le %u, re %u: ",
+ stream.sack_head->le, stream.sack_head->re);
+ goto end;
+ }
+
+ if (StreamTcpSackedSize(&stream) != 19) {
+ printf("size should be 19, is %u: ", StreamTcpSackedSize(&stream));
+ goto end;
+ }
+
+ retval = 1;
+end:
+ SCReturnInt(retval);
+}
+
+/**
+ * \test Test the insertion of SACK ranges.
+ *
+ * \retval On success it returns 1 and on failure 0.
+ */
+
+static int StreamTcpSackTest03 (void)
+{
+ TcpStream stream;
+ int retval = 0;
+
+ memset(&stream, 0, sizeof(stream));
+ stream.window = 100;
+
+ StreamTcpSackInsertRange(&stream, 10, 20);
+ StreamTcpSackInsertRange(&stream, 5, 15);
+#ifdef DEBUG
+ StreamTcpSackPrintList(&stream);
+#endif /* DEBUG */
+ StreamTcpSackInsertRange(&stream, 15, 25);
+#ifdef DEBUG
+ StreamTcpSackPrintList(&stream);
+#endif /* DEBUG */
+
+ if (stream.sack_head->le != 5) {
+ goto end;
+ }
+
+ if (StreamTcpSackedSize(&stream) != 20) {
+ printf("size should be 20, is %u: ", StreamTcpSackedSize(&stream));
+ goto end;
+ }
+
+ retval = 1;
+end:
+ SCReturnInt(retval);
+}
+
+/**
+ * \test Test the insertion of SACK ranges.
+ *
+ * \retval On success it returns 1 and on failure 0.
+ */
+
+static int StreamTcpSackTest04 (void)
+{
+ TcpStream stream;
+ int retval = 0;
+
+ memset(&stream, 0, sizeof(stream));
+ stream.window = 100;
+
+ StreamTcpSackInsertRange(&stream, 0, 20);
+ StreamTcpSackInsertRange(&stream, 30, 50);
+ StreamTcpSackInsertRange(&stream, 10, 25);
+#ifdef DEBUG
+ StreamTcpSackPrintList(&stream);
+#endif /* DEBUG */
+
+ if (stream.sack_head->le != 0) {
+ goto end;
+ }
+
+ if (StreamTcpSackedSize(&stream) != 45) {
+ printf("size should be 45, is %u: ", StreamTcpSackedSize(&stream));
+ goto end;
+ }
+
+ retval = 1;
+end:
+ SCReturnInt(retval);
+}
+
+/**
+ * \test Test the insertion of SACK ranges.
+ *
+ * \retval On success it returns 1 and on failure 0.
+ */
+
+static int StreamTcpSackTest05 (void)
+{
+ TcpStream stream;
+ int retval = 0;
+
+ memset(&stream, 0, sizeof(stream));
+ stream.window = 100;
+
+ StreamTcpSackInsertRange(&stream, 0, 20);
+ StreamTcpSackInsertRange(&stream, 30, 50);
+ StreamTcpSackInsertRange(&stream, 10, 35);
+#ifdef DEBUG
+ StreamTcpSackPrintList(&stream);
+#endif /* DEBUG */
+
+ if (stream.sack_head->le != 0) {
+ goto end;
+ }
+
+ if (StreamTcpSackedSize(&stream) != 50) {
+ printf("size should be 50, is %u: ", StreamTcpSackedSize(&stream));
+ goto end;
+ }
+
+ retval = 1;
+end:
+ SCReturnInt(retval);
+}
+
+/**
+ * \test Test the insertion of SACK ranges.
+ *
+ * \retval On success it returns 1 and on failure 0.
+ */
+
+static int StreamTcpSackTest06 (void)
+{
+ TcpStream stream;
+ int retval = 0;
+
+ memset(&stream, 0, sizeof(stream));
+ stream.window = 100;
+
+ StreamTcpSackInsertRange(&stream, 0, 9);
+ StreamTcpSackInsertRange(&stream, 11, 19);
+ StreamTcpSackInsertRange(&stream, 21, 29);
+ StreamTcpSackInsertRange(&stream, 31, 39);
+ StreamTcpSackInsertRange(&stream, 0, 40);
+#ifdef DEBUG
+ StreamTcpSackPrintList(&stream);
+#endif /* DEBUG */
+
+ if (stream.sack_head->le != 0) {
+ goto end;
+ }
+
+ if (StreamTcpSackedSize(&stream) != 40) {
+ printf("size should be 40, is %u: ", StreamTcpSackedSize(&stream));
+ goto end;
+ }
+
+ retval = 1;
+end:
+ SCReturnInt(retval);
+}
+
+/**
+ * \test Test the pruning of SACK ranges.
+ *
+ * \retval On success it returns 1 and on failure 0.
+ */
+
+static int StreamTcpSackTest07 (void)
+{
+ TcpStream stream;
+ int retval = 0;
+
+ memset(&stream, 0, sizeof(stream));
+ stream.window = 100;
+
+ StreamTcpSackInsertRange(&stream, 0, 9);
+ StreamTcpSackInsertRange(&stream, 11, 19);
+ StreamTcpSackInsertRange(&stream, 21, 29);
+ StreamTcpSackInsertRange(&stream, 31, 39);
+ StreamTcpSackInsertRange(&stream, 0, 40);
+#ifdef DEBUG
+ StreamTcpSackPrintList(&stream);
+#endif /* DEBUG */
+
+ if (stream.sack_head->le != 0) {
+ goto end;
+ }
+
+ if (StreamTcpSackedSize(&stream) != 40) {
+ printf("size should be 40, is %u: ", StreamTcpSackedSize(&stream));
+ goto end;
+ }
+
+ stream.last_ack = 10;
+
+ StreamTcpSackPruneList(&stream);
+
+ if (StreamTcpSackedSize(&stream) != 30) {
+ printf("size should be 30, is %u: ", StreamTcpSackedSize(&stream));
+ goto end;
+ }
+
+ retval = 1;
+end:
+ SCReturnInt(retval);
+}
+
+/**
+ * \test Test the pruning of SACK ranges.
+ *
+ * \retval On success it returns 1 and on failure 0.
+ */
+
+static int StreamTcpSackTest08 (void)
+{
+ TcpStream stream;
+ int retval = 0;
+
+ memset(&stream, 0, sizeof(stream));
+ stream.window = 100;
+
+ StreamTcpSackInsertRange(&stream, 0, 9);
+ StreamTcpSackInsertRange(&stream, 11, 19);
+ StreamTcpSackInsertRange(&stream, 21, 29);
+ StreamTcpSackInsertRange(&stream, 31, 39);
+ StreamTcpSackInsertRange(&stream, 0, 40);
+#ifdef DEBUG
+ StreamTcpSackPrintList(&stream);
+#endif /* DEBUG */
+
+ if (stream.sack_head->le != 0) {
+ goto end;
+ }
+
+ if (StreamTcpSackedSize(&stream) != 40) {
+ printf("size should be 40, is %u: ", StreamTcpSackedSize(&stream));
+ goto end;
+ }
+
+ stream.last_ack = 41;
+
+ StreamTcpSackPruneList(&stream);
+
+ if (StreamTcpSackedSize(&stream) != 0) {
+ printf("size should be 0, is %u: ", StreamTcpSackedSize(&stream));
+ goto end;
+ }
+
+ retval = 1;
+end:
+ SCReturnInt(retval);
+}
+
+/**
+ * \test Test the pruning of SACK ranges.
+ *
+ * \retval On success it returns 1 and on failure 0.
+ */
+
+static int StreamTcpSackTest09 (void)
+{
+ TcpStream stream;
+ int retval = 0;
+
+ memset(&stream, 0, sizeof(stream));
+ stream.window = 100;
+
+ StreamTcpSackInsertRange(&stream, 0, 9);
+ StreamTcpSackInsertRange(&stream, 11, 19);
+ StreamTcpSackInsertRange(&stream, 21, 29);
+ StreamTcpSackInsertRange(&stream, 31, 39);
+ StreamTcpSackInsertRange(&stream, 0, 40);
+
+#ifdef DEBUG
+ StreamTcpSackPrintList(&stream);
+#endif /* DEBUG */
+
+ if (stream.sack_head->le != 0) {
+ goto end;
+ }
+
+ if (StreamTcpSackedSize(&stream) != 40) {
+ printf("size should be 40, is %u: ", StreamTcpSackedSize(&stream));
+ goto end;
+ }
+
+ stream.last_ack = 39;
+
+ StreamTcpSackPruneList(&stream);
+
+ if (StreamTcpSackedSize(&stream) != 1) {
+ printf("size should be 1, is %u: ", StreamTcpSackedSize(&stream));
+ goto end;
+ }
+
+ retval = 1;
+end:
+ SCReturnInt(retval);
+}
+
+/**
+ * \test Test the pruning of SACK ranges.
+ *
+ * \retval On success it returns 1 and on failure 0.
+ */
+
+static int StreamTcpSackTest10 (void)
+{
+ TcpStream stream;
+ int retval = 0;
+
+ memset(&stream, 0, sizeof(stream));
+ stream.window = 1000;
+
+ StreamTcpSackInsertRange(&stream, 100, 119);
+ StreamTcpSackInsertRange(&stream, 111, 119);
+ StreamTcpSackInsertRange(&stream, 121, 129);
+ StreamTcpSackInsertRange(&stream, 131, 139);
+ StreamTcpSackInsertRange(&stream, 100, 140);
+#ifdef DEBUG
+ StreamTcpSackPrintList(&stream);
+#endif /* DEBUG */
+
+ if (stream.sack_head->le != 100) {
+ goto end;
+ }
+
+ if (StreamTcpSackedSize(&stream) != 40) {
+ printf("size should be 40, is %u: ", StreamTcpSackedSize(&stream));
+ goto end;
+ }
+
+ stream.last_ack = 99;
+
+ StreamTcpSackPruneList(&stream);
+
+ if (StreamTcpSackedSize(&stream) != 40) {
+ printf("size should be 40, is %u: ", StreamTcpSackedSize(&stream));
+ goto end;
+ }
+
+ retval = 1;
+end:
+ SCReturnInt(retval);
+}
+
+/**
+ * \test Test the pruning of SACK ranges.
+ *
+ * \retval On success it returns 1 and on failure 0.
+ */
+
+static int StreamTcpSackTest11 (void)
+{
+ TcpStream stream;
+ int retval = 0;
+
+ memset(&stream, 0, sizeof(stream));
+ stream.window = 1000;
+
+ StreamTcpSackInsertRange(&stream, 100, 119);
+ StreamTcpSackInsertRange(&stream, 111, 119);
+ StreamTcpSackInsertRange(&stream, 121, 129);
+ StreamTcpSackInsertRange(&stream, 131, 139);
+ StreamTcpSackInsertRange(&stream, 101, 140);
+#ifdef DEBUG
+ StreamTcpSackPrintList(&stream);
+#endif /* DEBUG */
+
+ if (stream.sack_head->le != 100) {
+ goto end;
+ }
+
+ if (StreamTcpSackedSize(&stream) != 40) {
+ printf("size should be 40, is %u: ", StreamTcpSackedSize(&stream));
+ goto end;
+ }
+
+ stream.last_ack = 99;
+
+ StreamTcpSackPruneList(&stream);
+
+ if (StreamTcpSackedSize(&stream) != 40) {
+ printf("size should be 40, is %u: ", StreamTcpSackedSize(&stream));
+ goto end;
+ }
+
+ retval = 1;
+end:
+ SCReturnInt(retval);
+}
+
+/**
+ * \test Test the pruning of SACK ranges.
+ *
+ * \retval On success it returns 1 and on failure 0.
+ */
+
+static int StreamTcpSackTest12 (void)
+{
+ TcpStream stream;
+ int retval = 0;
+
+ memset(&stream, 0, sizeof(stream));
+ stream.window = 2000;
+
+ StreamTcpSackInsertRange(&stream, 800, 1000);
+ StreamTcpSackInsertRange(&stream, 700, 900);
+ StreamTcpSackInsertRange(&stream, 600, 800);
+ StreamTcpSackInsertRange(&stream, 500, 700);
+ StreamTcpSackInsertRange(&stream, 100, 600);
+#ifdef DEBUG
+ StreamTcpSackPrintList(&stream);
+#endif /* DEBUG */
+
+ if (stream.sack_head->le != 100) {
+ goto end;
+ }
+
+ if (StreamTcpSackedSize(&stream) != 900) {
+ printf("size should be 900, is %u: ", StreamTcpSackedSize(&stream));
+ goto end;
+ }
+
+ StreamTcpSackInsertRange(&stream, 0, 1000);
+
+ if (StreamTcpSackedSize(&stream) != 1000) {
+ printf("size should be 1000, is %u: ", StreamTcpSackedSize(&stream));
+ goto end;
+ }
+
+ stream.last_ack = 500;
+
+ StreamTcpSackPruneList(&stream);
+
+ if (StreamTcpSackedSize(&stream) != 500) {
+ printf("size should be 500, is %u: ", StreamTcpSackedSize(&stream));
+ goto end;
+ }
+
+ retval = 1;
+end:
+ SCReturnInt(retval);
+}
+
+/**
+ * \test Test the insertion on out of window condition.
+ *
+ * \retval On success it returns 1 and on failure 0.
+ */
+
+static int StreamTcpSackTest13 (void) {
+ TcpStream stream;
+ int retval = 0;
+ int i;
+
+ memset(&stream, 0, sizeof(stream));
+ stream.last_ack = 10000;
+ stream.window = 2000;
+
+ for (i = 0; i < 10; i++) {
+ StreamTcpSackInsertRange(&stream, 100+(20*i), 110+(20*i));
+ }
+#ifdef DEBUG
+ StreamTcpSackPrintList(&stream);
+#endif /* DEBUG */
+
+ if (StreamTcpSackedSize(&stream) != 0) {
+ printf("Sacked size is %u: ", StreamTcpSackedSize(&stream));
+ goto end;
+ }
+
+ retval = 1;
+end:
+ SCReturnInt(retval);
+}
+
+/**
+ * \test Test the insertion of out of window condition.
+ *
+ * \retval On success it returns 1 and on failure 0.
+ */
+
+static int StreamTcpSackTest14 (void) {
+ TcpStream stream;
+ int retval = 0;
+ int i;
+
+ memset(&stream, 0, sizeof(stream));
+ stream.last_ack = 1000;
+ stream.window = 2000;
+
+ for (i = 0; i < 10; i++) {
+ StreamTcpSackInsertRange(&stream, 4000+(20*i), 4010+(20*i));
+ }
+#ifdef DEBUG
+ StreamTcpSackPrintList(&stream);
+#endif /* DEBUG */
+
+ if (StreamTcpSackedSize(&stream) != 0) {
+ printf("Sacked size is %u: ", StreamTcpSackedSize(&stream));
+ goto end;
+ }
+
+ retval = 1;
+end:
+ SCReturnInt(retval);
+}
+
+#endif /* UNITTESTS */
+
+void StreamTcpSackRegisterTests (void)
+{
+#ifdef UNITTESTS
+ UtRegisterTest("StreamTcpSackTest01 -- Insertion",
+ StreamTcpSackTest01, 1);
+ UtRegisterTest("StreamTcpSackTest02 -- Insertion",
+ StreamTcpSackTest02, 1);
+ UtRegisterTest("StreamTcpSackTest03 -- Insertion",
+ StreamTcpSackTest03, 1);
+ UtRegisterTest("StreamTcpSackTest04 -- Insertion",
+ StreamTcpSackTest04, 1);
+ UtRegisterTest("StreamTcpSackTest05 -- Insertion",
+ StreamTcpSackTest05, 1);
+ UtRegisterTest("StreamTcpSackTest06 -- Insertion",
+ StreamTcpSackTest06, 1);
+ UtRegisterTest("StreamTcpSackTest07 -- Pruning",
+ StreamTcpSackTest07, 1);
+ UtRegisterTest("StreamTcpSackTest08 -- Pruning",
+ StreamTcpSackTest08, 1);
+ UtRegisterTest("StreamTcpSackTest09 -- Pruning",
+ StreamTcpSackTest09, 1);
+ UtRegisterTest("StreamTcpSackTest10 -- Pruning",
+ StreamTcpSackTest10, 1);
+ UtRegisterTest("StreamTcpSackTest11 -- Insertion && Pruning",
+ StreamTcpSackTest11, 1);
+ UtRegisterTest("StreamTcpSackTest12 -- Insertion && Pruning",
+ StreamTcpSackTest12, 1);
+ UtRegisterTest("StreamTcpSackTest13 -- Insertion out of window",
+ StreamTcpSackTest13, 1);
+ UtRegisterTest("StreamTcpSackTest14 -- Insertion out of window",
+ StreamTcpSackTest14, 1);
+#endif
+}