|
|
|
/* Copyright (C) 2016 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-prefilter.h"
|
|
|
|
#include "detect-engine-mpm.h"
|
|
|
|
|
|
|
|
#include "app-layer-parser.h"
|
|
|
|
#include "app-layer-htp.h"
|
|
|
|
|
|
|
|
#include "util-profiling.h"
|
|
|
|
|
|
|
|
typedef struct PrefilterStore_ {
|
|
|
|
const char *name;
|
|
|
|
void (*FreeFunc)(void *);
|
|
|
|
uint32_t id;
|
|
|
|
} PrefilterStore;
|
|
|
|
|
|
|
|
static int PrefilterStoreGetId(const char *name, void (*FreeFunc)(void *));
|
|
|
|
static const PrefilterStore *PrefilterStoreGetStore(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("tx %p progress %d", tx->tx_ptr, tx->tx_progress);
|
|
|
|
|
|
|
|
PrefilterEngine *engine = sgh->tx_engines;
|
|
|
|
do {
|
|
|
|
if (engine->alproto != alproto)
|
|
|
|
goto next;
|
|
|
|
if (engine->tx_min_progress > tx->tx_progress)
|
|
|
|
goto next;
|
|
|
|
if (tx->tx_progress > engine->tx_min_progress) {
|
|
|
|
if (tx->prefilter_flags & (1<<(engine->local_id))) {
|
|
|
|
goto next;
|
mpm: run engines as few times as possible
In various scenarios buffers would be checked my MPM more than
once. This was because the buffers would be inspected for a
certain progress value or higher.
For example, for each packet in a file upload, the engine would
not just rerun the 'http client body' MPM on the new data, it
would also rerun the method, uri, headers, cookie, etc MPMs.
This was obviously inefficent, so this patch changes the logic.
The patch only runs the MPM engines when the progress is exactly
the intended progress. If the progress is beyond the desired
value, it is run once. A tracker is added to the app layer API,
where the completed MPMs are tracked.
Implemented for HTTP, TLS and SSH.
8 years ago
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
PROFILING_PREFILTER_START(p);
|
|
|
|
engine->cb.PrefilterTx(det_ctx, engine->pectx,
|
|
|
|
p, p->flow, tx->tx_ptr, tx->tx_id, flow_flags);
|
|
|
|
PROFILING_PREFILTER_END(p, engine->gid);
|
mpm: run engines as few times as possible
In various scenarios buffers would be checked my MPM more than
once. This was because the buffers would be inspected for a
certain progress value or higher.
For example, for each packet in a file upload, the engine would
not just rerun the 'http client body' MPM on the new data, it
would also rerun the method, uri, headers, cookie, etc MPMs.
This was obviously inefficent, so this patch changes the logic.
The patch only runs the MPM engines when the progress is exactly
the intended progress. If the progress is beyond the desired
value, it is run once. A tracker is added to the app layer API,
where the completed MPMs are tracked.
Implemented for HTTP, TLS and SSH.
8 years ago
|
|
|
|
|
|
|
if (tx->tx_progress > engine->tx_min_progress) {
|
|
|
|
tx->prefilter_flags |= (1<<(engine->local_id));
|
mpm: run engines as few times as possible
In various scenarios buffers would be checked my MPM more than
once. This was because the buffers would be inspected for a
certain progress value or higher.
For example, for each packet in a file upload, the engine would
not just rerun the 'http client body' MPM on the new data, it
would also rerun the method, uri, headers, cookie, etc MPMs.
This was obviously inefficent, so this patch changes the logic.
The patch only runs the MPM engines when the progress is exactly
the intended progress. If the progress is beyond the desired
value, it is run once. A tracker is added to the app layer API,
where the completed MPMs are tracked.
Implemented for HTTP, TLS and SSH.
8 years ago
|
|
|
}
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Prefilter(DetectEngineThreadCtx *det_ctx, const SigGroupHead *sgh,
|
|
|
|
Packet *p, const uint8_t flags)
|
|
|
|
{
|
|
|
|
SCEnter();
|
|
|
|
|
|
|
|
PROFILING_PREFILTER_RESET(p, det_ctx->de_ctx->prefilter_maxid);
|
|
|
|
|
|
|
|
if (sgh->pkt_engines) {
|
|
|
|
PACKET_PROFILING_DETECT_START(p, PROF_DETECT_PF_PKT);
|
|
|
|
/* run packet engines */
|
|
|
|
PrefilterEngine *engine = sgh->pkt_engines;
|
|
|
|
do {
|
|
|
|
PROFILING_PREFILTER_START(p);
|
|
|
|
engine->cb.Prefilter(det_ctx, p, engine->pectx);
|
|
|
|
PROFILING_PREFILTER_END(p, 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) {
|
|
|
|
PROFILING_PREFILTER_START(p);
|
|
|
|
engine->cb.Prefilter(det_ctx, p, engine->pectx);
|
|
|
|
PROFILING_PREFILTER_END(p, 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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int PrefilterAppendEngine(SigGroupHead *sgh,
|
|
|
|
void (*PrefilterFunc)(DetectEngineThreadCtx *det_ctx, Packet *p, const void *pectx),
|
|
|
|
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->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(e->name, e->Free);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int PrefilterAppendPayloadEngine(SigGroupHead *sgh,
|
|
|
|
void (*PrefilterFunc)(DetectEngineThreadCtx *det_ctx, Packet *p, const void *pectx),
|
|
|
|
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(e->name, e->Free);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int PrefilterAppendTxEngine(SigGroupHead *sgh,
|
|
|
|
void (*PrefilterTxFunc)(DetectEngineThreadCtx *det_ctx, const void *pectx,
|
|
|
|
Packet *p, Flow *f, void *tx,
|
|
|
|
const uint64_t idx, const uint8_t flags),
|
|
|
|
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;
|
|
|
|
e->tx_min_progress = 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(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(PrefilterEngine *list)
|
|
|
|
{
|
|
|
|
PrefilterEngine *t = list;
|
|
|
|
|
|
|
|
while (1) {
|
|
|
|
const PrefilterStore *s = PrefilterStoreGetStore(t->gid);
|
|
|
|
if (s && s->FreeFunc && t->pectx) {
|
|
|
|
s->FreeFunc(t->pectx);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (t->is_last)
|
|
|
|
break;
|
|
|
|
t++;
|
|
|
|
}
|
|
|
|
SCFreeAligned(list);
|
|
|
|
}
|
|
|
|
|
|
|
|
void PrefilterCleanupRuleGroup(SigGroupHead *sgh)
|
|
|
|
{
|
|
|
|
if (sgh->pkt_engines) {
|
|
|
|
PrefilterFreeEngines(sgh->pkt_engines);
|
|
|
|
sgh->pkt_engines = NULL;
|
|
|
|
}
|
|
|
|
if (sgh->payload_engines) {
|
|
|
|
PrefilterFreeEngines(sgh->payload_engines);
|
|
|
|
sgh->payload_engines = NULL;
|
|
|
|
}
|
|
|
|
if (sgh->tx_engines) {
|
|
|
|
PrefilterFreeEngines(sgh->tx_engines);
|
|
|
|
sgh->tx_engines = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void PrefilterSetupRuleGroup(DetectEngineCtx *de_ctx, SigGroupHead *sgh)
|
|
|
|
{
|
|
|
|
BUG_ON(PatternMatchPrepareGroup(de_ctx, sgh) != 0);
|
|
|
|
|
|
|
|
if (de_ctx->prefilter_setting == DETECT_PREFILTER_AUTO) {
|
|
|
|
int i = 0;
|
|
|
|
for (i = 0; i < DETECT_TBLSIZE; i++)
|
|
|
|
{
|
|
|
|
if (sigmatch_table[i].SetupPrefilter != NULL) {
|
|
|
|
sigmatch_table[i].SetupPrefilter(sgh);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* 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++;
|
|
|
|
de_ctx->prefilter_maxid = MAX(de_ctx->prefilter_maxid, el->gid);
|
|
|
|
}
|
|
|
|
sgh->pkt_engines = SCMallocAligned(cnt * sizeof(PrefilterEngine), CLS);
|
|
|
|
if (sgh->pkt_engines == NULL) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
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->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++;
|
|
|
|
de_ctx->prefilter_maxid = MAX(de_ctx->prefilter_maxid, el->gid);
|
|
|
|
}
|
|
|
|
sgh->payload_engines = SCMallocAligned(cnt * sizeof(PrefilterEngine), CLS);
|
|
|
|
if (sgh->payload_engines == NULL) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
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->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++;
|
|
|
|
de_ctx->prefilter_maxid = MAX(de_ctx->prefilter_maxid, el->gid);
|
|
|
|
}
|
|
|
|
sgh->tx_engines = SCMallocAligned(cnt * sizeof(PrefilterEngine), CLS);
|
|
|
|
if (sgh->tx_engines == NULL) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
memset(sgh->tx_engines, 0x00, (cnt * sizeof(PrefilterEngine)));
|
|
|
|
|
|
|
|
uint32_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->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;
|
|
|
|
if (el->next == NULL) {
|
|
|
|
e->is_last = TRUE;
|
|
|
|
}
|
|
|
|
e++;
|
|
|
|
}
|
|
|
|
SCLogDebug("sgh %p max local_id %u", sgh, local_id);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* 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);
|
|
|
|
uint16_t u;
|
|
|
|
|
|
|
|
for (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);
|
|
|
|
}
|
|
|
|
|
|
|
|
static SCMutex g_prefilter_mutex = SCMUTEX_INITIALIZER;
|
|
|
|
static uint32_t g_prefilter_id = 0;
|
|
|
|
static HashListTable *g_prefilter_hash_table = NULL;
|
|
|
|
|
|
|
|
static void PrefilterDeinit(void)
|
|
|
|
{
|
|
|
|
SCMutexLock(&g_prefilter_mutex);
|
|
|
|
BUG_ON(g_prefilter_hash_table == NULL);
|
|
|
|
HashListTableFree(g_prefilter_hash_table);
|
|
|
|
SCMutexUnlock(&g_prefilter_mutex);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void PrefilterInit(void)
|
|
|
|
{
|
|
|
|
SCMutexLock(&g_prefilter_mutex);
|
|
|
|
BUG_ON(g_prefilter_hash_table != NULL);
|
|
|
|
|
|
|
|
g_prefilter_hash_table = HashListTableInit(256,
|
|
|
|
PrefilterStoreHashFunc,
|
|
|
|
PrefilterStoreCompareFunc,
|
|
|
|
PrefilterStoreFreeFunc);
|
|
|
|
BUG_ON(g_prefilter_hash_table == NULL);
|
|
|
|
atexit(PrefilterDeinit);
|
|
|
|
SCMutexUnlock(&g_prefilter_mutex);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int PrefilterStoreGetId(const char *name, void (*FreeFunc)(void *))
|
|
|
|
{
|
|
|
|
PrefilterStore ctx = { name, FreeFunc, 0 };
|
|
|
|
|
|
|
|
if (g_prefilter_hash_table == NULL) {
|
|
|
|
PrefilterInit();
|
|
|
|
}
|
|
|
|
|
|
|
|
SCLogDebug("looking up %s", name);
|
|
|
|
|
|
|
|
SCMutexLock(&g_prefilter_mutex);
|
|
|
|
PrefilterStore *rctx = HashListTableLookup(g_prefilter_hash_table, (void *)&ctx, 0);
|
|
|
|
if (rctx != NULL) {
|
|
|
|
SCMutexUnlock(&g_prefilter_mutex);
|
|
|
|
return rctx->id;
|
|
|
|
}
|
|
|
|
|
|
|
|
PrefilterStore *actx = SCCalloc(1, sizeof(*actx));
|
|
|
|
if (actx == NULL) {
|
|
|
|
SCMutexUnlock(&g_prefilter_mutex);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
actx->name = name;
|
|
|
|
actx->FreeFunc = FreeFunc;
|
|
|
|
actx->id = g_prefilter_id++;
|
|
|
|
SCLogDebug("prefilter engine %s has profile id %u", actx->name, actx->id);
|
|
|
|
|
|
|
|
int ret = HashListTableAdd(g_prefilter_hash_table, actx, 0);
|
|
|
|
if (ret != 0) {
|
|
|
|
SCMutexUnlock(&g_prefilter_mutex);
|
|
|
|
SCFree(actx);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
int r = actx->id;
|
|
|
|
SCMutexUnlock(&g_prefilter_mutex);
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** \warning slow */
|
|
|
|
static const PrefilterStore *PrefilterStoreGetStore(const uint32_t id)
|
|
|
|
{
|
|
|
|
const PrefilterStore *store = NULL;
|
|
|
|
SCMutexLock(&g_prefilter_mutex);
|
|
|
|
if (g_prefilter_hash_table != NULL) {
|
|
|
|
HashListTableBucket *hb = HashListTableGetListHead(g_prefilter_hash_table);
|
|
|
|
for ( ; hb != NULL; hb = HashListTableGetListNext(hb)) {
|
|
|
|
PrefilterStore *ctx = HashListTableGetListData(hb);
|
|
|
|
if (ctx->id == id) {
|
|
|
|
store = ctx;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
SCMutexUnlock(&g_prefilter_mutex);
|
|
|
|
return store;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef PROFILING
|
|
|
|
/** \warning slow */
|
|
|
|
const char *PrefilterStoreGetName(const uint32_t id)
|
|
|
|
{
|
|
|
|
const char *name = NULL;
|
|
|
|
SCMutexLock(&g_prefilter_mutex);
|
|
|
|
if (g_prefilter_hash_table != NULL) {
|
|
|
|
HashListTableBucket *hb = HashListTableGetListHead(g_prefilter_hash_table);
|
|
|
|
for ( ; hb != NULL; hb = HashListTableGetListNext(hb)) {
|
|
|
|
PrefilterStore *ctx = HashListTableGetListData(hb);
|
|
|
|
if (ctx->id == id) {
|
|
|
|
name = ctx->name;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
SCMutexUnlock(&g_prefilter_mutex);
|
|
|
|
return name;
|
|
|
|
}
|
|
|
|
#endif
|