stream: raw content inspection inline mode

Implement the inline mode for raw content inspection. Packets
are leading, and when a packet's payload has been added to the
stream, the packet is inspected in the context of the stream.

Reassembly will return a buffer with the packet data with older
data in front of it and after it, if available.
pull/2673/head
Victor Julien 10 years ago
parent 149e324060
commit 0ef46a8fd2

@ -260,58 +260,29 @@ failure:
return;
}
/** \todo modifying p this way seems too hacky */
static int TCPProtoDetectTriggerOpposingSide(ThreadVars *tv,
TcpReassemblyThreadCtx *ra_ctx,
Packet *p, TcpSession *ssn, TcpStream *stream,
uint8_t flags)
Packet *p, TcpSession *ssn, TcpStream *stream)
{
TcpStream *opposing_stream = NULL;
if (stream == &ssn->client) {
opposing_stream = &ssn->server;
if (StreamTcpInlineMode()) {
p->flowflags &= ~FLOW_PKT_TOSERVER;
p->flowflags |= FLOW_PKT_TOCLIENT;
} else {
p->flowflags &= ~FLOW_PKT_TOCLIENT;
p->flowflags |= FLOW_PKT_TOSERVER;
}
} else {
opposing_stream = &ssn->client;
if (StreamTcpInlineMode()) {
p->flowflags &= ~FLOW_PKT_TOCLIENT;
p->flowflags |= FLOW_PKT_TOSERVER;
} else {
p->flowflags &= ~FLOW_PKT_TOSERVER;
p->flowflags |= FLOW_PKT_TOCLIENT;
}
}
int ret = 0;
/* if the opposing side is not going to work, then
* we just have to give up. */
if (opposing_stream->flags & STREAMTCP_STREAM_FLAG_NOREASSEMBLY)
ret = -1;
else
ret = StreamTcpReassembleAppLayer(tv, ra_ctx, ssn,
opposing_stream, p, UPDATE_DIR_OPPOSING); // TODO see if we can simplify this
if (stream == &ssn->client) {
if (StreamTcpInlineMode()) {
p->flowflags &= ~FLOW_PKT_TOCLIENT;
p->flowflags |= FLOW_PKT_TOSERVER;
} else {
p->flowflags &= ~FLOW_PKT_TOSERVER;
p->flowflags |= FLOW_PKT_TOCLIENT;
}
} else {
if (StreamTcpInlineMode()) {
p->flowflags &= ~FLOW_PKT_TOSERVER;
p->flowflags |= FLOW_PKT_TOCLIENT;
} else {
p->flowflags &= ~FLOW_PKT_TOCLIENT;
p->flowflags |= FLOW_PKT_TOSERVER;
}
if (opposing_stream->flags & STREAMTCP_STREAM_FLAG_NOREASSEMBLY) {
SCLogDebug("opposing dir has STREAMTCP_STREAM_FLAG_NOREASSEMBLY set");
return -1;
}
enum StreamUpdateDir dir = StreamTcpInlineMode() ?
UPDATE_DIR_OPPOSING :
UPDATE_DIR_PACKET;
int ret = StreamTcpReassembleAppLayer(tv, ra_ctx, ssn,
opposing_stream, p, dir);
return ret;
}
@ -389,8 +360,11 @@ static int TCPProtoDetect(ThreadVars *tv,
if ((ssn->data_first_seen_dir & (STREAM_TOSERVER | STREAM_TOCLIENT)) &&
!(flags & ssn->data_first_seen_dir))
{
SCLogDebug("protocol %s needs first data in other direction",
AppProtoToString(*alproto));
if (TCPProtoDetectTriggerOpposingSide(tv, ra_ctx,
p, ssn, stream, flags) != 0)
p, ssn, stream) != 0)
{
DisableAppLayer(tv, f, p);
goto failure;

@ -1,4 +1,4 @@
/* Copyright (C) 2007-2013 Open Information Security Foundation
/* Copyright (C) 2007-2017 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

@ -29,6 +29,7 @@
#include "stream-tcp-list.h"
#include "util-streaming-buffer.h"
#include "util-print.h"
#include "util-validate.h"
//static void PrintList2(TcpSegment *seg);
@ -663,6 +664,19 @@ static inline uint64_t GetLeftEdge(TcpSession *ssn, TcpStream *stream)
SCLogDebug("no app & raw: left_edge %"PRIu64" (full stream)", left_edge);
}
/* in inline mode keep at least unack'd segments so we can check for overlaps */
if (StreamTcpInlineMode() == TRUE) {
uint64_t last_ack_abs = STREAM_BASE_OFFSET(stream);
if (STREAM_LASTACK_GT_BASESEQ(stream)) {
/* get window of data that is acked */
uint32_t delta = stream->last_ack - stream->base_seq;
DEBUG_VALIDATE_BUG_ON(delta > 10000000ULL && delta > stream->window);
/* get max absolute offset */
last_ack_abs += delta;
}
left_edge = MIN(left_edge, last_ack_abs);
}
if (left_edge > 0) {
/* we know left edge based on the progress values now,
* lets adjust it to make sure in-use segments still have

@ -304,16 +304,6 @@ void StreamTcpReturnStreamSegments (TcpStream *stream)
stream->seg_list_tail = NULL;
}
static inline bool STREAM_LASTACK_GT_BASESEQ(const TcpStream *stream)
{
/* last ack not yet initialized */
if (STREAM_BASE_OFFSET(stream) == 0 && stream->last_ack == 0)
return false;
if (SEQ_GT(stream->last_ack, stream->base_seq))
return true;
return false;
}
/** \internal
* \brief check if segments falls before stream 'offset' */
static inline int SEGMENT_BEFORE_OFFSET(TcpStream *stream, TcpSegment *seg, uint64_t offset)
@ -1111,6 +1101,7 @@ static int GetRawBuffer(TcpStream *stream, const uint8_t **data, uint32_t *data_
if (*iter == NULL) {
*data = NULL;
*data_len = 0;
*data_offset = 0;
return 0;
}
@ -1200,6 +1191,186 @@ void StreamReassembleRawUpdateProgress(TcpSession *ssn, Packet *p, uint64_t prog
SCLogDebug("stream raw progress now %"PRIu64, STREAM_RAW_PROGRESS(stream));
}
/** \internal
* \brief get a buffer around the current packet and run the callback on it
*
* The inline/IPS scanning method takes the current payload and wraps it in
* data from other segments.
*
* How much data is inspected is controlled by the available data, chunk_size
* and the payload size of the packet.
*
* Large packets: if payload size is close to the chunk_size, where close is
* defined as more than 67% of the chunk_size, a larger chunk_size will be
* used: payload_len + 33% of the chunk_size.
* If the payload size if equal to or bigger than the chunk_size, we use
* payload len + 33% of the chunk size.
*/
static int StreamReassembleRawInline(TcpSession *ssn, const Packet *p,
StreamReassembleRawFunc Callback, void *cb_data, uint64_t *progress_out)
{
SCEnter();
int r = 0;
TcpStream *stream;
if (PKT_IS_TOSERVER(p)) {
stream = &ssn->client;
} else {
stream = &ssn->server;
}
if (p->payload_len == 0 || (p->flags & PKT_STREAM_ADD) == 0) {
*progress_out = STREAM_RAW_PROGRESS(stream);
return 0;
}
uint32_t chunk_size = PKT_IS_TOSERVER(p) ?
stream_config.reassembly_toserver_chunk_size :
stream_config.reassembly_toclient_chunk_size;
if (chunk_size <= p->payload_len) {
chunk_size = p->payload_len + (chunk_size / 3);
SCLogDebug("packet payload len %u, so chunk_size adjusted to %u",
p->payload_len, chunk_size);
} else if (((chunk_size / 3 ) * 2) < p->payload_len) {
chunk_size = p->payload_len + ((chunk_size / 3));
SCLogDebug("packet payload len %u, so chunk_size adjusted to %u",
p->payload_len, chunk_size);
}
uint64_t packet_leftedge_abs = STREAM_BASE_OFFSET(stream) + (TCP_GET_SEQ(p) - stream->base_seq);
uint64_t packet_rightedge_abs = packet_leftedge_abs + p->payload_len;
SCLogDebug("packet_leftedge_abs %"PRIu64", rightedge %"PRIu64,
packet_leftedge_abs, packet_rightedge_abs);
const uint8_t *mydata = NULL;
uint32_t mydata_len = 0;
uint64_t mydata_offset = 0;
/* simply return progress from the block we inspected. */
bool return_progress = false;
if (stream->sb.block_list == NULL) {
/* continues block */
StreamingBufferGetData(&stream->sb, &mydata, &mydata_len, &mydata_offset);
return_progress = true;
} else {
/* find our block */
StreamingBufferBlock *iter = stream->sb.block_list;
for ( ; iter != NULL; iter = iter->next) {
uint64_t iter_re_abs = iter->offset + iter->len;
BUG_ON(packet_leftedge_abs < iter->offset &&
packet_rightedge_abs > iter->offset &&
packet_rightedge_abs < iter_re_abs);
if (iter->offset <= packet_leftedge_abs && iter_re_abs >= packet_rightedge_abs) {
StreamingBufferSBBGetData(&stream->sb, iter, &mydata, &mydata_len);
mydata_offset = iter->offset;
break;
}
}
}
/* this can only happen if the segment insert of our current 'p' failed */
if (mydata == NULL || mydata_len == 0) {
SCLogDebug("no data: %p/%u", mydata, mydata_len);
*progress_out = STREAM_RAW_PROGRESS(stream);
return 0;
}
if (mydata_offset >= packet_rightedge_abs ||
(packet_leftedge_abs >= (mydata_offset + mydata_len))) {
SCLogDebug("no data overlap: %p/%u", mydata, mydata_len);
*progress_out = STREAM_RAW_PROGRESS(stream);
return 0;
}
/* adjust buffer to match chunk_size */
uint64_t mydata_rightedge_abs = mydata_offset + mydata_len;
SCLogDebug("chunk_size %u mydata_len %u", chunk_size, mydata_len);
if (mydata_len > chunk_size) {
uint32_t excess = mydata_len - chunk_size;
SCLogDebug("chunk_size %u mydata_len %u excess %u", chunk_size, mydata_len, excess);
if (mydata_rightedge_abs == packet_rightedge_abs) {
mydata += excess;
mydata_len -= excess;
mydata_offset += excess;
SCLogDebug("cutting front of the buffer with %u", excess);
} else if (mydata_offset == packet_leftedge_abs) {
mydata_len -= excess;
SCLogDebug("cutting tail of the buffer with %u", excess);
} else {
uint32_t before = (uint32_t)(packet_leftedge_abs - mydata_offset);
uint32_t after = (uint32_t)(mydata_rightedge_abs - packet_rightedge_abs);
SCLogDebug("before %u after %u", before, after);
if (after >= (chunk_size - p->payload_len) / 2) {
// more trailing data than we need
if (before >= (chunk_size - p->payload_len) / 2) {
// also more heading data, devide evenly
before = after = (chunk_size - p->payload_len) / 2;
} else {
// heading data is less than requested, give the
// rest to the trailing data
after = (chunk_size - p->payload_len) - before;
}
} else {
// less trailing data than requested
if (before >= (chunk_size - p->payload_len) / 2) {
before = (chunk_size - p->payload_len) - after;
} else {
// both smaller than their requested size
}
}
/* adjust the buffer */
uint32_t skip = (uint32_t)(packet_leftedge_abs - mydata_offset) - before;
BUG_ON(skip > mydata_len);
uint32_t cut = (uint32_t)(mydata_rightedge_abs - packet_rightedge_abs) - after;
BUG_ON(cut > mydata_len);
BUG_ON(skip + cut > mydata_len);
mydata += skip;
mydata_len -= (skip + cut);
mydata_offset += skip;
}
}
/* run the callback */
r = Callback(cb_data, mydata, mydata_len);
BUG_ON(r < 0);
if (return_progress) {
*progress_out = (mydata_offset + mydata_len);
} else {
/* several blocks of data, so we need to be a bit more careful:
* - if last_ack is beyond last progress, move progress forward to last_ack
* - if our block matches or starts before last ack, return right edge of
* our block.
*/
uint64_t last_ack_abs = STREAM_BASE_OFFSET(stream);
if (STREAM_LASTACK_GT_BASESEQ(stream)) {
uint32_t delta = stream->last_ack - stream->base_seq;
/* get max absolute offset */
last_ack_abs += delta;
}
SCLogDebug("last_ack_abs %"PRIu64, last_ack_abs);
if (STREAM_RAW_PROGRESS(stream) < last_ack_abs) {
if (mydata_offset > last_ack_abs) {
/* gap between us and last ack, set progress to last ack */
*progress_out = last_ack_abs;
} else {
*progress_out = (mydata_offset + mydata_len);
}
} else {
*progress_out = STREAM_RAW_PROGRESS(stream);
}
}
return r;
}
/** \brief access 'raw' reassembly data.
*
* Access data as tracked by 'raw' tracker. Data is made available to
@ -1232,6 +1403,11 @@ int StreamReassembleRaw(TcpSession *ssn, const Packet *p,
SCEnter();
int r = 0;
/* handle inline seperately as the logic is very different */
if (StreamTcpInlineMode() == TRUE) {
return StreamReassembleRawInline(ssn, p, Callback, cb_data, progress_out);
}
TcpStream *stream;
if (PKT_IS_TOSERVER(p)) {
stream = &ssn->client;
@ -1239,51 +1415,22 @@ int StreamReassembleRaw(TcpSession *ssn, const Packet *p,
stream = &ssn->server;
}
if (StreamTcpInlineMode() == FALSE &&
StreamTcpReassembleRawCheckLimit(ssn, stream, p) == 0)
{
if (StreamTcpReassembleRawCheckLimit(ssn, stream, p) == 0) {
*progress_out = STREAM_RAW_PROGRESS(stream);
return 0;
}
StreamingBufferBlock *iter = NULL;
uint64_t progress = STREAM_RAW_PROGRESS(stream);
uint64_t last_ack_abs = 0; /* absolute right edge of ack'd data */
uint64_t right_edge_abs = 0;
uint64_t last_ack_abs = STREAM_BASE_OFFSET(stream); /* absolute right edge of ack'd data */
/* get window of data that is acked */
SCLogDebug("last_ack %u, base_seq %u", stream->last_ack, stream->base_seq);
uint32_t delta = stream->last_ack - stream->base_seq;
/* get max absolute offset */
last_ack_abs = STREAM_BASE_OFFSET(stream) + delta;
right_edge_abs = last_ack_abs;
if (StreamTcpInlineMode() == TRUE) {
uint32_t chunk_size = PKT_IS_TOSERVER(p) ?
stream_config.reassembly_toserver_chunk_size :
stream_config.reassembly_toclient_chunk_size;
SCLogDebug("pkt SEQ %u, payload_len %u; base_seq %u => base_seq_offset %"PRIu64,
TCP_GET_SEQ(p), p->payload_len,
stream->base_seq, STREAM_BASE_OFFSET(stream));
SCLogDebug("progress before adjust %"PRIu64", chunk_size %"PRIu32, progress, chunk_size);
/* determine the left edge and right edge */
uint32_t rel_right_edge = TCP_GET_SEQ(p) + p->payload_len;
uint32_t rel_left_edge = rel_right_edge - chunk_size;
SCLogDebug("left_edge %"PRIu32", right_edge %"PRIu32", chunk_size %u", rel_left_edge, rel_right_edge, chunk_size);
if (SEQ_LT(rel_left_edge, stream->base_seq)) {
rel_left_edge = stream->base_seq;
rel_right_edge = rel_left_edge + chunk_size;
SCLogDebug("adjusting left_edge to not be before base_seq: left_edge %u", rel_left_edge);
}
progress = STREAM_BASE_OFFSET(stream) + (rel_left_edge - stream->base_seq);
right_edge_abs = progress + chunk_size;
SCLogDebug("working with progress %"PRIu64, progress);
SCLogDebug("left_edge %"PRIu32", right_edge %"PRIu32, rel_left_edge, rel_right_edge);
if (STREAM_LASTACK_GT_BASESEQ(stream)) {
SCLogDebug("last_ack %u, base_seq %u", stream->last_ack, stream->base_seq);
uint32_t delta = stream->last_ack - stream->base_seq;
/* get max absolute offset */
last_ack_abs += delta;
SCLogDebug("last_ack_abs %"PRIu64, last_ack_abs);
}
/* loop through available buffers. On no packet loss we'll have a single
@ -1302,16 +1449,16 @@ int StreamReassembleRaw(TcpSession *ssn, const Packet *p,
SCLogDebug("stream %p data in buffer %p of len %u and offset %u",
stream, &stream->sb, mydata_len, (uint)progress);
if (StreamTcpInlineMode() == 0 && (p->flags & PKT_PSEUDO_STREAM_END)) {
//
} else if (StreamTcpInlineMode() == FALSE) {
if (right_edge_abs < progress) {
if (p->flags & PKT_PSEUDO_STREAM_END) {
// inspect all remaining data, ack'd or not
} else {
if (last_ack_abs < progress) {
SCLogDebug("nothing to do");
goto end;
}
SCLogDebug("delta %u, right_edge_abs %"PRIu64", raw_progress %"PRIu64, delta, right_edge_abs, progress);
SCLogDebug("raw_progress + mydata_len %"PRIu64", right_edge_abs %"PRIu64, progress + mydata_len, right_edge_abs);
SCLogDebug("last_ack_abs %"PRIu64", raw_progress %"PRIu64, last_ack_abs, progress);
SCLogDebug("raw_progress + mydata_len %"PRIu64", last_ack_abs %"PRIu64, progress + mydata_len, last_ack_abs);
/* see if the buffer contains unack'd data as well */
if (progress + mydata_len > last_ack_abs) {
@ -1320,31 +1467,12 @@ int StreamReassembleRaw(TcpSession *ssn, const Packet *p,
"data is considered", mydata_len);
}
/* StreamTcpInlineMode() == TRUE */
} else {
if (progress + mydata_len > right_edge_abs) {
uint32_t delta2 = (progress + mydata_len) - right_edge_abs;
SCLogDebug("adjusting mydata_len %u to subtract %u", mydata_len, delta2);
mydata_len -= delta2;
}
}
if (mydata_len == 0)
break;
SCLogDebug("data %p len %u", mydata, mydata_len);
/*
[buffer with segment data]
^
|
base_seq_offset (0 on start start)
base_seq (ISN on start start)
going from 'progress' to SEQ => (progress - base_seq_offset) + base_seq;
*/
#define GET_SEQ_FOR_PROGRESS(stream, progress) \
(((progress) - STREAM_BASE_OFFSET((stream))) + (stream->base_seq))
/* we have data. */
r = Callback(cb_data, mydata, mydata_len);
BUG_ON(r < 0);
@ -1376,7 +1504,8 @@ end:
return r;
}
/** \brief update app layer and raw reassembly
/** \internal
* \brief update app layer based on received ACK
*
* \retval r 0 on success, -1 on error
*/
@ -3157,6 +3286,7 @@ static int StreamTcpReassembleInlineTest10(void)
ssn.server.last_ack = 2;
StreamTcpUTSetupStream(&ssn.client, 1);
ssn.client.last_ack = 2;
ssn.data_first_seen_dir = STREAM_TOSERVER;
f = UTHBuildFlow(AF_INET, "1.1.1.1", "2.2.2.2", 1024, 80);
if (f == NULL)
@ -3175,44 +3305,44 @@ static int StreamTcpReassembleInlineTest10(void)
}
p->tcph->th_seq = htonl(7);
p->flow = f;
p->flowflags |= FLOW_PKT_TOSERVER;
p->flowflags = FLOW_PKT_TOSERVER;
FLOWLOCK_WRLOCK(f);
if (StreamTcpUTAddSegmentWithPayload(&tv, ra_ctx, &ssn.server, 2, stream_payload1, 2) == -1) {
if (StreamTcpUTAddSegmentWithPayload(&tv, ra_ctx, &ssn.client, 2, stream_payload1, 2) == -1) {
printf("failed to add segment 1: ");
goto end;
}
ssn.server.next_seq = 4;
ssn.client.next_seq = 4;
int r = StreamTcpReassembleAppLayer(&tv, ra_ctx, &ssn, &ssn.server, p, UPDATE_DIR_PACKET);
int r = StreamTcpReassembleAppLayer(&tv, ra_ctx, &ssn, &ssn.client, p, UPDATE_DIR_PACKET);
if (r < 0) {
printf("StreamTcpReassembleAppLayer failed: ");
goto end;
}
/* ssn.server.ra_app_base_seq should be isn here. */
if (ssn.server.base_seq != 2 || ssn.server.base_seq != ssn.server.isn+1) {
printf("expected ra_app_base_seq 1, got %u: ", ssn.server.base_seq);
if (ssn.client.base_seq != 2 || ssn.client.base_seq != ssn.client.isn+1) {
printf("expected ra_app_base_seq 1, got %u: ", ssn.client.base_seq);
goto end;
}
if (StreamTcpUTAddSegmentWithPayload(&tv, ra_ctx, &ssn.server, 4, stream_payload2, 3) == -1) {
if (StreamTcpUTAddSegmentWithPayload(&tv, ra_ctx, &ssn.client, 4, stream_payload2, 3) == -1) {
printf("failed to add segment 2: ");
goto end;
}
if (StreamTcpUTAddSegmentWithPayload(&tv, ra_ctx, &ssn.server, 7, stream_payload3, 12) == -1) {
if (StreamTcpUTAddSegmentWithPayload(&tv, ra_ctx, &ssn.client, 7, stream_payload3, 12) == -1) {
printf("failed to add segment 3: ");
goto end;
}
ssn.server.next_seq = 19;
ssn.client.next_seq = 19;
r = StreamTcpReassembleAppLayer(&tv, ra_ctx, &ssn, &ssn.server, p, UPDATE_DIR_PACKET);
r = StreamTcpReassembleAppLayer(&tv, ra_ctx, &ssn, &ssn.client, p, UPDATE_DIR_PACKET);
if (r < 0) {
printf("StreamTcpReassembleAppLayer failed: ");
goto end;
}
FAIL_IF_NOT(STREAM_APP_PROGRESS(&ssn.server) == 17);
FAIL_IF_NOT(STREAM_APP_PROGRESS(&ssn.client) == 17);
ret = 1;
end:
@ -3347,6 +3477,7 @@ end:
return ret;
}
#include "tests/stream-tcp-reassemble.c"
#endif /* UNITTESTS */
/** \brief The Function Register the Unit tests to test the reassembly engine
@ -3411,5 +3542,6 @@ void StreamTcpReassembleRegisterTests(void)
StreamTcpInlineRegisterTests();
StreamTcpUtilRegisterTests();
StreamTcpListRegisterTests();
StreamTcpReassembleRawRegisterTests();
#endif /* UNITTESTS */
}

@ -125,5 +125,15 @@ int StreamTcpCheckStreamContents(uint8_t *, uint16_t , TcpStream *);
bool StreamReassembleRawHasDataReady(TcpSession *ssn, Packet *p);
static inline bool STREAM_LASTACK_GT_BASESEQ(const TcpStream *stream)
{
/* last ack not yet initialized */
if (STREAM_BASE_OFFSET(stream) == 0 && stream->last_ack == 0)
return false;
if (SEQ_GT(stream->last_ack, stream->base_seq))
return true;
return false;
}
#endif /* __STREAM_TCP_REASSEMBLE_H__ */

@ -0,0 +1,208 @@
/* Copyright (C) 2007-2017 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.
*/
#include "../suricata-common.h"
#include "../stream-tcp-private.h"
#include "../stream-tcp.h"
#include "../stream-tcp-reassemble.h"
#include "../stream-tcp-inline.h"
#include "../stream-tcp-list.h"
#include "../stream-tcp-util.h"
#include "../util-streaming-buffer.h"
#include "../util-print.h"
#include "../util-unittest.h"
struct TestReassembleRawCallbackData {
const uint8_t *expect_data;
const uint32_t expect_data_len;
};
static int TestReassembleRawCallback(void *cb_data, const uint8_t *data, const uint32_t data_len)
{
struct TestReassembleRawCallbackData *cb = cb_data;
SCLogNotice("have %u expect %u", data_len, cb->expect_data_len);
if (data_len == cb->expect_data_len &&
memcmp(data, cb->expect_data, data_len) == 0) {
return 1;
} else {
SCLogNotice("data mismatch. Expected:");
PrintRawDataFp(stdout, cb->expect_data, cb->expect_data_len);
SCLogNotice("Got:");
PrintRawDataFp(stdout, data, data_len);
return -1;
}
}
static int TestReassembleRawValidate(TcpSession *ssn, Packet *p,
const uint8_t *data, const uint32_t data_len)
{
struct TestReassembleRawCallbackData cb = { data, data_len };
uint64_t progress = 0;
int r = StreamReassembleRaw(ssn, p, TestReassembleRawCallback, &cb, &progress);
if (r == 1) {
StreamReassembleRawUpdateProgress(ssn, p, progress);
}
SCLogNotice("r %d", r);
return r;
}
#define RAWREASSEMBLY_START(isn) \
TcpReassemblyThreadCtx *ra_ctx = NULL; \
TcpSession ssn; \
ThreadVars tv; \
memset(&tv, 0, sizeof(tv)); \
Packet *p = NULL; \
\
\
StreamTcpUTInit(&ra_ctx); \
StreamTcpUTInitInline(); \
stream_config.reassembly_toserver_chunk_size = 9; \
stream_config.reassembly_toclient_chunk_size = 9; \
StreamTcpUTSetupSession(&ssn); \
StreamTcpUTSetupStream(&ssn.server, (isn)); \
StreamTcpUTSetupStream(&ssn.client, (isn)); \
ssn.server.last_ack = (isn) + 1; \
ssn.client.last_ack = (isn) + 1; \
\
TcpStream *stream = &ssn.client;
#define RAWREASSEMBLY_END \
StreamTcpUTClearSession(&ssn); \
StreamTcpUTDeinit(ra_ctx); \
PASS
#define RAWREASSEMBLY_STEP(seq, seg, seglen, buf, buflen) \
p = PacketGetFromAlloc(); \
FAIL_IF_NULL(p); \
{ \
SCLogNotice("SEQ %u block of %u", (seq), (seglen)); \
p->flowflags = FLOW_PKT_TOSERVER; \
TCPHdr tcphdr; \
memset(&tcphdr, 0, sizeof(tcphdr)); \
p->tcph = &tcphdr; \
p->tcph->th_seq = htonl((seq)); \
p->tcph->th_ack = htonl(10); \
p->payload_len = (seglen); \
\
FAIL_IF(StreamTcpUTAddPayload(&tv, ra_ctx, &ssn, stream, (seq), (uint8_t *)(seg), (seglen)) != 0); \
p->flags |= PKT_STREAM_ADD; \
FAIL_IF(!(TestReassembleRawValidate(&ssn, p, (uint8_t *)(buf), (buflen)))); \
}\
PacketFree(p);
#define RAWREASSEMBLY_STEP_WITH_PROGRESS(seq, seg, seglen, buf, buflen, lastack, progress) \
stream->last_ack = (lastack); \
RAWREASSEMBLY_STEP((seq),(seg),(seglen),(buf),(buflen)); \
FAIL_IF(STREAM_RAW_PROGRESS(stream) != (progress));
static int StreamTcpReassembleRawTest01 (void)
{
RAWREASSEMBLY_START(1);
RAWREASSEMBLY_STEP(2, "AAA", 3, "AAA", 3);
RAWREASSEMBLY_STEP(5, "BBB", 3, "AAABBB", 6);
RAWREASSEMBLY_STEP(8, "CCC", 3, "AAABBBCCC", 9);
RAWREASSEMBLY_END;
}
static int StreamTcpReassembleRawTest02 (void)
{
RAWREASSEMBLY_START(1);
RAWREASSEMBLY_STEP(2, "AAA", 3, "AAA", 3);
RAWREASSEMBLY_STEP(5, "BBB", 3, "AAABBB", 6);
RAWREASSEMBLY_STEP(11,"DDD", 3, "DDD", 3);
RAWREASSEMBLY_STEP(8, "CCC", 3, "BBBCCCDDD", 9);
RAWREASSEMBLY_END;
}
static int StreamTcpReassembleRawTest03 (void)
{
RAWREASSEMBLY_START(1);
RAWREASSEMBLY_STEP(2, "AAA", 3, "AAA", 3);
RAWREASSEMBLY_STEP(11,"DDD", 3, "DDD", 3);
RAWREASSEMBLY_STEP(8, "CCC", 3, "CCCDDD", 6);
RAWREASSEMBLY_END;
}
static int StreamTcpReassembleRawTest04 (void)
{
RAWREASSEMBLY_START(1);
RAWREASSEMBLY_STEP(2, "AAAAA", 5, "AAAAA", 5);
RAWREASSEMBLY_STEP(10,"CCCCC", 5, "CCCCC", 5);
RAWREASSEMBLY_STEP(7, "BBB", 3, "AAABBBCCC", 9);
RAWREASSEMBLY_END;
}
static int StreamTcpReassembleRawTest05 (void)
{
RAWREASSEMBLY_START(1);
RAWREASSEMBLY_STEP(2, "AAAAA", 5, "AAAAA", 5);
RAWREASSEMBLY_STEP(10,"CCCCC", 5, "CCCCC", 5);
RAWREASSEMBLY_STEP(2, "EEEEEEEEEEEEE", 13, "AAAAAEEECCCCC", 13);
RAWREASSEMBLY_END;
}
static int StreamTcpReassembleRawTest06 (void)
{
RAWREASSEMBLY_START(1);
RAWREASSEMBLY_STEP(2, "AAAAA", 5, "AAAAA", 5);
RAWREASSEMBLY_STEP(16,"CCCCC", 5, "CCCCC", 5);
RAWREASSEMBLY_STEP(7, "BBBBBBBBB", 9, "ABBBBBBBBBC", 11);
RAWREASSEMBLY_STEP(21,"DDDDDDDDDD",10,"CCCDDDDDDDDDD", 13);
RAWREASSEMBLY_END;
}
static int StreamTcpReassembleRawTest07 (void)
{
RAWREASSEMBLY_START(1);
RAWREASSEMBLY_STEP(2, "AAAAAAA", 7, "AAAAAAA", 7);
RAWREASSEMBLY_STEP(9, "BBBBBBB", 7, "AAABBBBBBB", 10);
RAWREASSEMBLY_STEP(16,"C", 1, "ABBBBBBBC", 9);
RAWREASSEMBLY_STEP(17,"DDDDDDDD",8,"BBCDDDDDDDD", 11);
RAWREASSEMBLY_END;
}
static int StreamTcpReassembleRawTest08 (void)
{
RAWREASSEMBLY_START(1);
RAWREASSEMBLY_STEP_WITH_PROGRESS(2, "AAA", 3, "AAA", 3, 3, 3);
RAWREASSEMBLY_STEP_WITH_PROGRESS(8, "CCC", 3, "CCC", 3, 3, 3);
// segment lost, last_ack updated
RAWREASSEMBLY_STEP_WITH_PROGRESS(11, "DDD", 3, "CCCDDD", 6, 8, 12);
RAWREASSEMBLY_END;
}
static void StreamTcpReassembleRawRegisterTests(void)
{
UtRegisterTest("StreamTcpReassembleRawTest01",
StreamTcpReassembleRawTest01);
UtRegisterTest("StreamTcpReassembleRawTest02",
StreamTcpReassembleRawTest02);
UtRegisterTest("StreamTcpReassembleRawTest03",
StreamTcpReassembleRawTest03);
UtRegisterTest("StreamTcpReassembleRawTest04",
StreamTcpReassembleRawTest04);
UtRegisterTest("StreamTcpReassembleRawTest05",
StreamTcpReassembleRawTest05);
UtRegisterTest("StreamTcpReassembleRawTest06",
StreamTcpReassembleRawTest06);
UtRegisterTest("StreamTcpReassembleRawTest07",
StreamTcpReassembleRawTest07);
UtRegisterTest("StreamTcpReassembleRawTest08",
StreamTcpReassembleRawTest08);
}
Loading…
Cancel
Save