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-com...

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);
}