From b5f8f386a37f61ae0c1c874b82f978f34394fb91 Mon Sep 17 00:00:00 2001 From: Victor Julien Date: Tue, 28 Jan 2014 13:48:26 +0100 Subject: [PATCH 1/3] stream: configurable segment pools The stream reassembly engine uses a set of pools in which preallocated segments are stored. There are various pools each with different packet sizes. The goal is to lower memory presure. Until now, these pools were hardcoded. This patch introduces the ability to configure them fully from the yaml. There can be at max 256 of these pools. Yaml layout is as follows: stream: reassemble: segments: - size: 2048 prealloc: 3000 - size: 4 prealloc: 1000 - size: 1024 prealloc: 2000 The size is the packet size. The prealloc value indicates how many segments are set up at startup. The pools have no limit wrt how many segments can be used of a certain size. If the engine needs more than the prealloc size, segments are malloc'd and free'd. The only limit here is the stream.reassemble.memcap. If the yaml part if omitted, the default values are the same as before. Feature #1093 --- src/stream-tcp-reassemble.c | 214 +++++++++++++++++++++++++++++------- 1 file changed, 177 insertions(+), 37 deletions(-) diff --git a/src/stream-tcp-reassemble.c b/src/stream-tcp-reassemble.c index 7e763f51cfd..a1b16f83870 100644 --- a/src/stream-tcp-reassemble.c +++ b/src/stream-tcp-reassemble.c @@ -32,6 +32,7 @@ #include "detect.h" #include "flow.h" #include "threads.h" +#include "conf.h" #include "flow-util.h" @@ -43,6 +44,7 @@ #include "util-print.h" #include "util-host-os-info.h" #include "util-unittest-helper.h" +#include "util-byte.h" #include "stream-tcp.h" #include "stream-tcp-private.h" @@ -72,21 +74,12 @@ static uint64_t segment_pool_memcnt = 0; /* We define several pools with prealloced segments with fixed size * payloads. We do this to prevent having to do an SCMalloc call for every * data segment we receive, which would be a large performance penalty. - * The cost is in memory of course. */ -#define segment_pool_num 8 -static uint16_t segment_pool_pktsizes[segment_pool_num] = {4, 16, 112, 248, 512, - 768, 1448, 0xffff}; -//static uint16_t segment_pool_poolsizes[segment_pool_num] = {2048, 3072, 3072, -// 3072, 3072, 8192, -// 8192, 512}; -static uint16_t segment_pool_poolsizes[segment_pool_num] = {0, 0, 0, - 0, 0, 0, - 0, 0}; -static uint16_t segment_pool_poolsizes_prealloc[segment_pool_num] = {256, 512, 512, - 512, 512, 1024, - 1024, 128}; -static Pool *segment_pool[segment_pool_num]; -static SCMutex segment_pool_mutex[segment_pool_num]; + * The cost is in memory of course. The number of pools and the properties + * of the pools are determined by the yaml. */ +static int segment_pool_num = 0; +static Pool **segment_pool = NULL; +static SCMutex *segment_pool_mutex = NULL; +static uint16_t *segment_pool_pktsizes = NULL; #ifdef DEBUG static SCMutex segment_pool_cnt_mutex; static uint64_t segment_pool_cnt = 0; @@ -280,36 +273,159 @@ void StreamTcpReturnStreamSegments (TcpStream *stream) stream->seg_list_tail = NULL; } -int StreamTcpReassembleInit(char quiet) +typedef struct SegmentSizes_ { - /* init the memcap/use tracker */ - SC_ATOMIC_INIT(ra_memuse); + uint16_t pktsize; + uint32_t prealloc; +} SegmentSizes; - StreamMsgQueuesInit(); +/* sort small to big */ +static int SortByPktsize(const void *a, const void *b) +{ + const SegmentSizes *s0 = a; + const SegmentSizes *s1 = b; + return s0->pktsize - s1->pktsize; +} -#ifdef DEBUG - SCMutexInit(&segment_pool_memuse_mutex, NULL); -#endif - uint16_t u16 = 0; - for (u16 = 0; u16 < segment_pool_num; u16++) - { - SCMutexInit(&segment_pool_mutex[u16], NULL); - SCMutexLock(&segment_pool_mutex[u16]); - segment_pool[u16] = PoolInit(segment_pool_poolsizes[u16], - segment_pool_poolsizes_prealloc[u16], - sizeof (TcpSegment), - TcpSegmentPoolAlloc, TcpSegmentPoolInit, - (void *) &segment_pool_pktsizes[u16], - TcpSegmentPoolCleanup, NULL); - SCMutexUnlock(&segment_pool_mutex[u16]); +int StreamTcpReassemblyConfig(char quiet) +{ + Pool **my_segment_pool = NULL; + SCMutex *my_segment_lock = NULL; + uint16_t *my_segment_pktsizes = NULL; + SegmentSizes sizes[256]; + memset(&sizes, 0x00, sizeof(sizes)); + + int npools = 0; + ConfNode *segs = ConfGetNode("stream.reassembly.segments"); + if (segs != NULL) { + ConfNode *seg; + TAILQ_FOREACH(seg, &segs->head, next) { + ConfNode *segsize = ConfNodeLookupChild(seg,"size"); + if (segsize == NULL) + continue; + ConfNode *segpre = ConfNodeLookupChild(seg,"prealloc"); + if (segpre == NULL) + continue; + + if (npools >= 256) { + SCLogError(SC_ERR_INVALID_ARGUMENT, "too many segment packet " + "pools defined, max is 256"); + return -1; + } + + SCLogDebug("segsize->val %s", segsize->val); + SCLogDebug("segpre->val %s", segpre->val); + + uint16_t pktsize = 0; + if (ByteExtractStringUint16(&pktsize, 10, strlen(segsize->val), + segsize->val) == -1) + { + SCLogError(SC_ERR_INVALID_ARGUMENT, "segment packet size " + "of %s is invalid", segsize->val); + return -1; + } + uint32_t prealloc = 0; + if (ByteExtractStringUint32(&prealloc, 10, strlen(segpre->val), + segpre->val) == -1) + { + SCLogError(SC_ERR_INVALID_ARGUMENT, "segment prealloc of " + "%s is invalid", segpre->val); + return -1; + } + + sizes[npools].pktsize = pktsize; + sizes[npools].prealloc = prealloc; + SCLogDebug("pktsize %u, prealloc %u", sizes[npools].pktsize, + sizes[npools].prealloc); + npools++; + } + } + + SCLogDebug("npools %d", npools); + if (npools > 0) { + /* sort the array as the index code below relies on it */ + qsort(&sizes, npools, sizeof(sizes[0]), SortByPktsize); + if (sizes[npools - 1].pktsize != 0xffff) { + sizes[npools].pktsize = 0xffff; + sizes[npools].prealloc = 8; + npools++; + SCLogInfo("appended a segment pool for pktsize 65536"); + } + } else if (npools == 0) { + /* defaults */ + sizes[0].pktsize = 4; + sizes[0].prealloc = 256; + sizes[1].pktsize = 16; + sizes[1].prealloc = 512; + sizes[2].pktsize = 112; + sizes[2].prealloc = 512; + sizes[3].pktsize = 248; + sizes[3].prealloc = 512; + sizes[4].pktsize = 512; + sizes[4].prealloc = 512; + sizes[5].pktsize = 768; + sizes[5].prealloc = 1024; + sizes[6].pktsize = 1448; + sizes[6].prealloc = 1024; + sizes[7].pktsize = 0xffff; + sizes[7].prealloc = 128; + npools = 8; + } + + int i = 0; + for (i = 0; i < npools; i++) { + SCLogDebug("pktsize %u, prealloc %u", sizes[i].pktsize, sizes[i].prealloc); + } + + my_segment_pool = SCMalloc(npools * sizeof(Pool *)); + if (my_segment_pool == NULL) { + SCLogError(SC_ERR_MEM_ALLOC, "malloc failed"); + return -1; + } + my_segment_lock = SCMalloc(npools * sizeof(SCMutex)); + if (my_segment_lock == NULL) { + SCLogError(SC_ERR_MEM_ALLOC, "malloc failed"); + + SCFree(my_segment_pool); + return -1; + } + my_segment_pktsizes = SCMalloc(npools * sizeof(uint16_t)); + if (my_segment_pktsizes == NULL) { + SCLogError(SC_ERR_MEM_ALLOC, "malloc failed"); + + SCFree(my_segment_lock); + SCFree(my_segment_pool); + return -1; + } + uint32_t my_segment_poolsizes[npools]; + + for (i = 0; i < npools; i++) { + my_segment_pktsizes[i] = sizes[i].pktsize; + my_segment_poolsizes[i] = sizes[i].prealloc; + SCMutexInit(&my_segment_lock[i], NULL); + + /* setup the pool */ + SCMutexLock(&my_segment_lock[i]); + my_segment_pool[i] = PoolInit(0, my_segment_poolsizes[i], 0, + TcpSegmentPoolAlloc, TcpSegmentPoolInit, + (void *) &my_segment_pktsizes[i], + TcpSegmentPoolCleanup, NULL); + SCMutexUnlock(&my_segment_lock[i]); + BUG_ON(my_segment_pool[i] == NULL); + + SCLogDebug("my_segment_pktsizes[i] %u, my_segment_poolsizes[i] %u", + my_segment_pktsizes[i], my_segment_poolsizes[i]); + if (!quiet) + SCLogInfo("segment pool: pktsize %u, prealloc %u", + my_segment_pktsizes[i], my_segment_poolsizes[i]); } uint16_t idx = 0; - u16 = 0; + uint16_t u16 = 0; while (1) { - if (idx <= segment_pool_pktsizes[u16]) { + if (idx <= my_segment_pktsizes[u16]) { segment_pool_idx[idx] = u16; - if (segment_pool_pktsizes[u16] == idx) + if (my_segment_pktsizes[u16] == idx) u16++; } @@ -318,7 +434,25 @@ int StreamTcpReassembleInit(char quiet) idx++; } + /* set the globals */ + segment_pool = my_segment_pool; + segment_pool_mutex = my_segment_lock; + segment_pool_pktsizes = my_segment_pktsizes; + segment_pool_num = npools; + return 0; +} + +int StreamTcpReassembleInit(char quiet) +{ + /* init the memcap/use tracker */ + SC_ATOMIC_INIT(ra_memuse); + + if (StreamTcpReassemblyConfig(quiet) < 0) + return -1; + StreamMsgQueuesInit(); + #ifdef DEBUG + SCMutexInit(&segment_pool_memuse_mutex, NULL); SCMutexInit(&segment_pool_cnt_mutex, NULL); #endif return 0; @@ -348,6 +482,12 @@ void StreamTcpReassembleFree(char quiet) SCMutexUnlock(&segment_pool_mutex[u16]); SCMutexDestroy(&segment_pool_mutex[u16]); } + SCFree(segment_pool); + SCFree(segment_pool_mutex); + SCFree(segment_pool_pktsizes); + segment_pool = NULL; + segment_pool_mutex = NULL; + segment_pool_pktsizes = NULL; StreamMsgQueuesDeinit(quiet); From fe1c4951f9ee3c9857143a33731792ff2ba1d643 Mon Sep 17 00:00:00 2001 From: Victor Julien Date: Tue, 28 Jan 2014 17:12:38 +0100 Subject: [PATCH 2/3] stream: silence stream.reassembly.raw message --- src/stream-tcp.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/stream-tcp.c b/src/stream-tcp.c index c0dc25bc908..5e7a5abc718 100644 --- a/src/stream-tcp.c +++ b/src/stream-tcp.c @@ -583,7 +583,8 @@ void StreamTcpInitConfig(char quiet) } else { enable_raw = 1; } - SCLogInfo("stream.reassembly.raw: %s", enable_raw ? "enabled" : "disabled"); + if (!quiet) + SCLogInfo("stream.reassembly.raw: %s", enable_raw ? "enabled" : "disabled"); /* init the memcap/use tracking */ SC_ATOMIC_INIT(st_memuse); From 84696ebe2a67339f654c21a0e8564b03590aef14 Mon Sep 17 00:00:00 2001 From: Victor Julien Date: Tue, 28 Jan 2014 17:13:05 +0100 Subject: [PATCH 3/3] stream: configurable stream chunk prealloc The stream chunk pool contains preallocating stream chunks (StreamMsg). These are used for raw reassembly, used in raw content inspection by the detection engine. The default setting so far has been 250, which was hardcoded. This meant that in setups that needed more, allocs and frees would be happen constantly. This patch introduces a yaml option to set the 'prealloc' value in the pool. The default is still 250. stream.reassembly.chunk-prealloc Related to feature #1093. --- src/stream-tcp-reassemble.c | 18 ++++++++++++++++-- src/stream.c | 6 ++++-- src/stream.h | 2 +- 3 files changed, 21 insertions(+), 5 deletions(-) diff --git a/src/stream-tcp-reassemble.c b/src/stream-tcp-reassemble.c index a1b16f83870..b3133c27a51 100644 --- a/src/stream-tcp-reassemble.c +++ b/src/stream-tcp-reassemble.c @@ -439,6 +439,22 @@ int StreamTcpReassemblyConfig(char quiet) segment_pool_mutex = my_segment_lock; segment_pool_pktsizes = my_segment_pktsizes; segment_pool_num = npools; + + uint32_t stream_chunk_prealloc = 250; + ConfNode *chunk = ConfGetNode("stream.reassembly.chunk-prealloc"); + if (chunk) { + uint32_t prealloc = 0; + if (ByteExtractStringUint32(&prealloc, 10, strlen(chunk->val), chunk->val) == -1) + { + SCLogError(SC_ERR_INVALID_ARGUMENT, "chunk-prealloc of " + "%s is invalid", chunk->val); + return -1; + } + stream_chunk_prealloc = prealloc; + } + if (!quiet) + SCLogInfo("stream.reassembly \"chunk-prealloc\": %u", stream_chunk_prealloc); + StreamMsgQueuesInit(stream_chunk_prealloc); return 0; } @@ -449,8 +465,6 @@ int StreamTcpReassembleInit(char quiet) if (StreamTcpReassemblyConfig(quiet) < 0) return -1; - StreamMsgQueuesInit(); - #ifdef DEBUG SCMutexInit(&segment_pool_memuse_mutex, NULL); SCMutexInit(&segment_pool_cnt_mutex, NULL); diff --git a/src/stream.c b/src/stream.c index d6bc3877d91..962530b0b4c 100644 --- a/src/stream.c +++ b/src/stream.c @@ -161,12 +161,14 @@ void StreamMsgPoolFree(void *ptr) { } } -void StreamMsgQueuesInit(void) { +void StreamMsgQueuesInit(uint32_t prealloc) { #ifdef DEBUG SCMutexInit(&stream_pool_memuse_mutex, NULL); #endif SCMutexLock(&stream_msg_pool_mutex); - stream_msg_pool = PoolInit(0,250,0,StreamMsgPoolAlloc,StreamMsgInit,NULL,NULL,StreamMsgPoolFree); + stream_msg_pool = PoolInit(0, prealloc, 0, + StreamMsgPoolAlloc,StreamMsgInit, + NULL,NULL,StreamMsgPoolFree); if (stream_msg_pool == NULL) exit(EXIT_FAILURE); /* XXX */ SCMutexUnlock(&stream_msg_pool_mutex); diff --git a/src/stream.h b/src/stream.h index c8453c931cb..6a5b7542754 100644 --- a/src/stream.h +++ b/src/stream.h @@ -56,7 +56,7 @@ typedef struct StreamMsgQueue_ { } StreamMsgQueue; /* prototypes */ -void StreamMsgQueuesInit(void); +void StreamMsgQueuesInit(uint32_t prealloc); void StreamMsgQueuesDeinit(char); StreamMsg *StreamMsgGetFromPool(void);