summaryrefslogtreecommitdiffstats
path: root/src/detect-engine-mpm.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/detect-engine-mpm.c')
-rw-r--r--src/detect-engine-mpm.c2609
1 files changed, 2609 insertions, 0 deletions
diff --git a/src/detect-engine-mpm.c b/src/detect-engine-mpm.c
new file mode 100644
index 0000000..f091a3d
--- /dev/null
+++ b/src/detect-engine-mpm.c
@@ -0,0 +1,2609 @@
+/* Copyright (C) 2007-2021 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 Victor Julien <victor@inliniac.net>
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ *
+ * Multi pattern matcher
+ */
+
+#include "suricata.h"
+#include "suricata-common.h"
+
+#include "app-layer-protos.h"
+
+#include "decode.h"
+#include "detect.h"
+#include "detect-engine.h"
+#include "detect-engine-siggroup.h"
+#include "detect-engine-mpm.h"
+#include "detect-engine-iponly.h"
+#include "detect-parse.h"
+#include "detect-engine-prefilter.h"
+#include "util-mpm.h"
+#include "util-memcmp.h"
+#include "util-memcpy.h"
+#include "conf.h"
+#include "detect-fast-pattern.h"
+
+#include "detect-tcphdr.h"
+#include "detect-udphdr.h"
+
+#include "flow.h"
+#include "flow-var.h"
+#include "detect-flow.h"
+
+#include "detect-content.h"
+
+#include "detect-engine-payload.h"
+
+#include "stream.h"
+
+#include "util-misc.h"
+#include "util-enum.h"
+#include "util-debug.h"
+#include "util-print.h"
+#include "util-validate.h"
+#include "util-hash-string.h"
+
+const char *builtin_mpms[] = {
+ "toserver TCP packet",
+ "toclient TCP packet",
+ "toserver TCP stream",
+ "toclient TCP stream",
+ "toserver UDP packet",
+ "toclient UDP packet",
+ "other IP packet",
+
+ NULL };
+
+/* Registry for mpm keywords
+ *
+ * Keywords are registered at engine start up
+ */
+
+static DetectBufferMpmRegistry *g_mpm_list[DETECT_BUFFER_MPM_TYPE_SIZE] = { NULL, NULL, NULL };
+static int g_mpm_list_cnt[DETECT_BUFFER_MPM_TYPE_SIZE] = { 0, 0, 0 };
+
+/** \brief register a MPM engine
+ *
+ * \note to be used at start up / registration only. Errors are fatal.
+ */
+void DetectAppLayerMpmRegister2(const char *name, int direction, int priority,
+ PrefilterRegisterFunc PrefilterRegister, InspectionBufferGetDataPtr GetData,
+ AppProto alproto, int tx_min_progress)
+{
+ SCLogDebug("registering %s/%d/%d/%p/%p/%u/%d", name, direction, priority,
+ PrefilterRegister, GetData, alproto, tx_min_progress);
+
+ BUG_ON(tx_min_progress >= 48);
+
+ if (PrefilterRegister == PrefilterGenericMpmRegister && GetData == NULL) {
+ // must register GetData with PrefilterGenericMpmRegister
+ abort();
+ }
+
+ DetectBufferTypeSupportsMpm(name);
+ DetectBufferTypeSupportsTransformations(name);
+ int sm_list = DetectBufferTypeGetByName(name);
+ if (sm_list == -1) {
+ FatalError("MPM engine registration for %s failed", name);
+ }
+
+ DetectBufferMpmRegistry *am = SCCalloc(1, sizeof(*am));
+ BUG_ON(am == NULL);
+ am->name = name;
+ snprintf(am->pname, sizeof(am->pname), "%s", am->name);
+ am->direction = direction;
+ DEBUG_VALIDATE_BUG_ON(sm_list < 0 || sm_list > INT16_MAX);
+ am->sm_list = (int16_t)sm_list;
+ am->sm_list_base = (int16_t)sm_list;
+ am->priority = priority;
+ am->type = DETECT_BUFFER_MPM_TYPE_APP;
+
+ am->PrefilterRegisterWithListId = PrefilterRegister;
+ am->app_v2.GetData = GetData;
+ am->app_v2.alproto = alproto;
+ am->app_v2.tx_min_progress = tx_min_progress;
+
+ if (g_mpm_list[DETECT_BUFFER_MPM_TYPE_APP] == NULL) {
+ g_mpm_list[DETECT_BUFFER_MPM_TYPE_APP] = am;
+ } else {
+ DetectBufferMpmRegistry *t = g_mpm_list[DETECT_BUFFER_MPM_TYPE_APP];
+ while (t->next != NULL) {
+ t = t->next;
+ }
+
+ t->next = am;
+ am->id = t->id + 1;
+ }
+ g_mpm_list_cnt[DETECT_BUFFER_MPM_TYPE_APP]++;
+
+ SupportFastPatternForSigMatchList(sm_list, priority);
+}
+
+/** \brief copy a mpm engine from parent_id, add in transforms */
+void DetectAppLayerMpmRegisterByParentId(DetectEngineCtx *de_ctx,
+ const int id, const int parent_id,
+ DetectEngineTransforms *transforms)
+{
+ SCLogDebug("registering %d/%d", id, parent_id);
+
+ DetectBufferMpmRegistry *t = de_ctx->app_mpms_list;
+ while (t) {
+ if (t->sm_list == parent_id) {
+ DetectBufferMpmRegistry *am = SCCalloc(1, sizeof(*am));
+ BUG_ON(am == NULL);
+ am->name = t->name;
+ am->direction = t->direction;
+ DEBUG_VALIDATE_BUG_ON(id < 0 || id > INT16_MAX);
+ am->sm_list = (uint16_t)id; // use new id
+ am->sm_list_base = t->sm_list;
+ am->type = DETECT_BUFFER_MPM_TYPE_APP;
+ am->PrefilterRegisterWithListId = t->PrefilterRegisterWithListId;
+ am->app_v2.GetData = t->app_v2.GetData;
+ am->app_v2.alproto = t->app_v2.alproto;
+ am->app_v2.tx_min_progress = t->app_v2.tx_min_progress;
+ am->priority = t->priority;
+ am->sgh_mpm_context = t->sgh_mpm_context;
+ am->sgh_mpm_context = MpmFactoryRegisterMpmCtxProfile(
+ de_ctx, am->name, am->sm_list, am->app_v2.alproto);
+ am->next = t->next;
+ if (transforms) {
+ memcpy(&am->transforms, transforms, sizeof(*transforms));
+
+ /* create comma separated string of the names of the
+ * transforms and then shorten it if necessary. Finally
+ * use it to construct the 'profile' name for the engine */
+ char xforms[1024] = "";
+ for (int i = 0; i < transforms->cnt; i++) {
+ char ttstr[64];
+ (void)snprintf(ttstr,sizeof(ttstr), "%s,",
+ sigmatch_table[transforms->transforms[i].transform].name);
+ strlcat(xforms, ttstr, sizeof(xforms));
+ }
+ xforms[strlen(xforms)-1] = '\0';
+
+ size_t space = sizeof(am->pname) - strlen(am->name) - 3;
+ char toprint[space + 1];
+ memset(toprint, 0x00, space + 1);
+ if (space < strlen(xforms)) {
+ ShortenString(xforms, toprint, space, '~');
+ } else {
+ strlcpy(toprint, xforms,sizeof(toprint));
+ }
+ (void)snprintf(am->pname, sizeof(am->pname), "%s#%d (%s)",
+ am->name, id, toprint);
+ } else {
+ (void)snprintf(am->pname, sizeof(am->pname), "%s#%d",
+ am->name, id);
+ }
+ am->id = de_ctx->app_mpms_list_cnt++;
+
+ DetectEngineRegisterFastPatternForId(de_ctx, am->sm_list, am->priority);
+ t->next = am;
+ SCLogDebug("copied mpm registration for %s id %u "
+ "with parent %u and GetData %p",
+ t->name, id, parent_id, am->app_v2.GetData);
+ t = am;
+ }
+ t = t->next;
+ }
+}
+
+void DetectMpmInitializeAppMpms(DetectEngineCtx *de_ctx)
+{
+ const DetectBufferMpmRegistry *list = g_mpm_list[DETECT_BUFFER_MPM_TYPE_APP];
+ DetectBufferMpmRegistry *toadd = de_ctx->app_mpms_list;
+ while (list != NULL) {
+ DetectBufferMpmRegistry *n = SCCalloc(1, sizeof(*n));
+ BUG_ON(n == NULL);
+
+ *n = *list;
+ n->next = NULL;
+
+ if (toadd == NULL) {
+ toadd = n;
+ de_ctx->app_mpms_list = n;
+ } else {
+ toadd->next = n;
+ toadd = toadd->next;
+ }
+
+ /* default to whatever the global setting is */
+ int shared = (de_ctx->sgh_mpm_ctx_cnf == ENGINE_SGH_MPM_FACTORY_CONTEXT_SINGLE);
+
+ /* see if we use a unique or shared mpm ctx for this type */
+ int confshared = 0;
+ char confstring[256] = "detect.mpm.";
+ strlcat(confstring, n->name, sizeof(confstring));
+ strlcat(confstring, ".shared", sizeof(confstring));
+ if (ConfGetBool(confstring, &confshared) == 1)
+ shared = confshared;
+
+ if (shared == 0) {
+ SCLogDebug("using unique mpm ctx' for %s", n->name);
+ n->sgh_mpm_context = MPM_CTX_FACTORY_UNIQUE_CONTEXT;
+ } else {
+ SCLogDebug("using shared mpm ctx' for %s", n->name);
+ n->sgh_mpm_context =
+ MpmFactoryRegisterMpmCtxProfile(de_ctx, n->name, n->sm_list, n->app_v2.alproto);
+ }
+
+ list = list->next;
+ }
+ de_ctx->app_mpms_list_cnt = g_mpm_list_cnt[DETECT_BUFFER_MPM_TYPE_APP];
+ SCLogDebug("mpm: de_ctx app_mpms_list %p %u",
+ de_ctx->app_mpms_list, de_ctx->app_mpms_list_cnt);
+}
+
+/**
+ * \brief initialize mpm contexts for applayer buffers that are in
+ * "single or "shared" mode.
+ */
+int DetectMpmPrepareAppMpms(DetectEngineCtx *de_ctx)
+{
+ int r = 0;
+ const DetectBufferMpmRegistry *am = de_ctx->app_mpms_list;
+ while (am != NULL) {
+ int dir = (am->direction == SIG_FLAG_TOSERVER) ? 1 : 0;
+
+ if (am->sgh_mpm_context != MPM_CTX_FACTORY_UNIQUE_CONTEXT)
+ {
+ MpmCtx *mpm_ctx = MpmFactoryGetMpmCtxForProfile(de_ctx, am->sgh_mpm_context, dir);
+ if (mpm_ctx != NULL) {
+ if (mpm_table[de_ctx->mpm_matcher].Prepare != NULL) {
+ r |= mpm_table[de_ctx->mpm_matcher].Prepare(mpm_ctx);
+ }
+ }
+ }
+ am = am->next;
+ }
+ return r;
+}
+
+/** \brief register a MPM engine
+ *
+ * \note to be used at start up / registration only. Errors are fatal.
+ */
+void DetectFrameMpmRegister(const char *name, int direction, int priority,
+ int (*PrefilterRegister)(DetectEngineCtx *de_ctx, SigGroupHead *sgh, MpmCtx *mpm_ctx,
+ const DetectBufferMpmRegistry *mpm_reg, int list_id),
+ AppProto alproto, uint8_t type)
+{
+ SCLogDebug("registering %s/%d/%p/%s/%u", name, priority, PrefilterRegister,
+ AppProtoToString(alproto), type);
+
+ DetectBufferTypeSupportsMpm(name);
+ DetectBufferTypeSupportsFrames(name);
+ DetectBufferTypeSupportsTransformations(name);
+ int sm_list = DetectBufferTypeGetByName(name);
+ if (sm_list < 0 || sm_list > UINT16_MAX) {
+ FatalError("MPM engine registration for %s failed", name);
+ }
+
+ DetectBufferMpmRegistry *am = SCCalloc(1, sizeof(*am));
+ BUG_ON(am == NULL);
+ am->name = name;
+ snprintf(am->pname, sizeof(am->pname), "%s", am->name);
+ am->sm_list = (uint16_t)sm_list;
+ am->direction = direction;
+ am->priority = priority;
+ am->type = DETECT_BUFFER_MPM_TYPE_FRAME;
+
+ am->PrefilterRegisterWithListId = PrefilterRegister;
+ am->frame_v1.alproto = alproto;
+ am->frame_v1.type = type;
+ SCLogDebug("type %u", type);
+ SCLogDebug("am type %u", am->frame_v1.type);
+
+ if (g_mpm_list[DETECT_BUFFER_MPM_TYPE_FRAME] == NULL) {
+ g_mpm_list[DETECT_BUFFER_MPM_TYPE_FRAME] = am;
+ } else {
+ DetectBufferMpmRegistry *t = g_mpm_list[DETECT_BUFFER_MPM_TYPE_FRAME];
+ while (t->next != NULL) {
+ t = t->next;
+ }
+ t->next = am;
+ am->id = t->id + 1;
+ }
+ g_mpm_list_cnt[DETECT_BUFFER_MPM_TYPE_FRAME]++;
+
+ SupportFastPatternForSigMatchList(sm_list, priority);
+ SCLogDebug("%s/%d done", name, sm_list);
+}
+
+/** \brief copy a mpm engine from parent_id, add in transforms */
+void DetectFrameMpmRegisterByParentId(DetectEngineCtx *de_ctx, const int id, const int parent_id,
+ DetectEngineTransforms *transforms)
+{
+ SCLogDebug("registering %d/%d", id, parent_id);
+
+ DetectBufferMpmRegistry *t = de_ctx->frame_mpms_list;
+ while (t) {
+ if (t->sm_list == parent_id) {
+ DetectBufferMpmRegistry *am = SCCalloc(1, sizeof(*am));
+ BUG_ON(am == NULL);
+ am->name = t->name;
+ snprintf(am->pname, sizeof(am->pname), "%s#%d", am->name, id);
+ DEBUG_VALIDATE_BUG_ON(id < 0 || id > UINT16_MAX);
+ am->sm_list = (uint16_t)id; // use new id
+ am->sm_list_base = t->sm_list;
+ am->type = DETECT_BUFFER_MPM_TYPE_FRAME;
+ am->PrefilterRegisterWithListId = t->PrefilterRegisterWithListId;
+ am->frame_v1 = t->frame_v1;
+ SCLogDebug("am type %u", am->frame_v1.type);
+ am->priority = t->priority;
+ am->direction = t->direction;
+ am->sgh_mpm_context = t->sgh_mpm_context;
+ am->next = t->next;
+ if (transforms) {
+ memcpy(&am->transforms, transforms, sizeof(*transforms));
+ }
+ am->id = de_ctx->frame_mpms_list_cnt++;
+
+ DetectEngineRegisterFastPatternForId(de_ctx, am->sm_list, am->priority);
+ t->next = am;
+ SCLogDebug("copied mpm registration for %s id %u "
+ "with parent %u",
+ t->name, id, parent_id);
+ t = am;
+ }
+ t = t->next;
+ }
+}
+
+void DetectEngineFrameMpmRegister(DetectEngineCtx *de_ctx, const char *name, int direction,
+ int priority,
+ int (*PrefilterRegister)(DetectEngineCtx *de_ctx, SigGroupHead *sgh, MpmCtx *mpm_ctx,
+ const DetectBufferMpmRegistry *mpm_reg, int list_id),
+ AppProto alproto, uint8_t type)
+{
+ SCLogDebug("registering %s/%d/%p/%s/%u", name, priority, PrefilterRegister,
+ AppProtoToString(alproto), type);
+
+ const int sm_list = DetectEngineBufferTypeRegister(de_ctx, name);
+ if (sm_list < 0 || sm_list > UINT16_MAX) {
+ FatalError("MPM engine registration for %s failed", name);
+ }
+
+ DetectEngineBufferTypeSupportsMpm(de_ctx, name);
+ DetectEngineBufferTypeSupportsFrames(de_ctx, name);
+ DetectEngineBufferTypeSupportsTransformations(de_ctx, name);
+
+ DetectBufferMpmRegistry *am = SCCalloc(1, sizeof(*am));
+ BUG_ON(am == NULL);
+ am->name = name;
+ snprintf(am->pname, sizeof(am->pname), "%s", am->name);
+ am->sm_list = (uint16_t)sm_list;
+ am->direction = direction;
+ am->priority = priority;
+ am->type = DETECT_BUFFER_MPM_TYPE_FRAME;
+
+ am->PrefilterRegisterWithListId = PrefilterRegister;
+ am->frame_v1.alproto = alproto;
+ am->frame_v1.type = type;
+
+ // TODO is it ok to do this here?
+
+ /* default to whatever the global setting is */
+ int shared = (de_ctx->sgh_mpm_ctx_cnf == ENGINE_SGH_MPM_FACTORY_CONTEXT_SINGLE);
+ /* see if we use a unique or shared mpm ctx for this type */
+ int confshared = 0;
+ if (ConfGetBool("detect.mpm.frame.shared", &confshared) == 1)
+ shared = confshared;
+
+ if (shared == 0) {
+ am->sgh_mpm_context = MPM_CTX_FACTORY_UNIQUE_CONTEXT;
+ } else {
+ am->sgh_mpm_context =
+ MpmFactoryRegisterMpmCtxProfile(de_ctx, am->name, am->sm_list, alproto);
+ }
+
+ if (de_ctx->frame_mpms_list == NULL) {
+ de_ctx->frame_mpms_list = am;
+ } else {
+ DetectBufferMpmRegistry *t = de_ctx->frame_mpms_list;
+ while (t->next != NULL) {
+ t = t->next;
+ }
+
+ t->next = am;
+ }
+ de_ctx->frame_mpms_list_cnt++;
+
+ DetectEngineRegisterFastPatternForId(de_ctx, sm_list, priority);
+ SCLogDebug("%s/%d done", name, sm_list);
+}
+
+void DetectMpmInitializeFrameMpms(DetectEngineCtx *de_ctx)
+{
+ const DetectBufferMpmRegistry *list = g_mpm_list[DETECT_BUFFER_MPM_TYPE_FRAME];
+ while (list != NULL) {
+ DetectBufferMpmRegistry *n = SCCalloc(1, sizeof(*n));
+ BUG_ON(n == NULL);
+
+ *n = *list;
+ n->next = NULL;
+
+ if (de_ctx->frame_mpms_list == NULL) {
+ de_ctx->frame_mpms_list = n;
+ } else {
+ DetectBufferMpmRegistry *t = de_ctx->frame_mpms_list;
+ while (t->next != NULL) {
+ t = t->next;
+ }
+
+ t->next = n;
+ }
+
+ /* default to whatever the global setting is */
+ int shared = (de_ctx->sgh_mpm_ctx_cnf == ENGINE_SGH_MPM_FACTORY_CONTEXT_SINGLE);
+
+ /* see if we use a unique or shared mpm ctx for this type */
+ int confshared = 0;
+ char confstring[256] = "detect.mpm.";
+ strlcat(confstring, n->name, sizeof(confstring));
+ strlcat(confstring, ".shared", sizeof(confstring));
+ if (ConfGetBool(confstring, &confshared) == 1)
+ shared = confshared;
+
+ if (shared == 0) {
+ SCLogDebug("using unique mpm ctx' for %s", n->name);
+ n->sgh_mpm_context = MPM_CTX_FACTORY_UNIQUE_CONTEXT;
+ } else {
+ SCLogDebug("using shared mpm ctx' for %s", n->name);
+ n->sgh_mpm_context = MpmFactoryRegisterMpmCtxProfile(
+ de_ctx, n->name, n->sm_list, n->frame_v1.alproto);
+ }
+
+ list = list->next;
+ }
+ de_ctx->frame_mpms_list_cnt = g_mpm_list_cnt[DETECT_BUFFER_MPM_TYPE_FRAME];
+ SCLogDebug("mpm: de_ctx frame_mpms_list %p %u", de_ctx->frame_mpms_list,
+ de_ctx->frame_mpms_list_cnt);
+}
+
+/**
+ * \brief initialize mpm contexts for applayer buffers that are in
+ * "single or "shared" mode.
+ */
+int DetectMpmPrepareFrameMpms(DetectEngineCtx *de_ctx)
+{
+ SCLogDebug("preparing frame mpm");
+ int r = 0;
+ const DetectBufferMpmRegistry *am = de_ctx->frame_mpms_list;
+ while (am != NULL) {
+ SCLogDebug("am %p %s sgh_mpm_context %d", am, am->name, am->sgh_mpm_context);
+ SCLogDebug("%s", am->name);
+ if (am->sgh_mpm_context != MPM_CTX_FACTORY_UNIQUE_CONTEXT) {
+ int dir = (am->direction == SIG_FLAG_TOSERVER) ? 1 : 0;
+ MpmCtx *mpm_ctx = MpmFactoryGetMpmCtxForProfile(de_ctx, am->sgh_mpm_context, dir);
+ SCLogDebug("%s: %d mpm_Ctx %p", am->name, r, mpm_ctx);
+ if (mpm_ctx != NULL) {
+ if (mpm_table[de_ctx->mpm_matcher].Prepare != NULL) {
+ r |= mpm_table[de_ctx->mpm_matcher].Prepare(mpm_ctx);
+ SCLogDebug("%s: %d", am->name, r);
+ }
+ }
+ }
+ am = am->next;
+ }
+ return r;
+}
+
+/** \brief register a MPM engine
+ *
+ * \note to be used at start up / registration only. Errors are fatal.
+ */
+void DetectPktMpmRegister(const char *name, int priority,
+ int (*PrefilterRegister)(DetectEngineCtx *de_ctx, SigGroupHead *sgh, MpmCtx *mpm_ctx,
+ const DetectBufferMpmRegistry *mpm_reg, int list_id),
+ InspectionBufferGetPktDataPtr GetData)
+{
+ SCLogDebug("registering %s/%d/%p/%p", name, priority,
+ PrefilterRegister, GetData);
+
+ if (PrefilterRegister == PrefilterGenericMpmPktRegister && GetData == NULL) {
+ // must register GetData with PrefilterGenericMpmRegister
+ abort();
+ }
+
+ DetectBufferTypeSupportsMpm(name);
+ DetectBufferTypeSupportsTransformations(name);
+ int sm_list = DetectBufferTypeGetByName(name);
+ if (sm_list == -1) {
+ FatalError("MPM engine registration for %s failed", name);
+ }
+
+ DetectBufferMpmRegistry *am = SCCalloc(1, sizeof(*am));
+ BUG_ON(am == NULL);
+ am->name = name;
+ snprintf(am->pname, sizeof(am->pname), "%s", am->name);
+ DEBUG_VALIDATE_BUG_ON(sm_list < 0 || sm_list > INT16_MAX);
+ am->sm_list = (uint16_t)sm_list;
+ am->priority = priority;
+ am->type = DETECT_BUFFER_MPM_TYPE_PKT;
+
+ am->PrefilterRegisterWithListId = PrefilterRegister;
+ am->pkt_v1.GetData = GetData;
+
+ if (g_mpm_list[DETECT_BUFFER_MPM_TYPE_PKT] == NULL) {
+ g_mpm_list[DETECT_BUFFER_MPM_TYPE_PKT] = am;
+ } else {
+ DetectBufferMpmRegistry *t = g_mpm_list[DETECT_BUFFER_MPM_TYPE_PKT];
+ while (t->next != NULL) {
+ t = t->next;
+ }
+ t->next = am;
+ am->id = t->id + 1;
+ }
+ g_mpm_list_cnt[DETECT_BUFFER_MPM_TYPE_PKT]++;
+
+ SupportFastPatternForSigMatchList(sm_list, priority);
+ SCLogDebug("%s/%d done", name, sm_list);
+}
+
+/** \brief copy a mpm engine from parent_id, add in transforms */
+void DetectPktMpmRegisterByParentId(DetectEngineCtx *de_ctx,
+ const int id, const int parent_id,
+ DetectEngineTransforms *transforms)
+{
+ SCLogDebug("registering %d/%d", id, parent_id);
+
+ DetectBufferMpmRegistry *t = de_ctx->pkt_mpms_list;
+ while (t) {
+ if (t->sm_list == parent_id) {
+ DetectBufferMpmRegistry *am = SCCalloc(1, sizeof(*am));
+ BUG_ON(am == NULL);
+ am->name = t->name;
+ snprintf(am->pname, sizeof(am->pname), "%s#%d", am->name, id);
+ DEBUG_VALIDATE_BUG_ON(id < 0 || id > INT16_MAX);
+ am->sm_list = (uint16_t)id; // use new id
+ am->sm_list_base = t->sm_list;
+ am->type = DETECT_BUFFER_MPM_TYPE_PKT;
+ am->PrefilterRegisterWithListId = t->PrefilterRegisterWithListId;
+ am->pkt_v1.GetData = t->pkt_v1.GetData;
+ am->priority = t->priority;
+ am->sgh_mpm_context = t->sgh_mpm_context;
+ am->next = t->next;
+ if (transforms) {
+ memcpy(&am->transforms, transforms, sizeof(*transforms));
+ }
+ am->id = de_ctx->pkt_mpms_list_cnt++;
+
+ DetectEngineRegisterFastPatternForId(de_ctx, am->sm_list, am->priority);
+ t->next = am;
+ SCLogDebug("copied mpm registration for %s id %u "
+ "with parent %u and GetData %p",
+ t->name, id, parent_id, am->pkt_v1.GetData);
+ t = am;
+ }
+ t = t->next;
+ }
+}
+
+void DetectMpmInitializePktMpms(DetectEngineCtx *de_ctx)
+{
+ const DetectBufferMpmRegistry *list = g_mpm_list[DETECT_BUFFER_MPM_TYPE_PKT];
+ while (list != NULL) {
+ DetectBufferMpmRegistry *n = SCCalloc(1, sizeof(*n));
+ BUG_ON(n == NULL);
+
+ *n = *list;
+ n->next = NULL;
+
+ if (de_ctx->pkt_mpms_list == NULL) {
+ de_ctx->pkt_mpms_list = n;
+ } else {
+ DetectBufferMpmRegistry *t = de_ctx->pkt_mpms_list;
+ while (t->next != NULL) {
+ t = t->next;
+ }
+
+ t->next = n;
+ }
+
+ /* default to whatever the global setting is */
+ int shared = (de_ctx->sgh_mpm_ctx_cnf == ENGINE_SGH_MPM_FACTORY_CONTEXT_SINGLE);
+
+ /* see if we use a unique or shared mpm ctx for this type */
+ int confshared = 0;
+ char confstring[256] = "detect.mpm.";
+ strlcat(confstring, n->name, sizeof(confstring));
+ strlcat(confstring, ".shared", sizeof(confstring));
+ if (ConfGetBool(confstring, &confshared) == 1)
+ shared = confshared;
+
+ if (shared == 0) {
+ SCLogDebug("using unique mpm ctx' for %s", n->name);
+ n->sgh_mpm_context = MPM_CTX_FACTORY_UNIQUE_CONTEXT;
+ } else {
+ SCLogDebug("using shared mpm ctx' for %s", n->name);
+ n->sgh_mpm_context =
+ MpmFactoryRegisterMpmCtxProfile(de_ctx, n->name, n->sm_list, ALPROTO_UNKNOWN);
+ }
+
+ list = list->next;
+ }
+ de_ctx->pkt_mpms_list_cnt = g_mpm_list_cnt[DETECT_BUFFER_MPM_TYPE_PKT];
+ SCLogDebug("mpm: de_ctx pkt_mpms_list %p %u",
+ de_ctx->pkt_mpms_list, de_ctx->pkt_mpms_list_cnt);
+}
+
+/**
+ * \brief initialize mpm contexts for applayer buffers that are in
+ * "single or "shared" mode.
+ */
+int DetectMpmPreparePktMpms(DetectEngineCtx *de_ctx)
+{
+ SCLogDebug("preparing pkt mpm");
+ int r = 0;
+ const DetectBufferMpmRegistry *am = de_ctx->pkt_mpms_list;
+ while (am != NULL) {
+ SCLogDebug("%s", am->name);
+ if (am->sgh_mpm_context != MPM_CTX_FACTORY_UNIQUE_CONTEXT)
+ {
+ MpmCtx *mpm_ctx = MpmFactoryGetMpmCtxForProfile(de_ctx, am->sgh_mpm_context, 0);
+ if (mpm_ctx != NULL) {
+ if (mpm_table[de_ctx->mpm_matcher].Prepare != NULL) {
+ r |= mpm_table[de_ctx->mpm_matcher].Prepare(mpm_ctx);
+ SCLogDebug("%s: %d", am->name, r);
+ }
+ }
+ }
+ am = am->next;
+ }
+ return r;
+}
+
+static int32_t SetupBuiltinMpm(DetectEngineCtx *de_ctx, const char *name)
+{
+ /* default to whatever the global setting is */
+ int shared = (de_ctx->sgh_mpm_ctx_cnf == ENGINE_SGH_MPM_FACTORY_CONTEXT_SINGLE);
+
+ /* see if we use a unique or shared mpm ctx for this type */
+ int confshared = 0;
+ char confstring[256] = "detect.mpm.";
+ strlcat(confstring, name, sizeof(confstring));
+ strlcat(confstring, ".shared", sizeof(confstring));
+ if (ConfGetBool(confstring, &confshared) == 1)
+ shared = confshared;
+
+ int32_t ctx;
+ if (shared == 0) {
+ ctx = MPM_CTX_FACTORY_UNIQUE_CONTEXT;
+ SCLogDebug("using unique mpm ctx' for %s", name);
+ } else {
+ ctx = MpmFactoryRegisterMpmCtxProfile(de_ctx, name, DETECT_SM_LIST_PMATCH, ALPROTO_UNKNOWN);
+ SCLogDebug("using shared mpm ctx' for %s", name);
+ }
+ return ctx;
+}
+
+void DetectMpmInitializeBuiltinMpms(DetectEngineCtx *de_ctx)
+{
+ de_ctx->sgh_mpm_context_proto_tcp_packet = SetupBuiltinMpm(de_ctx, "tcp-packet");
+ de_ctx->sgh_mpm_context_stream = SetupBuiltinMpm(de_ctx, "tcp-stream");
+
+ de_ctx->sgh_mpm_context_proto_udp_packet = SetupBuiltinMpm(de_ctx, "udp-packet");
+ de_ctx->sgh_mpm_context_proto_other_packet = SetupBuiltinMpm(de_ctx, "other-ip");
+}
+
+/**
+ * \brief initialize mpm contexts for builtin buffers that are in
+ * "single or "shared" mode.
+ */
+int DetectMpmPrepareBuiltinMpms(DetectEngineCtx *de_ctx)
+{
+ int r = 0;
+ MpmCtx *mpm_ctx = NULL;
+
+ if (de_ctx->sgh_mpm_context_proto_tcp_packet != MPM_CTX_FACTORY_UNIQUE_CONTEXT) {
+ mpm_ctx = MpmFactoryGetMpmCtxForProfile(de_ctx, de_ctx->sgh_mpm_context_proto_tcp_packet, 0);
+ if (mpm_table[de_ctx->mpm_matcher].Prepare != NULL) {
+ r |= mpm_table[de_ctx->mpm_matcher].Prepare(mpm_ctx);
+ }
+ mpm_ctx = MpmFactoryGetMpmCtxForProfile(de_ctx, de_ctx->sgh_mpm_context_proto_tcp_packet, 1);
+ if (mpm_table[de_ctx->mpm_matcher].Prepare != NULL) {
+ r |= mpm_table[de_ctx->mpm_matcher].Prepare(mpm_ctx);
+ }
+ }
+
+ if (de_ctx->sgh_mpm_context_proto_udp_packet != MPM_CTX_FACTORY_UNIQUE_CONTEXT) {
+ mpm_ctx = MpmFactoryGetMpmCtxForProfile(de_ctx, de_ctx->sgh_mpm_context_proto_udp_packet, 0);
+ if (mpm_table[de_ctx->mpm_matcher].Prepare != NULL) {
+ r |= mpm_table[de_ctx->mpm_matcher].Prepare(mpm_ctx);
+ }
+ mpm_ctx = MpmFactoryGetMpmCtxForProfile(de_ctx, de_ctx->sgh_mpm_context_proto_udp_packet, 1);
+ if (mpm_table[de_ctx->mpm_matcher].Prepare != NULL) {
+ r |= mpm_table[de_ctx->mpm_matcher].Prepare(mpm_ctx);
+ }
+ }
+
+ if (de_ctx->sgh_mpm_context_proto_other_packet != MPM_CTX_FACTORY_UNIQUE_CONTEXT) {
+ mpm_ctx = MpmFactoryGetMpmCtxForProfile(de_ctx, de_ctx->sgh_mpm_context_proto_other_packet, 0);
+ if (mpm_table[de_ctx->mpm_matcher].Prepare != NULL) {
+ r |= mpm_table[de_ctx->mpm_matcher].Prepare(mpm_ctx);
+ }
+ }
+
+ if (de_ctx->sgh_mpm_context_stream != MPM_CTX_FACTORY_UNIQUE_CONTEXT) {
+ mpm_ctx = MpmFactoryGetMpmCtxForProfile(de_ctx, de_ctx->sgh_mpm_context_stream, 0);
+ if (mpm_table[de_ctx->mpm_matcher].Prepare != NULL) {
+ r |= mpm_table[de_ctx->mpm_matcher].Prepare(mpm_ctx);
+ }
+ mpm_ctx = MpmFactoryGetMpmCtxForProfile(de_ctx, de_ctx->sgh_mpm_context_stream, 1);
+ if (mpm_table[de_ctx->mpm_matcher].Prepare != NULL) {
+ r |= mpm_table[de_ctx->mpm_matcher].Prepare(mpm_ctx);
+ }
+ }
+
+ return r;
+}
+
+/**
+ * \brief check if a signature has patterns that are to be inspected
+ * against a packets payload (as opposed to the stream payload)
+ *
+ * \param s signature
+ *
+ * \retval 1 true
+ * \retval 0 false
+ */
+int SignatureHasPacketContent(const Signature *s)
+{
+ SCEnter();
+
+ if (s == NULL) {
+ SCReturnInt(0);
+ }
+
+ if (!(s->proto.proto[IPPROTO_TCP / 8] & 1 << (IPPROTO_TCP % 8))) {
+ SCReturnInt(1);
+ }
+
+ if ((s->init_data != NULL && s->init_data->smlists[DETECT_SM_LIST_PMATCH] == NULL) ||
+ (s->init_data == NULL && s->sm_arrays[DETECT_SM_LIST_PMATCH] == NULL))
+ {
+ SCLogDebug("no mpm");
+ SCReturnInt(0);
+ }
+
+ if (!(s->flags & SIG_FLAG_REQUIRE_PACKET)) {
+ SCReturnInt(0);
+ }
+
+ SCReturnInt(1);
+}
+
+/**
+ * \brief check if a signature has patterns that are to be inspected
+ * against the stream payload (as opposed to the individual packets
+ * payload(s))
+ *
+ * \param s signature
+ *
+ * \retval 1 true
+ * \retval 0 false
+ */
+int SignatureHasStreamContent(const Signature *s)
+{
+ SCEnter();
+
+ if (s == NULL) {
+ SCReturnInt(0);
+ }
+
+ if (!(s->proto.proto[IPPROTO_TCP / 8] & 1 << (IPPROTO_TCP % 8))) {
+ SCReturnInt(0);
+ }
+
+ if ((s->init_data != NULL && s->init_data->smlists[DETECT_SM_LIST_PMATCH] == NULL) ||
+ (s->init_data == NULL && s->sm_arrays[DETECT_SM_LIST_PMATCH] == NULL))
+ {
+ SCLogDebug("no mpm");
+ SCReturnInt(0);
+ }
+
+ if (!(s->flags & SIG_FLAG_REQUIRE_STREAM)) {
+ SCReturnInt(0);
+ }
+
+ SCReturnInt(1);
+}
+
+
+/**
+ * \brief Function to return the multi pattern matcher algorithm to be
+ * used by the engine, based on the mpm-algo setting in yaml
+ * Use the default mpm if none is specified in the yaml file.
+ *
+ * \retval mpm algo value
+ */
+uint8_t PatternMatchDefaultMatcher(void)
+{
+ const char *mpm_algo;
+ uint8_t mpm_algo_val = mpm_default_matcher;
+
+ /* Get the mpm algo defined in config file by the user */
+ if ((ConfGet("mpm-algo", &mpm_algo)) == 1) {
+ if (mpm_algo != NULL) {
+#if __BYTE_ORDER == __BIG_ENDIAN
+ if (strcmp(mpm_algo, "ac-ks") == 0) {
+ FatalError("ac-ks does "
+ "not work on big endian systems at this time.");
+ }
+#endif
+ if (strcmp("auto", mpm_algo) == 0) {
+ goto done;
+ }
+ for (uint8_t u = 0; u < MPM_TABLE_SIZE; u++) {
+ if (mpm_table[u].name == NULL)
+ continue;
+
+ if (strcmp(mpm_table[u].name, mpm_algo) == 0) {
+ mpm_algo_val = u;
+ goto done;
+ }
+ }
+
+#ifndef BUILD_HYPERSCAN
+ if ((strcmp(mpm_algo, "hs") == 0)) {
+ FatalError("Hyperscan (hs) support for mpm-algo is "
+ "not compiled into Suricata.");
+ }
+#endif
+ }
+ FatalError("Invalid mpm algo supplied "
+ "in the yaml conf file: \"%s\"",
+ mpm_algo);
+ }
+
+ done:
+ return mpm_algo_val;
+}
+
+void PatternMatchDestroy(MpmCtx *mpm_ctx, uint16_t mpm_matcher)
+{
+ SCLogDebug("mpm_ctx %p, mpm_matcher %"PRIu16"", mpm_ctx, mpm_matcher);
+ mpm_table[mpm_matcher].DestroyCtx(mpm_ctx);
+}
+
+void PatternMatchThreadPrint(MpmThreadCtx *mpm_thread_ctx, uint16_t mpm_matcher)
+{
+ SCLogDebug("mpm_thread_ctx %p, mpm_matcher %"PRIu16" defunct", mpm_thread_ctx, mpm_matcher);
+ //mpm_table[mpm_matcher].PrintThreadCtx(mpm_thread_ctx);
+}
+void PatternMatchThreadDestroy(MpmThreadCtx *mpm_thread_ctx, uint16_t mpm_matcher)
+{
+ SCLogDebug("mpm_thread_ctx %p, mpm_matcher %"PRIu16"", mpm_thread_ctx, mpm_matcher);
+ if (mpm_table[mpm_matcher].DestroyThreadCtx != NULL)
+ mpm_table[mpm_matcher].DestroyThreadCtx(NULL, mpm_thread_ctx);
+}
+void PatternMatchThreadPrepare(MpmThreadCtx *mpm_thread_ctx, uint16_t mpm_matcher)
+{
+ SCLogDebug("mpm_thread_ctx %p, type %"PRIu16, mpm_thread_ctx, mpm_matcher);
+ MpmInitThreadCtx(mpm_thread_ctx, mpm_matcher);
+}
+
+/** \brief Predict a strength value for patterns
+ *
+ * Patterns with high character diversity score higher.
+ * Alpha chars score not so high
+ * Other printable + a few common codes a little higher
+ * Everything else highest.
+ * Longer patterns score better than short patters.
+ *
+ * \param pat pattern
+ * \param patlen length of the pattern
+ *
+ * \retval s pattern score
+ */
+uint32_t PatternStrength(uint8_t *pat, uint16_t patlen)
+{
+ uint8_t a[256];
+ memset(&a, 0 ,sizeof(a));
+
+ uint32_t s = 0;
+ uint16_t u = 0;
+ for (u = 0; u < patlen; u++) {
+ if (a[pat[u]] == 0) {
+ if (isalpha(pat[u]))
+ s += 3;
+ else if (isprint(pat[u]) || pat[u] == 0x00 || pat[u] == 0x01 || pat[u] == 0xFF)
+ s += 4;
+ else
+ s += 6;
+
+ a[pat[u]] = 1;
+ } else {
+ s++;
+ }
+ }
+
+ return s;
+}
+
+static void PopulateMpmHelperAddPattern(MpmCtx *mpm_ctx,
+ const DetectContentData *cd,
+ const Signature *s, uint8_t flags,
+ int chop)
+{
+ uint16_t pat_offset = cd->offset;
+ uint16_t pat_depth = cd->depth;
+
+ /* recompute offset/depth to cope with chop */
+ if (chop && (pat_depth || pat_offset)) {
+ pat_offset += cd->fp_chop_offset;
+ if (pat_depth) {
+ pat_depth -= cd->content_len;
+ pat_depth += cd->fp_chop_offset + cd->fp_chop_len;
+ }
+ }
+
+ /* We have to effectively "wild card" values that will be coming from
+ * byte_extract variables
+ */
+ if (cd->flags & (DETECT_CONTENT_DEPTH_VAR | DETECT_CONTENT_OFFSET_VAR)) {
+ pat_depth = pat_offset = 0;
+ }
+
+ if (cd->flags & DETECT_CONTENT_NOCASE) {
+ if (chop) {
+ MpmAddPatternCI(mpm_ctx,
+ cd->content + cd->fp_chop_offset, cd->fp_chop_len,
+ pat_offset, pat_depth,
+ cd->id, s->num, flags|MPM_PATTERN_CTX_OWNS_ID);
+ } else {
+ MpmAddPatternCI(mpm_ctx,
+ cd->content, cd->content_len,
+ pat_offset, pat_depth,
+ cd->id, s->num, flags|MPM_PATTERN_CTX_OWNS_ID);
+ }
+ } else {
+ if (chop) {
+ MpmAddPatternCS(mpm_ctx,
+ cd->content + cd->fp_chop_offset, cd->fp_chop_len,
+ pat_offset, pat_depth,
+ cd->id, s->num, flags|MPM_PATTERN_CTX_OWNS_ID);
+ } else {
+ MpmAddPatternCS(mpm_ctx,
+ cd->content, cd->content_len,
+ pat_offset, pat_depth,
+ cd->id, s->num, flags|MPM_PATTERN_CTX_OWNS_ID);
+ }
+ }
+
+ return;
+}
+
+#define SGH_PROTO(sgh, p) ((sgh)->init->protos[(p)] == 1)
+#define SGH_DIRECTION_TS(sgh) ((sgh)->init->direction & SIG_FLAG_TOSERVER)
+#define SGH_DIRECTION_TC(sgh) ((sgh)->init->direction & SIG_FLAG_TOCLIENT)
+
+static void SetMpm(Signature *s, SigMatch *mpm_sm, const int mpm_sm_list)
+{
+ if (s == NULL || mpm_sm == NULL)
+ return;
+
+ DetectContentData *cd = (DetectContentData *)mpm_sm->ctx;
+ if (cd->flags & DETECT_CONTENT_FAST_PATTERN_CHOP) {
+ if (DETECT_CONTENT_IS_SINGLE(cd) &&
+ !(cd->flags & DETECT_CONTENT_NEGATED) &&
+ !(cd->flags & DETECT_CONTENT_REPLACE) &&
+ cd->content_len == cd->fp_chop_len)
+ {
+ cd->flags |= DETECT_CONTENT_NO_DOUBLE_INSPECTION_REQUIRED;
+ }
+ } else {
+ if (DETECT_CONTENT_IS_SINGLE(cd) &&
+ !(cd->flags & DETECT_CONTENT_NEGATED) &&
+ !(cd->flags & DETECT_CONTENT_REPLACE))
+ {
+ cd->flags |= DETECT_CONTENT_NO_DOUBLE_INSPECTION_REQUIRED;
+ }
+ }
+ cd->flags |= DETECT_CONTENT_MPM;
+ s->init_data->mpm_sm_list = mpm_sm_list;
+ s->init_data->mpm_sm = mpm_sm;
+ return;
+}
+
+static SigMatch *GetMpmForList(const Signature *s, SigMatch *list, SigMatch *mpm_sm,
+ uint16_t max_len, bool skip_negated_content)
+{
+ for (SigMatch *sm = list; sm != NULL; sm = sm->next) {
+ if (sm->type != DETECT_CONTENT)
+ continue;
+
+ const DetectContentData *cd = (DetectContentData *)sm->ctx;
+ /* skip_negated_content is only set if there's absolutely no
+ * non-negated content present in the sig */
+ if ((cd->flags & DETECT_CONTENT_NEGATED) && skip_negated_content)
+ continue;
+ if (cd->content_len != max_len) {
+ SCLogDebug("content_len %u != max_len %u", cd->content_len, max_len);
+ continue;
+ }
+ if (mpm_sm == NULL) {
+ mpm_sm = sm;
+ } else {
+ DetectContentData *data1 = (DetectContentData *)sm->ctx;
+ DetectContentData *data2 = (DetectContentData *)mpm_sm->ctx;
+ uint32_t ls = PatternStrength(data1->content, data1->content_len);
+ uint32_t ss = PatternStrength(data2->content, data2->content_len);
+ if (ls > ss) {
+ mpm_sm = sm;
+ } else if (ls == ss) {
+ /* if 2 patterns are of equal strength, we pick the longest */
+ if (data1->content_len > data2->content_len)
+ mpm_sm = sm;
+ } else {
+ SCLogDebug("sticking with mpm_sm");
+ }
+ }
+ }
+ return mpm_sm;
+}
+
+void RetrieveFPForSig(const DetectEngineCtx *de_ctx, Signature *s)
+{
+ if (s->init_data->mpm_sm != NULL)
+ return;
+
+ const int nlists = s->init_data->max_content_list_id + 1;
+ int pos_sm_list[nlists];
+ int neg_sm_list[nlists];
+ memset(pos_sm_list, 0, nlists * sizeof(int));
+ memset(neg_sm_list, 0, nlists * sizeof(int));
+ int pos_sm_list_cnt = 0;
+ int neg_sm_list_cnt = 0;
+
+ /* inspect rule to see if we have the fast_pattern reg to
+ * force using a sig, otherwise keep stats about the patterns */
+ if (s->init_data->smlists[DETECT_SM_LIST_PMATCH] != NULL) {
+ if (FastPatternSupportEnabledForSigMatchList(de_ctx, DETECT_SM_LIST_PMATCH)) {
+ for (SigMatch *sm = s->init_data->smlists[DETECT_SM_LIST_PMATCH]; sm != NULL;
+ sm = sm->next) {
+ if (sm->type != DETECT_CONTENT)
+ continue;
+
+ const DetectContentData *cd = (DetectContentData *)sm->ctx;
+ /* fast_pattern set in rule, so using this pattern */
+ if ((cd->flags & DETECT_CONTENT_FAST_PATTERN)) {
+ SetMpm(s, sm, DETECT_SM_LIST_PMATCH);
+ return;
+ }
+
+ if (cd->flags & DETECT_CONTENT_NEGATED) {
+ neg_sm_list[DETECT_SM_LIST_PMATCH] = 1;
+ neg_sm_list_cnt++;
+ } else {
+ pos_sm_list[DETECT_SM_LIST_PMATCH] = 1;
+ pos_sm_list_cnt++;
+ }
+ }
+ }
+ }
+ for (uint32_t x = 0; x < s->init_data->buffer_index; x++) {
+ const int list_id = s->init_data->buffers[x].id;
+
+ SCLogDebug("%u: list_id %d: %s", s->id, list_id,
+ DetectEngineBufferTypeGetNameById(de_ctx, list_id));
+
+ if (!FastPatternSupportEnabledForSigMatchList(de_ctx, list_id)) {
+ SCLogDebug("skip");
+ continue;
+ }
+
+ for (SigMatch *sm = s->init_data->buffers[x].head; sm != NULL; sm = sm->next) {
+ if (sm->type != DETECT_CONTENT)
+ continue;
+
+ const DetectContentData *cd = (DetectContentData *)sm->ctx;
+ /* fast_pattern set in rule, so using this pattern */
+ if ((cd->flags & DETECT_CONTENT_FAST_PATTERN)) {
+ SetMpm(s, sm, list_id);
+ return;
+ }
+
+ if (cd->flags & DETECT_CONTENT_NEGATED) {
+ neg_sm_list[list_id] = 1;
+ neg_sm_list_cnt++;
+ } else {
+ pos_sm_list[list_id] = 1;
+ pos_sm_list_cnt++;
+ SCLogDebug("pos added for %d", list_id);
+ }
+ }
+ SCLogDebug("ok");
+ }
+
+ SCLogDebug("neg_sm_list_cnt %d pos_sm_list_cnt %d", neg_sm_list_cnt, pos_sm_list_cnt);
+
+ /* prefer normal not-negated over negated */
+ int *curr_sm_list = NULL;
+ int skip_negated_content = 1;
+ if (pos_sm_list_cnt > 0) {
+ curr_sm_list = pos_sm_list;
+ } else if (neg_sm_list_cnt > 0) {
+ curr_sm_list = neg_sm_list;
+ skip_negated_content = 0;
+ } else {
+ return;
+ }
+
+ int final_sm_list[nlists];
+ memset(&final_sm_list, 0, (nlists * sizeof(int)));
+
+ int count_final_sm_list = 0;
+ int priority;
+
+ const SCFPSupportSMList *tmp = de_ctx->fp_support_smlist_list;
+ while (tmp != NULL) {
+ for (priority = tmp->priority;
+ tmp != NULL && priority == tmp->priority;
+ tmp = tmp->next)
+ {
+ SCLogDebug("tmp->list_id %d tmp->priority %d", tmp->list_id, tmp->priority);
+ if (tmp->list_id >= nlists)
+ continue;
+ if (curr_sm_list[tmp->list_id] == 0)
+ continue;
+ final_sm_list[count_final_sm_list++] = tmp->list_id;
+ SCLogDebug("tmp->list_id %d", tmp->list_id);
+ }
+ if (count_final_sm_list != 0)
+ break;
+ }
+
+ BUG_ON(count_final_sm_list == 0);
+ SCLogDebug("count_final_sm_list %d skip_negated_content %d", count_final_sm_list,
+ skip_negated_content);
+
+ uint16_t max_len = 0;
+ for (int i = 0; i < count_final_sm_list; i++) {
+ SCLogDebug("i %d final_sm_list[i] %d", i, final_sm_list[i]);
+
+ if (final_sm_list[i] == DETECT_SM_LIST_PMATCH) {
+ for (SigMatch *sm = s->init_data->smlists[DETECT_SM_LIST_PMATCH]; sm != NULL;
+ sm = sm->next) {
+ if (sm->type != DETECT_CONTENT)
+ continue;
+
+ const DetectContentData *cd = (DetectContentData *)sm->ctx;
+ /* skip_negated_content is only set if there's absolutely no
+ * non-negated content present in the sig */
+ if ((cd->flags & DETECT_CONTENT_NEGATED) && skip_negated_content)
+ continue;
+ max_len = MAX(max_len, cd->content_len);
+ }
+ } else {
+ for (uint32_t x = 0; x < s->init_data->buffer_index; x++) {
+ const int list_id = s->init_data->buffers[x].id;
+ if (final_sm_list[i] == list_id) {
+ SCLogDebug("%u: list_id %d: %s", s->id, list_id,
+ DetectEngineBufferTypeGetNameById(de_ctx, list_id));
+
+ for (SigMatch *sm = s->init_data->buffers[x].head; sm != NULL; sm = sm->next) {
+ if (sm->type != DETECT_CONTENT)
+ continue;
+
+ const DetectContentData *cd = (DetectContentData *)sm->ctx;
+ /* skip_negated_content is only set if there's absolutely no
+ * non-negated content present in the sig */
+ if ((cd->flags & DETECT_CONTENT_NEGATED) && skip_negated_content)
+ continue;
+ max_len = MAX(max_len, cd->content_len);
+ }
+ }
+ }
+ }
+ }
+
+ SigMatch *mpm_sm = NULL;
+ int mpm_sm_list = -1;
+ for (int i = 0; i < count_final_sm_list; i++) {
+ SCLogDebug("i %d", i);
+ if (final_sm_list[i] == DETECT_SM_LIST_PMATCH) {
+ /* GetMpmForList may keep `mpm_sm` the same, so track if it changed */
+ SigMatch *prev_mpm_sm = mpm_sm;
+ mpm_sm = GetMpmForList(s, s->init_data->smlists[DETECT_SM_LIST_PMATCH], mpm_sm, max_len,
+ skip_negated_content);
+ if (mpm_sm != prev_mpm_sm) {
+ mpm_sm_list = final_sm_list[i];
+ }
+ } else {
+ SCLogDebug(
+ "%u: %s", s->id, DetectEngineBufferTypeGetNameById(de_ctx, final_sm_list[i]));
+ for (uint32_t x = 0; x < s->init_data->buffer_index; x++) {
+ const int list_id = s->init_data->buffers[x].id;
+ if (final_sm_list[i] == list_id) {
+ SCLogDebug("%u: list_id %d: %s", s->id, list_id,
+ DetectEngineBufferTypeGetNameById(de_ctx, list_id));
+ /* GetMpmForList may keep `mpm_sm` the same, so track if it changed */
+ SigMatch *prev_mpm_sm = mpm_sm;
+ mpm_sm = GetMpmForList(s, s->init_data->buffers[x].head, mpm_sm, max_len,
+ skip_negated_content);
+ SCLogDebug("mpm_sm %p from %p", mpm_sm, s->init_data->buffers[x].head);
+ if (mpm_sm != prev_mpm_sm) {
+ mpm_sm_list = list_id;
+ }
+ }
+ }
+ }
+ }
+
+#ifdef DEBUG
+ if (mpm_sm != NULL) {
+ BUG_ON(mpm_sm_list == -1);
+ int check_list = SigMatchListSMBelongsTo(s, mpm_sm);
+ BUG_ON(check_list != mpm_sm_list);
+ }
+#endif
+ /* assign to signature */
+ SetMpm(s, mpm_sm, mpm_sm_list);
+ return;
+}
+
+/** \internal
+ * \brief The hash function for MpmStore
+ *
+ * \param ht Pointer to the hash table.
+ * \param data Pointer to the MpmStore.
+ * \param datalen Not used in our case.
+ *
+ * \retval hash The generated hash value.
+ */
+static uint32_t MpmStoreHashFunc(HashListTable *ht, void *data, uint16_t datalen)
+{
+ const MpmStore *ms = (MpmStore *)data;
+ uint32_t hash = ms->alproto;
+
+ for (uint32_t b = 0; b < ms->sid_array_size; b++)
+ hash += ms->sid_array[b];
+
+ return hash % ht->array_size;
+}
+
+/**
+ * \brief The Compare function for MpmStore
+ *
+ * \param data1 Pointer to the first MpmStore.
+ * \param len1 Not used.
+ * \param data2 Pointer to the second MpmStore.
+ * \param len2 Not used.
+ *
+ * \retval 1 If the 2 MpmStores sent as args match.
+ * \retval 0 If the 2 MpmStores sent as args do not match.
+ */
+static char MpmStoreCompareFunc(void *data1, uint16_t len1, void *data2,
+ uint16_t len2)
+{
+ const MpmStore *ms1 = (MpmStore *)data1;
+ const MpmStore *ms2 = (MpmStore *)data2;
+
+ if (ms1->alproto != ms2->alproto)
+ return 0;
+
+ if (ms1->sid_array_size != ms2->sid_array_size)
+ return 0;
+
+ if (ms1->buffer != ms2->buffer)
+ return 0;
+
+ if (ms1->direction != ms2->direction)
+ return 0;
+
+ if (ms1->sm_list != ms2->sm_list)
+ return 0;
+
+ if (SCMemcmp(ms1->sid_array, ms2->sid_array,
+ ms1->sid_array_size) != 0)
+ {
+ return 0;
+ }
+
+ return 1;
+}
+
+static void MpmStoreFreeFunc(void *ptr)
+{
+ MpmStore *ms = ptr;
+ if (ms != NULL) {
+ if (ms->mpm_ctx != NULL && !(ms->mpm_ctx->flags & MPMCTX_FLAGS_GLOBAL))
+ {
+ SCLogDebug("destroying mpm_ctx %p", ms->mpm_ctx);
+ mpm_table[ms->mpm_ctx->mpm_type].DestroyCtx(ms->mpm_ctx);
+ SCFree(ms->mpm_ctx);
+ }
+ ms->mpm_ctx = NULL;
+
+ SCFree(ms->sid_array);
+ SCFree(ms);
+ }
+}
+
+/**
+ * \brief Initializes the MpmStore mpm hash table to be used by the detection
+ * engine context.
+ *
+ * \param de_ctx Pointer to the detection engine context.
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+int MpmStoreInit(DetectEngineCtx *de_ctx)
+{
+ de_ctx->mpm_hash_table = HashListTableInit(4096,
+ MpmStoreHashFunc,
+ MpmStoreCompareFunc,
+ MpmStoreFreeFunc);
+ if (de_ctx->mpm_hash_table == NULL)
+ goto error;
+
+ return 0;
+
+error:
+ return -1;
+}
+
+/**
+ * \brief Adds a MpmStore to the detection engine context MpmStore
+ *
+ * \param de_ctx Pointer to the detection engine context.
+ * \param sgh Pointer to the MpmStore.
+ *
+ * \retval ret 0 on Successfully adding the argument sgh; -1 on failure.
+ */
+static int MpmStoreAdd(DetectEngineCtx *de_ctx, MpmStore *s)
+{
+ int ret = HashListTableAdd(de_ctx->mpm_hash_table, (void *)s, 0);
+ return ret;
+}
+
+/**
+ * \brief Used to lookup a MpmStore from the MpmStore
+ *
+ * \param de_ctx Pointer to the detection engine context.
+ * \param sgh Pointer to the MpmStore.
+ *
+ * \retval rsgh On success a pointer to the MpmStore if the MpmStore is
+ * found in the hash table; NULL on failure.
+ */
+static MpmStore *MpmStoreLookup(DetectEngineCtx *de_ctx, MpmStore *s)
+{
+ MpmStore *rs = HashListTableLookup(de_ctx->mpm_hash_table,
+ (void *)s, 0);
+ return rs;
+}
+
+static const DetectBufferMpmRegistry *GetByMpmStore(
+ const DetectEngineCtx *de_ctx, const MpmStore *ms)
+{
+ const DetectBufferMpmRegistry *am = de_ctx->app_mpms_list;
+ while (am != NULL) {
+ if (ms->sm_list == am->sm_list &&
+ ms->direction == am->direction) {
+ return am;
+ }
+ am = am->next;
+ }
+ am = de_ctx->pkt_mpms_list;
+ while (am != NULL) {
+ if (ms->sm_list == am->sm_list) {
+ return am;
+ }
+ am = am->next;
+ }
+ return NULL;
+}
+
+void MpmStoreReportStats(const DetectEngineCtx *de_ctx)
+{
+ HashListTableBucket *htb = NULL;
+
+ uint32_t stats[MPMB_MAX] = {0};
+ int app_mpms_cnt = de_ctx->buffer_type_id;
+ uint32_t appstats[app_mpms_cnt + 1]; // +1 to silence scan-build
+ memset(&appstats, 0x00, sizeof(appstats));
+ int pkt_mpms_cnt = de_ctx->buffer_type_id;
+ uint32_t pktstats[pkt_mpms_cnt + 1]; // +1 to silence scan-build
+ memset(&pktstats, 0x00, sizeof(pktstats));
+ int frame_mpms_cnt = de_ctx->buffer_type_id;
+ uint32_t framestats[frame_mpms_cnt + 1]; // +1 to silence scan-build
+ memset(&framestats, 0x00, sizeof(framestats));
+
+ for (htb = HashListTableGetListHead(de_ctx->mpm_hash_table);
+ htb != NULL;
+ htb = HashListTableGetListNext(htb))
+ {
+ const MpmStore *ms = (MpmStore *)HashListTableGetListData(htb);
+ if (ms == NULL || ms->mpm_ctx == NULL) {
+ continue;
+ }
+ if (ms->buffer < MPMB_MAX)
+ stats[ms->buffer]++;
+ else if (ms->sm_list != DETECT_SM_LIST_PMATCH) {
+ const DetectBufferMpmRegistry *am = GetByMpmStore(de_ctx, ms);
+ if (am != NULL) {
+ switch (am->type) {
+ case DETECT_BUFFER_MPM_TYPE_PKT:
+ SCLogDebug("%s: %u patterns. Min %u, Max %u. Ctx %p",
+ am->name,
+ ms->mpm_ctx->pattern_cnt,
+ ms->mpm_ctx->minlen, ms->mpm_ctx->maxlen,
+ ms->mpm_ctx);
+ pktstats[am->sm_list]++;
+ break;
+ case DETECT_BUFFER_MPM_TYPE_APP:
+ SCLogDebug("%s %s %s: %u patterns. Min %u, Max %u. Ctx %p",
+ AppProtoToString(ms->alproto), am->name,
+ am->direction == SIG_FLAG_TOSERVER ? "toserver" : "toclient",
+ ms->mpm_ctx->pattern_cnt, ms->mpm_ctx->minlen, ms->mpm_ctx->maxlen,
+ ms->mpm_ctx);
+ appstats[am->sm_list]++;
+ break;
+ case DETECT_BUFFER_MPM_TYPE_FRAME:
+ SCLogDebug("%s: %u patterns. Min %u, Max %u. Ctx %p", am->name,
+ ms->mpm_ctx->pattern_cnt, ms->mpm_ctx->minlen, ms->mpm_ctx->maxlen,
+ ms->mpm_ctx);
+ framestats[am->sm_list]++;
+ break;
+ case DETECT_BUFFER_MPM_TYPE_SIZE:
+ break;
+ }
+ }
+ }
+ }
+
+ if (!(de_ctx->flags & DE_QUIET)) {
+ for (int x = 0; x < MPMB_MAX; x++) {
+ SCLogPerf("Builtin MPM \"%s\": %u", builtin_mpms[x], stats[x]);
+ }
+ const DetectBufferMpmRegistry *am = de_ctx->app_mpms_list;
+ while (am != NULL) {
+ if (appstats[am->sm_list] > 0) {
+ const char *name = am->name;
+ const char *direction = am->direction == SIG_FLAG_TOSERVER ? "toserver" : "toclient";
+ SCLogPerf("AppLayer MPM \"%s %s (%s)\": %u", direction, name,
+ AppProtoToString(am->app_v2.alproto), appstats[am->sm_list]);
+ }
+ am = am->next;
+ }
+ const DetectBufferMpmRegistry *pm = de_ctx->pkt_mpms_list;
+ while (pm != NULL) {
+ if (pktstats[pm->sm_list] > 0) {
+ const char *name = pm->name;
+ SCLogPerf("Pkt MPM \"%s\": %u", name, pktstats[pm->sm_list]);
+ }
+ pm = pm->next;
+ }
+ const DetectBufferMpmRegistry *um = de_ctx->frame_mpms_list;
+ while (um != NULL) {
+ if (framestats[um->sm_list] > 0) {
+ const char *name = um->name;
+ SCLogPerf("Frame MPM \"%s\": %u", name, framestats[um->sm_list]);
+ }
+ um = um->next;
+ }
+ }
+}
+
+/**
+ * \brief Frees the hash table - DetectEngineCtx->mpm_hash_table, allocated by
+ * MpmStoreInit() function.
+ *
+ * \param de_ctx Pointer to the detection engine context.
+ */
+void MpmStoreFree(DetectEngineCtx *de_ctx)
+{
+ if (de_ctx->mpm_hash_table == NULL)
+ return;
+
+ HashListTableFree(de_ctx->mpm_hash_table);
+ de_ctx->mpm_hash_table = NULL;
+ return;
+}
+
+static void MpmStoreSetup(const DetectEngineCtx *de_ctx, MpmStore *ms)
+{
+ const Signature *s = NULL;
+ uint32_t sig;
+ int dir = 0;
+
+ if (ms->buffer != MPMB_MAX) {
+ BUG_ON(ms->sm_list != DETECT_SM_LIST_PMATCH);
+
+ switch (ms->buffer) {
+ /* TS is 1 */
+ case MPMB_TCP_PKT_TS:
+ case MPMB_TCP_STREAM_TS:
+ case MPMB_UDP_TS:
+ dir = 1;
+ break;
+
+ /* TC is 0 */
+ default:
+ case MPMB_UDP_TC:
+ case MPMB_TCP_STREAM_TC:
+ case MPMB_TCP_PKT_TC:
+ case MPMB_OTHERIP: /**< use 0 for other */
+ dir = 0;
+ break;
+ }
+ } else {
+ BUG_ON(ms->sm_list == DETECT_SM_LIST_PMATCH);
+
+ if (ms->direction == SIG_FLAG_TOSERVER)
+ dir = 1;
+ else
+ dir = 0;
+ }
+
+ ms->mpm_ctx = MpmFactoryGetMpmCtxForProfile(de_ctx, ms->sgh_mpm_context, dir);
+ if (ms->mpm_ctx == NULL) {
+ return;
+ }
+
+ MpmInitCtx(ms->mpm_ctx, de_ctx->mpm_matcher);
+
+ /* add the patterns */
+ for (sig = 0; sig < (ms->sid_array_size * 8); sig++) {
+ if (ms->sid_array[sig / 8] & (1 << (sig % 8))) {
+ s = de_ctx->sig_array[sig];
+ DEBUG_VALIDATE_BUG_ON(s == NULL);
+ if (s == NULL)
+ continue;
+
+ SCLogDebug("%p: direction %d adding %u", ms, ms->direction, s->id);
+
+ const DetectContentData *cd = (DetectContentData *)s->init_data->mpm_sm->ctx;
+
+ int skip = 0;
+ /* negated logic: if mpm match can't be used to be sure about this
+ * pattern, we have to inspect the rule fully regardless of mpm
+ * match. So in this case there is no point of adding it at all.
+ * The non-mpm list entry for the sig will make sure the sig is
+ * inspected. */
+ if ((cd->flags & DETECT_CONTENT_NEGATED) &&
+ !(DETECT_CONTENT_MPM_IS_CONCLUSIVE(cd)))
+ {
+ skip = 1;
+ SCLogDebug("not adding negated mpm as it's not 'single'");
+ }
+
+ if (!skip) {
+ PopulateMpmHelperAddPattern(ms->mpm_ctx,
+ cd, s, 0, (cd->flags & DETECT_CONTENT_FAST_PATTERN_CHOP));
+ }
+ }
+ }
+
+ if (ms->mpm_ctx->pattern_cnt == 0) {
+ MpmFactoryReClaimMpmCtx(de_ctx, ms->mpm_ctx);
+ ms->mpm_ctx = NULL;
+ } else {
+ if (ms->sgh_mpm_context == MPM_CTX_FACTORY_UNIQUE_CONTEXT) {
+ if (mpm_table[ms->mpm_ctx->mpm_type].Prepare != NULL) {
+ mpm_table[ms->mpm_ctx->mpm_type].Prepare(ms->mpm_ctx);
+ }
+ }
+ }
+}
+
+
+/** \brief Get MpmStore for a built-in buffer type
+ *
+ */
+MpmStore *MpmStorePrepareBuffer(DetectEngineCtx *de_ctx, SigGroupHead *sgh,
+ enum MpmBuiltinBuffers buf)
+{
+ const Signature *s = NULL;
+ uint32_t sig;
+ uint32_t cnt = 0;
+ int direction = 0;
+ uint32_t max_sid = DetectEngineGetMaxSigId(de_ctx) / 8 + 1;
+ uint8_t sids_array[max_sid];
+ memset(sids_array, 0x00, max_sid);
+ int sgh_mpm_context = 0;
+ int sm_list = DETECT_SM_LIST_PMATCH;
+
+ switch (buf) {
+ case MPMB_TCP_PKT_TS:
+ case MPMB_TCP_PKT_TC:
+ sgh_mpm_context = de_ctx->sgh_mpm_context_proto_tcp_packet;
+ break;
+ case MPMB_TCP_STREAM_TS:
+ case MPMB_TCP_STREAM_TC:
+ sgh_mpm_context = de_ctx->sgh_mpm_context_stream;
+ break;
+ case MPMB_UDP_TS:
+ case MPMB_UDP_TC:
+ sgh_mpm_context = de_ctx->sgh_mpm_context_proto_udp_packet;
+ break;
+ case MPMB_OTHERIP:
+ sgh_mpm_context = de_ctx->sgh_mpm_context_proto_other_packet;
+ break;
+ default:
+ break;
+ }
+
+ switch(buf) {
+ case MPMB_TCP_PKT_TS:
+ case MPMB_TCP_STREAM_TS:
+ case MPMB_UDP_TS:
+ direction = SIG_FLAG_TOSERVER;
+ break;
+
+ case MPMB_TCP_PKT_TC:
+ case MPMB_TCP_STREAM_TC:
+ case MPMB_UDP_TC:
+ direction = SIG_FLAG_TOCLIENT;
+ break;
+
+ case MPMB_OTHERIP:
+ direction = (SIG_FLAG_TOCLIENT|SIG_FLAG_TOSERVER);
+ break;
+
+ case MPMB_MAX:
+ BUG_ON(1);
+ break;
+ }
+
+ for (sig = 0; sig < sgh->init->sig_cnt; sig++) {
+ s = sgh->init->match_array[sig];
+ if (s == NULL)
+ continue;
+
+ if (s->init_data->mpm_sm == NULL)
+ continue;
+
+ int list = s->init_data->mpm_sm_list;
+ if (list < 0)
+ continue;
+
+ if (list != DETECT_SM_LIST_PMATCH)
+ continue;
+
+ switch (buf) {
+ case MPMB_TCP_PKT_TS:
+ case MPMB_TCP_PKT_TC:
+ if (SignatureHasPacketContent(s) == 1)
+ {
+ sids_array[s->num / 8] |= 1 << (s->num % 8);
+ cnt++;
+ }
+ break;
+ case MPMB_TCP_STREAM_TS:
+ case MPMB_TCP_STREAM_TC:
+ if (SignatureHasStreamContent(s) == 1)
+ {
+ sids_array[s->num / 8] |= 1 << (s->num % 8);
+ cnt++;
+ }
+ break;
+ case MPMB_UDP_TS:
+ case MPMB_UDP_TC:
+ sids_array[s->num / 8] |= 1 << (s->num % 8);
+ cnt++;
+ break;
+ case MPMB_OTHERIP:
+ sids_array[s->num / 8] |= 1 << (s->num % 8);
+ cnt++;
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (cnt == 0)
+ return NULL;
+
+ MpmStore lookup = { sids_array, max_sid, direction, buf, sm_list, 0, 0, NULL };
+
+ MpmStore *result = MpmStoreLookup(de_ctx, &lookup);
+ if (result == NULL) {
+ MpmStore *copy = SCCalloc(1, sizeof(MpmStore));
+ if (copy == NULL)
+ return NULL;
+ uint8_t *sids = SCCalloc(1, max_sid);
+ if (sids == NULL) {
+ SCFree(copy);
+ return NULL;
+ }
+
+ memcpy(sids, sids_array, max_sid);
+ copy->sid_array = sids;
+ copy->sid_array_size = max_sid;
+ copy->buffer = buf;
+ copy->direction = direction;
+ copy->sm_list = sm_list;
+ copy->sgh_mpm_context = sgh_mpm_context;
+
+ MpmStoreSetup(de_ctx, copy);
+ MpmStoreAdd(de_ctx, copy);
+ return copy;
+ } else {
+ return result;
+ }
+}
+
+struct SidsArray {
+ uint8_t *sids_array;
+ uint32_t sids_array_size;
+ /* indicates this has an active engine */
+ bool active;
+
+ enum DetectBufferMpmType type;
+};
+
+static MpmStore *MpmStorePrepareBufferAppLayer(DetectEngineCtx *de_ctx, SigGroupHead *sgh,
+ const DetectBufferMpmRegistry *am, const struct SidsArray *sa)
+{
+ if (sa->sids_array_size == 0 || sa->sids_array == NULL)
+ return NULL;
+
+ SCLogDebug("handling %s direction %s for list %d", am->name,
+ am->direction == SIG_FLAG_TOSERVER ? "toserver" : "toclient",
+ am->sm_list);
+
+ MpmStore lookup = { sa->sids_array, sa->sids_array_size, am->direction, MPMB_MAX, am->sm_list,
+ 0, am->app_v2.alproto, NULL };
+ SCLogDebug("am->direction %d am->sm_list %d sgh_mpm_context %d", am->direction, am->sm_list,
+ am->sgh_mpm_context);
+
+ MpmStore *result = MpmStoreLookup(de_ctx, &lookup);
+ if (result == NULL) {
+ SCLogDebug("new unique mpm for %s %s", am->name,
+ am->direction == SIG_FLAG_TOSERVER ? "toserver" : "toclient");
+
+ MpmStore *copy = SCCalloc(1, sizeof(MpmStore));
+ if (copy == NULL)
+ return NULL;
+ uint8_t *sids = SCCalloc(1, sa->sids_array_size);
+ if (sids == NULL) {
+ SCFree(copy);
+ return NULL;
+ }
+
+ memcpy(sids, sa->sids_array, sa->sids_array_size);
+ copy->sid_array = sids;
+ copy->sid_array_size = sa->sids_array_size;
+ copy->buffer = MPMB_MAX;
+ copy->direction = am->direction;
+ copy->sm_list = am->sm_list;
+ copy->sgh_mpm_context = am->sgh_mpm_context;
+ copy->alproto = am->app_v2.alproto;
+
+ MpmStoreSetup(de_ctx, copy);
+ MpmStoreAdd(de_ctx, copy);
+ return copy;
+ } else {
+ SCLogDebug("using existing mpm %p", result);
+ return result;
+ }
+ return NULL;
+}
+
+static MpmStore *MpmStorePrepareBufferPkt(DetectEngineCtx *de_ctx, SigGroupHead *sgh,
+ const DetectBufferMpmRegistry *am, const struct SidsArray *sa)
+{
+ SCLogDebug("handling %s for list %d", am->name,
+ am->sm_list);
+
+ if (sa->sids_array_size == 0 || sa->sids_array == NULL)
+ return NULL;
+
+ MpmStore lookup = { sa->sids_array, sa->sids_array_size, SIG_FLAG_TOSERVER | SIG_FLAG_TOCLIENT,
+ MPMB_MAX, am->sm_list, 0, 0, NULL };
+ SCLogDebug("am->sm_list %d", am->sm_list);
+
+ MpmStore *result = MpmStoreLookup(de_ctx, &lookup);
+ if (result == NULL) {
+ SCLogDebug("new unique mpm for %s", am->name);
+
+ MpmStore *copy = SCCalloc(1, sizeof(MpmStore));
+ if (copy == NULL)
+ return NULL;
+ uint8_t *sids = SCCalloc(1, sa->sids_array_size);
+ if (sids == NULL) {
+ SCFree(copy);
+ return NULL;
+ }
+
+ memcpy(sids, sa->sids_array, sa->sids_array_size);
+ copy->sid_array = sids;
+ copy->sid_array_size = sa->sids_array_size;
+ copy->buffer = MPMB_MAX;
+ copy->direction = SIG_FLAG_TOSERVER|SIG_FLAG_TOCLIENT;
+ copy->sm_list = am->sm_list;
+ copy->sgh_mpm_context = am->sgh_mpm_context;
+
+ MpmStoreSetup(de_ctx, copy);
+ MpmStoreAdd(de_ctx, copy);
+ return copy;
+ } else {
+ SCLogDebug("using existing mpm %p", result);
+ return result;
+ }
+ return NULL;
+}
+
+static MpmStore *MpmStorePrepareBufferFrame(DetectEngineCtx *de_ctx, SigGroupHead *sgh,
+ const DetectBufferMpmRegistry *am, const struct SidsArray *sa)
+{
+ SCLogDebug("handling %s for list %d", am->name, am->sm_list);
+
+ if (sa->sids_array_size == 0 || sa->sids_array == NULL)
+ return NULL;
+
+ MpmStore lookup = { sa->sids_array, sa->sids_array_size, am->direction, MPMB_MAX, am->sm_list,
+ 0, am->frame_v1.alproto, NULL };
+ SCLogDebug("am->sm_list %d", am->sm_list);
+
+ MpmStore *result = MpmStoreLookup(de_ctx, &lookup);
+ if (result == NULL) {
+ SCLogDebug("new unique mpm for %s", am->name);
+
+ MpmStore *copy = SCCalloc(1, sizeof(MpmStore));
+ if (copy == NULL)
+ return NULL;
+ uint8_t *sids = SCCalloc(1, sa->sids_array_size);
+ if (sids == NULL) {
+ SCFree(copy);
+ return NULL;
+ }
+
+ memcpy(sids, sa->sids_array, sa->sids_array_size);
+ copy->sid_array = sids;
+ copy->sid_array_size = sa->sids_array_size;
+ copy->buffer = MPMB_MAX;
+ copy->direction = am->direction;
+ copy->sm_list = am->sm_list;
+ copy->sgh_mpm_context = am->sgh_mpm_context;
+ copy->alproto = am->frame_v1.alproto;
+
+ MpmStoreSetup(de_ctx, copy);
+ MpmStoreAdd(de_ctx, copy);
+ return copy;
+ } else {
+ SCLogDebug("using existing mpm %p", result);
+ return result;
+ }
+ return NULL;
+}
+
+static void SetRawReassemblyFlag(DetectEngineCtx *de_ctx, SigGroupHead *sgh)
+{
+ const Signature *s = NULL;
+ uint32_t sig;
+
+ for (sig = 0; sig < sgh->init->sig_cnt; sig++) {
+ s = sgh->init->match_array[sig];
+ if (s == NULL)
+ continue;
+
+ if (SignatureHasStreamContent(s) == 1) {
+ sgh->flags |= SIG_GROUP_HEAD_HAVERAWSTREAM;
+ SCLogDebug("rule group %p has SIG_GROUP_HEAD_HAVERAWSTREAM set", sgh);
+ return;
+ }
+ }
+ SCLogDebug("rule group %p does NOT have SIG_GROUP_HEAD_HAVERAWSTREAM set", sgh);
+}
+
+typedef struct DetectBufferInstance {
+ // key
+ int list;
+ AppProto alproto;
+
+ struct SidsArray ts;
+ struct SidsArray tc;
+} DetectBufferInstance;
+
+static uint32_t DetectBufferInstanceHashFunc(HashListTable *ht, void *data, uint16_t datalen)
+{
+ const DetectBufferInstance *ms = (const DetectBufferInstance *)data;
+ uint32_t hash = ms->list + ms->alproto;
+ return hash % ht->array_size;
+}
+
+static char DetectBufferInstanceCompareFunc(void *data1, uint16_t len1, void *data2, uint16_t len2)
+{
+ const DetectBufferInstance *ms1 = (DetectBufferInstance *)data1;
+ const DetectBufferInstance *ms2 = (DetectBufferInstance *)data2;
+ return (ms1->list == ms2->list && ms1->alproto == ms2->alproto);
+}
+
+static void DetectBufferInstanceFreeFunc(void *ptr)
+{
+ DetectBufferInstance *ms = ptr;
+ if (ms->ts.sids_array != NULL)
+ SCFree(ms->ts.sids_array);
+ if (ms->tc.sids_array != NULL)
+ SCFree(ms->tc.sids_array);
+ SCFree(ms);
+}
+
+static HashListTable *DetectBufferInstanceInit(void)
+{
+ return HashListTableInit(4096, DetectBufferInstanceHashFunc, DetectBufferInstanceCompareFunc,
+ DetectBufferInstanceFreeFunc);
+}
+
+static void PrepareMpms(DetectEngineCtx *de_ctx, SigGroupHead *sh)
+{
+ HashListTable *bufs = DetectBufferInstanceInit();
+ BUG_ON(bufs == NULL);
+
+ const int max_buffer_id = de_ctx->buffer_type_id + 1;
+ const uint32_t max_sid = DetectEngineGetMaxSigId(de_ctx) / 8 + 1;
+
+ AppProto engines[max_buffer_id][ALPROTO_MAX];
+ memset(engines, 0, sizeof(engines));
+ int engines_idx[max_buffer_id];
+ memset(engines_idx, 0, sizeof(engines_idx));
+ int types[max_buffer_id];
+ memset(types, 0, sizeof(types));
+
+ /* flag the list+directions we have engines for as active */
+ for (DetectBufferMpmRegistry *a = de_ctx->pkt_mpms_list; a != NULL; a = a->next) {
+ types[a->sm_list] = a->type;
+
+ DetectBufferInstance lookup = { .list = a->sm_list, .alproto = ALPROTO_UNKNOWN };
+ DetectBufferInstance *instance = HashListTableLookup(bufs, &lookup, 0);
+ if (instance == NULL) {
+ instance = SCCalloc(1, sizeof(*instance));
+ BUG_ON(instance == NULL);
+ instance->list = a->sm_list;
+ instance->alproto = ALPROTO_UNKNOWN;
+ HashListTableAdd(bufs, instance, 0);
+ }
+ instance->ts.active = true;
+ instance->tc.active = true;
+ }
+ for (DetectBufferMpmRegistry *a = de_ctx->frame_mpms_list; a != NULL; a = a->next) {
+ const bool add_ts = ((a->direction == SIG_FLAG_TOSERVER) && SGH_DIRECTION_TS(sh));
+ const bool add_tc = ((a->direction == SIG_FLAG_TOCLIENT) && SGH_DIRECTION_TC(sh));
+ if (add_ts || add_tc) {
+ types[a->sm_list] = a->type;
+ engines[a->sm_list][engines_idx[a->sm_list]++] = a->frame_v1.alproto;
+
+ DetectBufferInstance lookup = { .list = a->sm_list, .alproto = a->frame_v1.alproto };
+ DetectBufferInstance *instance = HashListTableLookup(bufs, &lookup, 0);
+ if (instance == NULL) {
+ instance = SCCalloc(1, sizeof(*instance));
+ BUG_ON(instance == NULL);
+ instance->list = a->sm_list;
+ instance->alproto = a->frame_v1.alproto;
+ HashListTableAdd(bufs, instance, 0);
+ }
+ instance->ts.active |= add_ts;
+ instance->tc.active |= add_tc;
+ }
+ }
+ for (DetectBufferMpmRegistry *a = de_ctx->app_mpms_list; a != NULL; a = a->next) {
+ const bool add_ts = ((a->direction == SIG_FLAG_TOSERVER) && SGH_DIRECTION_TS(sh));
+ const bool add_tc = ((a->direction == SIG_FLAG_TOCLIENT) && SGH_DIRECTION_TC(sh));
+ if (add_ts || add_tc) {
+ types[a->sm_list] = a->type;
+ engines[a->sm_list][engines_idx[a->sm_list]++] = a->app_v2.alproto;
+
+ DetectBufferInstance lookup = { .list = a->sm_list, .alproto = a->app_v2.alproto };
+ DetectBufferInstance *instance = HashListTableLookup(bufs, &lookup, 0);
+ if (instance == NULL) {
+ instance = SCCalloc(1, sizeof(*instance));
+ BUG_ON(instance == NULL);
+ instance->list = a->sm_list;
+ instance->alproto = a->app_v2.alproto;
+ HashListTableAdd(bufs, instance, 0);
+ }
+ instance->ts.active |= add_ts;
+ instance->tc.active |= add_tc;
+ }
+ }
+
+ for (uint32_t sig = 0; sig < sh->init->sig_cnt; sig++) {
+ const Signature *s = sh->init->match_array[sig];
+ if (s == NULL)
+ continue;
+ if (s->init_data->mpm_sm == NULL)
+ continue;
+ const int list = s->init_data->mpm_sm_list;
+ if (list < 0)
+ continue;
+ if (list == DETECT_SM_LIST_PMATCH)
+ continue;
+
+ switch (types[list]) {
+ /* app engines are direction aware */
+ case DETECT_BUFFER_MPM_TYPE_FRAME:
+ case DETECT_BUFFER_MPM_TYPE_APP: {
+ for (int e = 0; e < engines_idx[list]; e++) {
+ const AppProto alproto = engines[list][e];
+ if (!(AppProtoEquals(s->alproto, alproto) || s->alproto == 0))
+ continue;
+
+ DetectBufferInstance lookup = { .list = list, .alproto = alproto };
+ DetectBufferInstance *instance = HashListTableLookup(bufs, &lookup, 0);
+ if (instance == NULL)
+ continue;
+ if (s->flags & SIG_FLAG_TOSERVER) {
+ struct SidsArray *sa = &instance->ts;
+ if (sa->active) {
+ if (sa->sids_array == NULL) {
+ sa->sids_array = SCCalloc(1, max_sid);
+ sa->sids_array_size = max_sid;
+ BUG_ON(sa->sids_array == NULL); // TODO
+ }
+ sa->sids_array[s->num / 8] |= 1 << (s->num % 8);
+ SCLogDebug("instance %p: stored %u/%u ts", instance, s->id, s->num);
+ }
+ }
+ if (s->flags & SIG_FLAG_TOCLIENT) {
+ struct SidsArray *sa = &instance->tc;
+ if (sa->active) {
+ if (sa->sids_array == NULL) {
+ sa->sids_array = SCCalloc(1, max_sid);
+ sa->sids_array_size = max_sid;
+ BUG_ON(sa->sids_array == NULL); // TODO
+ }
+ sa->sids_array[s->num / 8] |= 1 << (s->num % 8);
+ SCLogDebug("instance %p: stored %u/%u tc", instance, s->id, s->num);
+ }
+ }
+ }
+ break;
+ }
+ /* pkt engines are directionless, so only use ts */
+ case DETECT_BUFFER_MPM_TYPE_PKT: {
+ DetectBufferInstance lookup = { .list = list, .alproto = ALPROTO_UNKNOWN };
+ DetectBufferInstance *instance = HashListTableLookup(bufs, &lookup, 0);
+ if (instance == NULL)
+ continue;
+ struct SidsArray *sa = &instance->ts;
+ if (sa->active) {
+ if (sa->sids_array == NULL) {
+ sa->sids_array = SCCalloc(1, max_sid);
+ sa->sids_array_size = max_sid;
+ BUG_ON(sa->sids_array == NULL); // TODO
+ }
+ sa->sids_array[s->num / 8] |= 1 << (s->num % 8);
+ }
+ break;
+ }
+ default:
+ abort();
+ break;
+ }
+ }
+
+ sh->init->app_mpms = SCCalloc(de_ctx->app_mpms_list_cnt, sizeof(MpmCtx *));
+ BUG_ON(sh->init->app_mpms == NULL);
+
+ sh->init->pkt_mpms = SCCalloc(de_ctx->pkt_mpms_list_cnt, sizeof(MpmCtx *));
+ BUG_ON(sh->init->pkt_mpms == NULL);
+
+ sh->init->frame_mpms = SCCalloc(de_ctx->frame_mpms_list_cnt, sizeof(MpmCtx *));
+ BUG_ON(sh->init->frame_mpms == NULL);
+
+ for (DetectBufferMpmRegistry *a = de_ctx->pkt_mpms_list; a != NULL; a = a->next) {
+ DetectBufferInstance lookup = { .list = a->sm_list, .alproto = ALPROTO_UNKNOWN };
+ DetectBufferInstance *instance = HashListTableLookup(bufs, &lookup, 0);
+ if (instance == NULL) {
+ continue;
+ }
+ struct SidsArray *sa = &instance->ts;
+ if (!sa->active)
+ continue;
+
+ MpmStore *mpm_store = MpmStorePrepareBufferPkt(de_ctx, sh, a, sa);
+ if (mpm_store != NULL) {
+ sh->init->pkt_mpms[a->id] = mpm_store->mpm_ctx;
+
+ SCLogDebug("a %p a->name %s a->reg->PrefilterRegisterWithListId %p "
+ "mpm_store->mpm_ctx %p", a, a->name,
+ a->PrefilterRegisterWithListId, mpm_store->mpm_ctx);
+
+ /* if we have just certain types of negated patterns,
+ * mpm_ctx can be NULL */
+ if (a->PrefilterRegisterWithListId && mpm_store->mpm_ctx) {
+ BUG_ON(a->PrefilterRegisterWithListId(de_ctx,
+ sh, mpm_store->mpm_ctx,
+ a, a->sm_list) != 0);
+ SCLogDebug("mpm %s %d set up", a->name, a->sm_list);
+ }
+ }
+ }
+ for (DetectBufferMpmRegistry *a = de_ctx->frame_mpms_list; a != NULL; a = a->next) {
+ if ((a->direction == SIG_FLAG_TOSERVER && SGH_DIRECTION_TS(sh)) ||
+ (a->direction == SIG_FLAG_TOCLIENT && SGH_DIRECTION_TC(sh))) {
+ DetectBufferInstance lookup = { .list = a->sm_list, .alproto = a->frame_v1.alproto };
+ DetectBufferInstance *instance = HashListTableLookup(bufs, &lookup, 0);
+ if (instance == NULL) {
+ continue;
+ }
+ struct SidsArray *sa =
+ (a->direction == SIG_FLAG_TOSERVER) ? &instance->ts : &instance->tc;
+ if (!sa->active)
+ continue;
+
+ SCLogDebug("a %s direction %d PrefilterRegisterWithListId %p", a->name, a->direction,
+ a->PrefilterRegisterWithListId);
+ MpmStore *mpm_store = MpmStorePrepareBufferFrame(de_ctx, sh, a, sa);
+ if (mpm_store != NULL) {
+ sh->init->frame_mpms[a->id] = mpm_store->mpm_ctx;
+
+ SCLogDebug("a %p a->name %s a->reg->PrefilterRegisterWithListId %p "
+ "mpm_store->mpm_ctx %p",
+ a, a->name, a->PrefilterRegisterWithListId, mpm_store->mpm_ctx);
+
+ /* if we have just certain types of negated patterns,
+ * mpm_ctx can be NULL */
+ SCLogDebug("mpm_store %p mpm_ctx %p", mpm_store, mpm_store->mpm_ctx);
+ if (a->PrefilterRegisterWithListId && mpm_store->mpm_ctx) {
+ BUG_ON(a->PrefilterRegisterWithListId(
+ de_ctx, sh, mpm_store->mpm_ctx, a, a->sm_list) != 0);
+ SCLogDebug("mpm %s %d set up", a->name, a->sm_list);
+ }
+ }
+ }
+ }
+ for (DetectBufferMpmRegistry *a = de_ctx->app_mpms_list; a != NULL; a = a->next) {
+ if ((a->direction == SIG_FLAG_TOSERVER && SGH_DIRECTION_TS(sh)) ||
+ (a->direction == SIG_FLAG_TOCLIENT && SGH_DIRECTION_TC(sh))) {
+
+ DetectBufferInstance lookup = { .list = a->sm_list, .alproto = a->app_v2.alproto };
+ DetectBufferInstance *instance = HashListTableLookup(bufs, &lookup, 0);
+ if (instance == NULL) {
+ continue;
+ }
+ struct SidsArray *sa =
+ (a->direction == SIG_FLAG_TOSERVER) ? &instance->ts : &instance->tc;
+ if (!sa->active)
+ continue;
+
+ MpmStore *mpm_store = MpmStorePrepareBufferAppLayer(de_ctx, sh, a, sa);
+ if (mpm_store != NULL) {
+ sh->init->app_mpms[a->id] = mpm_store->mpm_ctx;
+
+ SCLogDebug("a %p a->name %s a->PrefilterRegisterWithListId %p "
+ "mpm_store->mpm_ctx %p",
+ a, a->name, a->PrefilterRegisterWithListId, mpm_store->mpm_ctx);
+
+ /* if we have just certain types of negated patterns,
+ * mpm_ctx can be NULL */
+ if (a->PrefilterRegisterWithListId && mpm_store->mpm_ctx) {
+ BUG_ON(a->PrefilterRegisterWithListId(
+ de_ctx, sh, mpm_store->mpm_ctx, a, a->sm_list) != 0);
+ SCLogDebug("mpm %s %d set up", a->name, a->sm_list);
+ }
+ }
+ }
+ }
+ HashListTableFree(bufs);
+}
+
+/** \brief Prepare the pattern matcher ctx in a sig group head.
+ *
+ */
+int PatternMatchPrepareGroup(DetectEngineCtx *de_ctx, SigGroupHead *sh)
+{
+ MpmStore *mpm_store = NULL;
+ if (SGH_PROTO(sh, IPPROTO_TCP)) {
+ if (SGH_DIRECTION_TS(sh)) {
+ mpm_store = MpmStorePrepareBuffer(de_ctx, sh, MPMB_TCP_PKT_TS);
+ if (mpm_store != NULL) {
+ PrefilterPktPayloadRegister(de_ctx, sh, mpm_store->mpm_ctx);
+ }
+
+ mpm_store = MpmStorePrepareBuffer(de_ctx, sh, MPMB_TCP_STREAM_TS);
+ if (mpm_store != NULL) {
+ PrefilterPktStreamRegister(de_ctx, sh, mpm_store->mpm_ctx);
+ }
+
+ SetRawReassemblyFlag(de_ctx, sh);
+ }
+ if (SGH_DIRECTION_TC(sh)) {
+ mpm_store = MpmStorePrepareBuffer(de_ctx, sh, MPMB_TCP_PKT_TC);
+ if (mpm_store != NULL) {
+ PrefilterPktPayloadRegister(de_ctx, sh, mpm_store->mpm_ctx);
+ }
+
+ mpm_store = MpmStorePrepareBuffer(de_ctx, sh, MPMB_TCP_STREAM_TC);
+ if (mpm_store != NULL) {
+ PrefilterPktStreamRegister(de_ctx, sh, mpm_store->mpm_ctx);
+ }
+
+ SetRawReassemblyFlag(de_ctx, sh);
+ }
+ } else if (SGH_PROTO(sh, IPPROTO_UDP)) {
+ if (SGH_DIRECTION_TS(sh)) {
+ mpm_store = MpmStorePrepareBuffer(de_ctx, sh, MPMB_UDP_TS);
+ if (mpm_store != NULL) {
+ PrefilterPktPayloadRegister(de_ctx, sh, mpm_store->mpm_ctx);
+ }
+ }
+ if (SGH_DIRECTION_TC(sh)) {
+ mpm_store = MpmStorePrepareBuffer(de_ctx, sh, MPMB_UDP_TC);
+ if (mpm_store != NULL) {
+ PrefilterPktPayloadRegister(de_ctx, sh, mpm_store->mpm_ctx);
+ }
+ }
+ } else {
+ mpm_store = MpmStorePrepareBuffer(de_ctx, sh, MPMB_OTHERIP);
+ if (mpm_store != NULL) {
+ PrefilterPktPayloadRegister(de_ctx, sh, mpm_store->mpm_ctx);
+ }
+ }
+
+ PrepareMpms(de_ctx, sh);
+ return 0;
+}
+
+static inline uint32_t ContentFlagsForHash(const DetectContentData *cd)
+{
+ return cd->flags & (DETECT_CONTENT_NOCASE | DETECT_CONTENT_OFFSET | DETECT_CONTENT_DEPTH |
+ DETECT_CONTENT_NEGATED | DETECT_CONTENT_ENDS_WITH);
+}
+
+/** \internal
+ * \brief The hash function for Pattern. Hashes pattern after chop is applied.
+ *
+ * \param ht Pointer to the hash table.
+ * \param data Pointer to the Pattern.
+ * \param datalen Not used in our case.
+ *
+ * \retval hash The generated hash value.
+ */
+static uint32_t PatternChopHashFunc(HashListTable *ht, void *data, uint16_t datalen)
+{
+ const DetectPatternTracker *p = (DetectPatternTracker *)data;
+ uint32_t hash = p->sm_list + ContentFlagsForHash(p->cd);
+ uint16_t content_len = p->cd->content_len;
+ const uint8_t *content = p->cd->content;
+ if (p->cd->flags & DETECT_CONTENT_FAST_PATTERN_CHOP) {
+ content += p->cd->fp_chop_offset;
+ content_len = p->cd->fp_chop_len;
+ }
+ hash += StringHashDjb2(content, content_len);
+ return hash % ht->array_size;
+}
+
+/** \internal
+ * \brief The hash function for Pattern. Ignores chop.
+ *
+ * \param ht Pointer to the hash table.
+ * \param data Pointer to the Pattern.
+ * \param datalen Not used in our case.
+ *
+ * \retval hash The generated hash value.
+ */
+static uint32_t PatternNoChopHashFunc(HashListTable *ht, void *data, uint16_t datalen)
+{
+ const DetectPatternTracker *p = (DetectPatternTracker *)data;
+ uint32_t hash = p->sm_list + ContentFlagsForHash(p->cd);
+ hash += StringHashDjb2(p->cd->content, p->cd->content_len);
+ return hash % ht->array_size;
+}
+
+/**
+ * \brief The Compare function for Pattern. Compares patterns after chop is applied.
+ *
+ * \param data1 Pointer to the first Pattern.
+ * \param len1 Not used.
+ * \param data2 Pointer to the second Pattern.
+ * \param len2 Not used.
+ *
+ * \retval 1 If the 2 Patterns sent as args match.
+ * \retval 0 If the 2 Patterns sent as args do not match.
+ */
+static char PatternChopCompareFunc(void *data1, uint16_t len1, void *data2, uint16_t len2)
+{
+ const DetectPatternTracker *p1 = (DetectPatternTracker *)data1;
+ const DetectPatternTracker *p2 = (DetectPatternTracker *)data2;
+
+ if (p1->sm_list != p2->sm_list)
+ return 0;
+
+ if (ContentFlagsForHash(p1->cd) != ContentFlagsForHash(p2->cd))
+ return 0;
+
+ uint16_t p1_content_len = p1->cd->content_len;
+ uint8_t *p1_content = p1->cd->content;
+ if (p1->cd->flags & DETECT_CONTENT_FAST_PATTERN_CHOP) {
+ p1_content += p1->cd->fp_chop_offset;
+ p1_content_len = p1->cd->fp_chop_len;
+ }
+ uint16_t p2_content_len = p2->cd->content_len;
+ uint8_t *p2_content = p2->cd->content;
+ if (p2->cd->flags & DETECT_CONTENT_FAST_PATTERN_CHOP) {
+ p2_content += p2->cd->fp_chop_offset;
+ p2_content_len = p2->cd->fp_chop_len;
+ }
+
+ if (p1_content_len != p2_content_len)
+ return 0;
+
+ if (memcmp(p1_content, p2_content, p1_content_len) != 0) {
+ return 0;
+ }
+
+ return 1;
+}
+
+/**
+ * \brief The Compare function for Pattern. Ignores chop settings
+ *
+ * \param data1 Pointer to the first Pattern.
+ * \param len1 Not used.
+ * \param data2 Pointer to the second Pattern.
+ * \param len2 Not used.
+ *
+ * \retval 1 If the 2 Patterns sent as args match.
+ * \retval 0 If the 2 Patterns sent as args do not match.
+ */
+static char PatternNoChopCompareFunc(void *data1, uint16_t len1, void *data2, uint16_t len2)
+{
+ const DetectPatternTracker *p1 = (DetectPatternTracker *)data1;
+ const DetectPatternTracker *p2 = (DetectPatternTracker *)data2;
+
+ if (p1->sm_list != p2->sm_list)
+ return 0;
+
+ if (ContentFlagsForHash(p1->cd) != ContentFlagsForHash(p2->cd))
+ return 0;
+
+ if (p1->cd->content_len != p2->cd->content_len)
+ return 0;
+
+ if (memcmp(p1->cd->content, p2->cd->content, p1->cd->content_len) != 0) {
+ return 0;
+ }
+
+ return 1;
+}
+
+static void PatternFreeFunc(void *ptr)
+{
+ SCFree(ptr);
+}
+
+/**
+ * \brief Figure out the FP and their respective content ids for all the
+ * sigs in the engine.
+ *
+ * \param de_ctx Detection engine context.
+ *
+ * \retval 0 On success.
+ * \retval -1 On failure.
+ */
+int DetectSetFastPatternAndItsId(DetectEngineCtx *de_ctx)
+{
+ uint32_t cnt = 0;
+ for (Signature *s = de_ctx->sig_list; s != NULL; s = s->next) {
+ if (s->flags & SIG_FLAG_PREFILTER)
+ continue;
+
+ RetrieveFPForSig(de_ctx, s);
+ if (s->init_data->mpm_sm != NULL) {
+ s->flags |= SIG_FLAG_PREFILTER;
+ cnt++;
+ }
+ }
+ /* no mpm rules */
+ if (cnt == 0)
+ return 0;
+
+ HashListTable *ht =
+ HashListTableInit(4096, PatternChopHashFunc, PatternChopCompareFunc, PatternFreeFunc);
+ BUG_ON(ht == NULL);
+ PatIntId max_id = 0;
+
+ for (Signature *s = de_ctx->sig_list; s != NULL; s = s->next) {
+ if (s->init_data->mpm_sm == NULL)
+ continue;
+
+ const int sm_list = s->init_data->mpm_sm_list;
+ BUG_ON(sm_list == -1);
+
+ DetectContentData *cd = (DetectContentData *)s->init_data->mpm_sm->ctx;
+
+ DetectPatternTracker lookup = { .cd = cd, .sm_list = sm_list, .cnt = 0, .mpm = 0 };
+ DetectPatternTracker *res = HashListTableLookup(ht, &lookup, 0);
+ if (res) {
+ res->cnt++;
+ res->mpm += ((cd->flags & DETECT_CONTENT_MPM) != 0);
+
+ cd->id = res->cd->id;
+ SCLogDebug("%u: res id %u cnt %u", s->id, res->cd->id, res->cnt);
+ } else {
+ DetectPatternTracker *add = SCCalloc(1, sizeof(*add));
+ BUG_ON(add == NULL);
+ add->cd = cd;
+ add->sm_list = sm_list;
+ add->cnt = 1;
+ add->mpm = ((cd->flags & DETECT_CONTENT_MPM) != 0);
+ HashListTableAdd(ht, (void *)add, 0);
+
+ cd->id = max_id++;
+ SCLogDebug("%u: add id %u cnt %u", s->id, add->cd->id, add->cnt);
+ }
+ }
+
+ HashListTableFree(ht);
+
+ return 0;
+}
+
+/** \brief add all patterns on our stats hash
+ * Used to fill the hash later used by DumpPatterns()
+ * \note sets up the hash table on first call
+ */
+void EngineAnalysisAddAllRulePatterns(DetectEngineCtx *de_ctx, const Signature *s)
+{
+ if (de_ctx->pattern_hash_table == NULL) {
+ de_ctx->pattern_hash_table = HashListTableInit(
+ 4096, PatternNoChopHashFunc, PatternNoChopCompareFunc, PatternFreeFunc);
+ BUG_ON(de_ctx->pattern_hash_table == NULL);
+ }
+ if (s->sm_arrays[DETECT_SM_LIST_PMATCH]) {
+ SigMatchData *smd = s->sm_arrays[DETECT_SM_LIST_PMATCH];
+ do {
+ switch (smd->type) {
+ case DETECT_CONTENT: {
+ const DetectContentData *cd = (const DetectContentData *)smd->ctx;
+ DetectPatternTracker lookup = {
+ .cd = cd, .sm_list = DETECT_SM_LIST_PMATCH, .cnt = 0, .mpm = 0
+ };
+ DetectPatternTracker *res =
+ HashListTableLookup(de_ctx->pattern_hash_table, &lookup, 0);
+ if (res) {
+ res->cnt++;
+ res->mpm += ((cd->flags & DETECT_CONTENT_MPM) != 0);
+ } else {
+ DetectPatternTracker *add = SCCalloc(1, sizeof(*add));
+ BUG_ON(add == NULL);
+ add->cd = cd;
+ add->sm_list = DETECT_SM_LIST_PMATCH;
+ add->cnt = 1;
+ add->mpm = ((cd->flags & DETECT_CONTENT_MPM) != 0);
+ HashListTableAdd(de_ctx->pattern_hash_table, (void *)add, 0);
+ }
+ break;
+ }
+ }
+ if (smd->is_last)
+ break;
+ smd++;
+ } while (1);
+ }
+
+ const DetectEngineAppInspectionEngine *app = s->app_inspect;
+ for (; app != NULL; app = app->next) {
+ DEBUG_VALIDATE_BUG_ON(app->smd == NULL);
+ SigMatchData *smd = app->smd;
+ do {
+ switch (smd->type) {
+ case DETECT_CONTENT: {
+ const DetectContentData *cd = (const DetectContentData *)smd->ctx;
+
+ DetectPatternTracker lookup = {
+ .cd = cd, .sm_list = app->sm_list, .cnt = 0, .mpm = 0
+ };
+ DetectPatternTracker *res =
+ HashListTableLookup(de_ctx->pattern_hash_table, &lookup, 0);
+ if (res) {
+ res->cnt++;
+ res->mpm += ((cd->flags & DETECT_CONTENT_MPM) != 0);
+ } else {
+ DetectPatternTracker *add = SCCalloc(1, sizeof(*add));
+ BUG_ON(add == NULL);
+ add->cd = cd;
+ add->sm_list = app->sm_list;
+ add->cnt = 1;
+ add->mpm = ((cd->flags & DETECT_CONTENT_MPM) != 0);
+ HashListTableAdd(de_ctx->pattern_hash_table, (void *)add, 0);
+ }
+ break;
+ }
+ }
+ if (smd->is_last)
+ break;
+ smd++;
+ } while (1);
+ }
+ const DetectEnginePktInspectionEngine *pkt = s->pkt_inspect;
+ for (; pkt != NULL; pkt = pkt->next) {
+ SigMatchData *smd = pkt->smd;
+ do {
+ if (smd == NULL) {
+ BUG_ON(!(pkt->sm_list < DETECT_SM_LIST_DYNAMIC_START));
+ smd = s->sm_arrays[pkt->sm_list];
+ }
+ switch (smd->type) {
+ case DETECT_CONTENT: {
+ const DetectContentData *cd = (const DetectContentData *)smd->ctx;
+
+ DetectPatternTracker lookup = {
+ .cd = cd, .sm_list = pkt->sm_list, .cnt = 0, .mpm = 0
+ };
+ DetectPatternTracker *res =
+ HashListTableLookup(de_ctx->pattern_hash_table, &lookup, 0);
+ if (res) {
+ res->cnt++;
+ res->mpm += ((cd->flags & DETECT_CONTENT_MPM) != 0);
+ } else {
+ DetectPatternTracker *add = SCCalloc(1, sizeof(*add));
+ BUG_ON(add == NULL);
+ add->cd = cd;
+ add->sm_list = pkt->sm_list;
+ add->cnt = 1;
+ add->mpm = ((cd->flags & DETECT_CONTENT_MPM) != 0);
+ HashListTableAdd(de_ctx->pattern_hash_table, (void *)add, 0);
+ }
+ break;
+ }
+ }
+ if (smd->is_last)
+ break;
+ smd++;
+ } while (1);
+ }
+ const DetectEngineFrameInspectionEngine *frame = s->frame_inspect;
+ for (; frame != NULL; frame = frame->next) {
+ SigMatchData *smd = frame->smd;
+ do {
+ if (smd == NULL) {
+ BUG_ON(!(frame->sm_list < DETECT_SM_LIST_DYNAMIC_START));
+ smd = s->sm_arrays[frame->sm_list];
+ }
+ switch (smd->type) {
+ case DETECT_CONTENT: {
+ const DetectContentData *cd = (const DetectContentData *)smd->ctx;
+
+ DetectPatternTracker lookup = {
+ .cd = cd, .sm_list = frame->sm_list, .cnt = 0, .mpm = 0
+ };
+ DetectPatternTracker *res =
+ HashListTableLookup(de_ctx->pattern_hash_table, &lookup, 0);
+ if (res) {
+ res->cnt++;
+ res->mpm += ((cd->flags & DETECT_CONTENT_MPM) != 0);
+ } else {
+ DetectPatternTracker *add = SCCalloc(1, sizeof(*add));
+ BUG_ON(add == NULL);
+ add->cd = cd;
+ add->sm_list = frame->sm_list;
+ add->cnt = 1;
+ add->mpm = ((cd->flags & DETECT_CONTENT_MPM) != 0);
+ HashListTableAdd(de_ctx->pattern_hash_table, (void *)add, 0);
+ }
+ break;
+ }
+ }
+ if (smd->is_last)
+ break;
+ smd++;
+ } while (1);
+ }
+}