]> git.ipfire.org Git - thirdparty/suricata.git/commitdiff
app-layer-expectation: expectation system
authorEric Leblond <eric@regit.org>
Tue, 12 Sep 2017 13:11:01 +0000 (14:11 +0100)
committerVictor Julien <victor@inliniac.net>
Tue, 19 Dec 2017 20:00:15 +0000 (21:00 +0100)
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
src/app-layer-detect-proto.c
src/app-layer-detect-proto.h
src/app-layer-expectation.c [new file with mode: 0644]
src/app-layer-expectation.h [new file with mode: 0644]
src/app-layer.c
src/flow.h
src/stream-tcp-util.c

index 0c0c959e70e2579eac72cc8acc6d54a77a946b63..4b89861f38265a0bf56a10e382e452b669806402 100644 (file)
@@ -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 \
index dc21dbd45eb526255db62ac85407410d8dd474cd..f6ca40bd4a7deeea804f7d68c86c8261a52f4c61 100644 (file)
@@ -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
index 9533bbc02a4911a8bdcd4ac87b76e61a112d475c..89e430f8ce037badeec7b00baa8573d3070e995b 100644 (file)
@@ -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 (file)
index 0000000..f6d91a3
--- /dev/null
@@ -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 <eric@regit.org>
+ */
+
+#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 (file)
index 0000000..70f743a
--- /dev/null
@@ -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 <eric@regit.org>
+ */
+
+#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__ */
index 7b22a3846134ddd43cfd5a09487007acde8b3ace..66cd13a12d384211496cdf86480b12c57147aef8 100644 (file)
@@ -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 */\
index f6d080a23e2725efc827c74063337a5af1fca9f5..4c8e0bfb14e63c9a90dc9d962f0074f148c56c9f 100644 (file)
@@ -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_
index b10b94ddc20a4d62ebf97aef36a399e9f2634db3..26e5ead5c718199b5bfac21061bffdb3c8a7e4fb 100644 (file)
@@ -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);
 }