From 140f8baed99498b734d42254175e141ea8cb784a Mon Sep 17 00:00:00 2001 From: Eric Leblond Date: Tue, 12 Sep 2017 14:11:01 +0100 Subject: [PATCH] app-layer-expectation: expectation system This patch provides a working expectation system. This will allow suricata to have a way to identify parallel connections opened by a protocol such as FTP. Expectation are a chained list and there is a cleaning by timeout of the entries. This patch also defined a counter of expectations that is also used to check if we need to query IPPairs. This way we only query the IPPairs store if we have an expectation. --- src/Makefile.am | 1 + src/app-layer-detect-proto.c | 65 ++++++- src/app-layer-detect-proto.h | 2 + src/app-layer-expectation.c | 331 +++++++++++++++++++++++++++++++++++ src/app-layer-expectation.h | 35 ++++ src/app-layer.c | 5 + src/flow.h | 23 ++- src/stream-tcp-util.c | 2 + 8 files changed, 454 insertions(+), 10 deletions(-) create mode 100644 src/app-layer-expectation.c create mode 100644 src/app-layer-expectation.h diff --git a/src/Makefile.am b/src/Makefile.am index 0c0c959e70..4b89861f38 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -26,6 +26,7 @@ app-layer-dns-udp-rust.c app-layer-dns-udp-rust.h \ app-layer-enip.c app-layer-enip.h \ app-layer-enip-common.c app-layer-enip-common.h \ app-layer-events.c app-layer-events.h \ +app-layer-expectation.c app-layer-expectation.h \ app-layer-ftp.c app-layer-ftp.h \ app-layer-htp-body.c app-layer-htp-body.h \ app-layer-htp.c app-layer-htp.h \ diff --git a/src/app-layer-detect-proto.c b/src/app-layer-detect-proto.c index dc21dbd45e..f6ca40bd4a 100644 --- a/src/app-layer-detect-proto.c +++ b/src/app-layer-detect-proto.c @@ -55,6 +55,7 @@ #include "app-layer-protos.h" #include "app-layer-parser.h" #include "app-layer-detect-proto.h" +#include "app-layer-expectation.h" #include "conf.h" #include "util-memcmp.h" @@ -170,6 +171,9 @@ struct AppLayerProtoDetectThreadCtx_ { /* The global app layer proto detection context. */ static AppLayerProtoDetectCtx alpd_ctx; +static void AppLayerProtoDetectPEGetIpprotos(AppProto alproto, + uint8_t *ipprotos); + /***** Static Internal Calls: Protocol Retrieval *****/ /** \internal @@ -314,6 +318,24 @@ static AppLayerProtoDetectProbingParserPort *AppLayerProtoDetectGetProbingParser SCReturnPtr(pp_port, "AppLayerProtoDetectProbingParserPort *"); } + +/** + * \brief Call the probing expectation to see if there is some for this flow. + * + */ +static AppProto AppLayerProtoDetectPEGetProto(Flow *f, uint8_t ipproto, + uint8_t direction) +{ + AppProto alproto = ALPROTO_UNKNOWN; + + SCLogDebug("expectation check for %p (dir %d)", f, direction); + FLOW_SET_PE_DONE(f, direction); + + alproto = AppLayerExpectationHandle(f, direction); + + return alproto; +} + /** * \brief Call the probing parser if it exists for this flow. * @@ -1329,8 +1351,17 @@ AppProto AppLayerProtoDetectGetProto(AppLayerProtoDetectThreadCtx *tctx, } } - if (!FLOW_IS_PP_DONE(f, direction)) - alproto = AppLayerProtoDetectPPGetProto(f, buf, buflen, ipproto, direction); + if (!FLOW_IS_PP_DONE(f, direction)) { + alproto = AppLayerProtoDetectPPGetProto(f, buf, buflen, + ipproto, direction); + if (alproto != ALPROTO_UNKNOWN) + goto end; + } + + /* Look if flow can be found in expectation list */ + if (!FLOW_IS_PE_DONE(f, direction)) { + alproto = AppLayerProtoDetectPEGetProto(f, ipproto, direction); + } end: SCReturnUInt(alproto); @@ -1573,6 +1604,9 @@ int AppLayerProtoDetectSetup(void) MpmInitCtx(&alpd_ctx.ctx_ipp[i].ctx_pm[j].mpm_ctx, mpm_matcher); } } + + AppLayerExpectationSetup(); + SCReturnInt(0); } @@ -1662,6 +1696,8 @@ void AppLayerProtoDetectReset(Flow *f) FLOW_RESET_PM_DONE(f, STREAM_TOCLIENT); FLOW_RESET_PP_DONE(f, STREAM_TOSERVER); FLOW_RESET_PP_DONE(f, STREAM_TOCLIENT); + FLOW_RESET_PE_DONE(f, STREAM_TOSERVER); + FLOW_RESET_PE_DONE(f, STREAM_TOCLIENT); f->probing_parser_toserver_alproto_masks = 0; f->probing_parser_toclient_alproto_masks = 0; @@ -1827,6 +1863,7 @@ void AppLayerProtoDetectSupportedIpprotos(AppProto alproto, uint8_t *ipprotos) AppLayerProtoDetectPMGetIpprotos(alproto, ipprotos); AppLayerProtoDetectPPGetIpprotos(alproto, ipprotos); + AppLayerProtoDetectPEGetIpprotos(alproto, ipprotos); SCReturn; } @@ -1869,6 +1906,30 @@ void AppLayerProtoDetectSupportedAppProtocols(AppProto *alprotos) SCReturn; } +uint8_t expectation_proto[ALPROTO_MAX]; + +static void AppLayerProtoDetectPEGetIpprotos(AppProto alproto, + uint8_t *ipprotos) +{ + if (expectation_proto[alproto] == IPPROTO_TCP) { + ipprotos[IPPROTO_TCP / 8] |= 1 << (IPPROTO_TCP % 8); + } + if (expectation_proto[alproto] == IPPROTO_UDP) { + ipprotos[IPPROTO_UDP / 8] |= 1 << (IPPROTO_UDP % 8); + } +} + +void AppLayerRegisterExpectationProto(uint8_t proto, AppProto alproto) +{ + if (expectation_proto[alproto]) { + if (proto != expectation_proto[alproto]) { + SCLogError(SC_ERR_NOT_SUPPORTED, + "Expectation on 2 IP protocols are not supported"); + } + } + expectation_proto[alproto] = proto; +} + /***** Unittests *****/ #ifdef UNITTESTS diff --git a/src/app-layer-detect-proto.h b/src/app-layer-detect-proto.h index 9533bbc02a..89e430f8ce 100644 --- a/src/app-layer-detect-proto.h +++ b/src/app-layer-detect-proto.h @@ -181,6 +181,8 @@ AppProto AppLayerProtoDetectGetProtoByName(const char *alproto_name); const char *AppLayerProtoDetectGetProtoName(AppProto alproto); void AppLayerProtoDetectSupportedAppProtocols(AppProto *alprotos); +void AppLayerRegisterExpectationProto(uint8_t proto, AppProto alproto); + /***** Unittests *****/ #ifdef UNITTESTS diff --git a/src/app-layer-expectation.c b/src/app-layer-expectation.c new file mode 100644 index 0000000000..f6d91a3bd3 --- /dev/null +++ b/src/app-layer-expectation.c @@ -0,0 +1,331 @@ +/* Copyright (C) 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. + */ + +/** + * \defgroup applayerexpectation Application Layer Expectation + * + * Handling of dynamic parallel connection for application layer similar + * to FTP. + * + * @{ + * + * Some protocols like FTP create dynamic parallel flow (called expectation). In + * order to assign a application layer protocol to these expectation, Suricata + * needs to parse message of the initial protocol and create and maintain a list + * of expected flow. + * + * Application layers must use the here described API to implement this mechanism. + * + * When parsing a application layer message describing a parallel flow, the + * application layer can call AppLayerExpectationCreate() to declare an + * expectation. By doing that the next flow coming with corresponding IP parameters + * will be assigned the specified application layer. The resulting Flow will + * also have a Flow storage set that can be retrieved at index + * AppLayerExpectationGetDataId(): + * + * ``` + * data = (char *)FlowGetStorageById(f, AppLayerExpectationGetDataId()); + * ``` + * This storage can be used to store information that are only available in the + * parent connection and could be useful in the parent connection. For instance + * this is used by the FTP protocol to propagate information such as file name + * and ftp operation to the FTP data connection. + */ + +/** + * \file + * + * \author Eric Leblond + */ + +#include "suricata-common.h" +#include "debug.h" + +#include "ippair-storage.h" +#include "flow-storage.h" + +#include "app-layer-expectation.h" + +#include "util-print.h" + +static int g_expectation_id = -1; +static int g_expectation_data_id = -1; + +SC_ATOMIC_DECLARE(uint32_t, expectation_count); + +#define EXPECTATION_TIMEOUT 30 + +typedef struct Expectation_ { + struct timeval ts; + Port sp; + Port dp; + AppProto alproto; + int direction; + void *data; + struct Expectation_ *next; +} Expectation; + +typedef struct ExpectationData_ { + /** Start of Expectation Data structure must be a pointer + * to free function. Set to NULL to use SCFree() */ + void (*DFree)(void *); +} ExpectationData; + +static void ExpectationDataFree(void *e) +{ + SCLogDebug("Free expectation data"); + ExpectationData *ed = (ExpectationData *) e; + if (ed->DFree) { + ed->DFree(e); + } else { + SCFree(e); + } +} + +static void ExpectationListFree(void *e) +{ + Expectation *exp = (Expectation *)e; + Expectation *lexp; + while (exp) { + lexp = exp->next; + if (exp->data) { + ExpectationData *expdata = (ExpectationData *) exp->data; + if (expdata->DFree) { + expdata->DFree(exp->data); + } else { + SCFree(exp->data); + } + } + SCFree(exp); + exp = lexp; + } +} + +uint64_t ExpectationGetCounter(void) +{ + uint64_t x = SC_ATOMIC_GET(expectation_count); + return x; +} + +void AppLayerExpectationSetup(void) +{ + g_expectation_id = IPPairStorageRegister("expectation", sizeof(void *), NULL, ExpectationListFree); + g_expectation_data_id = FlowStorageRegister("expectation", sizeof(void *), NULL, ExpectationDataFree); + SC_ATOMIC_INIT(expectation_count); +} + +static inline int GetFlowAddresses(Flow *f, Address *ip_src, Address *ip_dst) +{ + memset(ip_src, 0, sizeof(*ip_src)); + memset(ip_dst, 0, sizeof(*ip_dst)); + if (FLOW_IS_IPV4(f)) { + FLOW_COPY_IPV4_ADDR_TO_PACKET(&f->src, ip_src); + FLOW_COPY_IPV4_ADDR_TO_PACKET(&f->dst, ip_dst); + } else if (FLOW_IS_IPV6(f)) { + FLOW_COPY_IPV6_ADDR_TO_PACKET(&f->src, ip_src); + FLOW_COPY_IPV6_ADDR_TO_PACKET(&f->dst, ip_dst); + } else { + return -1; + } + return 0; +} + +static Expectation *AppLayerExpectationLookup(Flow *f, int direction, IPPair **ipp) +{ + Address ip_src, ip_dst; + if (GetFlowAddresses(f, &ip_src, &ip_dst) == -1) + return NULL; + *ipp = IPPairLookupIPPairFromHash(&ip_src, &ip_dst); + if (*ipp == NULL) { + return NULL; + } + + return IPPairGetStorageById(*ipp, g_expectation_id); +} + +/** + * Create an entry in expectation list + * + * Create a expectation from an existing Flow. Currently, only Flow between + * the two original IP addresses are supported. + * + * \param f a pointer to the original Flow + * \param direction the direction of the data in the expectation flow + * \param src source port of the expected flow, use 0 for any + * \param dst destination port of the expected flow, use 0 for any + * \param alproto the protocol that need to be set on the expected flow + * \param data pointer to data that will be attached to the expected flow + * + * \return -1 if error + * \return 0 if success + */ +int AppLayerExpectationCreate(Flow *f, int direction, Port src, Port dst, + AppProto alproto, void *data) +{ + Expectation *iexp = NULL; + IPPair *ipp; + Address ip_src, ip_dst; + + Expectation *exp = SCCalloc(1, sizeof(*exp)); + if (exp == NULL) + return -1; + + exp->sp = src; + exp->dp = dst; + exp->alproto = alproto; + exp->ts = f->lastts; + exp->data = data; + exp->direction = direction; + + if (GetFlowAddresses(f, &ip_src, &ip_dst) == -1) + goto error; + ipp = IPPairGetIPPairFromHash(&ip_src, &ip_dst); + if (ipp == NULL) + goto error; + + iexp = IPPairGetStorageById(ipp, g_expectation_id); + exp->next = iexp; + IPPairSetStorageById(ipp, g_expectation_id, exp); + + SC_ATOMIC_ADD(expectation_count, 1); + /* As we are creating the expectation, we release lock on IPPair without + * setting the ref count to 0. This way the IPPair will be kept till + * cleanup */ + IPPairUnlock(ipp); + return 0; + +error: + SCFree(exp); + return -1; +} + +/** + * Return Flow storage identifier corresponding to expectation data + * + * \return expectation data identifier + */ +int AppLayerExpectationGetDataId(void) +{ + return g_expectation_data_id; +} + +/** + * + * Remove expectation and return next one + * + * \param ipp an IPPair + * \param pexp pointer to previous Expectation + * \param exp pointer to Expectation to remove + * \param lexp pointer to head of Expectation ist + * \return expectation + */ +static Expectation * RemoveExpectationAndGetNext(IPPair *ipp, + Expectation *pexp, Expectation *exp, + Expectation *lexp) +{ + /* we remove the object so we get ref count down by 1 to remove reference + * hold by the expectation + */ + (void) IPPairDecrUsecnt(ipp); + SC_ATOMIC_SUB(expectation_count, 1); + if (pexp == NULL) { + IPPairSetStorageById(ipp, g_expectation_id, lexp); + } else { + pexp->next = lexp; + } + if (exp->data) { + ExpectationData *expdata = (ExpectationData *)exp->data; + if (expdata->DFree) { + expdata->DFree(exp->data); + } else { + SCFree(exp->data); + } + } + SCFree(exp); + return lexp; +} + +/** + * Function doing a lookup in expectation list and updating Flow if needed. + * + * This function lookup for a existing expectation that could match the Flow. + * If found and if the expectation contains data it store the data in the + * expectation storage of the Flow. + * + * \return an AppProto value if found + * \return ALPROTO_UNKNOWN if not found + */ +AppProto AppLayerExpectationHandle(Flow *f, int direction) +{ + AppProto alproto = ALPROTO_UNKNOWN; + IPPair *ipp = NULL; + Expectation *lexp = NULL; + Expectation *pexp = NULL; + + int x = SC_ATOMIC_GET(expectation_count); + if (x == 0) { + return ALPROTO_UNKNOWN; + } + + /* Call will take reference of the ip pair in 'ipp' */ + Expectation *exp = AppLayerExpectationLookup(f, direction, &ipp); + if (exp == NULL) + goto out; + + time_t ctime = f->lastts.tv_sec; + + pexp = NULL; + while (exp) { + lexp = exp->next; + if ( (exp->direction & direction) && + ((exp->sp == 0) || (exp->sp == f->sp)) && + ((exp->dp == 0) || (exp->dp == f->dp))) { + alproto = exp->alproto; + f->alproto_ts = alproto; + f->alproto_tc = alproto; + void *fdata = FlowGetStorageById(f, g_expectation_id); + if (fdata) { + /* We already have an expectation so let's clean this one */ + ExpectationDataFree(exp->data); + } else { + /* Transfer ownership of Expectation data to the Flow */ + if (FlowSetStorageById(f, g_expectation_data_id, exp->data) != 0) { + SCLogDebug("Unable to set flow storage"); + } + } + exp->data = NULL; + exp = RemoveExpectationAndGetNext(ipp, pexp, exp, lexp); + continue; + } + /* Cleaning remove old entries */ + if (exp && (ctime > exp->ts.tv_sec + EXPECTATION_TIMEOUT)) { + exp = RemoveExpectationAndGetNext(ipp, pexp, exp, lexp); + continue; + } + pexp = exp; + exp = lexp; + } + +out: + if (ipp) + IPPairRelease(ipp); + return alproto; +} + +/** + * @} + */ diff --git a/src/app-layer-expectation.h b/src/app-layer-expectation.h new file mode 100644 index 0000000000..70f743a03f --- /dev/null +++ b/src/app-layer-expectation.h @@ -0,0 +1,35 @@ +/* Copyright (C) 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. + */ + +/** + * \file + * + * \author Eric Leblond + */ + +#ifndef __APP_LAYER_EXPECTATION__H__ +#define __APP_LAYER_EXPECTATION__H__ + +void AppLayerExpectationSetup(void); +int AppLayerExpectationCreate(Flow *f, int direction, Port src, Port dst, + AppProto alproto, void *data); +AppProto AppLayerExpectationHandle(Flow *f, int direction); +int AppLayerExpectationGetDataId(void); + +uint64_t ExpectationGetCounter(void); + +#endif /* __APP_LAYER_EXPECTATION__H__ */ diff --git a/src/app-layer.c b/src/app-layer.c index 7b22a38461..66cd13a12d 100644 --- a/src/app-layer.c +++ b/src/app-layer.c @@ -29,6 +29,7 @@ #include "app-layer.h" #include "app-layer-parser.h" #include "app-layer-protos.h" +#include "app-layer-expectation.h" #include "app-layer-detect-proto.h" #include "stream-tcp-reassemble.h" #include "stream-tcp-private.h" @@ -37,6 +38,7 @@ #include "flow.h" #include "flow-util.h" #include "flow-private.h" +#include "ippair.h" #include "util-debug.h" #include "util-print.h" @@ -415,6 +417,7 @@ static int TCPProtoDetect(ThreadVars *tv, StreamTcpResetStreamFlagAppProtoDetectionCompleted(stream); FLOW_RESET_PP_DONE(f, flags); FLOW_RESET_PM_DONE(f, flags); + FLOW_RESET_PE_DONE(f, flags); goto failure; } } @@ -830,6 +833,7 @@ void AppLayerRegisterGlobalCounters(void) StatsRegisterGlobalCounter("dns.memcap_global", DNSMemcapGetMemcapGlobalCounter); StatsRegisterGlobalCounter("http.memuse", HTPMemuseGlobalCounter); StatsRegisterGlobalCounter("http.memcap", HTPMemcapGlobalCounter); + StatsRegisterGlobalCounter("app_layer.expectations", ExpectationGetCounter); } #define IPPROTOS_MAX 2 @@ -948,6 +952,7 @@ void AppLayerDeSetupCounters() p->tcph = &tcph;\ \ StreamTcpInitConfig(TRUE);\ + IPPairInitConfig(TRUE); \ StreamTcpThreadInit(&tv, NULL, (void **)&stt);\ \ /* handshake */\ diff --git a/src/flow.h b/src/flow.h index f6d080a23e..4c8e0bfb14 100644 --- a/src/flow.h +++ b/src/flow.h @@ -79,22 +79,26 @@ typedef struct AppLayerParserState_ AppLayerParserState; #define FLOW_TS_PM_ALPROTO_DETECT_DONE BIT_U32(13) /** Probing parser alproto detection done */ #define FLOW_TS_PP_ALPROTO_DETECT_DONE BIT_U32(14) +/** Expectation alproto detection done */ +#define FLOW_TS_PE_ALPROTO_DETECT_DONE BIT_U32(15) /** Pattern matcher alproto detection done */ -#define FLOW_TC_PM_ALPROTO_DETECT_DONE BIT_U32(15) +#define FLOW_TC_PM_ALPROTO_DETECT_DONE BIT_U32(16) /** Probing parser alproto detection done */ -#define FLOW_TC_PP_ALPROTO_DETECT_DONE BIT_U32(16) -#define FLOW_TIMEOUT_REASSEMBLY_DONE BIT_U32(17) +#define FLOW_TC_PP_ALPROTO_DETECT_DONE BIT_U32(17) +/** Expectation alproto detection done */ +#define FLOW_TC_PE_ALPROTO_DETECT_DONE BIT_U32(18) +#define FLOW_TIMEOUT_REASSEMBLY_DONE BIT_U32(19) /** flow is ipv4 */ -#define FLOW_IPV4 BIT_U32(18) +#define FLOW_IPV4 BIT_U32(20) /** flow is ipv6 */ -#define FLOW_IPV6 BIT_U32(19) +#define FLOW_IPV6 BIT_U32(21) -#define FLOW_PROTO_DETECT_TS_DONE BIT_U32(20) -#define FLOW_PROTO_DETECT_TC_DONE BIT_U32(21) +#define FLOW_PROTO_DETECT_TS_DONE BIT_U32(22) +#define FLOW_PROTO_DETECT_TC_DONE BIT_U32(23) /** Indicate that alproto detection for flow should be done again */ -#define FLOW_CHANGE_PROTO BIT_U32(22) +#define FLOW_CHANGE_PROTO BIT_U32(24) /* File flags */ @@ -233,12 +237,15 @@ typedef struct AppLayerParserState_ AppLayerParserState; #define FLOW_IS_PM_DONE(f, dir) (((dir) & STREAM_TOSERVER) ? ((f)->flags & FLOW_TS_PM_ALPROTO_DETECT_DONE) : ((f)->flags & FLOW_TC_PM_ALPROTO_DETECT_DONE)) #define FLOW_IS_PP_DONE(f, dir) (((dir) & STREAM_TOSERVER) ? ((f)->flags & FLOW_TS_PP_ALPROTO_DETECT_DONE) : ((f)->flags & FLOW_TC_PP_ALPROTO_DETECT_DONE)) +#define FLOW_IS_PE_DONE(f, dir) (((dir) & STREAM_TOSERVER) ? ((f)->flags & FLOW_TS_PE_ALPROTO_DETECT_DONE) : ((f)->flags & FLOW_TC_PE_ALPROTO_DETECT_DONE)) #define FLOW_SET_PM_DONE(f, dir) (((dir) & STREAM_TOSERVER) ? ((f)->flags |= FLOW_TS_PM_ALPROTO_DETECT_DONE) : ((f)->flags |= FLOW_TC_PM_ALPROTO_DETECT_DONE)) #define FLOW_SET_PP_DONE(f, dir) (((dir) & STREAM_TOSERVER) ? ((f)->flags |= FLOW_TS_PP_ALPROTO_DETECT_DONE) : ((f)->flags |= FLOW_TC_PP_ALPROTO_DETECT_DONE)) +#define FLOW_SET_PE_DONE(f, dir) (((dir) & STREAM_TOSERVER) ? ((f)->flags |= FLOW_TS_PE_ALPROTO_DETECT_DONE) : ((f)->flags |= FLOW_TC_PE_ALPROTO_DETECT_DONE)) #define FLOW_RESET_PM_DONE(f, dir) (((dir) & STREAM_TOSERVER) ? ((f)->flags &= ~FLOW_TS_PM_ALPROTO_DETECT_DONE) : ((f)->flags &= ~FLOW_TC_PM_ALPROTO_DETECT_DONE)) #define FLOW_RESET_PP_DONE(f, dir) (((dir) & STREAM_TOSERVER) ? ((f)->flags &= ~FLOW_TS_PP_ALPROTO_DETECT_DONE) : ((f)->flags &= ~FLOW_TC_PP_ALPROTO_DETECT_DONE)) +#define FLOW_RESET_PE_DONE(f, dir) (((dir) & STREAM_TOSERVER) ? ((f)->flags &= ~FLOW_TS_PE_ALPROTO_DETECT_DONE) : ((f)->flags &= ~FLOW_TC_PE_ALPROTO_DETECT_DONE)) /* global flow config */ typedef struct FlowCnf_ diff --git a/src/stream-tcp-util.c b/src/stream-tcp-util.c index b10b94ddc2..26e5ead5c7 100644 --- a/src/stream-tcp-util.c +++ b/src/stream-tcp-util.c @@ -35,6 +35,7 @@ #include "util-unittest.h" #include "util-unittest-helper.h" +#include "ippair.h" #ifdef UNITTESTS @@ -43,6 +44,7 @@ void StreamTcpUTInit(TcpReassemblyThreadCtx **ra_ctx) { StreamTcpInitConfig(TRUE); + IPPairInitConfig(TRUE); *ra_ctx = StreamTcpReassembleInitThreadCtx(NULL); }