/* 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 * \author Anoop Saldanha * * 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->proto.proto[IPPROTO_TCP / 8] & 1 << (IPPROTO_TCP % 8))) { SCReturnInt(1); } if (s->init_data->smlists[DETECT_SM_LIST_PMATCH] == NULL) { SCLogDebug("no PMATCH"); 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->proto.proto[IPPROTO_TCP / 8] & 1 << (IPPROTO_TCP % 8))) { SCReturnInt(0); } if (s->init_data->smlists[DETECT_SM_LIST_PMATCH] == NULL) { SCLogDebug("no PMATCH"); 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); } }