Another iteration of the reassembly depth enforcement, now considering retransmissions.

remotes/origin/master-1.1.x
Victor Julien 15 years ago
parent 935958219d
commit 0f072648e6

@ -119,6 +119,8 @@ enum
#define STREAMTCP_STREAM_FLAG_NOREASSEMBLY 0x02
/** Flag to pause stream reassembly / app layer inspection for the stream.*/
#define STREAMTCP_STREAM_FLAG_PAUSE_REASSEMBLY 0x04
/** Stream has reached it's reassembly depth, all further packets are ignored */
#define STREAMTCP_STREAM_FLAG_DEPTH_REACHED 0x08
/*
* Per SEGMENT flags

@ -445,6 +445,21 @@ void PrintList(TcpSegment *seg)
}
}
/**
* \internal
* \brief Get the active ra_base_seq, considering stream gaps
*
* \retval seq the active ra_base_seq
*/
static inline uint32_t StreamTcpReassembleGetRaBaseSeq(TcpStream *stream)
{
if (!(stream->flags & STREAMTCP_STREAM_FLAG_GAP)) {
SCReturnUInt(stream->ra_app_base_seq);
} else {
SCReturnUInt(stream->ra_raw_base_seq);
}
}
/**
* \brief Function to handle the insertion newly arrived segment,
* The packet is handled based on its target OS.
@ -471,14 +486,11 @@ static int ReassembleInsertSegment(ThreadVars *tv, TcpReassemblyThreadCtx *ra_ct
/* before our ra_app_base_seq we don't insert it in our list,
* or ra_raw_base_seq if in stream gap state */
if ((!(stream->flags & STREAMTCP_STREAM_FLAG_GAP) &&
SEQ_LEQ((TCP_GET_SEQ(p)+p->payload_len),(stream->ra_app_base_seq+1))) ||
(stream->flags & STREAMTCP_STREAM_FLAG_GAP &&
SEQ_LEQ((TCP_GET_SEQ(p)+p->payload_len),(stream->ra_raw_base_seq+1))))
if (SEQ_LEQ((TCP_GET_SEQ(p)+p->payload_len),(StreamTcpReassembleGetRaBaseSeq(stream)+1)))
{
SCLogDebug("not inserting: SEQ+payload %"PRIu32", last_ack %"PRIu32", "
"ra_app_base_seq %"PRIu32, (TCP_GET_SEQ(p)+p->payload_len),
stream->last_ack, stream->ra_app_base_seq);
"ra_(app|raw)_base_seq %"PRIu32, (TCP_GET_SEQ(p)+p->payload_len),
stream->last_ack, StreamTcpReassembleGetRaBaseSeq(stream));
return_seg = TRUE;
ret_value = -1;
goto end;
@ -1384,30 +1396,65 @@ static int HandleSegmentStartsAfterListSegment(ThreadVars *tv, TcpReassemblyThre
}
/**
* \brief Function to Check the reassembly depth valuer against the
* \internal
* \brief Function to Check the reassembly depth valuer against the
* allowed max depth of the stream reassmbly for TCP streams.
*
* \todo maybe we can make an exception for packets that are retransmissions
* so they are between isn and next_seq
*
* \param stream stream direction
* \param size size of the segment that is added
* \param stream stream direction
* \param seq sequence number where "size" starts
* \param size size of the segment that is added
*
* \retval 1 if in bounds
* \retval 0 if not in bounds
* \retval size Part of the size that fits in the depth, 0 if none
*/
int StreamTcpReassembleCheckDepth(TcpStream *stream, uint32_t size) {
static uint32_t StreamTcpReassembleCheckDepth(TcpStream *stream,
uint32_t seq, uint32_t size)
{
SCEnter();
/* if the configured depth value is 0, it means there is no limit on
reassembly depth. Otherwise carry on my boy ;) */
if (stream_config.reassembly_depth == 0) {
SCReturnInt(1);
} else if (((stream->next_seq - stream->isn) + size) <= stream_config.reassembly_depth) {
SCReturnInt(1);
SCReturnUInt(size);
}
SCReturnInt(0);
/* if the final flag is set, we're not accepting anymore */
if (stream->flags & STREAMTCP_STREAM_FLAG_DEPTH_REACHED) {
SCReturnUInt(0);
}
/* if the ra_base_seq has moved passed the depth window we stop
* checking and just reject the rest of the packets including
* retransmissions. Saves us the hassle of dealing with sequence
* wraps as well */
if (SEQ_GEQ((StreamTcpReassembleGetRaBaseSeq(stream)+1),(stream->isn + stream_config.reassembly_depth))) {
stream->flags |= STREAMTCP_STREAM_FLAG_DEPTH_REACHED;
SCReturnUInt(0);
}
SCLogDebug("full Depth not yet reached: %"PRIu32" <= %"PRIu32,
(StreamTcpReassembleGetRaBaseSeq(stream)+1),
(stream->isn + stream_config.reassembly_depth));
if (SEQ_GEQ(seq, stream->isn) && SEQ_LT(seq, (stream->isn + stream_config.reassembly_depth))) {
/* packet (partly?) fits the depth window */
if (SEQ_LEQ((seq + size),(stream->isn + stream_config.reassembly_depth))) {
/* complete fit */
SCReturnUInt(size);
} else {
/* partial fit, return only what fits */
uint32_t part = (stream->isn + stream_config.reassembly_depth) - seq;
#if DEBUG
BUG_ON(part > size);
#else
if (part > size)
part = size;
#endif
SCReturnUInt(part);
}
}
SCReturnUInt(0);
}
int StreamTcpReassembleHandleSegmentHandleData(ThreadVars *tv, TcpReassemblyThreadCtx *ra_ctx,
@ -1417,21 +1464,37 @@ int StreamTcpReassembleHandleSegmentHandleData(ThreadVars *tv, TcpReassemblyThre
/* If we have reached the defined depth for either of the stream, then stop
reassembling the TCP session */
if (StreamTcpReassembleCheckDepth(stream, p->payload_len) != 1)
{
ssn->server.flags |= STREAMTCP_STREAM_FLAG_NOREASSEMBLY;
ssn->client.flags |= STREAMTCP_STREAM_FLAG_NOREASSEMBLY;
uint32_t size = StreamTcpReassembleCheckDepth(stream, TCP_GET_SEQ(p), p->payload_len);
SCLogDebug("ssn %p: check depth returned %"PRIu32, ssn, size);
if (stream->flags & STREAMTCP_STREAM_FLAG_DEPTH_REACHED) {
/* increment stream depth counter */
SCPerfCounterIncr(ra_ctx->counter_tcp_stream_depth, tv->sc_perf_pca);
stream->flags |= STREAMTCP_STREAM_FLAG_NOREASSEMBLY;
SCLogDebug("ssn %p: reassembly depth reached, "
"STREAMTCP_STREAM_FLAG_NOREASSEMBLY set", ssn);
}
if (size == 0) {
SCLogDebug("ssn %p: depth reached, not reassembling", ssn);
SCReturnInt(0);
}
#if DEBUG
BUG_ON(size > p->payload_len);
#else
if (size > p->payload_len)
size = p->payload_len;
#endif
TcpSegment *seg = StreamTcpGetSegment(tv, ra_ctx, p->payload_len);
if (seg == NULL) {
SCLogDebug("segment_pool[%"PRIu16"] is empty", segment_pool_idx[p->payload_len]);
SCReturnInt(-1);
}
memcpy(seg->payload, p->payload, p->payload_len);
seg->payload_len = p->payload_len;
memcpy(seg->payload, p->payload, size);
seg->payload_len = size;
seg->seq = TCP_GET_SEQ(p);
if (ReassembleInsertSegment(tv, ra_ctx, stream, seg, p) != 0) {
@ -6442,14 +6505,12 @@ static int StreamTcpReassembleTest45 (void) {
StreamTcpInitConfig(TRUE);
TcpReassemblyThreadCtx *ra_ctx = StreamTcpReassembleInitThreadCtx();
ssn.server.ra_raw_base_seq = 9;
STREAMTCP_SET_RA_BASE_SEQ(&ssn.server, 9);
ssn.server.isn = 9;
ssn.server.last_ack = 60;
ssn.server.next_seq = ssn.server.isn;
ssn.client.ra_raw_base_seq = 9;
STREAMTCP_SET_RA_BASE_SEQ(&ssn.client, 9);
ssn.client.isn = 9;
ssn.client.last_ack = 60;
ssn.client.next_seq = ssn.client.isn;
f.alproto = ALPROTO_UNKNOWN;
inet_pton(AF_INET, "1.2.3.4", &in);
@ -6486,50 +6547,47 @@ static int StreamTcpReassembleTest45 (void) {
s = &ssn.server;
if (StreamTcpReassembleHandleSegment(&tv, ra_ctx, &ssn, s, p, &pq) == -1) {
printf("failed in segments reassembly, while processing toclient packet\n");
printf("failed in segments reassembly, while processing toclient packet: ");
goto end;
}
/* Check if we have flags set or not */
if ((ssn.client.flags & STREAMTCP_STREAM_FLAG_NOREASSEMBLY) ||
(ssn.server.flags & STREAMTCP_STREAM_FLAG_NOREASSEMBLY)) {
printf("there shouldn't be any no reassembly flag be set \n");
if (s->flags & STREAMTCP_STREAM_FLAG_NOREASSEMBLY) {
printf("there shouldn't be a noreassembly flag be set: ");
goto end;
}
ssn.server.next_seq += httplen1;
STREAMTCP_SET_RA_BASE_SEQ(&ssn.server, ssn.server.isn + httplen1);
p->flowflags = FLOW_PKT_TOSERVER;
p->payload_len = httplen1;
s = &ssn.client;
if (StreamTcpReassembleHandleSegment(&tv, ra_ctx, &ssn, s, p, &pq) == -1) {
printf("failed in segments reassembly, while processing toserver packet\n");
printf("failed in segments reassembly, while processing toserver packet: ");
goto end;
}
/* Check if we have flags set or not */
if ((ssn.client.flags & STREAMTCP_STREAM_FLAG_NOREASSEMBLY) ||
(ssn.server.flags & STREAMTCP_STREAM_FLAG_NOREASSEMBLY)) {
printf("there shouldn't be any no reassembly flag be set \n");
if (s->flags & STREAMTCP_STREAM_FLAG_NOREASSEMBLY) {
printf("there shouldn't be a noreassembly flag be set: ");
goto end;
}
ssn.client.next_seq += httplen1;
STREAMTCP_SET_RA_BASE_SEQ(&ssn.client, ssn.client.isn + httplen1);
p->flowflags = FLOW_PKT_TOCLIENT;
p->payload_len = httplen1;
s = &ssn.server;
if (StreamTcpReassembleHandleSegment(&tv, ra_ctx, &ssn, s, p, &pq) == -1) {
printf("failed in segments reassembly, while processing toserver packet\n");
printf("failed in segments reassembly, while processing toserver packet: ");
goto end;
}
/* Check if we have flags set or not */
if (!(ssn.client.flags & STREAMTCP_STREAM_FLAG_NOREASSEMBLY) ||
!(ssn.server.flags & STREAMTCP_STREAM_FLAG_NOREASSEMBLY)) {
printf("the no_reassembly flags should be set, "
if (!(s->flags & STREAMTCP_STREAM_FLAG_NOREASSEMBLY)) {
printf("the noreassembly flags should be set, "
"p.payload_len %"PRIu16" stream_config.reassembly_"
"depth %"PRIu32"\n", p->payload_len,
"depth %"PRIu32": ", p->payload_len,
stream_config.reassembly_depth);
goto end;
}
@ -6583,11 +6641,11 @@ static int StreamTcpReassembleTest46 (void) {
StreamTcpInitConfig(TRUE);
TcpReassemblyThreadCtx *ra_ctx = StreamTcpReassembleInitThreadCtx();
ssn.server.ra_raw_base_seq = ssn.server.ra_app_base_seq = 9;
STREAMTCP_SET_RA_BASE_SEQ(&ssn.server, 9);
ssn.server.isn = 9;
ssn.server.last_ack = 60;
ssn.server.next_seq = ssn.server.isn;
ssn.client.ra_raw_base_seq = ssn.client.ra_app_base_seq = 9;
STREAMTCP_SET_RA_BASE_SEQ(&ssn.client, 9);
ssn.client.isn = 9;
ssn.client.last_ack = 60;
ssn.client.next_seq = ssn.client.isn;
@ -6636,7 +6694,7 @@ static int StreamTcpReassembleTest46 (void) {
printf("there shouldn't be any no reassembly flag be set \n");
goto end;
}
ssn.server.next_seq += httplen1;
STREAMTCP_SET_RA_BASE_SEQ(&ssn.server, ssn.server.isn + httplen1);
p->flowflags = FLOW_PKT_TOSERVER;
p->payload_len = httplen1;
@ -6653,10 +6711,12 @@ static int StreamTcpReassembleTest46 (void) {
printf("there shouldn't be any no reassembly flag be set \n");
goto end;
}
ssn.client.next_seq += httplen1;
STREAMTCP_SET_RA_BASE_SEQ(&ssn.client, ssn.client.isn + httplen1);
p->flowflags = FLOW_PKT_TOCLIENT;
p->payload_len = httplen1;
tcph.th_seq = htonl(10 + httplen1);
tcph.th_ack = htonl(20 + httplen1);
s = &ssn.server;
if (StreamTcpReassembleHandleSegment(&tv, ra_ctx, &ssn, s, p, &pq) == -1) {

@ -79,6 +79,8 @@ typedef struct TcpReassemblyThreadCtx_ {
AlpProtoDetectThreadCtx dp_ctx; /**< proto detection thread data */
/** TCP segments which are not being reassembled due to memcap was reached */
uint16_t counter_tcp_segment_memcap;
/** number of streams that stop reassembly because their depth is reached */
uint16_t counter_tcp_stream_depth;
} TcpReassemblyThreadCtx;
#define OS_POLICY_DEFAULT OS_POLICY_BSD

@ -3335,6 +3335,9 @@ TmEcode StreamTcpThreadInit(ThreadVars *tv, void *initdata, void **data)
stt->ra_ctx->counter_tcp_segment_memcap = SCPerfTVRegisterCounter("tcp.segment_memcap_drop", tv,
SC_PERF_TYPE_UINT64,
"NULL");
stt->ra_ctx->counter_tcp_stream_depth = SCPerfTVRegisterCounter("tcp.stream_depth_reached", tv,
SC_PERF_TYPE_UINT64,
"NULL");
tv->sc_perf_pca = SCPerfGetAllCountersArray(&tv->sc_perf_pctx);
SCPerfAddToClubbedTMTable(tv->name, &tv->sc_perf_pctx);

Loading…
Cancel
Save