You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
suricata/src/detect-engine-prefilter.c

1662 lines
56 KiB
C

/* Copyright (C) 2016-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.
*/
/**
* \file
*
* \author Victor Julien <victor@inliniac.net>
*
* Prefilter engine
*
* Prefilter engines have as purpose to check for a critical common part of
* a set of rules. If the condition is present in the traffic, the rules
* will have to be inspected individually. Otherwise, the rules can be
* skipped.
*
* The best example of this is the MPM. From each rule take a pattern and
* add it to the MPM state machine. Inspect that in one step and only
* individually inspect the rules that had a match in MPM.
*
* This prefilter API is designed to abstract this logic so that it becomes
* easier to add other types of prefilters.
*
* The prefilter engines are structured as a simple list of engines. Each
* engine checks for a condition using it's callback function and private
* data. It then adds the rule match candidates to the PrefilterRuleStore
* structure.
*
* After the engines have run the resulting list of match candidates is
* sorted by the rule id's so that the individual inspection happens in
* the correct order.
*/
#include "suricata-common.h"
#include "suricata.h"
#include "detect-engine.h"
#include "detect-engine-prefilter.h"
#include "detect-engine-mpm.h"
#include "detect-engine-frame.h"
#include "detect-engine-uint.h"
#include "app-layer-parser.h"
#include "app-layer-htp.h"
#include "util-profiling.h"
#include "util-validate.h"
#include "util-hash-string.h"
static int PrefilterStoreGetId(DetectEngineCtx *de_ctx,
const char *name, void (*FreeFunc)(void *));
static const PrefilterStore *PrefilterStoreGetStore(const DetectEngineCtx *de_ctx,
const uint32_t id);
static inline void QuickSortSigIntId(SigIntId *sids, uint32_t n)
{
if (n < 2)
return;
SigIntId p = sids[n / 2];
SigIntId *l = sids;
SigIntId *r = sids + n - 1;
while (l <= r) {
if (*l < p)
l++;
else if (*r > p)
r--;
else {
SigIntId t = *l;
*l = *r;
*r = t;
l++;
r--;
}
}
QuickSortSigIntId(sids, r - sids + 1);
QuickSortSigIntId(l, sids + n - l);
}
/**
* \brief run prefilter engines on a transaction
*/
void DetectRunPrefilterTx(DetectEngineThreadCtx *det_ctx,
const SigGroupHead *sgh,
Packet *p,
const uint8_t ipproto,
const uint8_t flow_flags,
const AppProto alproto,
void *alstate,
DetectTransaction *tx)
{
/* reset rule store */
det_ctx->pmq.rule_id_array_cnt = 0;
SCLogDebug("packet %" PRIu64 " tx %p progress %d tx->detect_progress %02x", p->pcap_cnt,
tx->tx_ptr, tx->tx_progress, tx->detect_progress);
PrefilterEngine *engine = sgh->tx_engines;
do {
// based on flow alproto, and engine, we get right tx_ptr
void *tx_ptr = DetectGetInnerTx(tx->tx_ptr, alproto, engine->alproto, flow_flags);
if (tx_ptr == NULL) {
// incompatible engine->alproto with flow alproto
goto next;
}
if (engine->ctx.tx_min_progress > tx->tx_progress)
break;
if (tx->tx_progress > engine->ctx.tx_min_progress) {
/* if state value is at or beyond engine state, we can skip it. It means we ran at
* least once already. */
if (tx->detect_progress > engine->ctx.tx_min_progress) {
SCLogDebug("tx already marked progress as beyond engine: %u > %u",
tx->detect_progress, engine->ctx.tx_min_progress);
goto next;
}
}
PREFILTER_PROFILING_START(det_ctx);
engine->cb.PrefilterTx(
det_ctx, engine->pectx, p, p->flow, tx_ptr, tx->tx_id, tx->tx_data_ptr, flow_flags);
PREFILTER_PROFILING_END(det_ctx, engine->gid);
if (tx->tx_progress > engine->ctx.tx_min_progress && engine->is_last_for_progress) {
/* track with an offset of one, so that tx->progress 0 complete is tracked
* as 1, progress 1 as 2, etc. This is to allow 0 to mean: nothing tracked, even
* though a parser may use 0 as a valid value. */
tx->detect_progress = engine->ctx.tx_min_progress + 1;
SCLogDebug("tx->tx_progress %d engine->ctx.tx_min_progress %d "
"engine->is_last_for_progress %d => tx->detect_progress updated to %02x",
tx->tx_progress, engine->ctx.tx_min_progress, engine->is_last_for_progress,
tx->detect_progress);
}
next:
if (engine->is_last)
break;
engine++;
} while (1);
/* Sort the rule list to lets look at pmq.
* NOTE due to merging of 'stream' pmqs we *MAY* have duplicate entries */
if (likely(det_ctx->pmq.rule_id_array_cnt > 1)) {
PACKET_PROFILING_DETECT_START(p, PROF_DETECT_PF_SORT1);
QuickSortSigIntId(det_ctx->pmq.rule_id_array, det_ctx->pmq.rule_id_array_cnt);
PACKET_PROFILING_DETECT_END(p, PROF_DETECT_PF_SORT1);
}
}
/** \brief invoke post-rule match "prefilter" engines
*
* Invoke prefilter engines that depend on a rule match to run.
* e.g. the flowbits:set prefilter that adds sids that depend on
* a flowbit "set" to the match array.
*/
void PrefilterPostRuleMatch(
DetectEngineThreadCtx *det_ctx, const SigGroupHead *sgh, Packet *p, Flow *f)
{
SCLogDebug("post-rule-match engines %p", sgh->post_rule_match_engines);
if (sgh->post_rule_match_engines) {
PrefilterEngine *engine = sgh->post_rule_match_engines;
do {
SCLogDebug("running post-rule-match engine");
PREFILTER_PROFILING_START(det_ctx);
engine->cb.PrefilterPostRule(det_ctx, engine->pectx, p, f);
PREFILTER_PROFILING_END(det_ctx, engine->gid);
if (engine->is_last)
break;
engine++;
} while (1);
if (det_ctx->pmq.rule_id_array_cnt > 1) {
QuickSortSigIntId(det_ctx->pmq.rule_id_array, det_ctx->pmq.rule_id_array_cnt);
}
}
}
void Prefilter(DetectEngineThreadCtx *det_ctx, const SigGroupHead *sgh, Packet *p,
const uint8_t flags, const SignatureMask mask)
{
SCEnter();
#if 0
/* TODO review this check */
SCLogDebug("sgh %p frame_engines %p", sgh, sgh->frame_engines);
if (p->proto == IPPROTO_TCP && sgh->frame_engines && p->flow &&
p->flow->alproto != ALPROTO_UNKNOWN && p->flow->alparser != NULL) {
PACKET_PROFILING_DETECT_START(p, PROF_DETECT_PF_RECORD);
PrefilterFrames(det_ctx, sgh, p, flags, p->flow->alproto);
PACKET_PROFILING_DETECT_END(p, PROF_DETECT_PF_RECORD);
}
#endif
if (sgh->pkt_engines) {
PACKET_PROFILING_DETECT_START(p, PROF_DETECT_PF_PKT);
/* run packet engines */
PrefilterEngine *engine = sgh->pkt_engines;
do {
/* run engine if:
* mask matches
* no hook is used OR hook matches
*/
if (((engine->ctx.pkt.mask & mask) == engine->ctx.pkt.mask) &&
(engine->ctx.pkt.hook == 0 || (p->pkt_hooks & BIT_U16(engine->ctx.pkt.hook)))) {
PREFILTER_PROFILING_START(det_ctx);
engine->cb.Prefilter(det_ctx, p, engine->pectx);
PREFILTER_PROFILING_END(det_ctx, engine->gid);
}
if (engine->is_last)
break;
engine++;
} while (1);
PACKET_PROFILING_DETECT_END(p, PROF_DETECT_PF_PKT);
}
/* run payload inspecting engines */
if (sgh->payload_engines &&
(p->payload_len || (p->flags & PKT_DETECT_HAS_STREAMDATA)) &&
!(p->flags & PKT_NOPAYLOAD_INSPECTION))
{
PACKET_PROFILING_DETECT_START(p, PROF_DETECT_PF_PAYLOAD);
PrefilterEngine *engine = sgh->payload_engines;
while (1) {
PREFILTER_PROFILING_START(det_ctx);
engine->cb.Prefilter(det_ctx, p, engine->pectx);
PREFILTER_PROFILING_END(det_ctx, engine->gid);
if (engine->is_last)
break;
engine++;
}
PACKET_PROFILING_DETECT_END(p, PROF_DETECT_PF_PAYLOAD);
}
/* Sort the rule list to lets look at pmq.
* NOTE due to merging of 'stream' pmqs we *MAY* have duplicate entries */
if (likely(det_ctx->pmq.rule_id_array_cnt > 1)) {
PACKET_PROFILING_DETECT_START(p, PROF_DETECT_PF_SORT1);
QuickSortSigIntId(det_ctx->pmq.rule_id_array, det_ctx->pmq.rule_id_array_cnt);
PACKET_PROFILING_DETECT_END(p, PROF_DETECT_PF_SORT1);
}
SCReturn;
}
int PrefilterAppendEngine(DetectEngineCtx *de_ctx, SigGroupHead *sgh, PrefilterPktFn PrefilterFunc,
SignatureMask mask, enum SignatureHookPkt hook, void *pectx, void (*FreeFunc)(void *pectx),
const char *name)
{
if (sgh == NULL || PrefilterFunc == NULL || pectx == NULL)
return -1;
PrefilterEngineList *e = SCMallocAligned(sizeof(*e), CLS);
if (e == NULL)
return -1;
memset(e, 0x00, sizeof(*e));
// TODO right now we represent the hook in a u8 in the prefilter engine for space reasons.
BUG_ON(hook >= 8);
e->Prefilter = PrefilterFunc;
e->pectx = pectx;
e->Free = FreeFunc;
e->pkt_mask = mask;
e->pkt_hook = hook;
if (sgh->init->pkt_engines == NULL) {
sgh->init->pkt_engines = e;
} else {
PrefilterEngineList *t = sgh->init->pkt_engines;
while (t->next != NULL) {
t = t->next;
}
t->next = e;
e->id = t->id + 1;
}
e->name = name;
e->gid = PrefilterStoreGetId(de_ctx, e->name, e->Free);
return 0;
}
int PrefilterAppendPayloadEngine(DetectEngineCtx *de_ctx, SigGroupHead *sgh,
PrefilterPktFn PrefilterFunc, void *pectx, void (*FreeFunc)(void *pectx), const char *name)
{
if (sgh == NULL || PrefilterFunc == NULL || pectx == NULL)
return -1;
PrefilterEngineList *e = SCMallocAligned(sizeof(*e), CLS);
if (e == NULL)
return -1;
memset(e, 0x00, sizeof(*e));
e->Prefilter = PrefilterFunc;
e->pectx = pectx;
e->Free = FreeFunc;
if (sgh->init->payload_engines == NULL) {
sgh->init->payload_engines = e;
} else {
PrefilterEngineList *t = sgh->init->payload_engines;
while (t->next != NULL) {
t = t->next;
}
t->next = e;
e->id = t->id + 1;
}
e->name = name;
e->gid = PrefilterStoreGetId(de_ctx, e->name, e->Free);
return 0;
}
int PrefilterAppendTxEngine(DetectEngineCtx *de_ctx, SigGroupHead *sgh,
PrefilterTxFn PrefilterTxFunc, AppProto alproto, int tx_min_progress, void *pectx,
void (*FreeFunc)(void *pectx), const char *name)
{
if (sgh == NULL || PrefilterTxFunc == NULL || pectx == NULL)
return -1;
PrefilterEngineList *e = SCMallocAligned(sizeof(*e), CLS);
if (e == NULL)
return -1;
memset(e, 0x00, sizeof(*e));
e->PrefilterTx = PrefilterTxFunc;
e->pectx = pectx;
e->alproto = alproto;
// TODO change function prototype ?
DEBUG_VALIDATE_BUG_ON(tx_min_progress > UINT8_MAX);
e->tx_min_progress = (uint8_t)tx_min_progress;
e->Free = FreeFunc;
if (sgh->init->tx_engines == NULL) {
sgh->init->tx_engines = e;
} else {
PrefilterEngineList *t = sgh->init->tx_engines;
while (t->next != NULL) {
t = t->next;
}
t->next = e;
e->id = t->id + 1;
}
e->name = name;
e->gid = PrefilterStoreGetId(de_ctx, e->name, e->Free);
return 0;
}
int PrefilterAppendFrameEngine(DetectEngineCtx *de_ctx, SigGroupHead *sgh,
PrefilterFrameFn PrefilterFrameFunc, AppProto alproto, uint8_t frame_type, void *pectx,
void (*FreeFunc)(void *pectx), const char *name)
{
if (sgh == NULL || PrefilterFrameFunc == NULL || pectx == NULL)
return -1;
PrefilterEngineList *e = SCMallocAligned(sizeof(*e), CLS);
if (e == NULL)
return -1;
memset(e, 0x00, sizeof(*e));
e->frame_type = frame_type;
e->alproto = alproto;
e->PrefilterFrame = PrefilterFrameFunc;
e->pectx = pectx;
e->Free = FreeFunc;
if (sgh->init->frame_engines == NULL) {
sgh->init->frame_engines = e;
} else {
PrefilterEngineList *t = sgh->init->frame_engines;
while (t->next != NULL) {
t = t->next;
}
t->next = e;
e->id = t->id + 1;
}
e->name = name;
e->gid = PrefilterStoreGetId(de_ctx, e->name, e->Free);
return 0;
}
int PrefilterAppendPostRuleEngine(DetectEngineCtx *de_ctx, SigGroupHead *sgh,
void (*PrefilterPostRuleFunc)(
DetectEngineThreadCtx *det_ctx, const void *pectx, Packet *p, Flow *f),
void *pectx, void (*FreeFunc)(void *pectx), const char *name)
{
if (sgh == NULL || PrefilterPostRuleFunc == NULL || pectx == NULL)
return -1;
PrefilterEngineList *e = SCMallocAligned(sizeof(*e), CLS);
if (e == NULL)
return -1;
memset(e, 0x00, sizeof(*e));
e->PrefilterPostRule = PrefilterPostRuleFunc;
e->pectx = pectx;
e->Free = FreeFunc;
if (sgh->init->post_rule_match_engines == NULL) {
sgh->init->post_rule_match_engines = e;
} else {
PrefilterEngineList *t = sgh->init->post_rule_match_engines;
while (t->next != NULL) {
t = t->next;
}
t->next = e;
e->id = t->id + 1;
}
e->name = name;
e->gid = PrefilterStoreGetId(de_ctx, e->name, e->Free);
return 0;
}
static void PrefilterFreeEngineList(PrefilterEngineList *e)
{
if (e->Free && e->pectx) {
e->Free(e->pectx);
}
SCFreeAligned(e);
}
void PrefilterFreeEnginesList(PrefilterEngineList *list)
{
PrefilterEngineList *t = list;
while (t != NULL) {
PrefilterEngineList *next = t->next;
PrefilterFreeEngineList(t);
t = next;
}
}
static void PrefilterFreeEngines(const DetectEngineCtx *de_ctx, PrefilterEngine *list)
{
PrefilterEngine *t = list;
while (1) {
const PrefilterStore *s = PrefilterStoreGetStore(de_ctx, t->gid);
if (s && s->FreeFunc && t->pectx) {
s->FreeFunc(t->pectx);
}
if (t->is_last)
break;
t++;
}
SCFreeAligned(list);
}
void PrefilterCleanupRuleGroup(const DetectEngineCtx *de_ctx, SigGroupHead *sgh)
{
if (sgh->pkt_engines) {
PrefilterFreeEngines(de_ctx, sgh->pkt_engines);
sgh->pkt_engines = NULL;
}
if (sgh->payload_engines) {
PrefilterFreeEngines(de_ctx, sgh->payload_engines);
sgh->payload_engines = NULL;
}
if (sgh->tx_engines) {
PrefilterFreeEngines(de_ctx, sgh->tx_engines);
sgh->tx_engines = NULL;
}
if (sgh->frame_engines) {
PrefilterFreeEngines(de_ctx, sgh->frame_engines);
sgh->frame_engines = NULL;
}
if (sgh->post_rule_match_engines) {
PrefilterFreeEngines(de_ctx, sgh->post_rule_match_engines);
sgh->post_rule_match_engines = NULL;
}
}
static int PrefilterSetupRuleGroupSortHelper(const void *a, const void *b)
{
const PrefilterEngine *s0 = a;
const PrefilterEngine *s1 = b;
if (s1->ctx.tx_min_progress == s0->ctx.tx_min_progress) {
if (s1->alproto == s0->alproto) {
return s0->local_id > s1->local_id ? 1 : -1;
} else {
return s0->alproto > s1->alproto ? 1 : -1;
}
} else {
return s0->ctx.tx_min_progress > s1->ctx.tx_min_progress ? 1 : -1;
}
}
/** prefilter engine data for the non-prefilter engine for the prefilter API */
struct PrefilterNonPFDataSig {
uint32_t sid : 30;
uint32_t type : 2; /**< type for `value` field below: 0:alproto 1:dport 2:dsize */
uint16_t value;
/* since we have 2 more bytes available due to padding, we can add some additional
* filters here. */
union {
struct {
SignatureMask sig_mask;
} pkt;
struct {
/* filter for frame type */
uint8_t type;
} frame;
struct {
uint8_t foo; // TODO unused
} app;
};
};
struct PrefilterNonPFData {
uint32_t size;
struct PrefilterNonPFDataSig array[];
};
struct PrefilterNonPFDataTx {
uint32_t size;
uint32_t array[];
};
/** \internal
* \brief wrapper for use in APIs */
static void PrefilterNonPFDataFree(void *data)
{
SCFree(data);
}
static void PrefilterTxNonPF(DetectEngineThreadCtx *det_ctx, const void *pectx, Packet *p, Flow *f,
void *tx, const uint64_t tx_id, const AppLayerTxData *tx_data, const uint8_t flags)
{
const struct PrefilterNonPFDataTx *data = (const struct PrefilterNonPFDataTx *)pectx;
SCLogDebug("adding %u sids", data->size);
PrefilterAddSids(&det_ctx->pmq, data->array, data->size);
}
#ifdef NONPF_PKT_STATS
static thread_local uint64_t prefilter_pkt_nonpf_called = 0;
static thread_local uint64_t prefilter_pkt_nonpf_mask_fail = 0;
static thread_local uint64_t prefilter_pkt_nonpf_alproto_fail = 0;
static thread_local uint64_t prefilter_pkt_nonpf_dsize_fail = 0;
static thread_local uint64_t prefilter_pkt_nonpf_dport_fail = 0;
static thread_local uint64_t prefilter_pkt_nonpf_sids = 0;
#define NONPF_PKT_STATS_INCR(s) (s)++
#else
#define NONPF_PKT_STATS_INCR(s)
#endif
void PrefilterPktNonPFStatsDump(void)
{
#ifdef NONPF_PKT_STATS
SCLogDebug("prefilter non-pf: called:%" PRIu64 ", mask_fail:%" PRIu64 ", alproto fail:%" PRIu64
", dport fail:%" PRIu64 ", dsize fail:%" PRIu64 ", sids:%" PRIu64
", avg sids:%" PRIu64,
prefilter_pkt_nonpf_called, prefilter_pkt_nonpf_mask_fail,
prefilter_pkt_nonpf_alproto_fail, prefilter_pkt_nonpf_dport_fail,
prefilter_pkt_nonpf_dsize_fail, prefilter_pkt_nonpf_sids,
prefilter_pkt_nonpf_called ? prefilter_pkt_nonpf_sids / prefilter_pkt_nonpf_called : 0);
#endif
}
static void PrefilterPktNonPF(DetectEngineThreadCtx *det_ctx, Packet *p, const void *pectx)
{
const uint16_t alproto = p->flow ? p->flow->alproto : ALPROTO_UNKNOWN;
const SignatureMask mask = p->sig_mask;
const struct PrefilterNonPFData *data = (const struct PrefilterNonPFData *)pectx;
SCLogDebug("adding %u sids", data->size);
NONPF_PKT_STATS_INCR(prefilter_pkt_nonpf_called);
for (uint32_t i = 0; i < data->size; i++) {
const struct PrefilterNonPFDataSig *ds = &data->array[i];
const SignatureMask rule_mask = ds->pkt.sig_mask;
if ((rule_mask & mask) == rule_mask) {
switch (ds->type) {
case 0:
if (ds->value == ALPROTO_UNKNOWN || AppProtoEquals(ds->value, alproto)) {
const uint32_t sid = ds->sid;
PrefilterAddSids(&det_ctx->pmq, &sid, 1);
NONPF_PKT_STATS_INCR(prefilter_pkt_nonpf_sids);
} else {
NONPF_PKT_STATS_INCR(prefilter_pkt_nonpf_alproto_fail);
}
break;
case 1:
if (ds->value == p->dp) {
const uint32_t sid = ds->sid;
PrefilterAddSids(&det_ctx->pmq, &sid, 1);
NONPF_PKT_STATS_INCR(prefilter_pkt_nonpf_sids);
} else {
NONPF_PKT_STATS_INCR(prefilter_pkt_nonpf_dport_fail);
}
break;
case 2:
if (ds->value == p->payload_len) {
const uint32_t sid = ds->sid;
PrefilterAddSids(&det_ctx->pmq, &sid, 1);
NONPF_PKT_STATS_INCR(prefilter_pkt_nonpf_sids);
} else {
NONPF_PKT_STATS_INCR(prefilter_pkt_nonpf_dsize_fail);
}
break;
}
} else {
NONPF_PKT_STATS_INCR(prefilter_pkt_nonpf_mask_fail);
}
}
}
static void PrefilterPktNonPFHookFlowStart(
DetectEngineThreadCtx *det_ctx, Packet *p, const void *pectx)
{
if (p->flowflags & (FLOW_PKT_TOSERVER_FIRST | FLOW_PKT_TOCLIENT_FIRST)) {
PrefilterPktNonPF(det_ctx, p, pectx);
}
}
/** \internal
* \brief engine to select the non-prefilter rules for frames
* Checks the alproto and type as well.
* Direction needs no checking as the rule groups are per direction. */
static void PrefilterFrameNonPF(DetectEngineThreadCtx *det_ctx, const void *pectx, Packet *p,
const Frames *frames, const Frame *frame)
{
DEBUG_VALIDATE_BUG_ON(p->flow == NULL);
const uint16_t alproto = p->flow->alproto;
const struct PrefilterNonPFData *data = (const struct PrefilterNonPFData *)pectx;
SCLogDebug("adding %u sids", data->size);
for (uint32_t i = 0; i < data->size; i++) {
const struct PrefilterNonPFDataSig *ds = &data->array[i];
if (ds->frame.type == frame->type &&
(ds->value == ALPROTO_UNKNOWN || AppProtoEquals(ds->value, alproto))) {
const uint32_t sid = ds->sid;
PrefilterAddSids(&det_ctx->pmq, &sid, 1);
}
}
}
/* helper funcs for the non prefilter names hash */
static uint32_t NonPFNamesHash(HashTable *h, void *data, uint16_t _len)
{
const char *str = data;
return StringHashDjb2((const uint8_t *)str, (uint16_t)strlen(str)) % h->array_size;
}
static char NonPFNamesCompare(void *data1, uint16_t _len1, void *data2, uint16_t len2)
{
const char *s1 = data1;
const char *s2 = data2;
return StringHashCompareFunc(data1, (uint16_t)strlen(s1), data2, (uint16_t)strlen(s2));
}
static void NonPFNamesFree(void *data)
{
SCFree(data);
}
/* helper funcs for assembling non-prefilter engines */
struct TxNonPFData {
AppProto alproto;
int dir; /**< 0: toserver, 1: toclient */
int progress; /**< progress state value to register at */
uint32_t sigs_cnt;
struct PrefilterNonPFDataSig *sigs;
const char *engine_name; /**< pointer to name owned by DetectEngineCtx::non_pf_engine_names */
};
static uint32_t TxNonPFHash(HashListTable *h, void *data, uint16_t _len)
{
struct TxNonPFData *d = data;
return (d->alproto + d->progress + d->dir) % h->array_size;
}
static char TxNonPFCompare(void *data1, uint16_t _len1, void *data2, uint16_t len2)
{
struct TxNonPFData *d1 = data1;
struct TxNonPFData *d2 = data2;
return d1->alproto == d2->alproto && d1->progress == d2->progress && d1->dir == d2->dir;
}
static void TxNonPFFree(void *data)
{
struct TxNonPFData *d = data;
SCFree(d->sigs);
SCFree(d);
}
static int TxNonPFAddSig(DetectEngineCtx *de_ctx, HashListTable *tx_engines_hash,
const AppProto alproto, const int dir, const int16_t progress, const char *name,
const Signature *s)
{
const uint32_t max_sids = DetectEngineGetMaxSigId(de_ctx);
struct TxNonPFData lookup = {
.alproto = alproto,
.dir = dir,
.progress = progress,
.sigs_cnt = 0,
.sigs = NULL,
.engine_name = NULL,
};
struct TxNonPFData *e = HashListTableLookup(tx_engines_hash, &lookup, 0);
if (e != NULL) {
bool found = false;
// avoid adding same sid multiple times
for (uint32_t y = 0; y < e->sigs_cnt; y++) {
if (e->sigs[y].sid == s->num) {
found = true;
break;
}
}
if (!found) {
BUG_ON(e->sigs_cnt == max_sids);
e->sigs[e->sigs_cnt].sid = s->num;
e->sigs[e->sigs_cnt].value = alproto;
e->sigs_cnt++;
}
return 0;
}
struct TxNonPFData *add = SCCalloc(1, sizeof(*add));
if (add == NULL) {
return -1;
}
add->dir = dir;
add->alproto = alproto;
add->progress = progress;
add->sigs = SCCalloc(max_sids, sizeof(struct PrefilterNonPFDataSig));
if (add->sigs == NULL) {
SCFree(add);
return -1;
}
add->sigs_cnt = 0;
add->sigs[add->sigs_cnt].sid = s->num;
add->sigs[add->sigs_cnt].value = alproto;
add->sigs_cnt++;
char engine_name[128];
snprintf(engine_name, sizeof(engine_name), "%s:%s:non_pf:%s", AppProtoToString(alproto), name,
dir == 0 ? "toserver" : "toclient");
char *engine_name_heap = SCStrdup(engine_name);
if (engine_name_heap == NULL) {
SCFree(add->sigs);
SCFree(add);
return -1;
}
int result = HashTableAdd(
de_ctx->non_pf_engine_names, engine_name_heap, (uint16_t)strlen(engine_name_heap));
if (result != 0) {
SCFree(add->sigs);
SCFree(add);
return -1;
}
add->engine_name = engine_name_heap;
SCLogDebug("engine_name_heap %s", engine_name_heap);
int ret = HashListTableAdd(tx_engines_hash, add, 0);
if (ret != 0) {
SCFree(add->sigs);
SCFree(add);
return -1;
}
return 0;
}
/** \internal
* \brief setup non-prefilter rules in special "non-prefilter" engines that are registered in the
* prefilter logic.
*
* \retval 0 ok
* \retval -1 error
*/
static int SetupNonPrefilter(DetectEngineCtx *de_ctx, SigGroupHead *sgh)
{
const uint32_t max_sids = DetectEngineGetMaxSigId(de_ctx);
SCLogDebug("max_sids %u", max_sids);
struct PrefilterNonPFDataSig *pkt_non_pf_array = SCCalloc(max_sids, sizeof(*pkt_non_pf_array));
if (pkt_non_pf_array == NULL) {
return -1;
}
uint32_t pkt_non_pf_array_size = 0;
struct PrefilterNonPFDataSig *frame_non_pf_array =
SCCalloc(max_sids, sizeof(*frame_non_pf_array));
if (frame_non_pf_array == NULL) {
SCFree(pkt_non_pf_array);
return -1;
}
uint32_t frame_non_pf_array_size = 0;
struct PrefilterNonPFDataSig *pkt_hook_flow_start_non_pf_array =
SCCalloc(max_sids, sizeof(*pkt_hook_flow_start_non_pf_array));
if (pkt_hook_flow_start_non_pf_array == NULL) {
SCFree(pkt_non_pf_array);
SCFree(frame_non_pf_array);
return -1;
}
uint32_t pkt_hook_flow_start_non_pf_array_size = 0;
SignatureMask pkt_hook_flow_start_mask = 0;
bool pkt_hook_flow_start_mask_init = false;
HashListTable *tx_engines_hash =
HashListTableInit(256, TxNonPFHash, TxNonPFCompare, TxNonPFFree);
if (tx_engines_hash == NULL) {
SCFree(pkt_non_pf_array);
SCFree(pkt_hook_flow_start_non_pf_array);
SCFree(frame_non_pf_array);
return -1;
}
if (de_ctx->non_pf_engine_names == NULL) {
de_ctx->non_pf_engine_names =
HashTableInit(512, NonPFNamesHash, NonPFNamesCompare, NonPFNamesFree);
if (de_ctx->non_pf_engine_names == NULL) {
SCFree(pkt_non_pf_array);
SCFree(pkt_hook_flow_start_non_pf_array);
SCFree(frame_non_pf_array);
HashListTableFree(tx_engines_hash);
return -1;
}
}
SignatureMask pkt_mask = 0;
bool pkt_mask_init = false;
#ifdef NONPF_PKT_STATS
uint32_t nonpf_pkt_alproto = 0;
uint32_t nonpf_pkt_dsize = 0;
uint32_t nonpf_pkt_dport = 0;
#endif
const int app_events_list_id = DetectBufferTypeGetByName("app-layer-events");
SCLogDebug("app_events_list_id %d", app_events_list_id);
for (uint32_t sig = 0; sig < sgh->init->sig_cnt; sig++) {
Signature *s = sgh->init->match_array[sig];
if (s == NULL)
continue;
SCLogDebug("checking sid %u for non-prefilter", s->id);
if (s->init_data->mpm_sm != NULL && (s->flags & SIG_FLAG_MPM_NEG) == 0)
continue;
if (s->init_data->prefilter_sm != NULL)
continue;
if ((s->flags & (SIG_FLAG_PREFILTER | SIG_FLAG_MPM_NEG)) == SIG_FLAG_PREFILTER)
continue;
SCLogDebug("setting up sid %u for non-prefilter", s->id);
uint8_t frame_type = 0; /**< only a single type per rule */
bool tx_non_pf = false;
bool frame_non_pf = false;
bool pkt_non_pf = false;
if (s->init_data->hook.type == SIGNATURE_HOOK_TYPE_PKT &&
s->init_data->hook.t.pkt.ph == SIGNATURE_HOOK_PKT_FLOW_START) {
// TODO code duplication with regular pkt case below
/* for pkt non prefilter, we have some space in the structure,
* so we can squeeze another filter */
uint8_t type;
uint16_t value;
if ((s->flags & SIG_FLAG_DSIZE) && s->dsize_mode == DETECT_UINT_EQ) {
SCLogDebug("dsize extra match");
type = 2;
value = s->dsize_low;
} else if (s->dp != NULL && s->dp->next == NULL && s->dp->port == s->dp->port2) {
type = 1;
value = s->dp->port;
} else {
type = 0;
value = s->alproto;
}
pkt_hook_flow_start_non_pf_array[pkt_hook_flow_start_non_pf_array_size].sid = s->num;
pkt_hook_flow_start_non_pf_array[pkt_hook_flow_start_non_pf_array_size].value = value;
pkt_hook_flow_start_non_pf_array[pkt_hook_flow_start_non_pf_array_size].type = type;
pkt_hook_flow_start_non_pf_array[pkt_hook_flow_start_non_pf_array_size].pkt.sig_mask =
s->mask;
pkt_hook_flow_start_non_pf_array_size++;
if (pkt_hook_flow_start_mask_init) {
pkt_hook_flow_start_mask &= s->mask;
} else {
pkt_hook_flow_start_mask = s->mask;
pkt_hook_flow_start_mask_init = true;
}
SCLogDebug("flow_start hook");
continue; // done for this sig
}
for (uint32_t x = 0; x < s->init_data->buffer_index; x++) {
const int list_id = s->init_data->buffers[x].id;
const DetectBufferType *buf = DetectEngineBufferTypeGetById(de_ctx, list_id);
/* for now, exclude app-layer-events, as they are not tied to a specific
* progress value like other keywords. */
SCLogDebug("list_id %d buf %p", list_id, buf);
if (list_id == app_events_list_id)
continue;
if (buf->packet) {
SCLogDebug("packet buf");
/* packet is handled below */
pkt_non_pf = true;
} else if (buf->frame) {
for (DetectEngineFrameInspectionEngine *f = de_ctx->frame_inspect_engines;
f != NULL; f = f->next) {
if (!((((s->flags & SIG_FLAG_TOSERVER) != 0 && f->dir == 0) ||
((s->flags & SIG_FLAG_TOCLIENT) != 0 && f->dir == 1)) &&
list_id == (int)f->sm_list &&
AppProtoEquals(s->alproto, f->alproto)))
continue;
SCLogDebug("frame '%s' type %u", buf->name, f->type);
frame_type = f->type;
frame_non_pf = true;
frame_non_pf_array[frame_non_pf_array_size].sid = s->num;
frame_non_pf_array[frame_non_pf_array_size].value = s->alproto;
frame_non_pf_array[frame_non_pf_array_size].frame.type = frame_type;
frame_non_pf_array_size++;
break;
}
} else {
SCLogDebug("x %u list_id %d", x, list_id);
for (DetectEngineAppInspectionEngine *app = de_ctx->app_inspect_engines;
app != NULL; app = app->next) {
SCLogDebug("app %p proto %s list_d %d sig dir %0x", app,
AppProtoToString(app->alproto), app->sm_list,
s->flags & (SIG_FLAG_TOSERVER | SIG_FLAG_TOCLIENT));
/* skip if:
* - not in our dir
* - not our list
* - app proto mismatch. Both sig and app can have proto or unknown */
if (!((((s->flags & SIG_FLAG_TOSERVER) != 0 && app->dir == 0) ||
((s->flags & SIG_FLAG_TOCLIENT) != 0 && app->dir == 1)) &&
list_id == (int)app->sm_list &&
(s->alproto == ALPROTO_UNKNOWN || app->alproto == ALPROTO_UNKNOWN ||
AppProtoEquals(s->alproto, app->alproto))))
continue;
if (TxNonPFAddSig(de_ctx, tx_engines_hash, app->alproto, app->dir,
app->progress, buf->name, s) != 0) {
goto error;
}
tx_non_pf = true;
}
}
}
/* mark as prefiltered as the sig is now part of a engine */
// s->flags |= SIG_FLAG_PREFILTER;
// TODO doesn't work for sigs that are in multiple sgh's
/* default to pkt if there was no tx or frame match */
if (!(tx_non_pf || frame_non_pf)) {
if (!pkt_non_pf) {
SCLogDebug("not frame, not tx, so pkt");
}
pkt_non_pf = true;
}
SCLogDebug("setting up sid %u for non-prefilter: %s", s->id,
tx_non_pf ? "tx engine" : (frame_non_pf ? "frame engine" : "pkt engine"));
if (pkt_non_pf) {
/* for pkt non prefilter, we have some space in the structure,
* so we can squeeze another filter */
uint8_t type;
uint16_t value;
if ((s->flags & SIG_FLAG_DSIZE) && s->dsize_mode == DETECT_UINT_EQ) {
SCLogDebug("dsize extra match");
type = 2;
value = s->dsize_low;
#ifdef NONPF_PKT_STATS
nonpf_pkt_dsize++;
#endif
} else if (s->dp != NULL && s->dp->next == NULL && s->dp->port == s->dp->port2) {
type = 1;
value = s->dp->port;
#ifdef NONPF_PKT_STATS
nonpf_pkt_dport++;
#endif
} else {
type = 0;
value = s->alproto;
#ifdef NONPF_PKT_STATS
nonpf_pkt_alproto++;
#endif
}
pkt_non_pf_array[pkt_non_pf_array_size].sid = s->num;
pkt_non_pf_array[pkt_non_pf_array_size].value = value;
pkt_non_pf_array[pkt_non_pf_array_size].type = type;
pkt_non_pf_array[pkt_non_pf_array_size].pkt.sig_mask = s->mask;
pkt_non_pf_array_size++;
if (pkt_mask_init) {
pkt_mask &= s->mask;
} else {
pkt_mask = s->mask;
pkt_mask_init = true;
}
}
}
/* for each unique sig set, add an engine */
for (HashListTableBucket *b = HashListTableGetListHead(tx_engines_hash); b != NULL;
b = HashListTableGetListNext(b)) {
struct TxNonPFData *t = HashListTableGetListData(b);
SCLogDebug("%s engine for %s hook %d has %u non-pf sigs",
t->dir == 0 ? "toserver" : "toclient", AppProtoToString(t->alproto), t->progress,
t->sigs_cnt);
if (((sgh->init->direction & SIG_FLAG_TOSERVER) && t->dir == 1) ||
((sgh->init->direction & SIG_FLAG_TOCLIENT) && t->dir == 0)) {
SCLogDebug("skipped");
continue;
}
struct PrefilterNonPFDataTx *data =
SCCalloc(1, sizeof(*data) + t->sigs_cnt * sizeof(data->array[0]));
if (data == NULL)
goto error;
data->size = t->sigs_cnt;
for (uint32_t i = 0; i < t->sigs_cnt; i++) {
data->array[i] = t->sigs[i].sid;
}
if (PrefilterAppendTxEngine(de_ctx, sgh, PrefilterTxNonPF, t->alproto, t->progress,
(void *)data, PrefilterNonPFDataFree, t->engine_name) < 0) {
SCFree(data);
goto error;
}
}
HashListTableFree(tx_engines_hash);
tx_engines_hash = NULL;
if (pkt_non_pf_array_size) {
struct PrefilterNonPFData *data =
SCCalloc(1, sizeof(*data) + pkt_non_pf_array_size * sizeof(data->array[0]));
if (data == NULL)
goto error;
data->size = pkt_non_pf_array_size;
memcpy((uint8_t *)&data->array, pkt_non_pf_array,
pkt_non_pf_array_size * sizeof(data->array[0]));
enum SignatureHookPkt hook = SIGNATURE_HOOK_PKT_NOT_SET; // TODO review
if (PrefilterAppendEngine(de_ctx, sgh, PrefilterPktNonPF, pkt_mask, hook, (void *)data,
PrefilterNonPFDataFree, "packet:non_pf") < 0) {
SCFree(data);
goto error;
}
}
if (pkt_hook_flow_start_non_pf_array_size) {
struct PrefilterNonPFData *data = SCCalloc(
1, sizeof(*data) + pkt_hook_flow_start_non_pf_array_size * sizeof(data->array[0]));
if (data == NULL)
goto error;
data->size = pkt_hook_flow_start_non_pf_array_size;
memcpy((uint8_t *)&data->array, pkt_hook_flow_start_non_pf_array,
pkt_hook_flow_start_non_pf_array_size * sizeof(data->array[0]));
SCLogDebug("packet:flow_start:non_pf added with %u rules", data->size);
enum SignatureHookPkt hook = SIGNATURE_HOOK_PKT_FLOW_START;
if (PrefilterAppendEngine(de_ctx, sgh,
PrefilterPktNonPFHookFlowStart, // TODO no longer needed to have a dedicated
// callback
pkt_hook_flow_start_mask, hook, (void *)data, PrefilterNonPFDataFree,
"packet:flow_start:non_pf") < 0) {
SCFree(data);
goto error;
}
}
if (frame_non_pf_array_size) {
SCLogDebug("%u frame non-pf sigs", frame_non_pf_array_size);
struct PrefilterNonPFData *data =
SCCalloc(1, sizeof(*data) + frame_non_pf_array_size * sizeof(data->array[0]));
if (data == NULL)
goto error;
data->size = frame_non_pf_array_size;
memcpy((uint8_t *)&data->array, frame_non_pf_array,
frame_non_pf_array_size * sizeof(data->array[0]));
if (PrefilterAppendFrameEngine(de_ctx, sgh, PrefilterFrameNonPF, ALPROTO_UNKNOWN,
FRAME_ANY_TYPE, (void *)data, PrefilterNonPFDataFree, "frame:non_pf") < 0) {
SCFree(data);
goto error;
}
}
SCFree(pkt_hook_flow_start_non_pf_array);
pkt_hook_flow_start_non_pf_array = NULL;
SCFree(pkt_non_pf_array);
pkt_non_pf_array = NULL;
SCFree(frame_non_pf_array);
frame_non_pf_array = NULL;
return 0;
error:
if (tx_engines_hash) {
HashListTableFree(tx_engines_hash);
}
SCFree(pkt_hook_flow_start_non_pf_array);
SCFree(pkt_non_pf_array);
SCFree(frame_non_pf_array);
return -1;
}
int PrefilterSetupRuleGroup(DetectEngineCtx *de_ctx, SigGroupHead *sgh)
{
int r = PatternMatchPrepareGroup(de_ctx, sgh);
if (r != 0) {
FatalError("failed to set up pattern matching");
}
/* set up engines if needed - when prefilter is set to auto we run
* all engines, otherwise only those that have been forced by the
* prefilter keyword. */
const enum DetectEnginePrefilterSetting setting = de_ctx->prefilter_setting;
for (int i = 0; i < DETECT_TBLSIZE; i++) {
if (sigmatch_table[i].SetupPrefilter != NULL &&
(setting == DETECT_PREFILTER_AUTO || de_ctx->sm_types_prefilter[i])) {
sigmatch_table[i].SetupPrefilter(de_ctx, sgh);
}
}
if (SetupNonPrefilter(de_ctx, sgh) != 0) {
return -1;
}
/* we have lists of engines in sgh->init now. Lets setup the
* match arrays */
PrefilterEngineList *el;
if (sgh->init->pkt_engines != NULL) {
uint32_t cnt = 0;
for (el = sgh->init->pkt_engines ; el != NULL; el = el->next) {
cnt++;
}
sgh->pkt_engines = SCMallocAligned(cnt * sizeof(PrefilterEngine), CLS);
if (sgh->pkt_engines == NULL) {
return -1;
}
memset(sgh->pkt_engines, 0x00, (cnt * sizeof(PrefilterEngine)));
PrefilterEngine *e = sgh->pkt_engines;
for (el = sgh->init->pkt_engines ; el != NULL; el = el->next) {
e->local_id = el->id;
e->cb.Prefilter = el->Prefilter;
e->ctx.pkt.mask = el->pkt_mask;
// TODO right now we represent the hook in a u8 in the prefilter engine for space
// reasons.
BUG_ON(el->pkt_hook >= 8);
e->ctx.pkt.hook = (uint8_t)el->pkt_hook;
e->pectx = el->pectx;
el->pectx = NULL; // e now owns the ctx
e->gid = el->gid;
if (el->next == NULL) {
e->is_last = true;
}
e++;
}
}
if (sgh->init->payload_engines != NULL) {
uint32_t cnt = 0;
for (el = sgh->init->payload_engines ; el != NULL; el = el->next) {
cnt++;
}
sgh->payload_engines = SCMallocAligned(cnt * sizeof(PrefilterEngine), CLS);
if (sgh->payload_engines == NULL) {
return -1;
}
memset(sgh->payload_engines, 0x00, (cnt * sizeof(PrefilterEngine)));
PrefilterEngine *e = sgh->payload_engines;
for (el = sgh->init->payload_engines ; el != NULL; el = el->next) {
e->local_id = el->id;
e->cb.Prefilter = el->Prefilter;
e->ctx.pkt.mask = el->pkt_mask;
// TODO right now we represent the hook in a u8 in the prefilter engine for space
// reasons.
BUG_ON(el->pkt_hook >= 8);
e->ctx.pkt.hook = (uint8_t)el->pkt_hook;
e->pectx = el->pectx;
el->pectx = NULL; // e now owns the ctx
e->gid = el->gid;
if (el->next == NULL) {
e->is_last = true;
}
e++;
}
}
if (sgh->init->tx_engines != NULL) {
uint32_t cnt = 0;
for (el = sgh->init->tx_engines ; el != NULL; el = el->next) {
cnt++;
}
sgh->tx_engines = SCMallocAligned(cnt * sizeof(PrefilterEngine), CLS);
if (sgh->tx_engines == NULL) {
return -1;
}
memset(sgh->tx_engines, 0x00, (cnt * sizeof(PrefilterEngine)));
uint16_t local_id = 0;
PrefilterEngine *e = sgh->tx_engines;
for (el = sgh->init->tx_engines ; el != NULL; el = el->next) {
e->local_id = local_id++;
e->alproto = el->alproto;
e->ctx.tx_min_progress = el->tx_min_progress;
e->cb.PrefilterTx = el->PrefilterTx;
e->pectx = el->pectx;
el->pectx = NULL; // e now owns the ctx
e->gid = el->gid;
e++;
}
/* sort by tx_min_progress, then alproto, then local_id */
qsort(sgh->tx_engines, local_id, sizeof(PrefilterEngine),
PrefilterSetupRuleGroupSortHelper);
sgh->tx_engines[local_id - 1].is_last = true;
sgh->tx_engines[local_id - 1].is_last_for_progress = true;
PrefilterEngine *engine;
/* per alproto to set is_last_for_progress per alproto because the inspect
* loop skips over engines that are not the correct alproto */
for (AppProto a = ALPROTO_FAILED + 1; a < g_alproto_max; a++) {
int last_tx_progress = 0;
bool last_tx_progress_set = false;
PrefilterEngine *prev_engine = NULL;
engine = sgh->tx_engines;
do {
BUG_ON(engine->ctx.tx_min_progress < last_tx_progress);
if (engine->alproto == a) {
if (last_tx_progress_set && engine->ctx.tx_min_progress > last_tx_progress) {
if (prev_engine) {
prev_engine->is_last_for_progress = true;
}
}
last_tx_progress_set = true;
prev_engine = engine;
} else {
if (prev_engine) {
prev_engine->is_last_for_progress = true;
}
}
last_tx_progress = engine->ctx.tx_min_progress;
if (engine->is_last)
break;
engine++;
} while (1);
}
#ifdef DEBUG
SCLogDebug("sgh %p", sgh);
engine = sgh->tx_engines;
do {
SCLogDebug("engine: gid %u alproto %s tx_min_progress %d is_last %s "
"is_last_for_progress %s",
engine->gid, AppProtoToString(engine->alproto), engine->ctx.tx_min_progress,
engine->is_last ? "true" : "false",
engine->is_last_for_progress ? "true" : "false");
if (engine->is_last)
break;
engine++;
} while (1);
#endif
}
if (sgh->init->frame_engines != NULL) {
uint32_t cnt = 0;
for (el = sgh->init->frame_engines; el != NULL; el = el->next) {
cnt++;
}
sgh->frame_engines = SCMallocAligned(cnt * sizeof(PrefilterEngine), CLS);
if (sgh->frame_engines == NULL) {
return -1;
}
memset(sgh->frame_engines, 0x00, (cnt * sizeof(PrefilterEngine)));
PrefilterEngine *e = sgh->frame_engines;
for (el = sgh->init->frame_engines; el != NULL; el = el->next) {
e->local_id = el->id;
e->ctx.frame_type = el->frame_type;
e->cb.PrefilterFrame = el->PrefilterFrame;
e->alproto = el->alproto;
e->pectx = el->pectx;
el->pectx = NULL; // e now owns the ctx
e->gid = el->gid;
if (el->next == NULL) {
e->is_last = true;
}
e++;
}
}
if (sgh->init->post_rule_match_engines != NULL) {
uint32_t cnt = 0;
for (el = sgh->init->post_rule_match_engines; el != NULL; el = el->next) {
cnt++;
}
sgh->post_rule_match_engines = SCMallocAligned(cnt * sizeof(PrefilterEngine), CLS);
if (sgh->post_rule_match_engines == NULL) {
return -1;
}
memset(sgh->post_rule_match_engines, 0x00, (cnt * sizeof(PrefilterEngine)));
uint16_t local_id = 0;
PrefilterEngine *e = sgh->post_rule_match_engines;
for (el = sgh->init->post_rule_match_engines; el != NULL; el = el->next) {
e->local_id = local_id++;
e->cb.PrefilterPostRule = el->PrefilterPostRule;
e->pectx = el->pectx;
el->pectx = NULL; // e now owns the ctx
e->gid = el->gid;
e->is_last = (el->next == NULL);
e++;
}
SCLogDebug("sgh %p max local_id %u", sgh, local_id);
}
return 0;
}
/* hash table for assigning a unique id to each engine type. */
static uint32_t PrefilterStoreHashFunc(HashListTable *ht, void *data, uint16_t datalen)
{
PrefilterStore *ctx = data;
uint32_t hash = strlen(ctx->name);
for (size_t u = 0; u < strlen(ctx->name); u++) {
hash += ctx->name[u];
}
hash %= ht->array_size;
return hash;
}
static char PrefilterStoreCompareFunc(void *data1, uint16_t len1,
void *data2, uint16_t len2)
{
PrefilterStore *ctx1 = data1;
PrefilterStore *ctx2 = data2;
return (strcmp(ctx1->name, ctx2->name) == 0);
}
static void PrefilterStoreFreeFunc(void *ptr)
{
SCFree(ptr);
}
void PrefilterDeinit(DetectEngineCtx *de_ctx)
{
if (de_ctx->prefilter_hash_table != NULL) {
HashListTableFree(de_ctx->prefilter_hash_table);
}
}
void PrefilterInit(DetectEngineCtx *de_ctx)
{
BUG_ON(de_ctx->prefilter_hash_table != NULL);
de_ctx->prefilter_hash_table = HashListTableInit(256,
PrefilterStoreHashFunc,
PrefilterStoreCompareFunc,
PrefilterStoreFreeFunc);
BUG_ON(de_ctx->prefilter_hash_table == NULL);
}
static int PrefilterStoreGetId(DetectEngineCtx *de_ctx,
const char *name, void (*FreeFunc)(void *))
{
PrefilterStore ctx = { name, FreeFunc, 0 };
BUG_ON(de_ctx->prefilter_hash_table == NULL);
SCLogDebug("looking up %s", name);
PrefilterStore *rctx = HashListTableLookup(de_ctx->prefilter_hash_table, (void *)&ctx, 0);
if (rctx != NULL) {
return rctx->id;
}
PrefilterStore *actx = SCCalloc(1, sizeof(*actx));
if (actx == NULL) {
return -1;
}
actx->name = name;
actx->FreeFunc = FreeFunc;
actx->id = de_ctx->prefilter_id++;
SCLogDebug("prefilter engine %s has profile id %u", actx->name, actx->id);
int ret = HashListTableAdd(de_ctx->prefilter_hash_table, actx, 0);
if (ret != 0) {
SCFree(actx);
return -1;
}
int r = actx->id;
return r;
}
/** \warning slow */
static const PrefilterStore *PrefilterStoreGetStore(const DetectEngineCtx *de_ctx,
const uint32_t id)
{
const PrefilterStore *store = NULL;
if (de_ctx->prefilter_hash_table != NULL) {
HashListTableBucket *hb = HashListTableGetListHead(de_ctx->prefilter_hash_table);
for ( ; hb != NULL; hb = HashListTableGetListNext(hb)) {
PrefilterStore *ctx = HashListTableGetListData(hb);
if (ctx->id == id) {
store = ctx;
break;
}
}
}
return store;
}
#ifdef PROFILING
const char *PrefilterStoreGetName(const uint32_t id)
{
return NULL;
}
#endif
#include "util-print.h"
typedef struct PrefilterMpmCtx {
int list_id;
InspectionBufferGetDataPtr GetData;
const MpmCtx *mpm_ctx;
const DetectEngineTransforms *transforms;
} PrefilterMpmCtx;
/** \brief Generic Mpm prefilter callback
*
* \param det_ctx detection engine thread ctx
* \param p packet to inspect
* \param f flow to inspect
* \param txv tx to inspect
* \param pectx inspection context
*/
static void PrefilterMpm(DetectEngineThreadCtx *det_ctx, const void *pectx, Packet *p, Flow *f,
void *txv, const uint64_t idx, const AppLayerTxData *_txd, const uint8_t flags)
{
SCEnter();
const PrefilterMpmCtx *ctx = (const PrefilterMpmCtx *)pectx;
const MpmCtx *mpm_ctx = ctx->mpm_ctx;
SCLogDebug("running on list %d", ctx->list_id);
InspectionBuffer *buffer = ctx->GetData(det_ctx, ctx->transforms,
f, flags, txv, ctx->list_id);
if (buffer == NULL)
return;
const uint32_t data_len = buffer->inspect_len;
const uint8_t *data = buffer->inspect;
SCLogDebug("mpm'ing buffer:");
//PrintRawDataFp(stdout, data, data_len);
if (data != NULL && data_len >= mpm_ctx->minlen) {
(void)mpm_table[mpm_ctx->mpm_type].Search(
mpm_ctx, &det_ctx->mtc, &det_ctx->pmq, data, data_len);
PREFILTER_PROFILING_ADD_BYTES(det_ctx, data_len);
}
}
static void PrefilterGenericMpmFree(void *ptr)
{
SCFree(ptr);
}
int PrefilterGenericMpmRegister(DetectEngineCtx *de_ctx, SigGroupHead *sgh, MpmCtx *mpm_ctx,
const DetectBufferMpmRegistry *mpm_reg, int list_id)
{
SCEnter();
PrefilterMpmCtx *pectx = SCCalloc(1, sizeof(*pectx));
if (pectx == NULL)
return -1;
pectx->list_id = list_id;
pectx->GetData = mpm_reg->app_v2.GetData;
pectx->mpm_ctx = mpm_ctx;
pectx->transforms = &mpm_reg->transforms;
int r = PrefilterAppendTxEngine(de_ctx, sgh, PrefilterMpm,
mpm_reg->app_v2.alproto, mpm_reg->app_v2.tx_min_progress,
pectx, PrefilterGenericMpmFree, mpm_reg->pname);
if (r != 0) {
SCFree(pectx);
}
return r;
}
static void PrefilterMultiGenericMpmFree(void *ptr)
{
// PrefilterMpmListId
SCFree(ptr);
}
static void PrefilterMultiMpm(DetectEngineThreadCtx *det_ctx, const void *pectx, Packet *p, Flow *f,
void *txv, const uint64_t idx, const AppLayerTxData *_txd, const uint8_t flags)
{
SCEnter();
const PrefilterMpmListId *ctx = (const PrefilterMpmListId *)pectx;
const MpmCtx *mpm_ctx = ctx->mpm_ctx;
SCLogDebug("running on list %d", ctx->list_id);
uint32_t local_id = 0;
do {
// loop until we get a NULL
InspectionBuffer *buffer =
ctx->GetData(det_ctx, ctx->transforms, f, flags, txv, ctx->list_id, local_id);
if (buffer == NULL)
break;
if (buffer->inspect_len >= mpm_ctx->minlen) {
(void)mpm_table[mpm_ctx->mpm_type].Search(
mpm_ctx, &det_ctx->mtc, &det_ctx->pmq, buffer->inspect, buffer->inspect_len);
PREFILTER_PROFILING_ADD_BYTES(det_ctx, buffer->inspect_len);
}
local_id++;
} while (1);
}
int PrefilterMultiGenericMpmRegister(DetectEngineCtx *de_ctx, SigGroupHead *sgh, MpmCtx *mpm_ctx,
const DetectBufferMpmRegistry *mpm_reg, int list_id)
{
SCEnter();
PrefilterMpmListId *pectx = SCCalloc(1, sizeof(*pectx));
if (pectx == NULL)
return -1;
pectx->list_id = list_id;
pectx->GetData = mpm_reg->app_v2.GetMultiData;
pectx->mpm_ctx = mpm_ctx;
pectx->transforms = &mpm_reg->transforms;
int r = PrefilterAppendTxEngine(de_ctx, sgh, PrefilterMultiMpm, mpm_reg->app_v2.alproto,
mpm_reg->app_v2.tx_min_progress, pectx, PrefilterMultiGenericMpmFree, mpm_reg->pname);
if (r != 0) {
SCFree(pectx);
}
return r;
}
/* generic mpm for pkt engines */
typedef struct PrefilterMpmPktCtx {
int list_id;
InspectionBufferGetPktDataPtr GetData;
const MpmCtx *mpm_ctx;
const DetectEngineTransforms *transforms;
} PrefilterMpmPktCtx;
/** \brief Generic Mpm prefilter callback
*
* \param det_ctx detection engine thread ctx
* \param p packet to inspect
* \param f flow to inspect
* \param txv tx to inspect
* \param pectx inspection context
*/
static void PrefilterMpmPkt(DetectEngineThreadCtx *det_ctx,
Packet *p, const void *pectx)
{
SCEnter();
const PrefilterMpmPktCtx *ctx = (const PrefilterMpmPktCtx *)pectx;
const MpmCtx *mpm_ctx = ctx->mpm_ctx;
SCLogDebug("running on list %d", ctx->list_id);
InspectionBuffer *buffer = ctx->GetData(det_ctx, ctx->transforms,
p, ctx->list_id);
if (buffer == NULL)
return;
const uint32_t data_len = buffer->inspect_len;
const uint8_t *data = buffer->inspect;
SCLogDebug("mpm'ing buffer:");
//PrintRawDataFp(stdout, data, data_len);
if (data != NULL && data_len >= mpm_ctx->minlen) {
(void)mpm_table[mpm_ctx->mpm_type].Search(
mpm_ctx, &det_ctx->mtc, &det_ctx->pmq, data, data_len);
PREFILTER_PROFILING_ADD_BYTES(det_ctx, data_len);
}
}
static void PrefilterMpmPktFree(void *ptr)
{
SCFree(ptr);
}
int PrefilterGenericMpmPktRegister(DetectEngineCtx *de_ctx, SigGroupHead *sgh, MpmCtx *mpm_ctx,
const DetectBufferMpmRegistry *mpm_reg, int list_id)
{
SCEnter();
PrefilterMpmPktCtx *pectx = SCCalloc(1, sizeof(*pectx));
if (pectx == NULL)
return -1;
pectx->list_id = list_id;
pectx->GetData = mpm_reg->pkt_v1.GetData;
pectx->mpm_ctx = mpm_ctx;
pectx->transforms = &mpm_reg->transforms;
enum SignatureHookPkt hook = SIGNATURE_HOOK_PKT_NOT_SET; // TODO review
int r = PrefilterAppendEngine(
de_ctx, sgh, PrefilterMpmPkt, 0, hook, pectx, PrefilterMpmPktFree, mpm_reg->pname);
if (r != 0) {
SCFree(pectx);
}
return r;
}
#define QUEUE_STEP 16
void PostRuleMatchWorkQueueAppend(
DetectEngineThreadCtx *det_ctx, const Signature *s, const int type, const uint32_t value)
{
if (det_ctx->post_rule_work_queue.q == NULL) {
det_ctx->post_rule_work_queue.q =
SCCalloc(1, sizeof(PostRuleMatchWorkQueueItem) * QUEUE_STEP);
if (det_ctx->post_rule_work_queue.q == NULL) {
DetectEngineSetEvent(det_ctx, DETECT_EVENT_POST_MATCH_QUEUE_FAILED);
return;
}
det_ctx->post_rule_work_queue.size = QUEUE_STEP;
} else if (det_ctx->post_rule_work_queue.len == det_ctx->post_rule_work_queue.size) {
void *ptr = SCRealloc(
det_ctx->post_rule_work_queue.q, (det_ctx->post_rule_work_queue.size + QUEUE_STEP) *
sizeof(PostRuleMatchWorkQueueItem));
if (ptr == NULL) {
DetectEngineSetEvent(det_ctx, DETECT_EVENT_POST_MATCH_QUEUE_FAILED);
return;
}
det_ctx->post_rule_work_queue.q = ptr;
det_ctx->post_rule_work_queue.size += QUEUE_STEP;
}
det_ctx->post_rule_work_queue.q[det_ctx->post_rule_work_queue.len].sm_type = type;
det_ctx->post_rule_work_queue.q[det_ctx->post_rule_work_queue.len].value = value;
#ifdef DEBUG
det_ctx->post_rule_work_queue.q[det_ctx->post_rule_work_queue.len].id = s->num;
#endif
det_ctx->post_rule_work_queue.len++;
SCLogDebug("det_ctx->post_rule_work_queue.len %u", det_ctx->post_rule_work_queue.len);
}