mirror of https://github.com/OISF/suricata
Implement SACK in the stream engine.
parent
6fc075d4ae
commit
d0374ced38
@ -0,0 +1,851 @@
|
||||
/* 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-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 */
|
||||
|
||||
/**
|
||||
* \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 (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 = SCMalloc(sizeof(StreamTcpSackRecord));
|
||||
if (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;
|
||||
}
|
||||
|
||||
le = rec->re;
|
||||
//int r = StreamTcpSackInsertRange(stream, rec->re+1, re);
|
||||
//SCReturnInt(r);
|
||||
continue;
|
||||
}
|
||||
|
||||
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 = SCMalloc(sizeof(StreamTcpSackRecord));
|
||||
if (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 = SCMalloc(sizeof(StreamTcpSackRecord));
|
||||
if (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 = SCMalloc(sizeof(StreamTcpSackRecord));
|
||||
if (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 = SCMalloc(sizeof(StreamTcpSackRecord));
|
||||
if (stsr == NULL) {
|
||||
SCReturnInt(-1);
|
||||
}
|
||||
stsr->le = le;
|
||||
stsr->re = re;
|
||||
stsr->next = NULL;
|
||||
|
||||
stream->sack_head = stsr;
|
||||
stream->sack_tail = stsr;
|
||||
}
|
||||
|
||||
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;
|
||||
StreamTcpSackRecord *prev = NULL;
|
||||
|
||||
while (rec != NULL) {
|
||||
if (SEQ_LT(rec->re, stream->last_ack)) {
|
||||
SCLogDebug("removing le %u re %u", rec->le, rec->re);
|
||||
|
||||
// fully before last_ack, remove
|
||||
if (prev != NULL) {
|
||||
if (rec == stream->sack_tail) {
|
||||
stream->sack_tail = prev;
|
||||
prev->next = NULL;
|
||||
SCFree(rec);
|
||||
break;
|
||||
} else {
|
||||
prev->next = rec->next;
|
||||
SCFree(rec);
|
||||
rec = prev->next;
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
if (rec->next != NULL) {
|
||||
stream->sack_head = rec->next;
|
||||
SCFree(rec);
|
||||
rec = stream->sack_head;
|
||||
continue;
|
||||
} else {
|
||||
stream->sack_head = NULL;
|
||||
stream->sack_tail = NULL;
|
||||
SCFree(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;
|
||||
SCFree(next);
|
||||
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));
|
||||
|
||||
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));
|
||||
|
||||
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));
|
||||
|
||||
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));
|
||||
|
||||
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));
|
||||
|
||||
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));
|
||||
|
||||
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));
|
||||
|
||||
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));
|
||||
|
||||
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));
|
||||
|
||||
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));
|
||||
|
||||
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));
|
||||
|
||||
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));
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
#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);
|
||||
#endif
|
||||
}
|
||||
@ -0,0 +1,62 @@
|
||||
/* 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>
|
||||
*/
|
||||
|
||||
#ifndef __STREAM_TCP_SACK_H__
|
||||
#define __STREAM_TCP_SACK_H__
|
||||
|
||||
#include "suricata-common.h"
|
||||
#include "util-optimize.h"
|
||||
|
||||
/**
|
||||
* \brief Get the size of the SACKed ranges
|
||||
*
|
||||
* \param stream Stream to get the size for.
|
||||
*
|
||||
* \retval size the size
|
||||
*
|
||||
* Optimized for case where SACK is not in use in the
|
||||
* stream, as it *should* only be used in case of packet
|
||||
* loss.
|
||||
*/
|
||||
static inline uint32_t StreamTcpSackedSize(TcpStream *stream) {
|
||||
if (likely(stream->sack_head == NULL)) {
|
||||
SCReturnUInt(0U);
|
||||
} else {
|
||||
uint32_t size = 0;
|
||||
|
||||
StreamTcpSackRecord *rec = NULL;
|
||||
|
||||
for (rec = stream->sack_head; rec != NULL; rec = rec->next) {
|
||||
size += (rec->re - rec->le);
|
||||
}
|
||||
|
||||
SCReturnUInt(size);
|
||||
}
|
||||
}
|
||||
|
||||
int StreamTcpSackUpdatePacket(TcpStream *, Packet *);
|
||||
void StreamTcpSackPruneList(TcpStream *);
|
||||
void StreamTcpSackFreeList(TcpStream *);
|
||||
void StreamTcpSackRegisterTests (void);
|
||||
|
||||
#endif /* __STREAM_TCP_SACK_H__*/
|
||||
Loading…
Reference in New Issue