/* Copyright (C) 2007-2025 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. */ #include "suricata-common.h" #include "detect.h" #include "detect-engine.h" #include "detect-parse.h" #include "detect-content.h" #include "detect-engine-build.h" #include "detect-engine-address.h" #include "detect-engine-analyzer.h" #include "detect-engine-iponly.h" #include "detect-engine-mpm.h" #include "detect-engine-siggroup.h" #include "detect-engine-port.h" #include "detect-engine-prefilter.h" #include "detect-engine-proto.h" #include "detect-engine-threshold.h" #include "detect-dsize.h" #include "detect-tcp-flags.h" #include "detect-flow.h" #include "detect-config.h" #include "detect-flowbits.h" #include "app-layer-events.h" #include "util-port-interval-tree.h" #include "util-profiling.h" #include "util-validate.h" #include "util-var-name.h" #include "util-conf.h" /* Magic numbers to make the rules of a certain order fall in the same group */ #define DETECT_PGSCORE_RULE_PORT_PRIORITIZED 111 /* Rule port group contains a priority port */ #define DETECT_PGSCORE_RULE_MPM_FAST_PATTERN 99 /* Rule contains an MPM fast pattern */ #define DETECT_PGSCORE_RULE_MPM_NEGATED 77 /* Rule contains a negated MPM */ #define DETECT_PGSCORE_RULE_NO_MPM 55 /* Rule does not contain MPM */ #define DETECT_PGSCORE_RULE_SYN_ONLY 33 /* Rule needs SYN check */ void SigCleanSignatures(DetectEngineCtx *de_ctx) { if (de_ctx == NULL) return; for (Signature *s = de_ctx->sig_list; s != NULL;) { Signature *ns = s->next; SigFree(de_ctx, s); s = ns; } de_ctx->sig_list = NULL; DetectEngineResetMaxSigId(de_ctx); de_ctx->sig_list = NULL; } /** \brief Find a specific signature by sid and gid * \param de_ctx detection engine ctx * \param sid the signature id * \param gid the signature group id * * \retval s sig found * \retval NULL sig not found */ Signature *SigFindSignatureBySidGid(DetectEngineCtx *de_ctx, uint32_t sid, uint32_t gid) { if (de_ctx == NULL) return NULL; for (Signature *s = de_ctx->sig_list; s != NULL; s = s->next) { if (s->id == sid && s->gid == gid) return s; } return NULL; } /** * \brief Check if a signature contains the filestore keyword. * * \param s signature * * \retval 0 no * \retval 1 yes */ int SignatureIsFilestoring(const Signature *s) { if (s == NULL) return 0; if (s->flags & SIG_FLAG_FILESTORE) return 1; return 0; } /** * \brief Check if a signature contains the filemagic keyword. * * \param s signature * * \retval 0 no * \retval 1 yes */ int SignatureIsFilemagicInspecting(const Signature *s) { if (s == NULL) return 0; if (s->file_flags & FILE_SIG_NEED_MAGIC) return 1; return 0; } /** * \brief Check if a signature contains the filemd5 keyword. * * \param s signature * * \retval 0 no * \retval 1 yes */ int SignatureIsFileMd5Inspecting(const Signature *s) { if ((s != NULL) && (s->file_flags & FILE_SIG_NEED_MD5)) return 1; return 0; } /** * \brief Check if a signature contains the filesha1 keyword. * * \param s signature * * \retval 0 no * \retval 1 yes */ int SignatureIsFileSha1Inspecting(const Signature *s) { if ((s != NULL) && (s->file_flags & FILE_SIG_NEED_SHA1)) return 1; return 0; } /** * \brief Check if a signature contains the filesha256 keyword. * * \param s signature * * \retval 0 no * \retval 1 yes */ int SignatureIsFileSha256Inspecting(const Signature *s) { if ((s != NULL) && (s->file_flags & FILE_SIG_NEED_SHA256)) return 1; return 0; } static bool SignatureInspectsBuffers(const Signature *s) { return (s->init_data->buffer_index > 0); } /** \brief Test is a initialized signature is IP only * \param de_ctx detection engine ctx * \param s the signature * \retval 1 sig is ip only * \retval 2 sig is like ip only * \retval 0 sig is not ip only */ int SignatureIsIPOnly(DetectEngineCtx *de_ctx, const Signature *s) { /* explicit hook means no IP-only */ if (s->init_data->hook.type != SIGNATURE_HOOK_TYPE_NOT_SET) return 0; if (s->alproto != ALPROTO_UNKNOWN) return 0; if (s->init_data->smlists[DETECT_SM_LIST_PMATCH] != NULL) return 0; // may happen for 'config' keyword, postmatch if (s->flags & SIG_FLAG_APPLAYER) return 0; /* if flow dir is set we can't process it in ip-only */ if (!(((s->flags & (SIG_FLAG_TOSERVER|SIG_FLAG_TOCLIENT)) == 0) || (s->flags & (SIG_FLAG_TOSERVER|SIG_FLAG_TOCLIENT)) == (SIG_FLAG_TOSERVER|SIG_FLAG_TOCLIENT))) return 0; /* for now assume that all registered buffer types are incompatible */ if (SignatureInspectsBuffers(s)) { SCReturnInt(0); } /* TMATCH list can be ignored, it contains TAGs and * tags are compatible to IP-only. */ SigMatch *sm = s->init_data->smlists[DETECT_SM_LIST_MATCH]; for (; sm != NULL; sm = sm->next) { if (!(sigmatch_table[sm->type].flags & SIGMATCH_IPONLY_COMPAT)) return 0; /* we have enabled flowbits to be compatible with ip only sigs, as long * as the sig only has a "set" flowbits */ if (sm->type == DETECT_FLOWBITS && (((DetectFlowbitsData *)sm->ctx)->cmd != DETECT_FLOWBITS_CMD_SET)) { return 0; } } sm = s->init_data->smlists[DETECT_SM_LIST_POSTMATCH]; for ( ; sm != NULL; sm = sm->next) { if ( !(sigmatch_table[sm->type].flags & SIGMATCH_IPONLY_COMPAT)) return 0; /* we have enabled flowbits to be compatible with ip only sigs, as long * as the sig only has a "set" flowbits */ if (sm->type == DETECT_FLOWBITS && (((DetectFlowbitsData *)sm->ctx)->cmd != DETECT_FLOWBITS_CMD_SET) ) { return 0; } } if (s->init_data->src_contains_negation || s->init_data->dst_contains_negation) { /* Rule is IP only, but contains negated addresses. */ return 2; } if (!(de_ctx->flags & DE_QUIET)) { SCLogDebug("IP-ONLY (%" PRIu32 "): source %s, dest %s", s->id, s->flags & SIG_FLAG_SRC_ANY ? "ANY" : "SET", s->flags & SIG_FLAG_DST_ANY ? "ANY" : "SET"); } return 1; } /** \internal * \brief Test is a initialized signature is inspecting protocol detection only * \param de_ctx detection engine ctx * \param s the signature * \retval 1 sig is dp only * \retval 0 sig is not dp only */ static int SignatureIsPDOnly(const DetectEngineCtx *de_ctx, const Signature *s) { /* explicit hook means no PD-only */ if (s->init_data->hook.type != SIGNATURE_HOOK_TYPE_NOT_SET) return 0; if (s->alproto != ALPROTO_UNKNOWN) return 0; if (s->init_data->smlists[DETECT_SM_LIST_PMATCH] != NULL) return 0; /* for now assume that all registered buffer types are incompatible */ if (SignatureInspectsBuffers(s)) { SCReturnInt(0); } /* TMATCH list can be ignored, it contains TAGs and * tags are compatible to DP-only. */ /* match list matches may be compatible to DP only. We follow the same * logic as IP-only so we can use that flag */ SigMatch *sm = s->init_data->smlists[DETECT_SM_LIST_MATCH]; if (sm == NULL) return 0; int pd = 0; for ( ; sm != NULL; sm = sm->next) { if (sm->type == DETECT_APP_LAYER_PROTOCOL) { pd = 1; } else { /* flowbits are supported for dp only sigs, as long * as the sig only has a "set" flowbits */ if (sm->type == DETECT_FLOWBITS) { if ((((DetectFlowbitsData *)sm->ctx)->cmd != DETECT_FLOWBITS_CMD_SET) ) { SCLogDebug("%u: not PD-only: flowbit settings other than 'set'", s->id); return 0; } } else if (sm->type == DETECT_FLOW) { if (((DetectFlowData *)sm->ctx)->flags & ~(DETECT_FLOW_FLAG_TOSERVER|DETECT_FLOW_FLAG_TOCLIENT)) { SCLogDebug("%u: not PD-only: flow settings other than toserver/toclient", s->id); return 0; } } else if ( !(sigmatch_table[sm->type].flags & SIGMATCH_IPONLY_COMPAT)) { SCLogDebug("%u: not PD-only: %s not PD/IP-only compat", s->id, sigmatch_table[sm->type].name); return 0; } } } if (pd) { SCLogDebug("PD-ONLY (%" PRIu32 ")", s->id); } return pd; } /** * \internal * \brief Check if the initialized signature is inspecting the packet payload * \param de_ctx detection engine ctx * \param s the signature * \retval 1 sig is inspecting the payload * \retval 0 sig is not inspecting the payload */ static int SignatureIsInspectingPayload(DetectEngineCtx *de_ctx, const Signature *s) { if (s->init_data->smlists[DETECT_SM_LIST_PMATCH] != NULL) { return 1; } return 0; } /** * \internal * \brief check if a signature is decoder event matching only * \param de_ctx detection engine * \param s the signature to test * \retval 0 not a DEOnly sig * \retval 1 DEOnly sig */ static int SignatureIsDEOnly(DetectEngineCtx *de_ctx, const Signature *s) { /* explicit hook means no DE-only */ if (s->init_data->hook.type != SIGNATURE_HOOK_TYPE_NOT_SET) SCReturnInt(0); if (s->alproto != ALPROTO_UNKNOWN) { SCReturnInt(0); } if (s->init_data->smlists[DETECT_SM_LIST_PMATCH] != NULL) { SCReturnInt(0); } /* for now assume that all registered buffer types are incompatible */ if (SignatureInspectsBuffers(s)) { SCReturnInt(0); } /* check for conflicting keywords */ SigMatch *sm = s->init_data->smlists[DETECT_SM_LIST_MATCH]; for ( ;sm != NULL; sm = sm->next) { if ( !(sigmatch_table[sm->type].flags & SIGMATCH_DEONLY_COMPAT)) SCReturnInt(0); } /* need at least one decode event keyword to be considered decode event. */ sm = s->init_data->smlists[DETECT_SM_LIST_MATCH]; for ( ;sm != NULL; sm = sm->next) { if (sm->type == DETECT_DECODE_EVENT) goto deonly; if (sm->type == DETECT_ENGINE_EVENT) goto deonly; if (sm->type == DETECT_STREAM_EVENT) goto deonly; } SCReturnInt(0); deonly: if (!(de_ctx->flags & DE_QUIET)) { SCLogDebug("DE-ONLY (%" PRIu32 "): source %s, dest %s", s->id, s->flags & SIG_FLAG_SRC_ANY ? "ANY" : "SET", s->flags & SIG_FLAG_DST_ANY ? "ANY" : "SET"); } SCReturnInt(1); } #define MASK_TCP_INITDEINIT_FLAGS (TH_SYN|TH_RST|TH_FIN) #define MASK_TCP_UNUSUAL_FLAGS (TH_URG|TH_ECN|TH_CWR) /* Create mask for this packet + it's flow if it has one */ void PacketCreateMask(Packet *p, SignatureMask *mask, AppProto alproto, bool app_decoder_events) { if (!(PKT_IS_PSEUDOPKT(p))) { (*mask) |= SIG_MASK_REQUIRE_REAL_PKT; } if (!(p->flags & PKT_NOPAYLOAD_INSPECTION) && p->payload_len > 0) { SCLogDebug("packet has payload"); (*mask) |= SIG_MASK_REQUIRE_PAYLOAD; } else if (p->flags & PKT_DETECT_HAS_STREAMDATA) { SCLogDebug("stream data available"); (*mask) |= SIG_MASK_REQUIRE_PAYLOAD; } else { SCLogDebug("packet has no payload"); (*mask) |= SIG_MASK_REQUIRE_NO_PAYLOAD; } if (p->events.cnt > 0 || app_decoder_events != 0 || (p->app_layer_events != NULL && p->app_layer_events->cnt)) { SCLogDebug("packet/flow has events set"); (*mask) |= SIG_MASK_REQUIRE_ENGINE_EVENT; } if (!(PKT_IS_PSEUDOPKT(p)) && PacketIsTCP(p)) { const TCPHdr *tcph = PacketGetTCP(p); if ((tcph->th_flags & MASK_TCP_INITDEINIT_FLAGS) != 0) { (*mask) |= SIG_MASK_REQUIRE_FLAGS_INITDEINIT; } if ((tcph->th_flags & MASK_TCP_UNUSUAL_FLAGS) != 0) { (*mask) |= SIG_MASK_REQUIRE_FLAGS_UNUSUAL; } } if (p->flags & PKT_HAS_FLOW) { SCLogDebug("packet has flow"); (*mask) |= SIG_MASK_REQUIRE_FLOW; } } static int SignatureCreateMask(Signature *s) { SCEnter(); if ((s->flags & (SIG_FLAG_REQUIRE_PACKET | SIG_FLAG_REQUIRE_STREAM)) == SIG_FLAG_REQUIRE_PACKET) { s->mask |= SIG_MASK_REQUIRE_REAL_PKT; } if (s->init_data->smlists[DETECT_SM_LIST_PMATCH] != NULL) { s->mask |= SIG_MASK_REQUIRE_PAYLOAD; SCLogDebug("sig requires payload"); } SigMatch *sm; for (sm = s->init_data->smlists[DETECT_SM_LIST_MATCH] ; sm != NULL; sm = sm->next) { switch(sm->type) { case DETECT_FLOWBITS: { /* figure out what flowbit action */ DetectFlowbitsData *fb = (DetectFlowbitsData *)sm->ctx; if (fb->cmd == DETECT_FLOWBITS_CMD_ISSET) { /* not a mask flag, but still set it here */ s->flags |= SIG_FLAG_REQUIRE_FLOWVAR; SCLogDebug("SIG_FLAG_REQUIRE_FLOWVAR set as sig has " "flowbit isset option."); } /* flow is required for any flowbit manipulation */ s->mask |= SIG_MASK_REQUIRE_FLOW; SCLogDebug("sig requires flow to be able to manipulate " "flowbit(s)"); break; } case DETECT_FLOWINT: /* flow is required for any flowint manipulation */ s->mask |= SIG_MASK_REQUIRE_FLOW; SCLogDebug("sig requires flow to be able to manipulate " "flowint(s)"); break; case DETECT_FLAGS: { DetectU8Data *fl = (DetectU8Data *)sm->ctx; uint8_t arg = 0; if (fl->mode == DetectUintModeBitmask) { arg = fl->arg2; } else if (fl->mode == DetectUintModeNegBitmask && fl->arg2 == 0) { arg = fl->arg1; } else if (fl->mode == DetectUintModeEqual) { arg = fl->arg1; } if (arg & MASK_TCP_INITDEINIT_FLAGS) { s->mask |= SIG_MASK_REQUIRE_FLAGS_INITDEINIT; SCLogDebug("sig requires SIG_MASK_REQUIRE_FLAGS_INITDEINIT"); } if (arg & MASK_TCP_UNUSUAL_FLAGS) { s->mask |= SIG_MASK_REQUIRE_FLAGS_UNUSUAL; SCLogDebug("sig requires SIG_MASK_REQUIRE_FLAGS_UNUSUAL"); } break; } case DETECT_DSIZE: { DetectU16Data *ds = (DetectU16Data *)sm->ctx; /* LT will include 0, so no payload. * if GT is used in the same rule the * flag will be set anyway. */ if (ds->mode == DETECT_UINT_RA || ds->mode == DETECT_UINT_GT || ds->mode == DETECT_UINT_NE || ds->mode == DETECT_UINT_GTE) { s->mask |= SIG_MASK_REQUIRE_PAYLOAD; SCLogDebug("sig requires payload"); } else if (ds->mode == DETECT_UINT_EQ) { if (ds->arg1 > 0) { s->mask |= SIG_MASK_REQUIRE_PAYLOAD; SCLogDebug("sig requires payload"); } else { s->mask |= SIG_MASK_REQUIRE_NO_PAYLOAD; SCLogDebug("sig requires no payload"); } } break; } case DETECT_DECODE_EVENT: // fallthrough case DETECT_STREAM_EVENT: // fallthrough case DETECT_APP_LAYER_EVENT: // fallthrough case DETECT_ENGINE_EVENT: s->mask |= SIG_MASK_REQUIRE_ENGINE_EVENT; break; } } for (sm = s->init_data->smlists[DETECT_SM_LIST_POSTMATCH]; sm != NULL; sm = sm->next) { switch (sm->type) { case DETECT_CONFIG: { DetectConfigData *fd = (DetectConfigData *)sm->ctx; if (fd->scope == CONFIG_SCOPE_FLOW) { s->mask |= SIG_MASK_REQUIRE_FLOW; } break; } } } if (s->init_data->init_flags & SIG_FLAG_INIT_FLOW) { s->mask |= SIG_MASK_REQUIRE_FLOW; SCLogDebug("sig requires flow"); } if (s->flags & SIG_FLAG_APPLAYER) { s->mask |= SIG_MASK_REQUIRE_FLOW; SCLogDebug("sig requires flow"); } SCLogDebug("mask %02X", s->mask); SCReturnInt(0); } static void SigInitStandardMpmFactoryContexts(DetectEngineCtx *de_ctx) { DetectMpmInitializeBuiltinMpms(de_ctx); } /** \brief Pure-PCRE or bytetest rule */ static bool RuleInspectsPayloadHasNoMpm(const Signature *s) { if (s->init_data->mpm_sm == NULL && s->init_data->smlists[DETECT_SM_LIST_PMATCH] != NULL) return true; return false; } static int RuleGetMpmPatternSize(const Signature *s) { if (s->init_data->mpm_sm == NULL) return -1; int mpm_list = s->init_data->mpm_sm_list; if (mpm_list < 0) return -1; const DetectContentData *cd = (const DetectContentData *)s->init_data->mpm_sm->ctx; if (cd == NULL) return -1; return (int)cd->content_len; } static bool RuleMpmIsNegated(const Signature *s) { if (s->flags & SIG_FLAG_MPM_NEG) return true; if (s->init_data->mpm_sm == NULL) return false; int mpm_list = s->init_data->mpm_sm_list; if (mpm_list < 0) return false; const DetectContentData *cd = (const DetectContentData *)s->init_data->mpm_sm->ctx; if (cd == NULL) return false; return (cd->flags & DETECT_CONTENT_NEGATED) ? true : false; } static SCJsonBuilder *RulesGroupPrintSghStats(const DetectEngineCtx *de_ctx, const SigGroupHead *sgh, const int add_rules, const int add_mpm_stats) { uint32_t prefilter_cnt = 0; uint32_t mpm_cnt = 0; uint32_t nonmpm_cnt = 0; uint32_t mpm_depth_cnt = 0; uint32_t mpm_endswith_cnt = 0; uint32_t negmpm_cnt = 0; uint32_t any5_cnt = 0; uint32_t payload_no_mpm_cnt = 0; uint32_t syn_cnt = 0; uint32_t mpms_min = 0; uint32_t mpms_max = 0; int max_buffer_type_id = de_ctx->buffer_type_id; struct { uint32_t total; uint32_t cnt; uint32_t min; uint32_t max; } mpm_stats[max_buffer_type_id]; memset(mpm_stats, 0x00, sizeof(mpm_stats)); uint32_t alstats[g_alproto_max]; memset(alstats, 0, g_alproto_max * sizeof(uint32_t)); uint32_t mpm_sizes[max_buffer_type_id][256]; memset(mpm_sizes, 0, sizeof(mpm_sizes)); uint32_t alproto_mpm_bufs[g_alproto_max][max_buffer_type_id]; memset(alproto_mpm_bufs, 0, sizeof(alproto_mpm_bufs)); DEBUG_VALIDATE_BUG_ON(sgh->init == NULL); if (sgh->init == NULL) return NULL; SCJsonBuilder *js = SCJbNewObject(); if (unlikely(js == NULL)) return NULL; SCJbSetUint(js, "id", sgh->id); SCJbOpenArray(js, "rules"); for (uint32_t x = 0; x < sgh->init->sig_cnt; x++) { const Signature *s = sgh->init->match_array[x]; if (s == NULL) continue; int any = 0; if (s->proto.flags & DETECT_PROTO_ANY) { any++; } if (s->flags & SIG_FLAG_DST_ANY) { any++; } if (s->flags & SIG_FLAG_SRC_ANY) { any++; } if (s->flags & SIG_FLAG_DP_ANY) { any++; } if (s->flags & SIG_FLAG_SP_ANY) { any++; } if (any == 5) { any5_cnt++; } prefilter_cnt += (s->init_data->prefilter_sm != NULL); if (s->init_data->mpm_sm == NULL) { nonmpm_cnt++; if (s->sm_arrays[DETECT_SM_LIST_MATCH] != NULL) { SCLogDebug("SGH %p Non-MPM inspecting only packets. Rule %u", sgh, s->id); } DetectPort *sp = s->sp; DetectPort *dp = s->dp; if (s->flags & SIG_FLAG_TOSERVER && (dp->port == 0 && dp->port2 == 65535)) { SCLogDebug("SGH %p Non-MPM toserver and to 'any'. Rule %u", sgh, s->id); } if (s->flags & SIG_FLAG_TOCLIENT && (sp->port == 0 && sp->port2 == 65535)) { SCLogDebug("SGH %p Non-MPM toclient and to 'any'. Rule %u", sgh, s->id); } if (DetectFlagsSignatureNeedsSynPackets(s)) { syn_cnt++; } } else { int mpm_list = s->init_data->mpm_sm_list; BUG_ON(mpm_list < 0); const DetectContentData *cd = (const DetectContentData *)s->init_data->mpm_sm->ctx; uint32_t size = cd->content_len < 256 ? cd->content_len : 255; mpm_sizes[mpm_list][size]++; alproto_mpm_bufs[s->alproto][mpm_list]++; if (mpm_list == DETECT_SM_LIST_PMATCH) { if (size == 1) { DetectPort *sp = s->sp; DetectPort *dp = s->dp; if (s->flags & SIG_FLAG_TOSERVER) { if (dp->port == 0 && dp->port2 == 65535) { SCLogDebug("SGH %p toserver 1byte fast_pattern to ANY. Rule %u", sgh, s->id); } else { SCLogDebug("SGH %p toserver 1byte fast_pattern to port(s) %u-%u. Rule %u", sgh, dp->port, dp->port2, s->id); } } if (s->flags & SIG_FLAG_TOCLIENT) { if (sp->port == 0 && sp->port2 == 65535) { SCLogDebug("SGH %p toclient 1byte fast_pattern to ANY. Rule %u", sgh, s->id); } else { SCLogDebug("SGH %p toclient 1byte fast_pattern to port(s) %u-%u. Rule %u", sgh, sp->port, sp->port2, s->id); } } } } uint32_t w = PatternStrength(cd->content, cd->content_len); if (mpms_min == 0) mpms_min = w; if (w < mpms_min) mpms_min = w; if (w > mpms_max) mpms_max = w; BUG_ON(mpm_list >= max_buffer_type_id); mpm_stats[mpm_list].total += w; mpm_stats[mpm_list].cnt++; if (mpm_stats[mpm_list].min == 0 || w < mpm_stats[mpm_list].min) mpm_stats[mpm_list].min = w; if (w > mpm_stats[mpm_list].max) mpm_stats[mpm_list].max = w; mpm_cnt++; if (w < 10) { SCLogDebug("SGH %p Weak MPM Pattern on %s. Rule %u", sgh, DetectListToString(mpm_list), s->id); } if (w < 10 && any == 5) { SCLogDebug("SGH %p Weak MPM Pattern on %s, rule is 5xAny. Rule %u", sgh, DetectListToString(mpm_list), s->id); } if (cd->flags & DETECT_CONTENT_NEGATED) { SCLogDebug("SGH %p MPM Pattern on %s, is negated. Rule %u", sgh, DetectListToString(mpm_list), s->id); negmpm_cnt++; } if (cd->flags & DETECT_CONTENT_ENDS_WITH) { mpm_endswith_cnt++; } if (cd->flags & DETECT_CONTENT_DEPTH) { mpm_depth_cnt++; } } if (RuleInspectsPayloadHasNoMpm(s)) { SCLogDebug("SGH %p No MPM. Payload inspecting. Rule %u", sgh, s->id); payload_no_mpm_cnt++; } alstats[s->alproto]++; if (add_rules) { SCJsonBuilder *e = SCJbNewObject(); if (e != NULL) { SCJbSetUint(e, "sig_id", s->id); SCJbClose(e); SCJbAppendObject(js, e); SCJbFree(e); } } } SCJbClose(js); SCJbOpenObject(js, "stats"); SCJbSetUint(js, "total", sgh->init->sig_cnt); SCJbOpenObject(js, "types"); SCJbSetUint(js, "mpm", mpm_cnt); SCJbSetUint(js, "non_mpm", nonmpm_cnt); SCJbSetUint(js, "mpm_depth", mpm_depth_cnt); SCJbSetUint(js, "mpm_endswith", mpm_endswith_cnt); SCJbSetUint(js, "negated_mpm", negmpm_cnt); SCJbSetUint(js, "payload_but_no_mpm", payload_no_mpm_cnt); SCJbSetUint(js, "prefilter", prefilter_cnt); SCJbSetUint(js, "syn", syn_cnt); SCJbSetUint(js, "any5", any5_cnt); SCJbClose(js); for (AppProto i = 0; i < g_alproto_max; i++) { if (alstats[i] > 0) { const char *proto_name = (i == ALPROTO_UNKNOWN) ? "payload" : AppProtoToString(i); SCJbOpenObject(js, proto_name); SCJbSetUint(js, "total", alstats[i]); for (int y = 0; y < max_buffer_type_id; y++) { if (alproto_mpm_bufs[i][y] == 0) continue; const char *name; if (y < DETECT_SM_LIST_DYNAMIC_START) name = DetectListToHumanString(y); else name = DetectEngineBufferTypeGetNameById(de_ctx, y); SCJbSetUint(js, name, alproto_mpm_bufs[i][y]); } SCJbClose(js); } } if (add_mpm_stats) { SCJbOpenObject(js, "mpm"); for (int i = 0; i < max_buffer_type_id; i++) { if (mpm_stats[i].cnt > 0) { const char *name; if (i < DETECT_SM_LIST_DYNAMIC_START) name = DetectListToHumanString(i); else name = DetectEngineBufferTypeGetNameById(de_ctx, i); SCJbOpenArray(js, name); for (int y = 0; y < 256; y++) { if (mpm_sizes[i][y] == 0) continue; SCJsonBuilder *e = SCJbNewObject(); if (e != NULL) { SCJbSetUint(e, "size", y); SCJbSetUint(e, "count", mpm_sizes[i][y]); SCJbClose(e); SCJbAppendObject(js, e); SCJbFree(e); } } SCJsonBuilder *e = SCJbNewObject(); if (e != NULL) { SCJbSetUint(e, "total", mpm_stats[i].cnt); SCJbSetUint(e, "avg_strength", mpm_stats[i].total / mpm_stats[i].cnt); SCJbSetUint(e, "min_strength", mpm_stats[i].min); SCJbSetUint(e, "max_strength", mpm_stats[i].max); SCJbClose(e); SCJbAppendObject(js, e); SCJbFree(e); } SCJbClose(js); } } SCJbClose(js); } SCJbClose(js); SCJbSetUint(js, "score", sgh->init->score); SCJbClose(js); return js; } static void RulesDumpGrouping(const DetectEngineCtx *de_ctx, const int add_rules, const int add_mpm_stats) { SCJsonBuilder *js = SCJbNewObject(); if (unlikely(js == NULL)) return; for (int p = 0; p < 256; p++) { if (p == IPPROTO_TCP || p == IPPROTO_UDP) { const char *name = (p == IPPROTO_TCP) ? "tcp" : "udp"; SCJbOpenObject(js, name); SCJbOpenArray(js, "toserver"); const DetectPort *list = (p == IPPROTO_TCP) ? de_ctx->flow_gh[1].tcp : de_ctx->flow_gh[1].udp; while (list != NULL) { SCJsonBuilder *port = SCJbNewObject(); SCJbSetUint(port, "port", list->port); SCJbSetUint(port, "port2", list->port2); SCJsonBuilder *stats = RulesGroupPrintSghStats(de_ctx, list->sh, add_rules, add_mpm_stats); SCJbSetObject(port, "rulegroup", stats); SCJbFree(stats); SCJbClose(port); SCJbAppendObject(js, port); SCJbFree(port); list = list->next; } SCJbClose(js); // toserver array SCJbOpenArray(js, "toclient"); list = (p == IPPROTO_TCP) ? de_ctx->flow_gh[0].tcp : de_ctx->flow_gh[0].udp; while (list != NULL) { SCJsonBuilder *port = SCJbNewObject(); SCJbSetUint(port, "port", list->port); SCJbSetUint(port, "port2", list->port2); SCJsonBuilder *stats = RulesGroupPrintSghStats(de_ctx, list->sh, add_rules, add_mpm_stats); SCJbSetObject(port, "rulegroup", stats); SCJbFree(stats); SCJbClose(port); SCJbAppendObject(js, port); SCJbFree(port); list = list->next; } SCJbClose(js); // toclient array SCJbClose(js); } else if (p == IPPROTO_ICMP || p == IPPROTO_ICMPV6) { const char *name = (p == IPPROTO_ICMP) ? "icmpv4" : "icmpv6"; SCJbOpenObject(js, name); if (de_ctx->flow_gh[1].sgh[p]) { SCJbOpenObject(js, "toserver"); SCJsonBuilder *stats = RulesGroupPrintSghStats( de_ctx, de_ctx->flow_gh[1].sgh[p], add_rules, add_mpm_stats); SCJbSetObject(js, "rulegroup", stats); SCJbFree(stats); SCJbClose(js); } if (de_ctx->flow_gh[0].sgh[p]) { SCJbOpenObject(js, "toclient"); SCJsonBuilder *stats = RulesGroupPrintSghStats( de_ctx, de_ctx->flow_gh[0].sgh[p], add_rules, add_mpm_stats); SCJbSetObject(js, "rulegroup", stats); SCJbFree(stats); SCJbClose(js); } SCJbClose(js); } } SCJbClose(js); const char *filename = "rule_group.json"; const char *log_dir = SCConfigGetLogDirectory(); char log_path[PATH_MAX] = ""; snprintf(log_path, sizeof(log_path), "%s/%s", log_dir, filename); FILE *fp = fopen(log_path, "w"); if (fp != NULL) { fwrite(SCJbPtr(js), SCJbLen(js), 1, fp); (void)fclose(fp); } SCJbFree(js); } static int RulesGroupByIPProto(DetectEngineCtx *de_ctx) { Signature *s = de_ctx->sig_list; SigGroupHead *sgh_ts[256] = {NULL}; SigGroupHead *sgh_tc[256] = {NULL}; for ( ; s != NULL; s = s->next) { if (s->type == SIG_TYPE_IPONLY) continue; /* traverse over IP protocol list from libc */ for (int p = 0; p < 256; p++) { if (p == IPPROTO_TCP || p == IPPROTO_UDP) { continue; } if (!(s->proto.proto[p / 8] & (1<<(p % 8)) || (s->proto.flags & DETECT_PROTO_ANY))) { continue; } /* Signatures that are ICMP, SCTP, not IP only are handled here */ if (s->flags & SIG_FLAG_TOCLIENT) { SigGroupHeadAppendSig(de_ctx, &sgh_tc[p], s); } if (s->flags & SIG_FLAG_TOSERVER) { SigGroupHeadAppendSig(de_ctx, &sgh_ts[p], s); } } } /* lets look at deduplicating this list */ SigGroupHeadHashFree(de_ctx); SigGroupHeadHashInit(de_ctx); uint32_t cnt = 0; uint32_t own = 0; uint32_t ref = 0; int p; for (p = 0; p < 256; p++) { if (p == IPPROTO_TCP || p == IPPROTO_UDP) continue; if (sgh_ts[p] == NULL) continue; cnt++; SigGroupHead *lookup_sgh = SigGroupHeadHashLookup(de_ctx, sgh_ts[p]); if (lookup_sgh == NULL) { SCLogDebug("proto group %d sgh %p is the original", p, sgh_ts[p]); SigGroupHeadSetSigCnt(sgh_ts[p], 0); SigGroupHeadBuildMatchArray(de_ctx, sgh_ts[p], 0); SigGroupHeadHashAdd(de_ctx, sgh_ts[p]); SigGroupHeadStore(de_ctx, sgh_ts[p]); own++; } else { SCLogDebug("proto group %d sgh %p is a copy", p, sgh_ts[p]); SigGroupHeadFree(de_ctx, sgh_ts[p]); sgh_ts[p] = lookup_sgh; ref++; } } SCLogPerf("OTHER %s: %u proto groups, %u unique SGH's, %u copies", "toserver", cnt, own, ref); cnt = 0; own = 0; ref = 0; for (p = 0; p < 256; p++) { if (p == IPPROTO_TCP || p == IPPROTO_UDP) continue; if (sgh_tc[p] == NULL) continue; cnt++; SigGroupHead *lookup_sgh = SigGroupHeadHashLookup(de_ctx, sgh_tc[p]); if (lookup_sgh == NULL) { SCLogDebug("proto group %d sgh %p is the original", p, sgh_tc[p]); SigGroupHeadSetSigCnt(sgh_tc[p], 0); SigGroupHeadBuildMatchArray(de_ctx, sgh_tc[p], 0); SigGroupHeadHashAdd(de_ctx, sgh_tc[p]); SigGroupHeadStore(de_ctx, sgh_tc[p]); own++; } else { SCLogDebug("proto group %d sgh %p is a copy", p, sgh_tc[p]); SigGroupHeadFree(de_ctx, sgh_tc[p]); sgh_tc[p] = lookup_sgh; ref++; } } SCLogPerf("OTHER %s: %u proto groups, %u unique SGH's, %u copies", "toclient", cnt, own, ref); for (p = 0; p < 256; p++) { if (p == IPPROTO_TCP || p == IPPROTO_UDP) continue; de_ctx->flow_gh[0].sgh[p] = sgh_tc[p]; de_ctx->flow_gh[1].sgh[p] = sgh_ts[p]; } return 0; } static int PortIsPriority(const DetectEngineCtx *de_ctx, const DetectPort *a, int ipproto) { DetectPort *w = de_ctx->tcp_priorityports; if (ipproto == IPPROTO_UDP) w = de_ctx->udp_priorityports; while (w) { /* Make sure the priority port falls in the port range of a */ DEBUG_VALIDATE_BUG_ON(a->port > a->port2); if (a->port == w->port && w->port2 == a->port2) { return 1; } w = w->next; } return 0; } static int RuleSetScore(Signature *s) { DetectPort *p = NULL; if (s->flags & SIG_FLAG_TOSERVER) p = s->dp; else if (s->flags & SIG_FLAG_TOCLIENT) p = s->sp; else return 0; /* for sigs that don't use 'any' as port, see if we want to * prioritize poor sigs */ int wl = 0; if (!(p->port == 0 && p->port2 == 65535)) { /* pure pcre, bytetest, etc rules */ if (RuleInspectsPayloadHasNoMpm(s)) { SCLogDebug("Rule %u MPM has 1 byte fast_pattern. Prioritizing SGH's.", s->id); wl = DETECT_PGSCORE_RULE_MPM_FAST_PATTERN; } else if (RuleMpmIsNegated(s)) { SCLogDebug("Rule %u MPM is negated. Prioritizing SGH's.", s->id); wl = DETECT_PGSCORE_RULE_MPM_NEGATED; /* one byte pattern in packet/stream payloads */ } else if (s->init_data->mpm_sm != NULL && s->init_data->mpm_sm_list == DETECT_SM_LIST_PMATCH && RuleGetMpmPatternSize(s) == 1) { SCLogDebug("Rule %u No MPM. Payload inspecting. Prioritizing SGH's.", s->id); wl = DETECT_PGSCORE_RULE_NO_MPM; } else if (DetectFlagsSignatureNeedsSynOnlyPackets(s)) { SCLogDebug("Rule %u Needs SYN, so inspected often. Prioritizing SGH's.", s->id); wl = DETECT_PGSCORE_RULE_SYN_ONLY; } } s->init_data->score = wl; return wl; } static int SortCompare(const void *a, const void *b) { const DetectPort *pa = *(const DetectPort **)a; const DetectPort *pb = *(const DetectPort **)b; if (pa->sh->init->score < pb->sh->init->score) { return 1; } else if (pa->sh->init->score > pb->sh->init->score) { return -1; } if (pa->sh->init->sig_cnt < pb->sh->init->sig_cnt) { return 1; } else if (pa->sh->init->sig_cnt > pb->sh->init->sig_cnt) { return -1; } /* Hack to make the qsort output deterministic across platforms. * This had to be done because the order of equal elements sorted * by qsort is undeterministic and showed different output on BSD, * MacOS and Windows. Sorting based on id makes it deterministic. */ if (pa->sh->id < pb->sh->id) return -1; return 1; } static inline void SortGroupList( uint32_t *groups, DetectPort **list, int (*CompareFunc)(const void *, const void *)) { int cnt = 0; for (DetectPort *x = *list; x != NULL; x = x->next) { DEBUG_VALIDATE_BUG_ON(x->port > x->port2); cnt++; } if (cnt <= 1) return; /* build temporary array to sort with qsort */ DetectPort **array = (DetectPort **)SCCalloc(cnt, sizeof(DetectPort *)); if (array == NULL) return; int idx = 0; for (DetectPort *x = *list; x != NULL;) { /* assign a temporary id to resolve otherwise equal groups */ x->sh->id = idx + 1; SigGroupHeadSetSigCnt(x->sh, 0); DetectPort *next = x->next; x->next = x->prev = x->last = NULL; DEBUG_VALIDATE_BUG_ON(x->port > x->port2); array[idx++] = x; x = next; } DEBUG_VALIDATE_BUG_ON(cnt != idx); qsort(array, idx, sizeof(DetectPort *), SortCompare); /* rebuild the list based on the qsort-ed array */ DetectPort *new_list = NULL, *tail = NULL; for (int i = 0; i < idx; i++) { DetectPort *p = array[i]; /* unset temporary group id */ p->sh->id = 0; if (new_list == NULL) { new_list = p; } if (tail != NULL) { tail->next = p; } p->prev = tail; tail = p; } *list = new_list; *groups = idx; #if DEBUG int dbgcnt = 0; SCLogDebug("SORTED LIST:"); for (DetectPort *tmp = *list; tmp != NULL; tmp = tmp->next) { SCLogDebug("item:= [%u:%u]; score: %d; sig_cnt: %d", tmp->port, tmp->port2, tmp->sh->init->score, tmp->sh->init->sig_cnt); dbgcnt++; BUG_ON(dbgcnt > cnt); } #endif SCFree(array); } /** \internal * \brief Create a list of DetectPort objects sorted based on CompareFunc's * logic. * * List can limit the number of groups. In this case an extra "join" group * is created that contains the sigs belonging to that. It's *appended* to * the list, meaning that if the list is walked linearly it's found last. * The joingr is meant to be a catch all. * */ static int CreateGroupedPortList(DetectEngineCtx *de_ctx, DetectPort *port_list, DetectPort **newhead, uint32_t unique_groups, int (*CompareFunc)(const void *, const void *)) { DetectPort *tmplist = NULL, *joingr = NULL; uint32_t groups = 0; /* insert the ports into the tmplist, where it will * be sorted descending on 'cnt' and on whether a group * is prioritized. */ tmplist = port_list; SortGroupList(&groups, &tmplist, SortCompare); uint32_t left = unique_groups; if (left == 0) left = groups; /* create another list: take the port groups from above * and add them to the 2nd list until we have met our * count. The rest is added to the 'join' group. */ DetectPort *tmplist2 = NULL, *tmplist2_tail = NULL; DetectPort *gr, *next_gr; for (gr = tmplist; gr != NULL;) { next_gr = gr->next; SCLogDebug("temp list gr %p %u:%u", gr, gr->port, gr->port2); DetectPortPrint(gr); /* if we've set up all the unique groups, add the rest to the * catch-all joingr */ if (left == 0) { if (joingr == NULL) { DetectPortParse(de_ctx, &joingr, "0:65535"); if (joingr == NULL) { goto error; } SCLogDebug("joingr => %u-%u", joingr->port, joingr->port2); joingr->next = NULL; } SigGroupHeadCopySigs(de_ctx, gr->sh, &joingr->sh); /* when a group's sigs are added to the joingr, we can free it */ gr->next = NULL; DetectPortFree(de_ctx, gr); /* append */ } else { gr->next = NULL; if (tmplist2 == NULL) { tmplist2 = gr; tmplist2_tail = gr; } else { tmplist2_tail->next = gr; tmplist2_tail = gr; } } if (left > 0) left--; gr = next_gr; } /* if present, append the joingr that covers the rest */ if (joingr != NULL) { SCLogDebug("appending joingr %p %u:%u", joingr, joingr->port, joingr->port2); if (tmplist2 == NULL) { tmplist2 = joingr; // tmplist2_tail = joingr; } else { tmplist2_tail->next = joingr; // tmplist2_tail = joingr; } } else { SCLogDebug("no joingr"); } /* pass back our new list to the caller */ *newhead = tmplist2; DetectPortPrintList(*newhead); return 0; error: return -1; } #define UNDEFINED_PORT 0 #define RANGE_PORT 1 #define SINGLE_PORT 2 typedef struct UniquePortPoint_ { uint16_t port; /* value of the port */ bool single; /* is the port single or part of a range */ } UniquePortPoint; /** * \brief Function to set unique port points. Consider all the ports * flattened out on one line, set the points that correspond * to a valid port. Also store whether the port point stored * was a single port or part of a range. * * \param p Port object to be set * \param unique_list List of unique port points to be updated * \param size_list Current size of the list * * \return Updated size of the list */ static inline uint32_t SetUniquePortPoints( const DetectPort *p, uint8_t *unique_list, uint32_t size_list) { if (unique_list[p->port] == UNDEFINED_PORT) { if (p->port == p->port2) { unique_list[p->port] = SINGLE_PORT; } else { unique_list[p->port] = RANGE_PORT; } size_list++; } else if (((unique_list[p->port] == SINGLE_PORT) && (p->port != p->port2)) || ((unique_list[p->port] == RANGE_PORT) && (p->port == p->port2))) { if ((p->port != UINT16_MAX) && (unique_list[p->port + 1] == UNDEFINED_PORT)) { unique_list[p->port + 1] = RANGE_PORT; size_list++; } } /* Treat right boundary as single point to avoid creating unneeded * ranges later on */ if (unique_list[p->port2] == UNDEFINED_PORT) { size_list++; } unique_list[p->port2] = SINGLE_PORT; return size_list; } /** * \brief Function to set the *final* unique port points and save them * for later use. The points are already sorted because of the way * they have been retrieved and saved earlier for use at this point. * * \param unique_list List of the unique port points to be used * \param size_unique_arr Number of unique port points * \param final_arr List of the final unique port points to be created */ static inline void SetFinalUniquePortPoints( const uint8_t *unique_list, const uint32_t size_unique_arr, UniquePortPoint *final_arr) { for (uint32_t i = 0, j = 0; i < (UINT16_MAX + 1); i++) { DEBUG_VALIDATE_BUG_ON(j > size_unique_arr); if (unique_list[i] == RANGE_PORT) { final_arr[j].port = (uint16_t)i; final_arr[j++].single = false; } else if (unique_list[i] == SINGLE_PORT) { final_arr[j].port = (uint16_t)i; final_arr[j++].single = true; } } } /** * \brief Function to create the list of ports with the smallest ranges * by resolving overlaps and end point conditions. These contain the * correct SGHs as well after going over the interval tree to find * any range overlaps. * * \param de_ctx Detection Engine Context * \param unique_list Final list of unique port points * \param size_list Size of the unique_list * \param it Pointer to the interval tree * \param list Pointer to the list where final ports will be stored * * \return 0 on success, -1 otherwise */ static inline int CreatePortList(DetectEngineCtx *de_ctx, const uint8_t *unique_list, const uint32_t size_list, SCPortIntervalTree *it, DetectPort **list) { /* Only do the operations if there is at least one unique port */ if (size_list == 0) return 0; UniquePortPoint *final_unique_points = (UniquePortPoint *)SCCalloc(size_list, sizeof(UniquePortPoint)); if (final_unique_points == NULL) return -1; SetFinalUniquePortPoints(unique_list, size_list, final_unique_points); /* Handle edge case when there is just one unique port */ if (size_list == 1) { SCPortIntervalFindOverlappingRanges( de_ctx, final_unique_points[0].port, final_unique_points[0].port, &it->tree, list); } else { UniquePortPoint *p1 = &final_unique_points[0]; UniquePortPoint *p2 = &final_unique_points[1]; uint16_t port = p1 ? p1->port : 0; // just for cppcheck uint16_t port2 = p2->port; for (uint32_t i = 1; i < size_list; i++) { DEBUG_VALIDATE_BUG_ON(port > port2); if ((p1 && p1->single) && p2->single) { SCPortIntervalFindOverlappingRanges(de_ctx, port, port, &it->tree, list); SCPortIntervalFindOverlappingRanges(de_ctx, port2, port2, &it->tree, list); port = port2 + 1; } else if (p1 && p1->single) { SCPortIntervalFindOverlappingRanges(de_ctx, port, port, &it->tree, list); if ((port2 > port + 1)) { SCPortIntervalFindOverlappingRanges( de_ctx, port + 1, port2 - 1, &it->tree, list); port = port2; } else { port = port + 1; } } else if (p2->single) { /* If port2 is boundary and less or equal to port + 1, create a range * keeping the boundary away as it is single port */ if ((port2 >= port + 1)) { SCPortIntervalFindOverlappingRanges(de_ctx, port, port2 - 1, &it->tree, list); } /* Deal with port2 as it is a single port */ SCPortIntervalFindOverlappingRanges(de_ctx, port2, port2, &it->tree, list); port = port2 + 1; } else { if ((port2 > port + 1)) { SCPortIntervalFindOverlappingRanges(de_ctx, port, port2 - 1, &it->tree, list); port = port2; } else { SCPortIntervalFindOverlappingRanges(de_ctx, port, port2, &it->tree, list); port = port2 + 1; } } /* if the current port matches the p2->port, assign it to p1 so that * there is a UniquePortPoint object to check other info like whether * the port with this value is single */ if (port == p2->port) { p1 = p2; } else { p1 = NULL; } if (i + 1 < size_list) { p2 = &final_unique_points[i + 1]; port2 = p2->port; } } } /* final_unique_points array is no longer needed */ SCFree(final_unique_points); return 0; } static DetectPort *RulesGroupByPorts(DetectEngineCtx *de_ctx, uint8_t ipproto, uint32_t direction) { /* step 1: create a hash of 'DetectPort' objects based on all the * rules. Each object will have a SGH with the sigs added * that belong to the SGH. */ DetectPortHashInit(de_ctx); uint32_t size_unique_port_arr = 0; const Signature *s = de_ctx->sig_list; DetectPort *list = NULL; uint8_t *unique_port_points = (uint8_t *)SCCalloc(UINT16_MAX + 1, sizeof(uint8_t)); if (unique_port_points == NULL) return NULL; while (s) { /* IP Only rules are handled separately */ if (s->type == SIG_TYPE_IPONLY) goto next; /* Protocol does not match the Signature protocol and is neither IP or pkthdr */ if (!(s->proto.proto[ipproto / 8] & (1<<(ipproto % 8)) || (s->proto.flags & DETECT_PROTO_ANY))) goto next; /* Direction does not match Signature direction */ if (direction == SIG_FLAG_TOSERVER) { if (!(s->flags & SIG_FLAG_TOSERVER)) goto next; } else if (direction == SIG_FLAG_TOCLIENT) { if (!(s->flags & SIG_FLAG_TOCLIENT)) goto next; } /* see if we want to exclude directionless sigs that really care only for * to_server syn scans/floods */ if ((direction == SIG_FLAG_TOCLIENT) && DetectFlagsSignatureNeedsSynOnlyPackets(s) && ((s->flags & (SIG_FLAG_TOSERVER | SIG_FLAG_TOCLIENT)) == (SIG_FLAG_TOSERVER | SIG_FLAG_TOCLIENT)) && (!(s->dp->port == 0 && s->dp->port2 == 65535))) { SCLogWarning("rule %u: SYN-only to port(s) %u:%u " "w/o direction specified, disabling for toclient direction", s->id, s->dp->port, s->dp->port2); goto next; } DetectPort *p = NULL; if (direction == SIG_FLAG_TOSERVER) p = s->dp; else if (direction == SIG_FLAG_TOCLIENT) p = s->sp; else BUG_ON(1); int wl = s->init_data->score; while (p) { int pwl = PortIsPriority(de_ctx, p, ipproto) ? DETECT_PGSCORE_RULE_PORT_PRIORITIZED : 0; pwl = MAX(wl,pwl); DetectPort *lookup = DetectPortHashLookup(de_ctx, p); if (lookup) { SigGroupHeadAppendSig(de_ctx, &lookup->sh, s); lookup->sh->init->score = MAX(lookup->sh->init->score, pwl); } else { DetectPort *tmp2 = DetectPortCopySingle(de_ctx, p); BUG_ON(tmp2 == NULL); SigGroupHeadAppendSig(de_ctx, &tmp2->sh, s); tmp2->sh->init->score = pwl; DetectPortHashAdd(de_ctx, tmp2); size_unique_port_arr = SetUniquePortPoints(tmp2, unique_port_points, size_unique_port_arr); } p = p->next; } next: s = s->next; } /* step 2: create a list of the smallest port ranges with * appropriate SGHs */ /* Create an interval tree of all the given ports to make the search * for overlaps later on easier */ SCPortIntervalTree *it = SCPortIntervalTreeInit(); if (it == NULL) goto error; HashListTableBucket *htb = NULL; for (htb = HashListTableGetListHead(de_ctx->dport_hash_table); htb != NULL; htb = HashListTableGetListNext(htb)) { DetectPort *p = HashListTableGetListData(htb); if (SCPortIntervalInsert(de_ctx, it, p) != SC_OK) { SCLogDebug("Port was not inserted in the tree"); goto error; } } /* Create a sorted list of ports in ascending order after resolving overlaps * and corresponding SGHs */ if (CreatePortList(de_ctx, unique_port_points, size_unique_port_arr, it, &list) < 0) goto error; /* unique_port_points array is no longer needed */ SCFree(unique_port_points); /* Port hashes are no longer needed */ DetectPortHashFree(de_ctx); SCLogDebug("rules analyzed"); /* step 3: group the list and shrink it if necessary */ DetectPort *newlist = NULL; uint16_t groupmax = (direction == SIG_FLAG_TOCLIENT) ? de_ctx->max_uniq_toclient_groups : de_ctx->max_uniq_toserver_groups; CreateGroupedPortList(de_ctx, list, &newlist, groupmax, SortCompare); list = newlist; /* step 4: deduplicate the SGH's */ SigGroupHeadHashFree(de_ctx); SigGroupHeadHashInit(de_ctx); uint32_t cnt = 0; uint32_t own = 0; uint32_t ref = 0; DetectPort *iter; for (iter = list ; iter != NULL; iter = iter->next) { BUG_ON (iter->sh == NULL); DEBUG_VALIDATE_BUG_ON(own + ref != cnt); cnt++; SigGroupHead *lookup_sgh = SigGroupHeadHashLookup(de_ctx, iter->sh); if (lookup_sgh == NULL) { SCLogDebug("port group %p sgh %p is the original", iter, iter->sh); SigGroupHeadSetSigCnt(iter->sh, 0); SigGroupHeadBuildMatchArray(de_ctx, iter->sh, 0); SigGroupHeadSetProtoAndDirection(iter->sh, ipproto, direction); SigGroupHeadHashAdd(de_ctx, iter->sh); SigGroupHeadStore(de_ctx, iter->sh); iter->flags |= PORT_SIGGROUPHEAD_COPY; own++; } else { SCLogDebug("port group %p sgh %p is a copy", iter, iter->sh); SigGroupHeadFree(de_ctx, iter->sh); iter->sh = lookup_sgh; iter->flags |= PORT_SIGGROUPHEAD_COPY; ref++; } } #if 0 for (iter = list ; iter != NULL; iter = iter->next) { SCLogInfo("PORT %u-%u %p (sgh=%s, prioritized=%s/%d)", iter->port, iter->port2, iter->sh, iter->flags & PORT_SIGGROUPHEAD_COPY ? "ref" : "own", iter->sh->init->score ? "true" : "false", iter->sh->init->score); } #endif SCLogPerf("%s %s: %u port groups, %u unique SGH's, %u copies", ipproto == 6 ? "TCP" : "UDP", direction == SIG_FLAG_TOSERVER ? "toserver" : "toclient", cnt, own, ref); SCPortIntervalTreeFree(de_ctx, it); return list; error: if (unique_port_points != NULL) SCFree(unique_port_points); if (it != NULL) SCPortIntervalTreeFree(de_ctx, it); return NULL; } void SignatureSetType(DetectEngineCtx *de_ctx, Signature *s) { BUG_ON(s->type != SIG_TYPE_NOT_SET); int iponly = 0; if (s->init_data->hook.type == SIGNATURE_HOOK_TYPE_APP) { s->type = SIG_TYPE_APP_TX; SCLogDebug("%u: set to app_tx due to hook type app", s->id); SCReturn; } /* see if the sig is dp only */ if (SignatureIsPDOnly(de_ctx, s) == 1) { s->type = SIG_TYPE_PDONLY; /* see if the sig is ip only */ } else if ((iponly = SignatureIsIPOnly(de_ctx, s)) > 0) { if (iponly == 1) { s->type = SIG_TYPE_IPONLY; } else if (iponly == 2) { s->type = SIG_TYPE_LIKE_IPONLY; } } else if (SignatureIsDEOnly(de_ctx, s) == 1) { s->type = SIG_TYPE_DEONLY; } else { const bool has_match = s->init_data->smlists[DETECT_SM_LIST_MATCH] != NULL; const bool has_pmatch = s->init_data->smlists[DETECT_SM_LIST_PMATCH] != NULL; bool has_buffer_frame_engine = false; bool has_buffer_packet_engine = false; bool has_buffer_app_engine = false; for (uint32_t x = 0; x < s->init_data->buffer_index; x++) { const uint32_t id = s->init_data->buffers[x].id; if (DetectEngineBufferTypeSupportsPacketGetById(de_ctx, id)) { has_buffer_packet_engine = true; } else if (DetectEngineBufferTypeSupportsFramesGetById(de_ctx, id)) { has_buffer_frame_engine = true; } else { has_buffer_app_engine = true; } } if (has_buffer_packet_engine) { s->type = SIG_TYPE_PKT; } else if (has_buffer_frame_engine || has_buffer_app_engine) { s->type = SIG_TYPE_APP_TX; } else if (has_pmatch) { if ((s->flags & (SIG_FLAG_REQUIRE_PACKET | SIG_FLAG_REQUIRE_STREAM)) == SIG_FLAG_REQUIRE_PACKET) { s->type = SIG_TYPE_PKT; } else if ((s->flags & (SIG_FLAG_REQUIRE_PACKET | SIG_FLAG_REQUIRE_STREAM)) == SIG_FLAG_REQUIRE_STREAM) { s->type = SIG_TYPE_STREAM; } else { s->type = SIG_TYPE_PKT_STREAM; } } else if (has_match) { s->type = SIG_TYPE_PKT; /* app-layer but no inspect engines */ } else if (s->flags & SIG_FLAG_APPLAYER) { s->type = SIG_TYPE_APPLAYER; } else { s->type = SIG_TYPE_PKT; } } } /** * \brief Preprocess signature, classify ip-only, etc, build sig array * * \param de_ctx Pointer to the Detection Engine Context * * \retval 0 on success * \retval -1 on failure */ int SigPrepareStage1(DetectEngineCtx *de_ctx) { uint32_t cnt_iponly = 0; uint32_t cnt_payload = 0; uint32_t cnt_applayer = 0; uint32_t cnt_deonly = 0; if (!(de_ctx->flags & DE_QUIET)) { SCLogDebug("building signature grouping structure, stage 1: " "preprocessing rules..."); } de_ctx->sig_array_len = DetectEngineGetMaxSigId(de_ctx); de_ctx->sig_array = (Signature **)SCCalloc(de_ctx->sig_array_len, sizeof(Signature *)); if (de_ctx->sig_array == NULL) goto error; /* now for every rule add the source group */ for (Signature *s = de_ctx->sig_list; s != NULL; s = s->next) { de_ctx->sig_array[s->iid] = s; SCLogDebug("Signature %" PRIu32 ", internal id %" PRIu32 ", ptrs %p %p ", s->id, s->iid, s, de_ctx->sig_array[s->iid]); if (s->type == SIG_TYPE_PDONLY) { SCLogDebug("Signature %"PRIu32" is considered \"PD only\"", s->id); } else if (s->type == SIG_TYPE_IPONLY) { SCLogDebug("Signature %"PRIu32" is considered \"IP only\"", s->id); cnt_iponly++; } else if (SignatureIsInspectingPayload(de_ctx, s) == 1) { SCLogDebug("Signature %"PRIu32" is considered \"Payload inspecting\"", s->id); cnt_payload++; } else if (s->type == SIG_TYPE_DEONLY) { SCLogDebug("Signature %"PRIu32" is considered \"Decoder Event only\"", s->id); cnt_deonly++; } else if (s->flags & SIG_FLAG_APPLAYER) { SCLogDebug("Signature %"PRIu32" is considered \"Applayer inspecting\"", s->id); cnt_applayer++; } #ifdef DEBUG if (SCLogDebugEnabled()) { uint16_t colen = 0; char copresent = 0; SigMatch *sm; DetectContentData *co; for (sm = s->init_data->smlists[DETECT_SM_LIST_MATCH]; sm != NULL; sm = sm->next) { if (sm->type != DETECT_CONTENT) continue; copresent = 1; co = (DetectContentData *)sm->ctx; if (co->content_len > colen) colen = co->content_len; } if (copresent && colen == 1) { SCLogDebug("signature %8u content maxlen 1", s->id); for (int proto = 0; proto < 256; proto++) { if (s->proto.proto[(proto/8)] & (1<<(proto%8))) SCLogDebug("=> proto %" PRId32 "", proto); } } } #endif /* DEBUG */ if (RuleMpmIsNegated(s)) { s->flags |= SIG_FLAG_MPM_NEG; } SignatureCreateMask(s); DetectContentPropagateLimits(s); SigParseApplyDsizeToContent(s); RuleSetScore(s); /* run buffer type callbacks if any */ for (int x = 0; x < DETECT_SM_LIST_MAX; x++) { if (s->init_data->smlists[x]) DetectEngineBufferRunSetupCallback(de_ctx, x, s); } for (uint32_t x = 0; x < s->init_data->buffer_index; x++) { DetectEngineBufferRunSetupCallback(de_ctx, s->init_data->buffers[x].id, s); } de_ctx->sig_cnt++; } if (!(de_ctx->flags & DE_QUIET)) { if (strlen(de_ctx->config_prefix) > 0) SCLogInfo("tenant id %d: %" PRIu32 " signatures processed. %" PRIu32 " are IP-only " "rules, %" PRIu32 " are inspecting packet payload, %" PRIu32 " inspect application layer, %" PRIu32 " are decoder event only", de_ctx->tenant_id, de_ctx->sig_cnt, cnt_iponly, cnt_payload, cnt_applayer, cnt_deonly); else SCLogInfo("%" PRIu32 " signatures processed. %" PRIu32 " are IP-only " "rules, %" PRIu32 " are inspecting packet payload, %" PRIu32 " inspect application layer, %" PRIu32 " are decoder event only", de_ctx->sig_cnt, cnt_iponly, cnt_payload, cnt_applayer, cnt_deonly); SCLogConfig("building signature grouping structure, stage 1: " "preprocessing rules... complete"); } if (DetectFlowbitsAnalyze(de_ctx) != 0) goto error; return 0; error: return -1; } /** * \internal * \brief add a decoder event signature to the detection engine ctx */ static void DetectEngineAddDecoderEventSig(DetectEngineCtx *de_ctx, Signature *s) { SCLogDebug("adding signature %"PRIu32" to the decoder event sgh", s->id); SigGroupHeadAppendSig(de_ctx, &de_ctx->decoder_event_sgh, s); } static void DetectEngineAddSigToPreStreamHook(DetectEngineCtx *de_ctx, Signature *s) { SCLogDebug("adding signature %" PRIu32 " to the pre_stream hook sgh", s->id); if ((s->flags & (SIG_FLAG_TOSERVER | SIG_FLAG_TOCLIENT)) == (SIG_FLAG_TOSERVER | SIG_FLAG_TOCLIENT)) { SigGroupHeadAppendSig(de_ctx, &de_ctx->pre_stream_sgh[0], s); SigGroupHeadAppendSig(de_ctx, &de_ctx->pre_stream_sgh[1], s); } else if (s->flags & SIG_FLAG_TOSERVER) { SigGroupHeadAppendSig(de_ctx, &de_ctx->pre_stream_sgh[0], s); } else if (s->flags & SIG_FLAG_TOCLIENT) { SigGroupHeadAppendSig(de_ctx, &de_ctx->pre_stream_sgh[1], s); } } static void DetectEngineAddSigToPreFlowHook(DetectEngineCtx *de_ctx, Signature *s) { SCLogDebug("adding signature %" PRIu32 " to the pre_flow hook sgh", s->id); SigGroupHeadAppendSig(de_ctx, &de_ctx->pre_flow_sgh, s); } /** * \brief Fill the global src group head, with the sigs included * * \param de_ctx Pointer to the Detection Engine Context whose Signatures have * to be processed * * \retval 0 On success * \retval -1 On failure */ int SigPrepareStage2(DetectEngineCtx *de_ctx) { SCLogDebug("building signature grouping structure, stage 2: " "building source address lists..."); IPOnlyInit(de_ctx, &de_ctx->io_ctx); de_ctx->flow_gh[1].tcp = RulesGroupByPorts(de_ctx, IPPROTO_TCP, SIG_FLAG_TOSERVER); de_ctx->flow_gh[0].tcp = RulesGroupByPorts(de_ctx, IPPROTO_TCP, SIG_FLAG_TOCLIENT); de_ctx->flow_gh[1].udp = RulesGroupByPorts(de_ctx, IPPROTO_UDP, SIG_FLAG_TOSERVER); de_ctx->flow_gh[0].udp = RulesGroupByPorts(de_ctx, IPPROTO_UDP, SIG_FLAG_TOCLIENT); /* Setup the other IP Protocols (so not TCP/UDP) */ RulesGroupByIPProto(de_ctx); /* now for every rule add the source group to our temp lists */ for (Signature *s = de_ctx->sig_list; s != NULL; s = s->next) { SCLogDebug("s->id %"PRIu32, s->id); if (s->type == SIG_TYPE_IPONLY) { IPOnlyAddSignature(de_ctx, &de_ctx->io_ctx, s); } else if (s->type == SIG_TYPE_DEONLY) { DetectEngineAddDecoderEventSig(de_ctx, s); } else if (s->type == SIG_TYPE_PKT && s->init_data->hook.type == SIGNATURE_HOOK_TYPE_PKT && s->init_data->hook.t.pkt.ph == SIGNATURE_HOOK_PKT_PRE_STREAM) { DetectEngineAddSigToPreStreamHook(de_ctx, s); } else if (s->type == SIG_TYPE_PKT && s->init_data->hook.type == SIGNATURE_HOOK_TYPE_PKT && s->init_data->hook.t.pkt.ph == SIGNATURE_HOOK_PKT_PRE_FLOW) { DetectEngineAddSigToPreFlowHook(de_ctx, s); } } IPOnlyPrepare(de_ctx); IPOnlyPrint(de_ctx, &de_ctx->io_ctx); return 0; } static void DetectEngineBuildDecoderEventSgh(DetectEngineCtx *de_ctx) { if (de_ctx->decoder_event_sgh == NULL) return; uint32_t max_idx = DetectEngineGetMaxSigId(de_ctx); SigGroupHeadSetSigCnt(de_ctx->decoder_event_sgh, max_idx); SigGroupHeadBuildMatchArray(de_ctx, de_ctx->decoder_event_sgh, max_idx); } static void DetectEngineBuildPreStreamHookSghs(DetectEngineCtx *de_ctx) { uint32_t max_idx = DetectEngineGetMaxSigId(de_ctx); if (de_ctx->pre_stream_sgh[0] != NULL) { SigGroupHeadSetSigCnt(de_ctx->pre_stream_sgh[0], max_idx); SigGroupHeadBuildMatchArray(de_ctx, de_ctx->pre_stream_sgh[0], max_idx); PrefilterSetupRuleGroup(de_ctx, de_ctx->pre_stream_sgh[0]); } if (de_ctx->pre_stream_sgh[1] != NULL) { SigGroupHeadSetSigCnt(de_ctx->pre_stream_sgh[1], max_idx); SigGroupHeadBuildMatchArray(de_ctx, de_ctx->pre_stream_sgh[1], max_idx); PrefilterSetupRuleGroup(de_ctx, de_ctx->pre_stream_sgh[1]); } if (de_ctx->pre_stream_sgh[0] != NULL || de_ctx->pre_stream_sgh[1] != NULL) { de_ctx->PreStreamHook = DetectPreStream; } } static void DetectEngineBuildPreFlowHookSghs(DetectEngineCtx *de_ctx) { if (de_ctx->pre_flow_sgh != NULL) { uint32_t max_idx = DetectEngineGetMaxSigId(de_ctx); SigGroupHeadSetSigCnt(de_ctx->pre_flow_sgh, max_idx); SigGroupHeadBuildMatchArray(de_ctx, de_ctx->pre_flow_sgh, max_idx); PrefilterSetupRuleGroup(de_ctx, de_ctx->pre_flow_sgh); de_ctx->PreFlowHook = DetectPreFlow; } } int SigPrepareStage3(DetectEngineCtx *de_ctx) { /* prepare the decoder event sgh */ DetectEngineBuildDecoderEventSgh(de_ctx); /* pre_flow hook sgh */ DetectEngineBuildPreFlowHookSghs(de_ctx); /* pre_stream hook sghs */ DetectEngineBuildPreStreamHookSghs(de_ctx); return 0; } int SigAddressCleanupStage1(DetectEngineCtx *de_ctx) { BUG_ON(de_ctx == NULL); SCLogDebug("cleaning up signature grouping structure..."); if (de_ctx->decoder_event_sgh) SigGroupHeadFree(de_ctx, de_ctx->decoder_event_sgh); de_ctx->decoder_event_sgh = NULL; if (de_ctx->pre_flow_sgh) SigGroupHeadFree(de_ctx, de_ctx->pre_flow_sgh); de_ctx->pre_flow_sgh = NULL; if (de_ctx->pre_stream_sgh[0]) SigGroupHeadFree(de_ctx, de_ctx->pre_stream_sgh[0]); de_ctx->pre_stream_sgh[0] = NULL; if (de_ctx->pre_stream_sgh[1]) SigGroupHeadFree(de_ctx, de_ctx->pre_stream_sgh[1]); de_ctx->pre_stream_sgh[1] = NULL; for (int f = 0; f < FLOW_STATES; f++) { for (int p = 0; p < 256; p++) { de_ctx->flow_gh[f].sgh[p] = NULL; } /* free lookup lists */ DetectPortCleanupList(de_ctx, de_ctx->flow_gh[f].tcp); de_ctx->flow_gh[f].tcp = NULL; DetectPortCleanupList(de_ctx, de_ctx->flow_gh[f].udp); de_ctx->flow_gh[f].udp = NULL; } for (uint32_t idx = 0; idx < de_ctx->sgh_array_cnt; idx++) { SigGroupHead *sgh = de_ctx->sgh_array[idx]; if (sgh == NULL) continue; SCLogDebug("sgh %p", sgh); SigGroupHeadFree(de_ctx, sgh); } SCFree(de_ctx->sgh_array); de_ctx->sgh_array = NULL; de_ctx->sgh_array_cnt = 0; de_ctx->sgh_array_size = 0; IPOnlyDeinit(de_ctx, &de_ctx->io_ctx); SCLogDebug("cleaning up signature grouping structure... complete"); return 0; } #if 0 static void DbgPrintSigs(DetectEngineCtx *de_ctx, SigGroupHead *sgh) { if (sgh == NULL) { printf("\n"); return; } uint32_t sig; for (sig = 0; sig < sgh->sig_cnt; sig++) { printf("%" PRIu32 " ", sgh->match_array[sig]->id); } printf("\n"); } static void DbgPrintSigs2(DetectEngineCtx *de_ctx, SigGroupHead *sgh) { if (sgh == NULL || sgh->init == NULL) { printf("\n"); return; } uint32_t sig; for (sig = 0; sig < DetectEngineGetMaxSigId(de_ctx); sig++) { if (sgh->init->sig_array[(sig/8)] & (1<<(sig%8))) { printf("%" PRIu32 " ", de_ctx->sig_array[sig]->id); } } printf("\n"); } #endif /** \brief finalize preparing sgh's */ int SigPrepareStage4(DetectEngineCtx *de_ctx) { SCEnter(); //SCLogInfo("sgh's %"PRIu32, de_ctx->sgh_array_cnt); uint32_t cnt = 0; for (uint32_t idx = 0; idx < de_ctx->sgh_array_cnt; idx++) { SigGroupHead *sgh = de_ctx->sgh_array[idx]; if (sgh == NULL) continue; SCLogDebug("sgh %p", sgh); SigGroupHeadSetupFiles(de_ctx, sgh); SCLogDebug("filestore count %u", sgh->filestore_cnt); PrefilterSetupRuleGroup(de_ctx, sgh); sgh->id = idx; cnt++; } SCLogPerf("Unique rule groups: %u", cnt); MpmStoreReportStats(de_ctx); if (de_ctx->decoder_event_sgh != NULL) { /* no need to set filestore count here as that would make a * signature not decode event only. */ PrefilterSetupRuleGroup(de_ctx, de_ctx->decoder_event_sgh); } int dump_grouping = 0; (void)SCConfGetBool("detect.profiling.grouping.dump-to-disk", &dump_grouping); if (dump_grouping) { int add_rules = 0; (void)SCConfGetBool("detect.profiling.grouping.include-rules", &add_rules); int add_mpm_stats = 0; (void)SCConfGetBool("detect.profiling.grouping.include-mpm-stats", &add_mpm_stats); RulesDumpGrouping(de_ctx, add_rules, add_mpm_stats); } for (uint32_t idx = 0; idx < de_ctx->sgh_array_cnt; idx++) { SigGroupHead *sgh = de_ctx->sgh_array[idx]; if (sgh == NULL) continue; SigGroupHeadInitDataFree(sgh->init); sgh->init = NULL; } /* cleanup the hashes now since we won't need them * after the initialization phase. */ SigGroupHeadHashFree(de_ctx); #ifdef PROFILING SCProfilingSghInitCounters(de_ctx); #endif SCReturnInt(0); } extern bool rule_engine_analysis_set; /** \internal * \brief perform final per signature setup tasks * * - Create SigMatchData arrays from the init only SigMatch lists * - Setup per signature inspect engines * - remove signature init data. */ static int SigMatchPrepare(DetectEngineCtx *de_ctx) { SCEnter(); Signature *s = de_ctx->sig_list; for (; s != NULL; s = s->next) { /* set up inspect engines */ DetectEngineAppInspectionEngine2Signature(de_ctx, s); /* built-ins */ for (int type = 0; type < DETECT_SM_LIST_MAX; type++) { /* skip PMATCH if it is used in a stream 'app engine' instead */ if (type == DETECT_SM_LIST_PMATCH && (s->init_data->init_flags & SIG_FLAG_INIT_STATE_MATCH)) continue; SigMatch *sm = s->init_data->smlists[type]; s->sm_arrays[type] = SigMatchList2DataArray(sm); } /* set up the pkt inspection engines */ DetectEnginePktInspectionSetup(s); if (rule_engine_analysis_set) { EngineAnalysisAddAllRulePatterns(de_ctx, s); EngineAnalysisRules2(de_ctx, s); } /* free lists. Ctx' are xferred to sm_arrays so won't get freed */ for (uint32_t i = 0; i < DETECT_SM_LIST_MAX; i++) { SigMatch *sm = s->init_data->smlists[i]; while (sm != NULL) { SigMatch *nsm = sm->next; SigMatchFree(de_ctx, sm); sm = nsm; } } for (uint32_t i = 0; i < (uint32_t)s->init_data->transforms.cnt; i++) { if (s->init_data->transforms.transforms[i].options) { int transform = s->init_data->transforms.transforms[i].transform; sigmatch_table[transform].Free( de_ctx, s->init_data->transforms.transforms[i].options); s->init_data->transforms.transforms[i].options = NULL; } } for (uint32_t x = 0; x < s->init_data->buffer_index; x++) { SigMatch *sm = s->init_data->buffers[x].head; while (sm != NULL) { SigMatch *nsm = sm->next; SigMatchFree(de_ctx, sm); sm = nsm; } } if (s->init_data->cidr_dst != NULL) IPOnlyCIDRListFree(s->init_data->cidr_dst); if (s->init_data->cidr_src != NULL) IPOnlyCIDRListFree(s->init_data->cidr_src); SCFree(s->init_data->buffers); SCFree(s->init_data->rule_state_dependant_sids_array); SCFree(s->init_data->rule_state_flowbits_ids_array); SCFree(s->init_data); s->init_data = NULL; } DumpPatterns(de_ctx); SCReturnInt(0); } /** * \brief Convert the signature list into the runtime match structure. * * \param de_ctx Pointer to the Detection Engine Context whose Signatures have * to be processed * * \retval 0 On Success. * \retval -1 On failure. */ int SigGroupBuild(DetectEngineCtx *de_ctx) { Signature *s = de_ctx->sig_list; /* Assign the unique order id of signatures after sorting, * so the IP Only engine process them in order too. Also * reset the old signums and assign new signums. We would * have experienced Sig reordering by now, hence the new * signums. */ de_ctx->signum = 0; while (s != NULL) { s->iid = de_ctx->signum++; s = s->next; } if (DetectSetFastPatternAndItsId(de_ctx) < 0) return -1; SigInitStandardMpmFactoryContexts(de_ctx); if (SigPrepareStage1(de_ctx) != 0) { FatalError("initializing the detection engine failed"); } if (SigPrepareStage2(de_ctx) != 0) { FatalError("initializing the detection engine failed"); } if (SigPrepareStage3(de_ctx) != 0) { FatalError("initializing the detection engine failed"); } if (SigPrepareStage4(de_ctx) != 0) { FatalError("initializing the detection engine failed"); } int r = DetectMpmPrepareBuiltinMpms(de_ctx); r |= DetectMpmPrepareAppMpms(de_ctx); r |= DetectMpmPreparePktMpms(de_ctx); r |= DetectMpmPrepareFrameMpms(de_ctx); if (r != 0) { FatalError("initializing the detection engine failed"); } if (SigMatchPrepare(de_ctx) != 0) { FatalError("initializing the detection engine failed"); } #ifdef PROFILING SCProfilingKeywordInitCounters(de_ctx); SCProfilingPrefilterInitCounters(de_ctx); de_ctx->profile_match_logging_threshold = UINT_MAX; // disabled intmax_t v = 0; if (SCConfGetInt("detect.profiling.inspect-logging-threshold", &v) == 1) de_ctx->profile_match_logging_threshold = (uint32_t)v; #endif #ifdef PROFILE_RULES SCProfilingRuleInitCounters(de_ctx); #endif if (!DetectEngineMultiTenantEnabled()) { VarNameStoreActivate(); } if (EngineModeIsFirewall()) { FirewallAnalyzer(de_ctx); } return 0; } int SigGroupCleanup (DetectEngineCtx *de_ctx) { SigAddressCleanupStage1(de_ctx); return 0; }