mirror of https://github.com/OISF/suricata
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.
426 lines
13 KiB
C
426 lines
13 KiB
C
/* Copyright (C) 2007-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.
|
|
*/
|
|
|
|
#include "suricata-common.h"
|
|
#include "detect-engine-prefilter.h"
|
|
#include "detect-engine-prefilter-common.h"
|
|
|
|
typedef struct PrefilterPacketHeaderHashCtx_ {
|
|
PrefilterPacketHeaderValue v1;
|
|
|
|
uint16_t type; /**< PREFILTER_EXTRA_MATCH_* */
|
|
uint16_t value;
|
|
|
|
uint32_t cnt;
|
|
} PrefilterPacketHeaderHashCtx;
|
|
|
|
static uint32_t PrefilterPacketHeaderHashFunc(HashListTable *ht, void *data, uint16_t datalen)
|
|
{
|
|
PrefilterPacketHeaderCtx *ctx = data;
|
|
uint64_t hash = ctx->v1.u64[0] + ctx->v1.u64[1] + ctx->type + ctx->value;
|
|
hash %= ht->array_size;
|
|
return hash;
|
|
}
|
|
|
|
static char PrefilterPacketHeaderCompareFunc(void *data1, uint16_t len1,
|
|
void *data2, uint16_t len2)
|
|
{
|
|
PrefilterPacketHeaderHashCtx *ctx1 = data1;
|
|
PrefilterPacketHeaderHashCtx *ctx2 = data2;
|
|
return (ctx1->v1.u64[0] == ctx2->v1.u64[0] &&
|
|
ctx1->v1.u64[1] == ctx2->v1.u64[1] &&
|
|
ctx1->type == ctx2->type &&
|
|
ctx1->value == ctx2->value);
|
|
}
|
|
|
|
static void PrefilterPacketHeaderFreeFunc(void *ptr)
|
|
{
|
|
SCFree(ptr);
|
|
}
|
|
|
|
static void PrefilterPacketHeaderFree(void *pectx)
|
|
{
|
|
PrefilterPacketHeaderCtx *ctx = pectx;
|
|
SCFree(ctx->sigs_array);
|
|
SCFree(ctx);
|
|
}
|
|
|
|
static void PrefilterPacketU8HashCtxFree(void *vctx)
|
|
{
|
|
PrefilterPacketU8HashCtx *ctx = vctx;
|
|
int i;
|
|
for (i = 0; i < 256; i++) {
|
|
SigsArray *sa = ctx->array[i];
|
|
if (sa == NULL)
|
|
continue;
|
|
SCFree(sa->sigs);
|
|
SCFree(sa);
|
|
}
|
|
SCFree(ctx);
|
|
}
|
|
|
|
static void GetExtraMatch(const Signature *s, uint16_t *type, uint16_t *value)
|
|
{
|
|
if (s->sp != NULL && s->sp->next == NULL && s->sp->port == s->sp->port2 &&
|
|
!(s->sp->flags & PORT_FLAG_NOT))
|
|
{
|
|
*type = PREFILTER_EXTRA_MATCH_SRCPORT;
|
|
*value = s->sp->port;
|
|
} else if (s->alproto != ALPROTO_UNKNOWN) {
|
|
*type = PREFILTER_EXTRA_MATCH_ALPROTO;
|
|
*value = s->alproto;
|
|
} else if (s->dp != NULL && s->dp->next == NULL && s->dp->port == s->dp->port2 &&
|
|
!(s->dp->flags & PORT_FLAG_NOT))
|
|
{
|
|
*type = PREFILTER_EXTRA_MATCH_DSTPORT;
|
|
*value = s->dp->port;
|
|
}
|
|
}
|
|
|
|
/** \internal
|
|
*/
|
|
static int
|
|
SetupEngineForPacketHeader(DetectEngineCtx *de_ctx, SigGroupHead *sgh,
|
|
int sm_type, PrefilterPacketHeaderHashCtx *hctx,
|
|
bool (*Compare)(PrefilterPacketHeaderValue v, void *),
|
|
void (*Match)(DetectEngineThreadCtx *det_ctx, Packet *p, const void *pectx))
|
|
{
|
|
Signature *s = NULL;
|
|
uint32_t sig = 0;
|
|
uint32_t sig_offset = 0;
|
|
|
|
PrefilterPacketHeaderCtx *ctx = SCCalloc(1, sizeof(PrefilterPacketHeaderCtx));
|
|
if (ctx == NULL)
|
|
return -1;
|
|
|
|
ctx->v1 = hctx->v1;
|
|
ctx->type = hctx->type;
|
|
ctx->value = hctx->value;
|
|
|
|
ctx->sigs_cnt = hctx->cnt;
|
|
ctx->sigs_array = SCCalloc(ctx->sigs_cnt, sizeof(SigIntId));
|
|
if (ctx->sigs_array == NULL) {
|
|
SCFree(ctx);
|
|
return -1;
|
|
}
|
|
|
|
for (sig = 0; sig < sgh->init->sig_cnt; sig++) {
|
|
s = sgh->init->match_array[sig];
|
|
if (s == NULL)
|
|
continue;
|
|
if (s->init_data->prefilter_sm == NULL || s->init_data->prefilter_sm->type != sm_type)
|
|
continue;
|
|
|
|
uint16_t type = 0;
|
|
uint16_t value = 0;
|
|
GetExtraMatch(s, &type, &value);
|
|
|
|
if (Compare(ctx->v1, s->init_data->prefilter_sm->ctx) &&
|
|
ctx->type == type && ctx->value == value)
|
|
{
|
|
SCLogDebug("appending sid %u on %u", s->id, sig_offset);
|
|
ctx->sigs_array[sig_offset] = s->num;
|
|
sig_offset++;
|
|
|
|
s->flags |= SIG_FLAG_PREFILTER;
|
|
}
|
|
}
|
|
|
|
SCLogDebug("%s: ctx %p extra type %u extra value %u, sig cnt %u",
|
|
sigmatch_table[sm_type].name, ctx, ctx->type, ctx->value,
|
|
ctx->sigs_cnt);
|
|
PrefilterAppendEngine(de_ctx, sgh, Match, ctx,
|
|
PrefilterPacketHeaderFree, sigmatch_table[sm_type].name);
|
|
return 0;
|
|
}
|
|
|
|
/** \internal
|
|
* \brief apply signature to each value */
|
|
static void ApplyToU8Hash(PrefilterPacketU8HashCtx *ctx, PrefilterPacketHeaderValue v, Signature *s)
|
|
{
|
|
switch (v.u8[0]) {
|
|
case PREFILTER_U8HASH_MODE_EQ:
|
|
{
|
|
SigsArray *sa = ctx->array[v.u8[1]];
|
|
sa->sigs[sa->offset++] = s->num;
|
|
break;
|
|
}
|
|
case PREFILTER_U8HASH_MODE_LT:
|
|
{
|
|
uint8_t x = v.u8[1] - 1;
|
|
do {
|
|
SigsArray *sa = ctx->array[x];
|
|
sa->sigs[sa->offset++] = s->num;
|
|
} while (x--);
|
|
|
|
break;
|
|
}
|
|
case PREFILTER_U8HASH_MODE_GT:
|
|
{
|
|
int x = v.u8[1] + 1;
|
|
do {
|
|
SigsArray *sa = ctx->array[x];
|
|
sa->sigs[sa->offset++] = s->num;
|
|
} while (++x < 256);
|
|
|
|
break;
|
|
}
|
|
case PREFILTER_U8HASH_MODE_RA:
|
|
{
|
|
int x = v.u8[1] + 1;
|
|
do {
|
|
SigsArray *sa = ctx->array[x];
|
|
sa->sigs[sa->offset++] = s->num;
|
|
} while (++x < v.u8[2]);
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/** \internal
|
|
* \brief turn values into a u8 hash map
|
|
* \todo improve error handling
|
|
* \todo deduplicate sigs arrays
|
|
*/
|
|
static int
|
|
SetupEngineForPacketHeaderPrefilterPacketU8HashCtx(DetectEngineCtx *de_ctx,
|
|
SigGroupHead *sgh, int sm_type, uint32_t *counts,
|
|
void (*Set)(PrefilterPacketHeaderValue *v, void *),
|
|
bool (*Compare)(PrefilterPacketHeaderValue v, void *),
|
|
void (*Match)(DetectEngineThreadCtx *det_ctx, Packet *p, const void *pectx))
|
|
{
|
|
Signature *s = NULL;
|
|
uint32_t sig = 0;
|
|
uint32_t cnt = 0;
|
|
|
|
PrefilterPacketU8HashCtx *ctx = SCCalloc(1, sizeof(PrefilterPacketU8HashCtx));
|
|
if (ctx == NULL)
|
|
return -1;
|
|
|
|
int set_cnt = 0;
|
|
for (int i = 0; i < 256; i++) {
|
|
if (counts[i] == 0)
|
|
continue;
|
|
ctx->array[i] = SCCalloc(1, sizeof(SigsArray));
|
|
BUG_ON(ctx->array[i] == NULL);
|
|
|
|
ctx->array[i]->cnt = counts[i];
|
|
ctx->array[i]->sigs = SCCalloc(ctx->array[i]->cnt, sizeof(SigIntId));
|
|
BUG_ON(ctx->array[i]->sigs == NULL);
|
|
set_cnt++;
|
|
}
|
|
if (set_cnt == 0) {
|
|
/* not an error */
|
|
PrefilterPacketU8HashCtxFree(ctx);
|
|
return 0;
|
|
}
|
|
|
|
for (sig = 0; sig < sgh->init->sig_cnt; sig++) {
|
|
s = sgh->init->match_array[sig];
|
|
if (s == NULL)
|
|
continue;
|
|
if (s->init_data->prefilter_sm == NULL || s->init_data->prefilter_sm->type != sm_type)
|
|
continue;
|
|
|
|
PrefilterPacketHeaderValue v;
|
|
memset(&v, 0, sizeof(v));
|
|
Set(&v, s->init_data->prefilter_sm->ctx);
|
|
|
|
ApplyToU8Hash(ctx, v, s);
|
|
s->flags |= SIG_FLAG_PREFILTER;
|
|
cnt++;
|
|
}
|
|
|
|
if (cnt) {
|
|
PrefilterAppendEngine(de_ctx, sgh, Match, ctx,
|
|
PrefilterPacketU8HashCtxFree,
|
|
sigmatch_table[sm_type].name);
|
|
} else {
|
|
PrefilterPacketU8HashCtxFree(ctx);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/** \internal
|
|
* \brief setup a engine for each unique value
|
|
*/
|
|
static void SetupSingle(DetectEngineCtx *de_ctx, HashListTable *hash_table,
|
|
SigGroupHead *sgh, int sm_type,
|
|
bool (*Compare)(PrefilterPacketHeaderValue v, void *),
|
|
void (*Match)(DetectEngineThreadCtx *det_ctx,
|
|
Packet *p, const void *pectx))
|
|
{
|
|
HashListTableBucket *hb = HashListTableGetListHead(hash_table);
|
|
for ( ; hb != NULL; hb = HashListTableGetListNext(hb)) {
|
|
PrefilterPacketHeaderHashCtx *ctx = HashListTableGetListData(hb);
|
|
|
|
SetupEngineForPacketHeader(de_ctx, sgh, sm_type,
|
|
ctx, Compare, Match);
|
|
}
|
|
}
|
|
|
|
/** \internal
|
|
* \brief setup a single engine with a hash map for u8 values
|
|
*/
|
|
static void SetupU8Hash(DetectEngineCtx *de_ctx, HashListTable *hash_table,
|
|
SigGroupHead *sgh, int sm_type,
|
|
void (*Set)(PrefilterPacketHeaderValue *v, void *),
|
|
bool (*Compare)(PrefilterPacketHeaderValue v, void *),
|
|
void (*Match)(DetectEngineThreadCtx *det_ctx,
|
|
Packet *p, const void *pectx))
|
|
{
|
|
uint32_t counts[256];
|
|
memset(&counts, 0, sizeof(counts));
|
|
|
|
HashListTableBucket *hb = HashListTableGetListHead(hash_table);
|
|
for ( ; hb != NULL; hb = HashListTableGetListNext(hb)) {
|
|
PrefilterPacketHeaderHashCtx *ctx = HashListTableGetListData(hb);
|
|
|
|
switch (ctx->v1.u8[0]) {
|
|
case PREFILTER_U8HASH_MODE_EQ:
|
|
counts[ctx->v1.u8[1]] += ctx->cnt;
|
|
break;
|
|
case PREFILTER_U8HASH_MODE_LT:
|
|
{
|
|
uint8_t v = ctx->v1.u8[1];
|
|
while (v > 0) {
|
|
v--;
|
|
counts[v] += ctx->cnt;
|
|
}
|
|
|
|
break;
|
|
}
|
|
case PREFILTER_U8HASH_MODE_GT:
|
|
{
|
|
uint8_t v = ctx->v1.u8[1];
|
|
while (v < UINT8_MAX) {
|
|
v++;
|
|
counts[v] += ctx->cnt;
|
|
}
|
|
|
|
break;
|
|
}
|
|
case PREFILTER_U8HASH_MODE_RA:
|
|
{
|
|
if (ctx->v1.u8[1] < ctx->v1.u8[2]) {
|
|
// ctx->v1.u8[1] is not UINT8_MAX
|
|
uint8_t v = ctx->v1.u8[1] + 1;
|
|
while (v < ctx->v1.u8[2]) {
|
|
counts[v] += ctx->cnt;
|
|
v++;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
SetupEngineForPacketHeaderPrefilterPacketU8HashCtx(de_ctx, sgh, sm_type,
|
|
counts, Set, Compare, Match);
|
|
}
|
|
|
|
static int PrefilterSetupPacketHeaderCommon(DetectEngineCtx *de_ctx,
|
|
SigGroupHead *sgh, int sm_type,
|
|
void (*Set)(PrefilterPacketHeaderValue *v, void *),
|
|
bool (*Compare)(PrefilterPacketHeaderValue v, void *),
|
|
void (*Match)(DetectEngineThreadCtx *det_ctx,
|
|
Packet *p, const void *pectx),
|
|
bool u8hash)
|
|
{
|
|
Signature *s = NULL;
|
|
uint32_t sig = 0;
|
|
|
|
if (sgh == NULL)
|
|
return 0;
|
|
|
|
/* first count how many engines we will need */
|
|
|
|
HashListTable *hash_table = HashListTableInit(4096,
|
|
PrefilterPacketHeaderHashFunc,
|
|
PrefilterPacketHeaderCompareFunc,
|
|
PrefilterPacketHeaderFreeFunc);
|
|
if (hash_table == NULL)
|
|
return -1;
|
|
|
|
for (sig = 0; sig < sgh->init->sig_cnt; sig++) {
|
|
s = sgh->init->match_array[sig];
|
|
if (s == NULL)
|
|
continue;
|
|
if (s->init_data->prefilter_sm == NULL || s->init_data->prefilter_sm->type != sm_type)
|
|
continue;
|
|
|
|
PrefilterPacketHeaderHashCtx ctx;
|
|
memset(&ctx, 0, sizeof(ctx));
|
|
Set(&ctx.v1, s->init_data->prefilter_sm->ctx);
|
|
|
|
GetExtraMatch(s, &ctx.type, &ctx.value);
|
|
|
|
PrefilterPacketHeaderHashCtx *rctx = HashListTableLookup(hash_table, (void *)&ctx, 0);
|
|
if (rctx != 0) {
|
|
rctx->cnt++;
|
|
} else {
|
|
PrefilterPacketHeaderHashCtx *actx = SCCalloc(1, sizeof(*actx));
|
|
if (actx == NULL)
|
|
goto error;
|
|
|
|
Set(&actx->v1, s->init_data->prefilter_sm->ctx);
|
|
actx->cnt = 1;
|
|
actx->type = ctx.type;
|
|
actx->value = ctx.value;
|
|
|
|
int ret = HashListTableAdd(hash_table, actx, 0);
|
|
if (ret != 0) {
|
|
SCFree(actx);
|
|
goto error;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!u8hash) {
|
|
SetupSingle(de_ctx, hash_table, sgh, sm_type, Compare, Match);
|
|
} else {
|
|
SetupU8Hash(de_ctx, hash_table, sgh, sm_type, Set, Compare, Match);
|
|
}
|
|
|
|
HashListTableFree(hash_table);
|
|
return 0;
|
|
error:
|
|
HashListTableFree(hash_table);
|
|
return -1;
|
|
}
|
|
|
|
int PrefilterSetupPacketHeaderU8Hash(DetectEngineCtx *de_ctx,
|
|
SigGroupHead *sgh, int sm_type,
|
|
void (*Set)(PrefilterPacketHeaderValue *v, void *),
|
|
bool (*Compare)(PrefilterPacketHeaderValue v, void *),
|
|
void (*Match)(DetectEngineThreadCtx *det_ctx,
|
|
Packet *p, const void *pectx))
|
|
{
|
|
return PrefilterSetupPacketHeaderCommon(de_ctx, sgh, sm_type, Set, Compare, Match, true);
|
|
}
|
|
|
|
int PrefilterSetupPacketHeader(DetectEngineCtx *de_ctx,
|
|
SigGroupHead *sgh, int sm_type,
|
|
void (*Set)(PrefilterPacketHeaderValue *v, void *),
|
|
bool (*Compare)(PrefilterPacketHeaderValue v, void *),
|
|
void (*Match)(DetectEngineThreadCtx *det_ctx,
|
|
Packet *p, const void *pectx))
|
|
{
|
|
return PrefilterSetupPacketHeaderCommon(de_ctx, sgh, sm_type, Set, Compare, Match, false);
|
|
}
|