|
|
|
#include "suricata-common.h"
|
|
|
|
#include "util-mpm.h"
|
|
|
|
#include "util-debug.h"
|
|
|
|
|
|
|
|
/* include pattern matchers */
|
|
|
|
#include "util-mpm-wumanber.h"
|
|
|
|
#include "util-mpm-b2g.h"
|
|
|
|
#include "util-mpm-b3g.h"
|
|
|
|
|
|
|
|
/** \brief Setup a pmq
|
|
|
|
* \param pmq Pattern matcher queue to be initialized
|
|
|
|
* \param maxid Max id to be matched on
|
|
|
|
* \retval -1 error
|
|
|
|
* \retval 0 ok
|
|
|
|
*/
|
|
|
|
int PmqSetup(PatternMatcherQueue *pmq, uint32_t maxid) {
|
|
|
|
if (pmq == NULL)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
memset(pmq, 0, sizeof(PatternMatcherQueue));
|
|
|
|
|
|
|
|
pmq->sig_id_array = malloc(maxid * sizeof(uint32_t));
|
|
|
|
if (pmq->sig_id_array == NULL) {
|
|
|
|
printf("ERROR: could not setup memory for pattern matcher: %s\n", strerror(errno));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
memset(pmq->sig_id_array, 0, maxid * sizeof(uint32_t));
|
|
|
|
pmq->sig_id_array_cnt = 0;
|
|
|
|
|
|
|
|
/* lookup bitarray */
|
|
|
|
pmq->sig_bitarray = malloc(maxid / 8 + 1);
|
|
|
|
if (pmq->sig_bitarray == NULL) {
|
|
|
|
printf("ERROR: could not setup memory for pattern matcher: %s\n", strerror(errno));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
memset(pmq->sig_bitarray, 0, maxid / 8 + 1);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** \brief Reset a Pmq for reusage. Meant to be called after a single search.
|
|
|
|
* \param pmq Pattern matcher to be reset.
|
|
|
|
*/
|
|
|
|
void PmqReset(PatternMatcherQueue *pmq) {
|
|
|
|
uint32_t u;
|
|
|
|
for (u = 0; u < pmq->sig_id_array_cnt; u++) {
|
|
|
|
pmq->sig_bitarray[(pmq->sig_id_array[u] / 8)] &= ~(1<<(pmq->sig_id_array[u] % 8));
|
|
|
|
}
|
|
|
|
pmq->sig_id_array_cnt = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** \brief Cleanup a Pmq
|
|
|
|
* \param pmq Pattern matcher queue to be cleaned up.
|
|
|
|
*/
|
|
|
|
void PmqCleanup(PatternMatcherQueue *pmq) {
|
|
|
|
if (pmq == NULL)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (pmq->sig_id_array != NULL) {
|
|
|
|
free(pmq->sig_id_array);
|
|
|
|
pmq->sig_id_array = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pmq->sig_bitarray != NULL) {
|
|
|
|
free(pmq->sig_bitarray);
|
|
|
|
pmq->sig_bitarray = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
pmq->sig_id_array_cnt = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** \brief Cleanup and free a Pmq
|
|
|
|
* \param pmq Pattern matcher queue to be free'd.
|
|
|
|
*/
|
|
|
|
void PmqFree(PatternMatcherQueue *pmq) {
|
|
|
|
if (pmq == NULL)
|
|
|
|
return;
|
|
|
|
|
|
|
|
PmqCleanup(pmq);
|
|
|
|
free(pmq);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* cleanup list with all matches
|
|
|
|
*
|
|
|
|
* used at search runtime (or actually once per search) */
|
|
|
|
void
|
|
|
|
MpmMatchCleanup(MpmThreadCtx *thread_ctx) {
|
|
|
|
SCLogDebug("mem %" PRIu32 "", thread_ctx->memory_size);
|
|
|
|
|
|
|
|
MpmMatch *nxt;
|
|
|
|
MpmMatch *m = thread_ctx->qlist;
|
|
|
|
|
|
|
|
while (m != NULL) {
|
|
|
|
BUG_ON(m == m->qnext);
|
|
|
|
nxt = m->qnext;
|
|
|
|
|
|
|
|
/* clear the bucket */
|
|
|
|
m->mb->top = NULL;
|
|
|
|
m->mb->bot = NULL;
|
|
|
|
m->mb->len = 0;
|
|
|
|
|
|
|
|
thread_ctx->qlist = m->qnext;
|
|
|
|
|
|
|
|
/* add to the spare list */
|
|
|
|
if (thread_ctx->sparelist == NULL) {
|
|
|
|
thread_ctx->sparelist = m;
|
|
|
|
m->qnext = NULL;
|
|
|
|
} else {
|
|
|
|
m->qnext = thread_ctx->sparelist;
|
|
|
|
thread_ctx->sparelist = m;
|
|
|
|
}
|
|
|
|
|
|
|
|
m = nxt;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/** \brief allocate a match
|
|
|
|
*
|
|
|
|
* used at search runtime */
|
|
|
|
inline MpmMatch *
|
|
|
|
MpmMatchAlloc(MpmThreadCtx *thread_ctx) {
|
|
|
|
MpmMatch *m = malloc(sizeof(MpmMatch));
|
|
|
|
if (m == NULL)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
thread_ctx->memory_cnt++;
|
|
|
|
thread_ctx->memory_size += sizeof(MpmMatch);
|
|
|
|
|
|
|
|
m->offset = 0;
|
|
|
|
m->next = NULL;
|
|
|
|
m->qnext = NULL;
|
|
|
|
m->mb = NULL;
|
|
|
|
return m;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** \brief append a match to a bucket
|
|
|
|
*
|
|
|
|
* used at search runtime */
|
|
|
|
inline int
|
|
|
|
MpmMatchAppend(MpmThreadCtx *thread_ctx, PatternMatcherQueue *pmq, MpmEndMatch *em, MpmMatchBucket *mb, uint16_t offset, uint16_t patlen)
|
|
|
|
{
|
|
|
|
/* don't bother looking at sigs that didn't match
|
|
|
|
* when we scanned. There's no matching anyway. */
|
|
|
|
if (pmq != NULL && pmq->mode == PMQ_MODE_SEARCH) {
|
|
|
|
if (!(pmq->sig_bitarray[(em->sig_id / 8)] & (1<<(em->sig_id % 8))))
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* if our endmatch is set to a single match being enough,
|
|
|
|
we're not going to add more if we already have one */
|
|
|
|
if (em->flags & MPM_ENDMATCH_SINGLE && mb->len)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
/* check offset */
|
|
|
|
if (offset < em->offset)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
/* check depth */
|
|
|
|
if (em->depth && (offset+patlen) > em->depth)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
/* ok all checks passed, now append the match */
|
|
|
|
MpmMatch *m;
|
|
|
|
/* pull a match from the spare list */
|
|
|
|
if (thread_ctx->sparelist != NULL) {
|
|
|
|
m = thread_ctx->sparelist;
|
|
|
|
thread_ctx->sparelist = m->qnext;
|
|
|
|
} else {
|
|
|
|
m = MpmMatchAlloc(thread_ctx);
|
|
|
|
if (m == NULL)
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
m->offset = offset;
|
|
|
|
m->mb = mb;
|
|
|
|
m->next = NULL;
|
|
|
|
m->qnext = NULL;
|
|
|
|
|
|
|
|
/* append to the mb list */
|
|
|
|
if (mb->bot == NULL) { /* empty list */
|
|
|
|
mb->top = m;
|
|
|
|
mb->bot = m;
|
|
|
|
} else { /* more items in list */
|
|
|
|
mb->bot->next = m;
|
|
|
|
mb->bot = m;
|
|
|
|
}
|
|
|
|
|
|
|
|
mb->len++;
|
|
|
|
|
|
|
|
/* put in the queue list */
|
|
|
|
if (thread_ctx->qlist == NULL) { /* empty list */
|
|
|
|
thread_ctx->qlist = m;
|
|
|
|
} else { /* more items in list */
|
|
|
|
m->qnext = thread_ctx->qlist;
|
|
|
|
thread_ctx->qlist = m;
|
|
|
|
}
|
|
|
|
|
|
|
|
BUG_ON(m == m->qnext);
|
|
|
|
|
|
|
|
if (pmq != NULL) {
|
|
|
|
/* make sure we only append a sig with a matching pattern once,
|
|
|
|
* so we won't inspect it more than once. For this we keep a
|
|
|
|
* bitarray of sig internal id's and flag each sig that matched */
|
|
|
|
if (!(pmq->sig_bitarray[(em->sig_id / 8)] & (1<<(em->sig_id % 8)))) {
|
|
|
|
/* flag this sig_id as being added now */
|
|
|
|
pmq->sig_bitarray[(em->sig_id / 8)] |= (1<<(em->sig_id % 8));
|
|
|
|
/* append the sig_id to the array with matches */
|
|
|
|
pmq->sig_id_array[pmq->sig_id_array_cnt] = em->sig_id;
|
|
|
|
pmq->sig_id_array_cnt++;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* nosearch flag */
|
|
|
|
if (pmq->mode == PMQ_MODE_SCAN && !(em->flags & MPM_ENDMATCH_NOSEARCH)) {
|
|
|
|
pmq->searchable++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
SCLogDebug("len %" PRIu32 " (offset %" PRIu32 ")", mb->len, m->offset);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
void MpmMatchFree(MpmThreadCtx *ctx, MpmMatch *m) {
|
|
|
|
ctx->memory_cnt--;
|
|
|
|
ctx->memory_size -= sizeof(MpmMatch);
|
|
|
|
free(m);
|
|
|
|
}
|
|
|
|
|
|
|
|
void MpmMatchFreeSpares(MpmThreadCtx *mpm_ctx, MpmMatch *m) {
|
|
|
|
while(m) {
|
|
|
|
MpmMatch *tm = m->qnext;
|
|
|
|
MpmMatchFree(mpm_ctx, m);
|
|
|
|
m = tm;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* allocate an endmatch
|
|
|
|
*
|
|
|
|
* Only used in the initialization phase */
|
|
|
|
MpmEndMatch *MpmAllocEndMatch (MpmCtx *ctx)
|
|
|
|
{
|
|
|
|
MpmEndMatch *e = malloc(sizeof(MpmEndMatch));
|
|
|
|
if (e == NULL)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
memset(e, 0, sizeof(MpmEndMatch));
|
|
|
|
|
|
|
|
ctx->memory_cnt++;
|
|
|
|
ctx->memory_size += sizeof(MpmEndMatch);
|
|
|
|
ctx->endmatches++;
|
|
|
|
return e;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* \brief Return the pattern max length of a registered matcher
|
|
|
|
* \retval 0 if it has no limit
|
|
|
|
* \retval max_pattern_length of the specified matcher type
|
|
|
|
* \retval -1 if the type is not registered return -1
|
|
|
|
*/
|
|
|
|
int32_t MpmMatcherGetMaxPatternLength(uint16_t matcher) {
|
|
|
|
if (matcher < MPM_TABLE_SIZE)
|
|
|
|
return mpm_table[matcher].max_pattern_length;
|
|
|
|
else
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
void MpmEndMatchFree(MpmCtx *ctx, MpmEndMatch *em) {
|
|
|
|
ctx->memory_cnt--;
|
|
|
|
ctx->memory_size -= sizeof(MpmEndMatch);
|
|
|
|
free(em);
|
|
|
|
}
|
|
|
|
|
|
|
|
void MpmEndMatchFreeAll(MpmCtx *mpm_ctx, MpmEndMatch *em) {
|
|
|
|
while(em) {
|
|
|
|
MpmEndMatch *tem = em->next;
|
|
|
|
MpmEndMatchFree(mpm_ctx, em);
|
|
|
|
em = tem;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void MpmInitThreadCtx(MpmThreadCtx *mpm_thread_ctx, uint16_t matcher, uint32_t max_id) {
|
|
|
|
mpm_table[matcher].InitThreadCtx(NULL, mpm_thread_ctx, max_id);
|
|
|
|
}
|
|
|
|
|
|
|
|
void MpmInitCtx (MpmCtx *mpm_ctx, uint16_t matcher) {
|
|
|
|
mpm_ctx->mpm_type = matcher;
|
|
|
|
mpm_table[matcher].InitCtx(mpm_ctx);
|
|
|
|
}
|
|
|
|
|
|
|
|
void MpmTableSetup(void) {
|
|
|
|
memset(mpm_table, 0, sizeof(mpm_table));
|
|
|
|
|
|
|
|
MpmWuManberRegister();
|
|
|
|
MpmB2gRegister();
|
|
|
|
MpmB3gRegister();
|
|
|
|
}
|
|
|
|
|
|
|
|
void MpmRegisterTests(void) {
|
|
|
|
#ifdef UNITTESTS
|
|
|
|
uint16_t i;
|
|
|
|
|
|
|
|
for (i = 0; i < MPM_TABLE_SIZE; i++) {
|
|
|
|
if (mpm_table[i].RegisterUnittests != NULL) {
|
|
|
|
mpm_table[i].RegisterUnittests();
|
|
|
|
} else {
|
|
|
|
printf("Warning: mpm %s has no unittest registration function...", mpm_table[i].name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|