From f0928a4555700001589e4b072f00d974bd9fe8f9 Mon Sep 17 00:00:00 2001 From: Gurvinder Singh Date: Wed, 7 Jul 2010 22:40:40 +0200 Subject: [PATCH] support for enforcing the depth until when the reassembly will be performed --- src/stream-tcp-private.h | 2 + src/stream-tcp-reassemble.c | 294 ++++++++++++++++++++++++++++++++++++ src/stream-tcp.c | 6 + src/stream-tcp.h | 1 + suricata.yaml | 2 + 5 files changed, 305 insertions(+) diff --git a/src/stream-tcp-private.h b/src/stream-tcp-private.h index f1eee1dcfc..4ff2ef8d13 100644 --- a/src/stream-tcp-private.h +++ b/src/stream-tcp-private.h @@ -58,6 +58,8 @@ typedef struct TcpStream_ { app layer protocol has not been detected, beacuse every smsg needs to contain all the initial segments too */ + uint32_t reassembly_depth; /**< The depth value of a stream until when, we + will reassemble the stream */ } TcpStream; /* from /usr/include/netinet/tcp.h */ diff --git a/src/stream-tcp-reassemble.c b/src/stream-tcp-reassemble.c index 70e014f003..caf4420e94 100644 --- a/src/stream-tcp-reassemble.c +++ b/src/stream-tcp-reassemble.c @@ -1315,11 +1315,45 @@ static int HandleSegmentStartsAfterListSegment(TcpStream *stream, SCReturnInt(0); } +/** + * \brief Function to Check the reassembly depth valuer against the + * allowed max depth of the stream reassmbly for TCP streams. + * + * \param size Size of the depth util now and received packet payload length + * \retval 1 if in bounds + * \retval 0 if not in bounds + */ +int StreamTcpReassembleCheckDepth(uint32_t size) { + SCEnter(); + + int ret = 0; + + /* 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) { + ret = 1; + } else if (size <= stream_config.reassembly_depth) { + ret = 1; + } + + SCReturnInt(ret); +} + int StreamTcpReassembleHandleSegmentHandleData(TcpSession *ssn, TcpStream *stream, Packet *p) { SCEnter(); + /* If we have reached the defined depth for either of the stream, then stop + reassembling the TCP session */ + if (StreamTcpReassembleCheckDepth(stream->reassembly_depth + p->payload_len) + != 1) + { + ssn->flags |= STREAMTCP_FLAG_NOCLIENT_REASSEMBLY; + ssn->flags |= STREAMTCP_FLAG_NOSERVER_REASSEMBLY; + SCReturnInt(0); + } + TcpSegment *seg = StreamTcpGetSegment(p->payload_len); if (seg == NULL) { SCLogDebug("segment_pool[%"PRIu16"] is empty", segment_pool_idx[p->payload_len]); @@ -1331,6 +1365,7 @@ int StreamTcpReassembleHandleSegmentHandleData(TcpSession *ssn, seg->seq = TCP_GET_SEQ(p); seg->next = NULL; seg->prev = NULL; + stream->reassembly_depth += p->payload_len; if (ReassembleInsertSegment(stream, seg, p) != 0) { SCLogDebug("ReassembleInsertSegment failed"); @@ -5574,6 +5609,263 @@ end: return ret; } +/** + * \test Test to make sure that reassembly_depth is enforced. + * + * \retval On success it returns 1 and on failure 0. + */ + +static int StreamTcpReassembleTest45 (void) { + int ret = 0; + Packet p; + Flow f; + TCPHdr tcph; + Port sp; + Port dp; + Address src; + Address dst; + struct in_addr in; + TcpSession ssn; + + memset(&p, 0, sizeof (Packet)); + memset(&f, 0, sizeof (Flow)); + memset(&tcph, 0, sizeof (TCPHdr)); + memset(&src, 0, sizeof(Address)); + memset(&dst, 0, sizeof(Address)); + memset(&ssn, 0, sizeof(TcpSession)); + + uint8_t httpbuf1[] = "/ HTTP/1.0\r\nUser-Agent: Victor/1.0"; + + uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */ + + FLOW_INITIALIZE(&f); + StreamTcpInitConfig(TRUE); + TcpReassemblyThreadCtx *ra_ctx = StreamTcpReassembleInitThreadCtx(); + + ssn.server.ra_base_seq = 9; + ssn.server.isn = 9; + ssn.server.last_ack = 60; + ssn.client.ra_base_seq = 9; + ssn.client.isn = 9; + ssn.client.last_ack = 60; + f.alproto = ALPROTO_UNKNOWN; + + inet_pton(AF_INET, "1.2.3.4", &in); + src.family = AF_INET; + src.addr_data32[0] = in.s_addr; + inet_pton(AF_INET, "1.2.3.5", &in); + dst.family = AF_INET; + dst.addr_data32[0] = in.s_addr; + sp = 200; + dp = 220; + + f.src = src; + f.dst = dst; + f.sp = sp; + f.dp = dp; + f.protoctx = &ssn; + p.flow = &f; + + tcph.th_win = htons(5480); + tcph.th_seq = htonl(10); + tcph.th_ack = htonl(20); + tcph.th_flags = TH_ACK|TH_PUSH; + p.tcph = &tcph; + p.flowflags = FLOW_PKT_TOCLIENT; + + p.payload = httpbuf1; + p.payload_len = httplen1; + ssn.state = TCP_ESTABLISHED; + /* set the default value of reassembly depth, as there is no config file */ + stream_config.reassembly_depth = 1048576; + ssn.server.reassembly_depth = 1048530; + + TcpStream *s = NULL; + s = &ssn.server; + + if (StreamTcpReassembleHandleSegment(ra_ctx, &ssn, s, &p) == -1) { + printf("failed in segments reassembly, while processing toclient packet\n"); + goto end; + } + + /* Check if we have flags set or not */ + if ((ssn.flags & STREAMTCP_FLAG_NOCLIENT_REASSEMBLY) || + (ssn.flags & STREAMTCP_FLAG_NOSERVER_REASSEMBLY)) { + printf("there shouldn't be any no reassembly flag be set \n"); + goto end; + } + + p.flowflags = FLOW_PKT_TOSERVER; + p.payload_len = httplen1; + s = &ssn.client; + + if (StreamTcpReassembleHandleSegment(ra_ctx, &ssn, s, &p) == -1) { + printf("failed in segments reassembly, while processing toserver packet\n"); + goto end; + } + + /* Check if we have flags set or not */ + if ((ssn.flags & STREAMTCP_FLAG_NOCLIENT_REASSEMBLY) || + (ssn.flags & STREAMTCP_FLAG_NOSERVER_REASSEMBLY)) { + printf("there shouldn't be any no reassembly flag be set \n"); + goto end; + } + + p.flowflags = FLOW_PKT_TOCLIENT; + p.payload_len = httplen1; + s = &ssn.server; + + if (StreamTcpReassembleHandleSegment(ra_ctx, &ssn, s, &p) == -1) { + printf("failed in segments reassembly, while processing toserver packet\n"); + goto end; + } + + /* Check if we have flags set or not */ + if (! (ssn.flags & STREAMTCP_FLAG_NOCLIENT_REASSEMBLY) || + ! (ssn.flags & STREAMTCP_FLAG_NOSERVER_REASSEMBLY)) { + printf("the no_reassembly flags should be set, server.reassembly_depth " + "%"PRIu32" and p.payload_len %"PRIu16" stream_config.reassembly_" + "depth %"PRIu32"\n", ssn.server.reassembly_depth, p.payload_len, + stream_config.reassembly_depth); + goto end; + } + + ret = 1; +end: + StreamTcpFreeConfig(TRUE); + StreamTcpReassembleFreeThreadCtx(ra_ctx); + return ret; +} + +/** + * \test Test the undefined config value of reassembly depth. + * the default value of 0 will be loaded and stream will be reassembled + * until the session ended + * + * \retval On success it returns 1 and on failure 0. + */ + +static int StreamTcpReassembleTest46 (void) { + int ret = 0; + Packet p; + Flow f; + TCPHdr tcph; + Port sp; + Port dp; + Address src; + Address dst; + struct in_addr in; + TcpSession ssn; + + memset(&p, 0, sizeof (Packet)); + memset(&f, 0, sizeof (Flow)); + memset(&tcph, 0, sizeof (TCPHdr)); + memset(&src, 0, sizeof(Address)); + memset(&dst, 0, sizeof(Address)); + memset(&ssn, 0, sizeof(TcpSession)); + + uint8_t httpbuf1[] = "/ HTTP/1.0\r\nUser-Agent: Victor/1.0"; + + uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */ + + FLOW_INITIALIZE(&f); + StreamTcpInitConfig(TRUE); + TcpReassemblyThreadCtx *ra_ctx = StreamTcpReassembleInitThreadCtx(); + + ssn.server.ra_base_seq = 9; + ssn.server.isn = 9; + ssn.server.last_ack = 60; + ssn.client.ra_base_seq = 9; + ssn.client.isn = 9; + ssn.client.last_ack = 60; + f.alproto = ALPROTO_UNKNOWN; + + inet_pton(AF_INET, "1.2.3.4", &in); + src.family = AF_INET; + src.addr_data32[0] = in.s_addr; + inet_pton(AF_INET, "1.2.3.5", &in); + dst.family = AF_INET; + dst.addr_data32[0] = in.s_addr; + sp = 200; + dp = 220; + + f.src = src; + f.dst = dst; + f.sp = sp; + f.dp = dp; + f.protoctx = &ssn; + p.flow = &f; + + tcph.th_win = htons(5480); + tcph.th_seq = htonl(10); + tcph.th_ack = htonl(20); + tcph.th_flags = TH_ACK|TH_PUSH; + p.tcph = &tcph; + p.flowflags = FLOW_PKT_TOCLIENT; + + p.payload = httpbuf1; + p.payload_len = httplen1; + ssn.state = TCP_ESTABLISHED; + + ssn.server.reassembly_depth = 1048530; + + TcpStream *s = NULL; + s = &ssn.server; + + if (StreamTcpReassembleHandleSegment(ra_ctx, &ssn, s, &p) == -1) { + printf("failed in segments reassembly, while processing toclient packet\n"); + goto end; + } + + /* Check if we have flags set or not */ + if ((ssn.flags & STREAMTCP_FLAG_NOCLIENT_REASSEMBLY) || + (ssn.flags & STREAMTCP_FLAG_NOSERVER_REASSEMBLY)) { + printf("there shouldn't be any no reassembly flag be set \n"); + goto end; + } + + p.flowflags = FLOW_PKT_TOSERVER; + p.payload_len = httplen1; + s = &ssn.client; + + if (StreamTcpReassembleHandleSegment(ra_ctx, &ssn, s, &p) == -1) { + printf("failed in segments reassembly, while processing toserver packet\n"); + goto end; + } + + /* Check if we have flags set or not */ + if ((ssn.flags & STREAMTCP_FLAG_NOCLIENT_REASSEMBLY) || + (ssn.flags & STREAMTCP_FLAG_NOSERVER_REASSEMBLY)) { + printf("there shouldn't be any no reassembly flag be set \n"); + goto end; + } + + p.flowflags = FLOW_PKT_TOCLIENT; + p.payload_len = httplen1; + s = &ssn.server; + + if (StreamTcpReassembleHandleSegment(ra_ctx, &ssn, s, &p) == -1) { + printf("failed in segments reassembly, while processing toserver packet\n"); + goto end; + } + + /* Check if we have flags set or not */ + if ((ssn.flags & STREAMTCP_FLAG_NOCLIENT_REASSEMBLY) || + (ssn.flags & STREAMTCP_FLAG_NOSERVER_REASSEMBLY)) { + printf("the no_reassembly flags should not be set, server.reassembly_depth " + "%"PRIu32" and p.payload_len %"PRIu16" stream_config.reassembly_" + "depth %"PRIu32"\n", ssn.server.reassembly_depth, p.payload_len, + stream_config.reassembly_depth); + goto end; + } + + ret = 1; +end: + StreamTcpFreeConfig(TRUE); + StreamTcpReassembleFreeThreadCtx(ra_ctx); + return ret; +} + #endif /* UNITTESTS */ /** \brief The Function Register the Unit tests to test the reassembly engine @@ -5626,5 +5918,7 @@ void StreamTcpReassembleRegisterTests(void) { UtRegisterTest("StreamTcpReassembleTest42 -- pause/unpause reassembly test", StreamTcpReassembleTest42, 1); UtRegisterTest("StreamTcpReassembleTest43 -- min smsg size test", StreamTcpReassembleTest43, 1); UtRegisterTest("StreamTcpReassembleTest44 -- Memcap Test", StreamTcpReassembleTest44, 1); + UtRegisterTest("StreamTcpReassembleTest45 -- Depth Test", StreamTcpReassembleTest45, 1); + UtRegisterTest("StreamTcpReassembleTest46 -- Depth Test", StreamTcpReassembleTest46, 1); #endif /* UNITTESTS */ } diff --git a/src/stream-tcp.c b/src/stream-tcp.c index 54de8d8288..255a3db700 100644 --- a/src/stream-tcp.c +++ b/src/stream-tcp.c @@ -389,6 +389,12 @@ void StreamTcpInitConfig(char quiet) stream_config.reassembly_memcap = STREAMTCP_DEFAULT_REASSEMBLY_MEMCAP; } + if ((ConfGetInt("stream.reassembly.depth", &value)) == 1) { + stream_config.reassembly_depth = (uint32_t)value; + } else { + stream_config.reassembly_depth = 0; + } + if (!quiet) { SCLogInfo("stream \"memcap\": %"PRIu32"", stream_config.memcap); } diff --git a/src/stream-tcp.h b/src/stream-tcp.h index 7646a5ba40..b7f2dcace4 100644 --- a/src/stream-tcp.h +++ b/src/stream-tcp.h @@ -43,6 +43,7 @@ typedef struct TcpStreamCnf_ { int midstream; int async_oneside; uint32_t reassembly_memcap; /**< max memory usage for stream reassembly */ + uint32_t reassembly_depth; /**< Depth until when we reassemble the stream */ } TcpStreamCnf; TcpStreamCnf stream_config; diff --git a/suricata.yaml b/suricata.yaml index 75c0e9afa2..19ec39ef14 100644 --- a/suricata.yaml +++ b/suricata.yaml @@ -239,6 +239,7 @@ flow-timeouts: # memcap: 33554432 # 32mb memcap # reassembly: # memcap: 67108864 # 64mb reassembly memcap +# depth: 1048576 # 1 MB reassembly depth # max_sessions: 262144 # 256k concurrent sessions # prealloc_sessions: 32768 # 32k sessions prealloc'd # midstream: false # don't allow midstream session pickups @@ -247,6 +248,7 @@ stream: memcap: 33554432 reassembly: memcap: 67108864 + depth: 1048576 # Logging configuration. This is not about logging IDS alerts, but # IDS output about what its doing, errors, etc.