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.
2776 lines
90 KiB
C
2776 lines
90 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.
|
|
*/
|
|
|
|
/**
|
|
* \ingroup threshold
|
|
* @{
|
|
*/
|
|
|
|
/**
|
|
* \file
|
|
*
|
|
* \author Breno Silva Pinto <breno.silva@gmail.com>
|
|
*
|
|
* \todo Need to support suppress
|
|
*
|
|
* Implements Threshold support
|
|
*/
|
|
|
|
#include "suricata-common.h"
|
|
|
|
#include "host.h"
|
|
#include "ippair.h"
|
|
|
|
#include "detect.h"
|
|
#include "detect-engine.h"
|
|
#include "detect-engine-address.h"
|
|
#include "detect-threshold.h"
|
|
#include "detect-parse.h"
|
|
|
|
#include "conf.h"
|
|
#include "util-threshold-config.h"
|
|
#include "util-unittest.h"
|
|
#include "util-unittest-helper.h"
|
|
#include "util-byte.h"
|
|
#include "util-time.h"
|
|
#include "util-error.h"
|
|
#include "util-debug.h"
|
|
#include "util-fmemopen.h"
|
|
|
|
typedef enum ThresholdRuleType {
|
|
THRESHOLD_TYPE_EVENT_FILTER,
|
|
THRESHOLD_TYPE_THRESHOLD,
|
|
THRESHOLD_TYPE_RATE,
|
|
THRESHOLD_TYPE_SUPPRESS,
|
|
} ThresholdRuleType;
|
|
|
|
#ifdef UNITTESTS
|
|
/* File descriptor for unittests */
|
|
static FILE *g_ut_threshold_fp = NULL;
|
|
#endif
|
|
|
|
/* common base for all options */
|
|
#define DETECT_BASE_REGEX "^\\s*(event_filter|threshold|rate_filter|suppress)\\s*gen_id\\s*(\\d+)\\s*,\\s*sig_id\\s*(\\d+)\\s*(.*)\\s*$"
|
|
|
|
#define DETECT_THRESHOLD_REGEX "^,\\s*type\\s*(limit|both|threshold)\\s*,\\s*track\\s*(by_dst|by_src)\\s*,\\s*count\\s*(\\d+)\\s*,\\s*seconds\\s*(\\d+)\\s*$"
|
|
|
|
/* TODO: "apply_to" */
|
|
#define DETECT_RATE_REGEX "^,\\s*track\\s*(by_dst|by_src|by_both|by_rule)\\s*,\\s*count\\s*(\\d+)\\s*,\\s*seconds\\s*(\\d+)\\s*,\\s*new_action\\s*(alert|drop|pass|log|sdrop|reject)\\s*,\\s*timeout\\s*(\\d+)\\s*$"
|
|
|
|
/*
|
|
* suppress has two form:
|
|
* suppress gen_id 0, sig_id 0, track by_dst, ip 10.88.0.14
|
|
* suppress gen_id 1, sig_id 2000328
|
|
* suppress gen_id 1, sig_id 2000328, track by_src, ip fe80::/10
|
|
*/
|
|
#define DETECT_SUPPRESS_REGEX "^,\\s*track\\s*(by_dst|by_src|by_either)\\s*,\\s*ip\\s*([\\[\\],\\$\\s\\da-zA-Z.:/_]+)*\\s*$"
|
|
|
|
/* Default path for the threshold.config file */
|
|
#if defined OS_WIN32 || defined __CYGWIN__
|
|
#define THRESHOLD_CONF_DEF_CONF_FILEPATH CONFIG_DIR "\\\\threshold.config"
|
|
#else
|
|
#define THRESHOLD_CONF_DEF_CONF_FILEPATH CONFIG_DIR "/threshold.config"
|
|
#endif
|
|
|
|
static pcre *regex_base = NULL;
|
|
static pcre_extra *regex_base_study = NULL;
|
|
|
|
static pcre *regex_threshold = NULL;
|
|
static pcre_extra *regex_threshold_study = NULL;
|
|
|
|
static pcre *regex_rate = NULL;
|
|
static pcre_extra *regex_rate_study = NULL;
|
|
|
|
static pcre *regex_suppress = NULL;
|
|
static pcre_extra *regex_suppress_study = NULL;
|
|
|
|
static void SCThresholdConfDeInitContext(DetectEngineCtx *de_ctx, FILE *fd);
|
|
|
|
void SCThresholdConfGlobalInit(void)
|
|
{
|
|
const char *eb = NULL;
|
|
int eo;
|
|
int opts = 0;
|
|
|
|
regex_base = pcre_compile(DETECT_BASE_REGEX, opts, &eb, &eo, NULL);
|
|
if (regex_base == NULL) {
|
|
FatalError(SC_ERR_PCRE_COMPILE, "Compile of \"%s\" failed at offset %" PRId32 ": %s",DETECT_BASE_REGEX, eo, eb);
|
|
}
|
|
|
|
regex_base_study = pcre_study(regex_base, 0, &eb);
|
|
if (eb != NULL) {
|
|
FatalError(SC_ERR_PCRE_STUDY, "pcre study failed: %s", eb);
|
|
}
|
|
|
|
regex_threshold = pcre_compile(DETECT_THRESHOLD_REGEX, opts, &eb, &eo, NULL);
|
|
if (regex_threshold == NULL) {
|
|
FatalError(SC_ERR_PCRE_COMPILE, "Compile of \"%s\" failed at offset %" PRId32 ": %s",DETECT_THRESHOLD_REGEX, eo, eb);
|
|
}
|
|
|
|
regex_threshold_study = pcre_study(regex_threshold, 0, &eb);
|
|
if (eb != NULL) {
|
|
FatalError(SC_ERR_PCRE_STUDY, "pcre study failed: %s", eb);
|
|
}
|
|
|
|
regex_rate = pcre_compile(DETECT_RATE_REGEX, opts, &eb, &eo, NULL);
|
|
if (regex_rate == NULL) {
|
|
FatalError(SC_ERR_PCRE_COMPILE, "Compile of \"%s\" failed at offset %" PRId32 ": %s",DETECT_RATE_REGEX, eo, eb);
|
|
}
|
|
|
|
regex_rate_study = pcre_study(regex_rate, 0, &eb);
|
|
if (eb != NULL) {
|
|
FatalError(SC_ERR_PCRE_STUDY, "pcre study failed: %s", eb);
|
|
}
|
|
|
|
regex_suppress = pcre_compile(DETECT_SUPPRESS_REGEX, opts, &eb, &eo, NULL);
|
|
if (regex_suppress == NULL) {
|
|
FatalError(SC_ERR_PCRE_COMPILE, "Compile of \"%s\" failed at offset %" PRId32 ": %s",DETECT_SUPPRESS_REGEX, eo, eb);
|
|
}
|
|
|
|
regex_suppress_study = pcre_study(regex_suppress, 0, &eb);
|
|
if (eb != NULL) {
|
|
FatalError(SC_ERR_PCRE_STUDY, "pcre study failed: %s", eb);
|
|
}
|
|
|
|
}
|
|
|
|
void SCThresholdConfGlobalFree(void)
|
|
{
|
|
if (regex_base != NULL) {
|
|
pcre_free(regex_base);
|
|
regex_base = NULL;
|
|
}
|
|
if (regex_base_study != NULL) {
|
|
pcre_free(regex_base_study);
|
|
regex_base_study = NULL;
|
|
}
|
|
|
|
if (regex_threshold != NULL) {
|
|
pcre_free(regex_threshold);
|
|
regex_threshold = NULL;
|
|
}
|
|
if (regex_threshold_study != NULL) {
|
|
pcre_free(regex_threshold_study);
|
|
regex_threshold_study = NULL;
|
|
}
|
|
|
|
if (regex_rate != NULL) {
|
|
pcre_free(regex_rate);
|
|
regex_rate = NULL;
|
|
}
|
|
if (regex_rate_study != NULL) {
|
|
pcre_free(regex_rate_study);
|
|
regex_rate_study = NULL;
|
|
}
|
|
|
|
if (regex_suppress != NULL) {
|
|
pcre_free(regex_suppress);
|
|
regex_suppress = NULL;
|
|
}
|
|
if (regex_suppress_study != NULL) {
|
|
pcre_free(regex_suppress_study);
|
|
regex_suppress_study = NULL;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* \brief Returns the path for the Threshold Config file. We check if we
|
|
* can retrieve the path from the yaml conf file. If it is not present,
|
|
* return the default path for the threshold file which is
|
|
* "./threshold.config".
|
|
*
|
|
* \retval log_filename Pointer to a string containing the path for the
|
|
* Threshold Config file.
|
|
*/
|
|
static const char *SCThresholdConfGetConfFilename(const DetectEngineCtx *de_ctx)
|
|
{
|
|
const char *log_filename = NULL;
|
|
|
|
if (de_ctx != NULL && strlen(de_ctx->config_prefix) > 0) {
|
|
char config_value[256];
|
|
snprintf(config_value, sizeof(config_value),
|
|
"%s.threshold-file", de_ctx->config_prefix);
|
|
|
|
/* try loading prefix setting, fall back to global if that
|
|
* fails. */
|
|
if (ConfGet(config_value, &log_filename) != 1) {
|
|
if (ConfGet("threshold-file", &log_filename) != 1) {
|
|
log_filename = (char *)THRESHOLD_CONF_DEF_CONF_FILEPATH;
|
|
}
|
|
}
|
|
} else {
|
|
if (ConfGet("threshold-file", &log_filename) != 1) {
|
|
log_filename = (char *)THRESHOLD_CONF_DEF_CONF_FILEPATH;
|
|
}
|
|
}
|
|
return log_filename;
|
|
}
|
|
|
|
/**
|
|
* \brief Inits the context to be used by the Threshold Config parsing API.
|
|
*
|
|
* This function initializes the hash table to be used by the Detection
|
|
* Engine Context to hold the data from the threshold.config file,
|
|
* obtains the file desc to parse the threshold.config file, and
|
|
* inits the regex used to parse the lines from threshold.config
|
|
* file.
|
|
*
|
|
* \param de_ctx Pointer to the Detection Engine Context.
|
|
*
|
|
* \retval 0 On success.
|
|
* \retval -1 On failure.
|
|
*/
|
|
int SCThresholdConfInitContext(DetectEngineCtx *de_ctx)
|
|
{
|
|
const char *filename = NULL;
|
|
#ifndef UNITTESTS
|
|
FILE *fd = NULL;
|
|
#else
|
|
FILE *fd = g_ut_threshold_fp;
|
|
if (fd == NULL) {
|
|
#endif
|
|
filename = SCThresholdConfGetConfFilename(de_ctx);
|
|
if ( (fd = fopen(filename, "r")) == NULL) {
|
|
SCLogWarning(SC_ERR_FOPEN, "Error opening file: \"%s\": %s", filename, strerror(errno));
|
|
goto error;
|
|
}
|
|
#ifdef UNITTESTS
|
|
}
|
|
#endif
|
|
|
|
SCThresholdConfParseFile(de_ctx, fd);
|
|
SCThresholdConfDeInitContext(de_ctx, fd);
|
|
|
|
#ifdef UNITTESTS
|
|
g_ut_threshold_fp = NULL;
|
|
#endif
|
|
SCLogDebug("Global thresholding options defined");
|
|
return 0;
|
|
|
|
error:
|
|
SCThresholdConfDeInitContext(de_ctx, fd);
|
|
return -1;
|
|
}
|
|
|
|
/**
|
|
* \brief Releases resources used by the Threshold Config API.
|
|
*
|
|
* \param de_ctx Pointer to the Detection Engine Context.
|
|
* \param fd Pointer to file descriptor.
|
|
*/
|
|
static void SCThresholdConfDeInitContext(DetectEngineCtx *de_ctx, FILE *fd)
|
|
{
|
|
if (fd != NULL)
|
|
fclose(fd);
|
|
return;
|
|
}
|
|
|
|
/** \internal
|
|
* \brief setup suppress rules
|
|
* \retval 0 ok
|
|
* \retval -1 error
|
|
*/
|
|
static int SetupSuppressRule(DetectEngineCtx *de_ctx, uint32_t id, uint32_t gid,
|
|
uint8_t parsed_type, uint8_t parsed_track, uint32_t parsed_count,
|
|
uint32_t parsed_seconds, uint32_t parsed_timeout, uint8_t parsed_new_action,
|
|
const char *th_ip)
|
|
{
|
|
Signature *s = NULL;
|
|
SigMatch *sm = NULL;
|
|
DetectThresholdData *de = NULL;
|
|
|
|
BUG_ON(parsed_type != TYPE_SUPPRESS);
|
|
|
|
/* Install it */
|
|
if (id == 0 && gid == 0) {
|
|
if (parsed_track == TRACK_RULE) {
|
|
SCLogWarning(SC_ERR_EVENT_ENGINE, "suppressing all rules");
|
|
}
|
|
|
|
/* update each sig with our suppress info */
|
|
for (s = de_ctx->sig_list; s != NULL; s = s->next) {
|
|
/* tag the rule as noalert */
|
|
if (parsed_track == TRACK_RULE) {
|
|
s->flags |= SIG_FLAG_NOALERT;
|
|
continue;
|
|
}
|
|
|
|
de = SCMalloc(sizeof(DetectThresholdData));
|
|
if (unlikely(de == NULL))
|
|
goto error;
|
|
memset(de,0,sizeof(DetectThresholdData));
|
|
|
|
de->type = TYPE_SUPPRESS;
|
|
de->track = parsed_track;
|
|
de->count = parsed_count;
|
|
de->seconds = parsed_seconds;
|
|
de->new_action = parsed_new_action;
|
|
de->timeout = parsed_timeout;
|
|
|
|
if (parsed_track != TRACK_RULE) {
|
|
if (DetectAddressParse((const DetectEngineCtx *)de_ctx, &de->addrs, (char *)th_ip) != 0) {
|
|
SCLogError(SC_ERR_INVALID_IP_NETBLOCK, "failed to parse %s", th_ip);
|
|
goto error;
|
|
}
|
|
}
|
|
|
|
sm = SigMatchAlloc();
|
|
if (sm == NULL) {
|
|
SCLogError(SC_ERR_MEM_ALLOC, "Error allocating SigMatch");
|
|
goto error;
|
|
}
|
|
|
|
sm->type = DETECT_THRESHOLD;
|
|
sm->ctx = (void *)de;
|
|
SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_SUPPRESS);
|
|
}
|
|
} else if (id == 0 && gid > 0) {
|
|
if (parsed_track == TRACK_RULE) {
|
|
SCLogWarning(SC_ERR_EVENT_ENGINE, "suppressing all rules with gid %"PRIu32, gid);
|
|
}
|
|
/* set up suppression for each signature with a matching gid */
|
|
for (s = de_ctx->sig_list; s != NULL; s = s->next) {
|
|
if (s->gid != gid)
|
|
continue;
|
|
|
|
/* tag the rule as noalert */
|
|
if (parsed_track == TRACK_RULE) {
|
|
s->flags |= SIG_FLAG_NOALERT;
|
|
continue;
|
|
}
|
|
|
|
de = SCMalloc(sizeof(DetectThresholdData));
|
|
if (unlikely(de == NULL))
|
|
goto error;
|
|
|
|
memset(de,0,sizeof(DetectThresholdData));
|
|
|
|
de->type = TYPE_SUPPRESS;
|
|
de->track = parsed_track;
|
|
de->count = parsed_count;
|
|
de->seconds = parsed_seconds;
|
|
de->new_action = parsed_new_action;
|
|
de->timeout = parsed_timeout;
|
|
|
|
if (parsed_track != TRACK_RULE) {
|
|
if (DetectAddressParse((const DetectEngineCtx *)de_ctx, &de->addrs, (char *)th_ip) != 0) {
|
|
SCLogError(SC_ERR_INVALID_IP_NETBLOCK, "failed to parse %s", th_ip);
|
|
goto error;
|
|
}
|
|
}
|
|
|
|
sm = SigMatchAlloc();
|
|
if (sm == NULL) {
|
|
SCLogError(SC_ERR_MEM_ALLOC, "Error allocating SigMatch");
|
|
goto error;
|
|
}
|
|
|
|
sm->type = DETECT_THRESHOLD;
|
|
sm->ctx = (void *)de;
|
|
|
|
SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_SUPPRESS);
|
|
}
|
|
} else if (id > 0 && gid == 0) {
|
|
SCLogError(SC_ERR_INVALID_VALUE, "Can't use a event config that has "
|
|
"sid > 0 and gid == 0. Please fix this "
|
|
"in your threshold.conf file");
|
|
goto error;
|
|
} else {
|
|
s = SigFindSignatureBySidGid(de_ctx, id, gid);
|
|
if (s == NULL) {
|
|
SCLogWarning(SC_ERR_EVENT_ENGINE, "can't suppress sid "
|
|
"%"PRIu32", gid %"PRIu32": unknown rule", id, gid);
|
|
} else {
|
|
if (parsed_track == TRACK_RULE) {
|
|
s->flags |= SIG_FLAG_NOALERT;
|
|
goto end;
|
|
}
|
|
|
|
de = SCMalloc(sizeof(DetectThresholdData));
|
|
if (unlikely(de == NULL))
|
|
goto error;
|
|
memset(de,0,sizeof(DetectThresholdData));
|
|
|
|
de->type = TYPE_SUPPRESS;
|
|
de->track = parsed_track;
|
|
de->count = parsed_count;
|
|
de->seconds = parsed_seconds;
|
|
de->new_action = parsed_new_action;
|
|
de->timeout = parsed_timeout;
|
|
|
|
if (DetectAddressParse((const DetectEngineCtx *)de_ctx, &de->addrs, (char *)th_ip) != 0) {
|
|
SCLogError(SC_ERR_INVALID_IP_NETBLOCK, "failed to parse %s", th_ip);
|
|
goto error;
|
|
}
|
|
|
|
sm = SigMatchAlloc();
|
|
if (sm == NULL) {
|
|
SCLogError(SC_ERR_MEM_ALLOC, "Error allocating SigMatch");
|
|
goto error;
|
|
}
|
|
|
|
sm->type = DETECT_THRESHOLD;
|
|
sm->ctx = (void *)de;
|
|
|
|
SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_SUPPRESS);
|
|
}
|
|
}
|
|
|
|
end:
|
|
return 0;
|
|
error:
|
|
if (de != NULL) {
|
|
DetectAddressHeadCleanup(&de->addrs);
|
|
SCFree(de);
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
/** \internal
|
|
* \brief setup suppress rules
|
|
* \retval 0 ok
|
|
* \retval -1 error
|
|
*/
|
|
static int SetupThresholdRule(DetectEngineCtx *de_ctx, uint32_t id, uint32_t gid,
|
|
uint8_t parsed_type, uint8_t parsed_track, uint32_t parsed_count,
|
|
uint32_t parsed_seconds, uint32_t parsed_timeout, uint8_t parsed_new_action,
|
|
const char *th_ip)
|
|
{
|
|
Signature *s = NULL;
|
|
SigMatch *sm = NULL;
|
|
DetectThresholdData *de = NULL;
|
|
void *ptmp;
|
|
|
|
BUG_ON(parsed_type == TYPE_SUPPRESS);
|
|
|
|
/* Install it */
|
|
if (id == 0 && gid == 0) {
|
|
for (s = de_ctx->sig_list; s != NULL; s = s->next) {
|
|
sm = DetectGetLastSMByListId(s,
|
|
DETECT_SM_LIST_THRESHOLD, DETECT_THRESHOLD, -1);
|
|
if (sm != NULL) {
|
|
SCLogWarning(SC_ERR_EVENT_ENGINE, "signature sid:%"PRIu32 " has "
|
|
"an event var set. The signature event var is "
|
|
"given precedence over the threshold.conf one. "
|
|
"We'll change this in the future though.", s->id);
|
|
continue;
|
|
}
|
|
|
|
sm = DetectGetLastSMByListId(s,
|
|
DETECT_SM_LIST_THRESHOLD, DETECT_DETECTION_FILTER, -1);
|
|
if (sm != NULL) {
|
|
SCLogWarning(SC_ERR_EVENT_ENGINE, "signature sid:%"PRIu32 " has "
|
|
"an event var set. The signature event var is "
|
|
"given precedence over the threshold.conf one. "
|
|
"We'll change this in the future though.", s->id);
|
|
continue;
|
|
}
|
|
|
|
de = SCMalloc(sizeof(DetectThresholdData));
|
|
if (unlikely(de == NULL))
|
|
goto error;
|
|
memset(de,0,sizeof(DetectThresholdData));
|
|
|
|
de->type = parsed_type;
|
|
de->track = parsed_track;
|
|
de->count = parsed_count;
|
|
de->seconds = parsed_seconds;
|
|
de->new_action = parsed_new_action;
|
|
de->timeout = parsed_timeout;
|
|
|
|
sm = SigMatchAlloc();
|
|
if (sm == NULL) {
|
|
SCLogError(SC_ERR_MEM_ALLOC, "Error allocating SigMatch");
|
|
goto error;
|
|
}
|
|
|
|
if (parsed_type == TYPE_RATE)
|
|
sm->type = DETECT_DETECTION_FILTER;
|
|
else
|
|
sm->type = DETECT_THRESHOLD;
|
|
sm->ctx = (void *)de;
|
|
|
|
if (parsed_track == TRACK_RULE) {
|
|
ptmp = SCRealloc(de_ctx->ths_ctx.th_entry, (de_ctx->ths_ctx.th_size + 1) * sizeof(DetectThresholdEntry *));
|
|
if (ptmp == NULL) {
|
|
SCFree(de_ctx->ths_ctx.th_entry);
|
|
de_ctx->ths_ctx.th_entry = NULL;
|
|
SCLogError(SC_ERR_MEM_ALLOC, "Error allocating memory for threshold config"
|
|
" (tried to allocate %"PRIu32"th_entrys for rule tracking with rate_filter)", de_ctx->ths_ctx.th_size + 1);
|
|
} else {
|
|
de_ctx->ths_ctx.th_entry = ptmp;
|
|
de_ctx->ths_ctx.th_entry[de_ctx->ths_ctx.th_size] = NULL;
|
|
de_ctx->ths_ctx.th_size++;
|
|
}
|
|
}
|
|
SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_THRESHOLD);
|
|
}
|
|
|
|
} else if (id == 0 && gid > 0) {
|
|
for (s = de_ctx->sig_list; s != NULL; s = s->next) {
|
|
if (s->gid == gid) {
|
|
sm = DetectGetLastSMByListId(s, DETECT_SM_LIST_THRESHOLD,
|
|
DETECT_THRESHOLD, DETECT_DETECTION_FILTER, -1);
|
|
if (sm != NULL) {
|
|
SCLogWarning(SC_ERR_EVENT_ENGINE, "signature sid:%"PRIu32 " has "
|
|
"an event var set. The signature event var is "
|
|
"given precedence over the threshold.conf one. "
|
|
"We'll change this in the future though.", id);
|
|
continue;
|
|
}
|
|
|
|
de = SCMalloc(sizeof(DetectThresholdData));
|
|
if (unlikely(de == NULL))
|
|
goto error;
|
|
memset(de,0,sizeof(DetectThresholdData));
|
|
|
|
de->type = parsed_type;
|
|
de->track = parsed_track;
|
|
de->count = parsed_count;
|
|
de->seconds = parsed_seconds;
|
|
de->new_action = parsed_new_action;
|
|
de->timeout = parsed_timeout;
|
|
|
|
sm = SigMatchAlloc();
|
|
if (sm == NULL) {
|
|
SCLogError(SC_ERR_MEM_ALLOC, "Error allocating SigMatch");
|
|
goto error;
|
|
}
|
|
|
|
if (parsed_type == TYPE_RATE)
|
|
sm->type = DETECT_DETECTION_FILTER;
|
|
else
|
|
sm->type = DETECT_THRESHOLD;
|
|
sm->ctx = (void *)de;
|
|
|
|
if (parsed_track == TRACK_RULE) {
|
|
ptmp = SCRealloc(de_ctx->ths_ctx.th_entry, (de_ctx->ths_ctx.th_size + 1) * sizeof(DetectThresholdEntry *));
|
|
if (ptmp == NULL) {
|
|
SCFree(de_ctx->ths_ctx.th_entry);
|
|
de_ctx->ths_ctx.th_entry = NULL;
|
|
SCLogError(SC_ERR_MEM_ALLOC, "Error allocating memory for threshold config"
|
|
" (tried to allocate %"PRIu32"th_entrys for rule tracking with rate_filter)", de_ctx->ths_ctx.th_size + 1);
|
|
} else {
|
|
de_ctx->ths_ctx.th_entry = ptmp;
|
|
de_ctx->ths_ctx.th_entry[de_ctx->ths_ctx.th_size] = NULL;
|
|
de_ctx->ths_ctx.th_size++;
|
|
}
|
|
}
|
|
SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_THRESHOLD);
|
|
}
|
|
}
|
|
} else if (id > 0 && gid == 0) {
|
|
SCLogError(SC_ERR_INVALID_VALUE, "Can't use a event config that has "
|
|
"sid > 0 and gid == 0. Please fix this "
|
|
"in your threshold.conf file");
|
|
} else {
|
|
s = SigFindSignatureBySidGid(de_ctx, id, gid);
|
|
if (s == NULL) {
|
|
SCLogWarning(SC_ERR_EVENT_ENGINE, "can't suppress sid "
|
|
"%"PRIu32", gid %"PRIu32": unknown rule", id, gid);
|
|
} else {
|
|
if (parsed_type != TYPE_SUPPRESS && parsed_type != TYPE_THRESHOLD &&
|
|
parsed_type != TYPE_BOTH && parsed_type != TYPE_LIMIT)
|
|
{
|
|
sm = DetectGetLastSMByListId(s,
|
|
DETECT_SM_LIST_THRESHOLD, DETECT_THRESHOLD, -1);
|
|
if (sm != NULL) {
|
|
SCLogWarning(SC_ERR_EVENT_ENGINE, "signature sid:%"PRIu32 " has "
|
|
"a threshold set. The signature event var is "
|
|
"given precedence over the threshold.conf one. "
|
|
"Bug #425.", s->id);
|
|
goto end;
|
|
}
|
|
|
|
sm = DetectGetLastSMByListId(s, DETECT_SM_LIST_THRESHOLD,
|
|
DETECT_DETECTION_FILTER, -1);
|
|
if (sm != NULL) {
|
|
SCLogWarning(SC_ERR_EVENT_ENGINE, "signature sid:%"PRIu32 " has "
|
|
"a detection_filter set. The signature event var is "
|
|
"given precedence over the threshold.conf one. "
|
|
"Bug #425.", s->id);
|
|
goto end;
|
|
}
|
|
|
|
/* replace threshold on sig if we have a global override for it */
|
|
} else if (parsed_type == TYPE_THRESHOLD || parsed_type == TYPE_BOTH || parsed_type == TYPE_LIMIT) {
|
|
sm = DetectGetLastSMByListId(s, DETECT_SM_LIST_THRESHOLD,
|
|
DETECT_THRESHOLD, DETECT_DETECTION_FILTER, -1);
|
|
if (sm != NULL) {
|
|
SigMatchRemoveSMFromList(s, sm, DETECT_SM_LIST_THRESHOLD);
|
|
SigMatchFree(sm);
|
|
sm = NULL;
|
|
}
|
|
}
|
|
|
|
de = SCMalloc(sizeof(DetectThresholdData));
|
|
if (unlikely(de == NULL))
|
|
goto error;
|
|
memset(de,0,sizeof(DetectThresholdData));
|
|
|
|
de->type = parsed_type;
|
|
de->track = parsed_track;
|
|
de->count = parsed_count;
|
|
de->seconds = parsed_seconds;
|
|
de->new_action = parsed_new_action;
|
|
de->timeout = parsed_timeout;
|
|
|
|
sm = SigMatchAlloc();
|
|
if (sm == NULL) {
|
|
SCLogError(SC_ERR_MEM_ALLOC, "Error allocating SigMatch");
|
|
goto error;
|
|
}
|
|
|
|
if (parsed_type == TYPE_RATE)
|
|
sm->type = DETECT_DETECTION_FILTER;
|
|
else
|
|
sm->type = DETECT_THRESHOLD;
|
|
sm->ctx = (void *)de;
|
|
|
|
if (parsed_track == TRACK_RULE) {
|
|
ptmp = SCRealloc(de_ctx->ths_ctx.th_entry, (de_ctx->ths_ctx.th_size + 1) * sizeof(DetectThresholdEntry *));
|
|
if (ptmp == NULL) {
|
|
SCFree(de_ctx->ths_ctx.th_entry);
|
|
de_ctx->ths_ctx.th_entry = NULL;
|
|
SCLogError(SC_ERR_MEM_ALLOC, "Error allocating memory for threshold config"
|
|
" (tried to allocate %"PRIu32"th_entrys for rule tracking with rate_filter)", de_ctx->ths_ctx.th_size + 1);
|
|
} else {
|
|
de_ctx->ths_ctx.th_entry = ptmp;
|
|
de_ctx->ths_ctx.th_entry[de_ctx->ths_ctx.th_size] = NULL;
|
|
de_ctx->ths_ctx.th_size++;
|
|
}
|
|
}
|
|
|
|
SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_THRESHOLD);
|
|
}
|
|
}
|
|
end:
|
|
return 0;
|
|
error:
|
|
if (de != NULL) {
|
|
DetectAddressHeadCleanup(&de->addrs);
|
|
SCFree(de);
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
static int ParseThresholdRule(DetectEngineCtx *de_ctx, char *rawstr,
|
|
uint32_t *ret_id, uint32_t *ret_gid,
|
|
uint8_t *ret_parsed_type, uint8_t *ret_parsed_track,
|
|
uint32_t *ret_parsed_count, uint32_t *ret_parsed_seconds, uint32_t *ret_parsed_timeout,
|
|
uint8_t *ret_parsed_new_action,
|
|
const char **ret_th_ip)
|
|
{
|
|
char th_rule_type[32];
|
|
char th_gid[16];
|
|
char th_sid[16];
|
|
char rule_extend[1024];
|
|
const char *th_type = NULL;
|
|
const char *th_track = NULL;
|
|
const char *th_count = NULL;
|
|
const char *th_seconds = NULL;
|
|
const char *th_new_action= NULL;
|
|
const char *th_timeout = NULL;
|
|
const char *th_ip = NULL;
|
|
|
|
uint8_t parsed_type = 0;
|
|
uint8_t parsed_track = 0;
|
|
uint8_t parsed_new_action = 0;
|
|
uint32_t parsed_count = 0;
|
|
uint32_t parsed_seconds = 0;
|
|
uint32_t parsed_timeout = 0;
|
|
|
|
#define MAX_SUBSTRINGS 30
|
|
int ret = 0;
|
|
int ov[MAX_SUBSTRINGS];
|
|
uint32_t id = 0, gid = 0;
|
|
ThresholdRuleType rule_type;
|
|
|
|
if (de_ctx == NULL)
|
|
return -1;
|
|
|
|
ret = pcre_exec(regex_base, regex_base_study, rawstr, strlen(rawstr), 0, 0, ov, MAX_SUBSTRINGS);
|
|
if (ret < 4) {
|
|
SCLogError(SC_ERR_PCRE_MATCH, "pcre_exec parse error, ret %" PRId32 ", string %s", ret, rawstr);
|
|
goto error;
|
|
}
|
|
|
|
/* retrieve the classtype name */
|
|
ret = pcre_copy_substring((char *)rawstr, ov, 30, 1, th_rule_type, sizeof(th_rule_type));
|
|
if (ret < 0) {
|
|
SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_copy_substring failed");
|
|
goto error;
|
|
}
|
|
|
|
/* retrieve the classtype name */
|
|
ret = pcre_copy_substring((char *)rawstr, ov, 30, 2, th_gid, sizeof(th_gid));
|
|
if (ret < 0) {
|
|
SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_copy_substring failed");
|
|
goto error;
|
|
}
|
|
|
|
ret = pcre_copy_substring((char *)rawstr, ov, 30, 3, th_sid, sizeof(th_sid));
|
|
if (ret < 0) {
|
|
SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_copy_substring failed");
|
|
goto error;
|
|
}
|
|
|
|
ret = pcre_copy_substring((char *)rawstr, ov, 30, 4, rule_extend, sizeof(rule_extend));
|
|
if (ret < 0) {
|
|
SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_copy_substring failed");
|
|
goto error;
|
|
}
|
|
|
|
/* get type of rule */
|
|
if (strcasecmp(th_rule_type,"event_filter") == 0) {
|
|
rule_type = THRESHOLD_TYPE_EVENT_FILTER;
|
|
} else if (strcasecmp(th_rule_type,"threshold") == 0) {
|
|
rule_type = THRESHOLD_TYPE_THRESHOLD;
|
|
} else if (strcasecmp(th_rule_type,"rate_filter") == 0) {
|
|
rule_type = THRESHOLD_TYPE_RATE;
|
|
} else if (strcasecmp(th_rule_type,"suppress") == 0) {
|
|
rule_type = THRESHOLD_TYPE_SUPPRESS;
|
|
} else {
|
|
SCLogError(SC_ERR_INVALID_VALUE, "rule type %s is unknown", th_rule_type);
|
|
goto error;
|
|
}
|
|
|
|
/* get end of rule */
|
|
switch(rule_type) {
|
|
case THRESHOLD_TYPE_EVENT_FILTER:
|
|
case THRESHOLD_TYPE_THRESHOLD:
|
|
if (strlen(rule_extend) > 0) {
|
|
ret = pcre_exec(regex_threshold, regex_threshold_study,
|
|
rule_extend, strlen(rule_extend),
|
|
0, 0, ov, MAX_SUBSTRINGS);
|
|
if (ret < 4) {
|
|
SCLogError(SC_ERR_PCRE_MATCH,
|
|
"pcre_exec parse error, ret %" PRId32 ", string %s",
|
|
ret, rule_extend);
|
|
goto error;
|
|
}
|
|
|
|
ret = pcre_get_substring((char *)rule_extend, ov, 30, 1, &th_type);
|
|
if (ret < 0) {
|
|
SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed");
|
|
goto error;
|
|
}
|
|
|
|
ret = pcre_get_substring((char *)rule_extend, ov, 30, 2, &th_track);
|
|
if (ret < 0) {
|
|
SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed");
|
|
goto error;
|
|
}
|
|
|
|
ret = pcre_get_substring((char *)rule_extend, ov, 30, 3, &th_count);
|
|
if (ret < 0) {
|
|
SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed");
|
|
goto error;
|
|
}
|
|
|
|
ret = pcre_get_substring((char *)rule_extend, ov, 30, 4, &th_seconds);
|
|
if (ret < 0) {
|
|
SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed");
|
|
goto error;
|
|
}
|
|
|
|
if (strcasecmp(th_type,"limit") == 0)
|
|
parsed_type = TYPE_LIMIT;
|
|
else if (strcasecmp(th_type,"both") == 0)
|
|
parsed_type = TYPE_BOTH;
|
|
else if (strcasecmp(th_type,"threshold") == 0)
|
|
parsed_type = TYPE_THRESHOLD;
|
|
else {
|
|
SCLogError(SC_ERR_INVALID_ARGUMENTS, "limit type not supported: %s", th_type);
|
|
goto error;
|
|
}
|
|
} else {
|
|
SCLogError(SC_ERR_INVALID_ARGUMENTS, "rule invalid: %s", rawstr);
|
|
goto error;
|
|
}
|
|
break;
|
|
case THRESHOLD_TYPE_SUPPRESS:
|
|
if (strlen(rule_extend) > 0) {
|
|
ret = pcre_exec(regex_suppress, regex_suppress_study,
|
|
rule_extend, strlen(rule_extend),
|
|
0, 0, ov, MAX_SUBSTRINGS);
|
|
if (ret < 2) {
|
|
SCLogError(SC_ERR_PCRE_MATCH,
|
|
"pcre_exec parse error, ret %" PRId32 ", string %s",
|
|
ret, rule_extend);
|
|
goto error;
|
|
}
|
|
/* retrieve the track mode */
|
|
ret = pcre_get_substring((char *)rule_extend, ov, 30, 1, &th_track);
|
|
if (ret < 0) {
|
|
SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed");
|
|
goto error;
|
|
}
|
|
/* retrieve the IP */
|
|
ret = pcre_get_substring((char *)rule_extend, ov, 30, 2, &th_ip);
|
|
if (ret < 0) {
|
|
SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed");
|
|
goto error;
|
|
}
|
|
} else {
|
|
parsed_track = TRACK_RULE;
|
|
}
|
|
parsed_type = TYPE_SUPPRESS;
|
|
break;
|
|
case THRESHOLD_TYPE_RATE:
|
|
if (strlen(rule_extend) > 0) {
|
|
ret = pcre_exec(regex_rate, regex_rate_study,
|
|
rule_extend, strlen(rule_extend),
|
|
0, 0, ov, MAX_SUBSTRINGS);
|
|
if (ret < 5) {
|
|
SCLogError(SC_ERR_PCRE_MATCH,
|
|
"pcre_exec parse error, ret %" PRId32 ", string %s",
|
|
ret, rule_extend);
|
|
goto error;
|
|
}
|
|
|
|
ret = pcre_get_substring((char *)rule_extend, ov, MAX_SUBSTRINGS, 1, &th_track);
|
|
if (ret < 0) {
|
|
SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed");
|
|
goto error;
|
|
}
|
|
|
|
ret = pcre_get_substring((char *)rule_extend, ov, MAX_SUBSTRINGS, 2, &th_count);
|
|
if (ret < 0) {
|
|
SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed");
|
|
goto error;
|
|
}
|
|
|
|
ret = pcre_get_substring((char *)rule_extend, ov, MAX_SUBSTRINGS, 3, &th_seconds);
|
|
if (ret < 0) {
|
|
SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed");
|
|
goto error;
|
|
}
|
|
|
|
ret = pcre_get_substring((char *)rule_extend, ov, MAX_SUBSTRINGS, 4, &th_new_action);
|
|
if (ret < 0) {
|
|
SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed");
|
|
goto error;
|
|
}
|
|
|
|
ret = pcre_get_substring((char *)rule_extend, ov, MAX_SUBSTRINGS, 5, &th_timeout);
|
|
if (ret < 0) {
|
|
SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed");
|
|
goto error;
|
|
}
|
|
|
|
/* TODO: implement option "apply_to" */
|
|
|
|
if (ByteExtractStringUint32(&parsed_timeout, 10, strlen(th_timeout), th_timeout) <= 0) {
|
|
goto error;
|
|
}
|
|
|
|
/* Get the new action to take */
|
|
if (strcasecmp(th_new_action, "alert") == 0)
|
|
parsed_new_action = TH_ACTION_ALERT;
|
|
if (strcasecmp(th_new_action, "drop") == 0)
|
|
parsed_new_action = TH_ACTION_DROP;
|
|
if (strcasecmp(th_new_action, "pass") == 0)
|
|
parsed_new_action = TH_ACTION_PASS;
|
|
if (strcasecmp(th_new_action, "reject") == 0)
|
|
parsed_new_action = TH_ACTION_REJECT;
|
|
if (strcasecmp(th_new_action, "log") == 0) {
|
|
SCLogInfo("log action for rate_filter not supported yet");
|
|
parsed_new_action = TH_ACTION_LOG;
|
|
}
|
|
if (strcasecmp(th_new_action, "sdrop") == 0) {
|
|
SCLogInfo("sdrop action for rate_filter not supported yet");
|
|
parsed_new_action = TH_ACTION_SDROP;
|
|
}
|
|
parsed_type = TYPE_RATE;
|
|
} else {
|
|
SCLogError(SC_ERR_INVALID_ARGUMENTS, "rule invalid: %s", rawstr);
|
|
goto error;
|
|
}
|
|
break;
|
|
}
|
|
|
|
switch (rule_type) {
|
|
/* This part is common to threshold/event_filter/rate_filter */
|
|
case THRESHOLD_TYPE_EVENT_FILTER:
|
|
case THRESHOLD_TYPE_THRESHOLD:
|
|
case THRESHOLD_TYPE_RATE:
|
|
if (strcasecmp(th_track,"by_dst") == 0)
|
|
parsed_track = TRACK_DST;
|
|
else if (strcasecmp(th_track,"by_src") == 0)
|
|
parsed_track = TRACK_SRC;
|
|
else if (strcasecmp(th_track, "by_both") == 0) {
|
|
parsed_track = TRACK_BOTH;
|
|
}
|
|
else if (strcasecmp(th_track,"by_rule") == 0)
|
|
parsed_track = TRACK_RULE;
|
|
else {
|
|
SCLogError(SC_ERR_INVALID_VALUE, "Invalid track parameter %s in %s", th_track, rawstr);
|
|
goto error;
|
|
}
|
|
|
|
if (ByteExtractStringUint32(&parsed_count, 10, strlen(th_count), th_count) <= 0) {
|
|
goto error;
|
|
}
|
|
if (parsed_count == 0) {
|
|
SCLogError(SC_ERR_INVALID_VALUE, "rate filter count should be > 0");
|
|
goto error;
|
|
}
|
|
|
|
if (ByteExtractStringUint32(&parsed_seconds, 10, strlen(th_seconds), th_seconds) <= 0) {
|
|
goto error;
|
|
}
|
|
|
|
break;
|
|
case THRESHOLD_TYPE_SUPPRESS:
|
|
/* need to get IP if extension is provided */
|
|
if (th_track != NULL) {
|
|
if (strcasecmp(th_track,"by_dst") == 0)
|
|
parsed_track = TRACK_DST;
|
|
else if (strcasecmp(th_track,"by_src") == 0)
|
|
parsed_track = TRACK_SRC;
|
|
else if (strcasecmp(th_track,"by_either") == 0) {
|
|
parsed_track = TRACK_EITHER;
|
|
}
|
|
else {
|
|
SCLogError(SC_ERR_INVALID_VALUE, "Invalid track parameter %s in %s", th_track, rule_extend);
|
|
goto error;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (ByteExtractStringUint32(&id, 10, strlen(th_sid), th_sid) <= 0) {
|
|
goto error;
|
|
}
|
|
|
|
if (ByteExtractStringUint32(&gid, 10, strlen(th_gid), th_gid) <= 0) {
|
|
goto error;
|
|
}
|
|
|
|
*ret_id = id;
|
|
*ret_gid = gid;
|
|
*ret_parsed_type = parsed_type;
|
|
*ret_parsed_track = parsed_track;
|
|
*ret_parsed_new_action = parsed_new_action;
|
|
*ret_parsed_count = parsed_count;
|
|
*ret_parsed_seconds = parsed_seconds;
|
|
*ret_parsed_timeout = parsed_timeout;
|
|
*ret_th_ip = th_ip;
|
|
return 0;
|
|
error:
|
|
if (th_track != NULL)
|
|
SCFree((char *)th_track);
|
|
if (th_count != NULL)
|
|
SCFree((char *)th_count);
|
|
if (th_seconds != NULL)
|
|
SCFree((char *)th_seconds);
|
|
if (th_type != NULL)
|
|
SCFree((char *)th_type);
|
|
if (th_ip != NULL)
|
|
SCFree((char *)th_ip);
|
|
return -1;
|
|
}
|
|
|
|
/**
|
|
* \brief Parses a line from the threshold file and applies it to the
|
|
* detection engine
|
|
*
|
|
* \param rawstr Pointer to the string to be parsed.
|
|
* \param de_ctx Pointer to the Detection Engine Context.
|
|
*
|
|
* \retval 0 On success.
|
|
* \retval -1 On failure.
|
|
*/
|
|
static int SCThresholdConfAddThresholdtype(char *rawstr, DetectEngineCtx *de_ctx)
|
|
{
|
|
uint8_t parsed_type = 0;
|
|
uint8_t parsed_track = 0;
|
|
uint8_t parsed_new_action = 0;
|
|
uint32_t parsed_count = 0;
|
|
uint32_t parsed_seconds = 0;
|
|
uint32_t parsed_timeout = 0;
|
|
const char *th_ip = NULL;
|
|
uint32_t id = 0, gid = 0;
|
|
|
|
int r = 0;
|
|
r = ParseThresholdRule(de_ctx, rawstr, &id, &gid, &parsed_type, &parsed_track,
|
|
&parsed_count, &parsed_seconds, &parsed_timeout, &parsed_new_action,
|
|
&th_ip);
|
|
if (r < 0)
|
|
goto error;
|
|
|
|
if (parsed_type == TYPE_SUPPRESS) {
|
|
r = SetupSuppressRule(de_ctx, id, gid, parsed_type, parsed_track,
|
|
parsed_count, parsed_seconds, parsed_timeout, parsed_new_action,
|
|
th_ip);
|
|
} else {
|
|
r = SetupThresholdRule(de_ctx, id, gid, parsed_type, parsed_track,
|
|
parsed_count, parsed_seconds, parsed_timeout, parsed_new_action,
|
|
th_ip);
|
|
}
|
|
if (r < 0) {
|
|
goto error;
|
|
}
|
|
|
|
return 0;
|
|
error:
|
|
if (th_ip != NULL)
|
|
SCFree((char *)th_ip);
|
|
return -1;
|
|
}
|
|
|
|
/**
|
|
* \brief Checks if a string is a comment or a blank line.
|
|
*
|
|
* Comments lines are lines of the following format -
|
|
* "# This is a comment string" or
|
|
* " # This is a comment string".
|
|
*
|
|
* \param line String that has to be checked
|
|
*
|
|
* \retval 1 On the argument string being a comment or blank line
|
|
* \retval 0 Otherwise
|
|
*/
|
|
static int SCThresholdConfIsLineBlankOrComment(char *line)
|
|
{
|
|
while (*line != '\0') {
|
|
/* we have a comment */
|
|
if (*line == '#')
|
|
return 1;
|
|
|
|
/* this line is neither a comment line, nor a blank line */
|
|
if (!isspace((unsigned char)*line))
|
|
return 0;
|
|
|
|
line++;
|
|
}
|
|
|
|
/* we have a blank line */
|
|
return 1;
|
|
}
|
|
|
|
/**
|
|
* \brief Checks if the rule is multiline, by searching an ending slash
|
|
*
|
|
* \param line String that has to be checked
|
|
*
|
|
* \retval the position of the slash making it multiline
|
|
* \retval 0 Otherwise
|
|
*/
|
|
static int SCThresholdConfLineIsMultiline(char *line)
|
|
{
|
|
int flag = 0;
|
|
char *rline = line;
|
|
int len = strlen(line);
|
|
|
|
while (line < rline + len && *line != '\n') {
|
|
/* we have a comment */
|
|
if (*line == '\\')
|
|
flag = line - rline;
|
|
else
|
|
if (!isspace((unsigned char)*line))
|
|
flag = 0;
|
|
|
|
line++;
|
|
}
|
|
|
|
/* we have a blank line */
|
|
return flag;
|
|
}
|
|
|
|
/**
|
|
* \brief Parses the Threshold Config file
|
|
*
|
|
* \param de_ctx Pointer to the Detection Engine Context.
|
|
* \param fd Pointer to file descriptor.
|
|
*/
|
|
void SCThresholdConfParseFile(DetectEngineCtx *de_ctx, FILE *fp)
|
|
{
|
|
char line[8192] = "";
|
|
int rule_num = 0;
|
|
|
|
/* position of "\", on multiline rules */
|
|
int esc_pos = 0;
|
|
|
|
if (fp == NULL)
|
|
return;
|
|
|
|
while (fgets(line + esc_pos, (int)sizeof(line) - esc_pos, fp) != NULL) {
|
|
if (SCThresholdConfIsLineBlankOrComment(line)) {
|
|
continue;
|
|
}
|
|
|
|
esc_pos = SCThresholdConfLineIsMultiline(line);
|
|
if (esc_pos == 0) {
|
|
rule_num++;
|
|
SCLogDebug("Adding threshold.config rule num %"PRIu32"( %s )", rule_num, line);
|
|
SCThresholdConfAddThresholdtype(line, de_ctx);
|
|
}
|
|
}
|
|
|
|
SCLogInfo("Threshold config parsed: %d rule(s) found", rule_num);
|
|
|
|
return;
|
|
}
|
|
|
|
#ifdef UNITTESTS
|
|
|
|
/**
|
|
* \brief Creates a dummy threshold file, with all valid options, for testing purposes.
|
|
*
|
|
* \retval fd Pointer to file descriptor.
|
|
*/
|
|
static FILE *SCThresholdConfGenerateValidDummyFD01(void)
|
|
{
|
|
FILE *fd = NULL;
|
|
const char *buffer =
|
|
"event_filter gen_id 1, sig_id 10, type limit, track by_src, count 1, seconds 60\n"
|
|
"threshold gen_id 1, sig_id 100, type both, track by_dst, count 10, seconds 60\n"
|
|
"event_filter gen_id 1, sig_id 1000, type threshold, track by_src, count 100, seconds 60\n";
|
|
|
|
fd = SCFmemopen((void *)buffer, strlen(buffer), "r");
|
|
if (fd == NULL)
|
|
SCLogDebug("Error with SCFmemopen() called by Threshold Config test code");
|
|
|
|
return fd;
|
|
}
|
|
|
|
/**
|
|
* \brief Creates a dummy threshold file, with some valid options and a couple of invalid options.
|
|
* For testing purposes.
|
|
*
|
|
* \retval fd Pointer to file descriptor.
|
|
*/
|
|
static FILE *SCThresholdConfGenerateInValidDummyFD02(void)
|
|
{
|
|
FILE *fd;
|
|
const char *buffer =
|
|
"event_filter gen_id 1, sig_id 1000, type invalid, track by_src, count 100, seconds 60\n";
|
|
|
|
fd = SCFmemopen((void *)buffer, strlen(buffer), "r");
|
|
if (fd == NULL)
|
|
SCLogDebug("Error with SCFmemopen() called by Threshold Config test code");
|
|
|
|
return fd;
|
|
}
|
|
|
|
/**
|
|
* \brief Creates a dummy threshold file, with all valid options, for testing purposes.
|
|
*
|
|
* \retval fd Pointer to file descriptor.
|
|
*/
|
|
static FILE *SCThresholdConfGenerateValidDummyFD03(void)
|
|
{
|
|
FILE *fd;
|
|
const char *buffer =
|
|
"event_filter gen_id 0, sig_id 0, type threshold, track by_src, count 100, seconds 60\n";
|
|
|
|
fd = SCFmemopen((void *)buffer, strlen(buffer), "r");
|
|
if (fd == NULL)
|
|
SCLogDebug("Error with SCFmemopen() called by Threshold Config test code");
|
|
|
|
return fd;
|
|
}
|
|
|
|
/**
|
|
* \brief Creates a dummy threshold file, with all valid options, but
|
|
* with splitted rules (multiline), for testing purposes.
|
|
*
|
|
* \retval fd Pointer to file descriptor.
|
|
*/
|
|
static FILE *SCThresholdConfGenerateValidDummyFD04(void)
|
|
{
|
|
FILE *fd = NULL;
|
|
const char *buffer =
|
|
"event_filter gen_id 1 \\\n, sig_id 10, type limit, track by_src, \\\ncount 1, seconds 60\n"
|
|
"threshold gen_id 1, \\\nsig_id 100, type both\\\n, track by_dst, count 10, \\\n seconds 60\n"
|
|
"event_filter gen_id 1, sig_id 1000, \\\ntype threshold, track \\\nby_src, count 100, seconds 60\n";
|
|
|
|
fd = SCFmemopen((void *)buffer, strlen(buffer), "r");
|
|
if (fd == NULL)
|
|
SCLogDebug("Error with SCFmemopen() called by Threshold Config test code");
|
|
|
|
return fd;
|
|
}
|
|
|
|
/**
|
|
* \brief Creates a dummy threshold file, with all valid options, for testing purposes.
|
|
*
|
|
* \retval fd Pointer to file descriptor.
|
|
*/
|
|
static FILE *SCThresholdConfGenerateValidDummyFD05(void)
|
|
{
|
|
FILE *fd = NULL;
|
|
const char *buffer =
|
|
"rate_filter gen_id 1, sig_id 10, track by_src, count 1, seconds 60, new_action drop, timeout 10\n"
|
|
"rate_filter gen_id 1, sig_id 100, track by_dst, count 10, seconds 60, new_action pass, timeout 5\n"
|
|
"rate_filter gen_id 1, sig_id 1000, track by_rule, count 100, seconds 60, new_action alert, timeout 30\n"
|
|
"rate_filter gen_id 1, sig_id 10000, track by_both, count 1000, seconds 60, new_action reject, timeout 21\n";
|
|
|
|
fd = SCFmemopen((void *)buffer, strlen(buffer), "r");
|
|
if (fd == NULL)
|
|
SCLogDebug("Error with SCFmemopen() called by Threshold Config test code");
|
|
|
|
return fd;
|
|
}
|
|
|
|
/**
|
|
* \brief Creates a dummy threshold file, with all valid options, but
|
|
* with splitted rules (multiline), for testing purposes.
|
|
*
|
|
* \retval fd Pointer to file descriptor.
|
|
*/
|
|
static FILE *SCThresholdConfGenerateValidDummyFD06(void)
|
|
{
|
|
FILE *fd = NULL;
|
|
const char *buffer =
|
|
"rate_filter \\\ngen_id 1, sig_id 10, track by_src, count 1, seconds 60\\\n, new_action drop, timeout 10\n"
|
|
"rate_filter gen_id 1, \\\nsig_id 100, track by_dst, \\\ncount 10, seconds 60, new_action pass, timeout 5\n"
|
|
"rate_filter gen_id 1, sig_id 1000, \\\ntrack by_rule, count 100, seconds 60, new_action alert, timeout 30\n"
|
|
"rate_filter gen_id 1, sig_id 10000, track by_both, count 1000, \\\nseconds 60, new_action reject, timeout 21\n";
|
|
|
|
fd = SCFmemopen((void *)buffer, strlen(buffer), "r");
|
|
if (fd == NULL)
|
|
SCLogDebug("Error with SCFmemopen() called by Threshold Config test code");
|
|
|
|
return fd;
|
|
}
|
|
|
|
/**
|
|
* \brief Creates a dummy threshold file, with all valid options, but
|
|
* with splitted rules (multiline), for testing purposes.
|
|
*
|
|
* \retval fd Pointer to file descriptor.
|
|
*/
|
|
static FILE *SCThresholdConfGenerateValidDummyFD07(void)
|
|
{
|
|
FILE *fd = NULL;
|
|
const char *buffer =
|
|
"rate_filter gen_id 1, sig_id 10, track by_src, count 3, seconds 3, new_action drop, timeout 10\n"
|
|
"rate_filter gen_id 1, sig_id 11, track by_src, count 3, seconds 1, new_action drop, timeout 5\n";
|
|
|
|
fd = SCFmemopen((void *)buffer, strlen(buffer), "r");
|
|
if (fd == NULL)
|
|
SCLogDebug("Error with SCFmemopen() called by Threshold Config test code");
|
|
|
|
return fd;
|
|
}
|
|
|
|
/**
|
|
* \brief Creates a dummy threshold file, for testing rate_filter, track by_rule
|
|
*
|
|
* \retval fd Pointer to file descriptor.
|
|
*/
|
|
static FILE *SCThresholdConfGenerateValidDummyFD08(void)
|
|
{
|
|
FILE *fd = NULL;
|
|
const char *buffer =
|
|
"rate_filter gen_id 1, sig_id 10, track by_rule, count 3, seconds 3, new_action drop, timeout 10\n";
|
|
|
|
fd = SCFmemopen((void *)buffer, strlen(buffer), "r");
|
|
if (fd == NULL)
|
|
SCLogDebug("Error with SCFmemopen() called by Threshold Config test code");
|
|
|
|
return fd;
|
|
}
|
|
|
|
/**
|
|
* \brief Creates a dummy threshold file, with all valid options, but
|
|
* with splitted rules (multiline), for testing purposes.
|
|
*
|
|
* \retval fd Pointer to file descriptor.
|
|
*/
|
|
static FILE *SCThresholdConfGenerateValidDummyFD09(void)
|
|
{
|
|
FILE *fd = NULL;
|
|
const char *buffer =
|
|
"event_filter gen_id 1 \\\n, sig_id 10, type limit, track by_src, \\\ncount 2, seconds 60\n"
|
|
"threshold gen_id 1, \\\nsig_id 11, type threshold\\\n, track by_dst, count 3, \\\n seconds 60\n"
|
|
"event_filter gen_id 1, sig_id 12, \\\ntype both, track \\\nby_src, count 2, seconds 60\n";
|
|
|
|
fd = SCFmemopen((void *)buffer, strlen(buffer), "r");
|
|
if (fd == NULL)
|
|
SCLogDebug("Error with SCFmemopen() called by Threshold Config test code");
|
|
|
|
return fd;
|
|
}
|
|
|
|
/**
|
|
* \brief Creates a dummy threshold file, with all valid options, but
|
|
* with splitted rules (multiline), for testing purposes.
|
|
*
|
|
* \retval fd Pointer to file descriptor.
|
|
*/
|
|
static FILE *SCThresholdConfGenerateValidDummyFD10(void)
|
|
{
|
|
FILE *fd = NULL;
|
|
const char *buffer =
|
|
"event_filter gen_id 1 \\\n, sig_id 10, type limit, track by_src, \\\ncount 5, seconds 2\n"
|
|
"threshold gen_id 1, \\\nsig_id 11, type threshold\\\n, track by_dst, count 5, \\\n seconds 2\n"
|
|
"event_filter gen_id 1, sig_id 12, \\\ntype both, track \\\nby_src, count 5, seconds 2\n";
|
|
|
|
fd = SCFmemopen((void *)buffer, strlen(buffer), "r");
|
|
if (fd == NULL)
|
|
SCLogDebug("Error with SCFmemopen() called by Threshold Config test code");
|
|
|
|
return fd;
|
|
}
|
|
|
|
/**
|
|
* \brief Creates a dummy threshold file, with all valid options, for testing purposes.
|
|
*
|
|
* \retval fd Pointer to file descriptor.
|
|
*/
|
|
static FILE *SCThresholdConfGenerateValidDummyFD11(void)
|
|
{
|
|
FILE *fd = NULL;
|
|
const char *buffer =
|
|
"suppress gen_id 1, sig_id 10000\n"
|
|
"suppress gen_id 1, sig_id 1000, track by_src, ip 192.168.1.1\n";
|
|
|
|
fd = SCFmemopen((void *)buffer, strlen(buffer), "r");
|
|
if (fd == NULL)
|
|
SCLogDebug("Error with SCFmemopen() called by Threshold Config test code");
|
|
|
|
return fd;
|
|
}
|
|
|
|
/**
|
|
* \test Check if the threshold file is loaded and well parsed
|
|
*
|
|
* \retval 1 on succces
|
|
* \retval 0 on failure
|
|
*/
|
|
static int SCThresholdConfTest01(void)
|
|
{
|
|
DetectEngineCtx *de_ctx = DetectEngineCtxInit();
|
|
FAIL_IF_NULL(de_ctx);
|
|
de_ctx->flags |= DE_QUIET;
|
|
|
|
Signature *sig = DetectEngineAppendSig(de_ctx,
|
|
"alert tcp any any -> any any (msg:\"Threshold limit\"; gid:1; sid:10;)");
|
|
FAIL_IF_NULL(sig);
|
|
|
|
FAIL_IF_NOT_NULL(g_ut_threshold_fp);
|
|
g_ut_threshold_fp = SCThresholdConfGenerateValidDummyFD01();
|
|
FAIL_IF_NULL(g_ut_threshold_fp);
|
|
SCThresholdConfInitContext(de_ctx);
|
|
|
|
SigMatch *m = DetectGetLastSMByListId(sig, DETECT_SM_LIST_THRESHOLD,
|
|
DETECT_THRESHOLD, -1);
|
|
FAIL_IF_NULL(m);
|
|
|
|
DetectThresholdData *de = (DetectThresholdData *)m->ctx;
|
|
FAIL_IF_NULL(de);
|
|
|
|
FAIL_IF_NOT(de->type == TYPE_LIMIT && de->track == TRACK_SRC && de->count == 1 && de->seconds == 60);
|
|
DetectEngineCtxFree(de_ctx);
|
|
PASS;
|
|
}
|
|
|
|
/**
|
|
* \test Check if the threshold file is loaded and well parsed
|
|
*
|
|
* \retval 1 on succces
|
|
* \retval 0 on failure
|
|
*/
|
|
static int SCThresholdConfTest02(void)
|
|
{
|
|
DetectEngineCtx *de_ctx = DetectEngineCtxInit();
|
|
FAIL_IF_NULL(de_ctx);
|
|
de_ctx->flags |= DE_QUIET;
|
|
|
|
Signature *sig = DetectEngineAppendSig(de_ctx,
|
|
"alert tcp any any -> any any (msg:\"Threshold limit\"; gid:1; sid:100;)");
|
|
FAIL_IF_NULL(sig);
|
|
|
|
FAIL_IF_NOT_NULL(g_ut_threshold_fp);
|
|
g_ut_threshold_fp = SCThresholdConfGenerateValidDummyFD01();
|
|
FAIL_IF_NULL(g_ut_threshold_fp);
|
|
SCThresholdConfInitContext(de_ctx);
|
|
|
|
SigMatch *m = DetectGetLastSMByListId(sig, DETECT_SM_LIST_THRESHOLD,
|
|
DETECT_THRESHOLD, -1);
|
|
FAIL_IF_NULL(m);
|
|
|
|
DetectThresholdData *de = (DetectThresholdData *)m->ctx;
|
|
FAIL_IF_NULL(de);
|
|
|
|
FAIL_IF_NOT(de->type == TYPE_BOTH && de->track == TRACK_DST && de->count == 10 && de->seconds == 60);
|
|
DetectEngineCtxFree(de_ctx);
|
|
PASS;
|
|
}
|
|
|
|
/**
|
|
* \test Check if the threshold file is loaded and well parsed
|
|
*
|
|
* \retval 1 on succces
|
|
* \retval 0 on failure
|
|
*/
|
|
static int SCThresholdConfTest03(void)
|
|
{
|
|
DetectEngineCtx *de_ctx = DetectEngineCtxInit();
|
|
FAIL_IF_NULL(de_ctx);
|
|
de_ctx->flags |= DE_QUIET;
|
|
|
|
Signature *sig = DetectEngineAppendSig(de_ctx,
|
|
"alert tcp any any -> any any (msg:\"Threshold limit\"; gid:1; sid:1000;)");
|
|
FAIL_IF_NULL(sig);
|
|
|
|
FAIL_IF_NOT_NULL(g_ut_threshold_fp);
|
|
g_ut_threshold_fp = SCThresholdConfGenerateValidDummyFD01();
|
|
FAIL_IF_NULL(g_ut_threshold_fp);
|
|
SCThresholdConfInitContext(de_ctx);
|
|
|
|
SigMatch *m = DetectGetLastSMByListId(sig, DETECT_SM_LIST_THRESHOLD,
|
|
DETECT_THRESHOLD, -1);
|
|
FAIL_IF_NULL(m);
|
|
|
|
DetectThresholdData *de = (DetectThresholdData *)m->ctx;
|
|
FAIL_IF_NULL(de);
|
|
|
|
FAIL_IF_NOT(de->type == TYPE_THRESHOLD && de->track == TRACK_SRC && de->count == 100 && de->seconds == 60);
|
|
DetectEngineCtxFree(de_ctx);
|
|
PASS;
|
|
}
|
|
|
|
/**
|
|
* \test Check if the threshold file is loaded and well parsed
|
|
*
|
|
* \retval 1 on succces
|
|
* \retval 0 on failure
|
|
*/
|
|
static int SCThresholdConfTest04(void)
|
|
{
|
|
DetectEngineCtx *de_ctx = DetectEngineCtxInit();
|
|
FAIL_IF_NULL(de_ctx);
|
|
de_ctx->flags |= DE_QUIET;
|
|
|
|
Signature *sig = DetectEngineAppendSig(de_ctx,
|
|
"alert tcp any any -> any any (msg:\"Threshold limit\"; gid:1; sid:1000;)");
|
|
FAIL_IF_NULL(sig);
|
|
|
|
FAIL_IF_NOT_NULL(g_ut_threshold_fp);
|
|
g_ut_threshold_fp = SCThresholdConfGenerateInValidDummyFD02();
|
|
FAIL_IF_NULL(g_ut_threshold_fp);
|
|
SCThresholdConfInitContext(de_ctx);
|
|
|
|
SigMatch *m = DetectGetLastSMByListId(sig, DETECT_SM_LIST_THRESHOLD,
|
|
DETECT_THRESHOLD, -1);
|
|
FAIL_IF_NOT_NULL(m);
|
|
|
|
DetectEngineCtxFree(de_ctx);
|
|
PASS;
|
|
}
|
|
|
|
/**
|
|
* \test Check if the threshold file is loaded and well parsed
|
|
*
|
|
* \retval 1 on succces
|
|
* \retval 0 on failure
|
|
*/
|
|
static int SCThresholdConfTest05(void)
|
|
{
|
|
DetectEngineCtx *de_ctx = DetectEngineCtxInit();
|
|
FAIL_IF_NULL(de_ctx);
|
|
de_ctx->flags |= DE_QUIET;
|
|
|
|
Signature *sig = DetectEngineAppendSig(de_ctx,
|
|
"alert tcp any any -> any any (msg:\"Threshold limit\"; gid:1; sid:1;)");
|
|
FAIL_IF_NULL(sig);
|
|
sig = DetectEngineAppendSig(de_ctx,
|
|
"alert tcp any any -> any 80 (msg:\"Threshold limit\"; gid:1; sid:10;)");
|
|
FAIL_IF_NULL(sig);
|
|
|
|
sig = DetectEngineAppendSig(de_ctx,
|
|
"alert tcp any any -> any 80 (msg:\"Threshold limit\"; gid:1; sid:100;)");
|
|
FAIL_IF_NULL(sig);
|
|
|
|
FAIL_IF_NOT_NULL(g_ut_threshold_fp);
|
|
g_ut_threshold_fp = SCThresholdConfGenerateValidDummyFD03();
|
|
FAIL_IF_NULL(g_ut_threshold_fp);
|
|
SCThresholdConfInitContext(de_ctx);
|
|
|
|
Signature *s = de_ctx->sig_list;
|
|
SigMatch *m = DetectGetLastSMByListId(s, DETECT_SM_LIST_THRESHOLD,
|
|
DETECT_THRESHOLD, -1);
|
|
FAIL_IF_NULL(m);
|
|
FAIL_IF_NULL(m->ctx);
|
|
DetectThresholdData *de = (DetectThresholdData *)m->ctx;
|
|
FAIL_IF_NOT(de->type == TYPE_THRESHOLD && de->track == TRACK_SRC && de->count == 100 && de->seconds == 60);
|
|
|
|
s = de_ctx->sig_list->next;
|
|
m = DetectGetLastSMByListId(s, DETECT_SM_LIST_THRESHOLD,
|
|
DETECT_THRESHOLD, -1);
|
|
FAIL_IF_NULL(m);
|
|
FAIL_IF_NULL(m->ctx);
|
|
de = (DetectThresholdData *)m->ctx;
|
|
FAIL_IF_NOT(de->type == TYPE_THRESHOLD && de->track == TRACK_SRC && de->count == 100 && de->seconds == 60);
|
|
|
|
s = de_ctx->sig_list->next->next;
|
|
m = DetectGetLastSMByListId(s, DETECT_SM_LIST_THRESHOLD,
|
|
DETECT_THRESHOLD, -1);
|
|
FAIL_IF_NULL(m);
|
|
FAIL_IF_NULL(m->ctx);
|
|
de = (DetectThresholdData *)m->ctx;
|
|
FAIL_IF_NOT(de->type == TYPE_THRESHOLD && de->track == TRACK_SRC && de->count == 100 && de->seconds == 60);
|
|
|
|
PASS;
|
|
}
|
|
|
|
/**
|
|
* \test Check if the threshold file is loaded and well parsed
|
|
*
|
|
* \retval 1 on succces
|
|
* \retval 0 on failure
|
|
*/
|
|
static int SCThresholdConfTest06(void)
|
|
{
|
|
DetectEngineCtx *de_ctx = DetectEngineCtxInit();
|
|
FAIL_IF_NULL(de_ctx);
|
|
de_ctx->flags |= DE_QUIET;
|
|
|
|
Signature *sig = DetectEngineAppendSig(de_ctx,
|
|
"alert tcp any any -> any any (msg:\"Threshold limit\"; gid:1; sid:10;)");
|
|
FAIL_IF_NULL(sig);
|
|
|
|
FAIL_IF_NOT_NULL(g_ut_threshold_fp);
|
|
g_ut_threshold_fp = SCThresholdConfGenerateValidDummyFD04();
|
|
FAIL_IF_NULL(g_ut_threshold_fp);
|
|
SCThresholdConfInitContext(de_ctx);
|
|
|
|
SigMatch *m = DetectGetLastSMByListId(sig, DETECT_SM_LIST_THRESHOLD,
|
|
DETECT_THRESHOLD, -1);
|
|
FAIL_IF_NULL(m);
|
|
|
|
DetectThresholdData *de = (DetectThresholdData *)m->ctx;
|
|
FAIL_IF_NULL(de);
|
|
FAIL_IF_NOT(de->type == TYPE_LIMIT && de->track == TRACK_SRC && de->count == 1 && de->seconds == 60);
|
|
|
|
DetectEngineCtxFree(de_ctx);
|
|
PASS;
|
|
}
|
|
|
|
/**
|
|
* \test Check if the rate_filter rules are loaded and well parsed
|
|
*
|
|
* \retval 1 on succces
|
|
* \retval 0 on failure
|
|
*/
|
|
static int SCThresholdConfTest07(void)
|
|
{
|
|
DetectEngineCtx *de_ctx = DetectEngineCtxInit();
|
|
FAIL_IF_NULL(de_ctx);
|
|
de_ctx->flags |= DE_QUIET;
|
|
|
|
Signature *sig = DetectEngineAppendSig(de_ctx,
|
|
"alert tcp any any -> any any (msg:\"Threshold limit\"; gid:1; sid:10;)");
|
|
FAIL_IF_NULL(sig);
|
|
|
|
FAIL_IF_NOT_NULL(g_ut_threshold_fp);
|
|
g_ut_threshold_fp = SCThresholdConfGenerateValidDummyFD05();
|
|
FAIL_IF_NULL(g_ut_threshold_fp);
|
|
SCThresholdConfInitContext(de_ctx);
|
|
|
|
SigMatch *m = DetectGetLastSMByListId(sig, DETECT_SM_LIST_THRESHOLD,
|
|
DETECT_DETECTION_FILTER, -1);
|
|
FAIL_IF_NULL(m);
|
|
|
|
DetectThresholdData *de = (DetectThresholdData *)m->ctx;
|
|
FAIL_IF_NULL(de);
|
|
FAIL_IF_NOT(de->type == TYPE_RATE && de->track == TRACK_SRC && de->count == 1 && de->seconds == 60);
|
|
|
|
DetectEngineCtxFree(de_ctx);
|
|
PASS;
|
|
}
|
|
|
|
/**
|
|
* \test Check if the rate_filter rules are loaded and well parsed
|
|
* with multilines
|
|
*
|
|
* \retval 1 on succces
|
|
* \retval 0 on failure
|
|
*/
|
|
static int SCThresholdConfTest08(void)
|
|
{
|
|
DetectEngineCtx *de_ctx = DetectEngineCtxInit();
|
|
FAIL_IF_NULL(de_ctx);
|
|
de_ctx->flags |= DE_QUIET;
|
|
|
|
Signature *sig = DetectEngineAppendSig(de_ctx,
|
|
"alert tcp any any -> any any (msg:\"Threshold limit\"; gid:1; sid:10;)");
|
|
FAIL_IF_NULL(sig);
|
|
|
|
FAIL_IF_NOT_NULL(g_ut_threshold_fp);
|
|
g_ut_threshold_fp = SCThresholdConfGenerateValidDummyFD06();
|
|
FAIL_IF_NULL(g_ut_threshold_fp);
|
|
SCThresholdConfInitContext(de_ctx);
|
|
|
|
SigMatch *m = DetectGetLastSMByListId(sig, DETECT_SM_LIST_THRESHOLD,
|
|
DETECT_DETECTION_FILTER, -1);
|
|
FAIL_IF_NULL(m);
|
|
|
|
DetectThresholdData *de = (DetectThresholdData *)m->ctx;
|
|
FAIL_IF_NULL(de);
|
|
FAIL_IF_NOT(de->type == TYPE_RATE && de->track == TRACK_SRC && de->count == 1 && de->seconds == 60);
|
|
|
|
DetectEngineCtxFree(de_ctx);
|
|
PASS;
|
|
}
|
|
|
|
/**
|
|
* \test Check if the rate_filter rules work
|
|
*
|
|
* \retval 1 on succces
|
|
* \retval 0 on failure
|
|
*/
|
|
static int SCThresholdConfTest09(void)
|
|
{
|
|
ThreadVars th_v;
|
|
memset(&th_v, 0, sizeof(th_v));
|
|
|
|
HostInitConfig(HOST_QUIET);
|
|
|
|
struct timeval ts;
|
|
memset (&ts, 0, sizeof(struct timeval));
|
|
TimeGet(&ts);
|
|
|
|
Packet *p = UTHBuildPacket((uint8_t*)"lalala", 6, IPPROTO_TCP);
|
|
FAIL_IF_NULL(p);
|
|
|
|
DetectEngineThreadCtx *det_ctx = NULL;
|
|
|
|
DetectEngineCtx *de_ctx = DetectEngineCtxInit();
|
|
FAIL_IF_NULL(de_ctx);
|
|
de_ctx->flags |= DE_QUIET;
|
|
|
|
Signature *s = DetectEngineAppendSig(de_ctx,
|
|
"alert tcp any any -> any any (msg:\"ratefilter test\"; gid:1; sid:10;)");
|
|
FAIL_IF_NULL(s);
|
|
|
|
FAIL_IF_NOT_NULL(g_ut_threshold_fp);
|
|
g_ut_threshold_fp = SCThresholdConfGenerateValidDummyFD07();
|
|
FAIL_IF_NULL(g_ut_threshold_fp);
|
|
SCThresholdConfInitContext(de_ctx);
|
|
|
|
SigGroupBuild(de_ctx);
|
|
DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
|
|
|
|
TimeGet(&p->ts);
|
|
p->alerts.cnt = 0;
|
|
p->action = 0;
|
|
SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
|
|
FAIL_IF(p->alerts.cnt != 1 || PACKET_TEST_ACTION(p, ACTION_DROP));
|
|
p->alerts.cnt = 0;
|
|
p->action = 0;
|
|
SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
|
|
FAIL_IF(p->alerts.cnt != 1 || PACKET_TEST_ACTION(p, ACTION_DROP));
|
|
p->alerts.cnt = 0;
|
|
p->action = 0;
|
|
SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
|
|
FAIL_IF(p->alerts.cnt != 1 || PACKET_TEST_ACTION(p, ACTION_DROP));
|
|
|
|
TimeSetIncrementTime(2);
|
|
TimeGet(&p->ts);
|
|
|
|
p->alerts.cnt = 0;
|
|
p->action = 0;
|
|
SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
|
|
FAIL_IF(p->alerts.cnt != 1 || !(PACKET_TEST_ACTION(p, ACTION_DROP)));
|
|
|
|
TimeSetIncrementTime(3);
|
|
TimeGet(&p->ts);
|
|
|
|
p->alerts.cnt = 0;
|
|
p->action = 0;
|
|
SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
|
|
FAIL_IF(p->alerts.cnt != 1 || !(PACKET_TEST_ACTION(p, ACTION_DROP)));
|
|
|
|
TimeSetIncrementTime(10);
|
|
TimeGet(&p->ts);
|
|
|
|
p->alerts.cnt = 0;
|
|
p->action = 0;
|
|
SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
|
|
FAIL_IF(p->alerts.cnt != 1 || PACKET_TEST_ACTION(p, ACTION_DROP));
|
|
|
|
p->alerts.cnt = 0;
|
|
p->action = 0;
|
|
SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
|
|
FAIL_IF(p->alerts.cnt != 1 || PACKET_TEST_ACTION(p, ACTION_DROP));
|
|
|
|
UTHFreePacket(p);
|
|
DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
|
|
DetectEngineCtxFree(de_ctx);
|
|
HostShutdown();
|
|
PASS;
|
|
}
|
|
|
|
/**
|
|
* \test Check if the rate_filter rules work with track by_rule
|
|
*
|
|
* \retval 1 on succces
|
|
* \retval 0 on failure
|
|
*/
|
|
static int SCThresholdConfTest10(void)
|
|
{
|
|
HostInitConfig(HOST_QUIET);
|
|
|
|
struct timeval ts;
|
|
memset (&ts, 0, sizeof(struct timeval));
|
|
TimeGet(&ts);
|
|
|
|
/* Create two different packets falling to the same rule, and
|
|
* because count:3, we should drop on match #4.
|
|
*/
|
|
Packet *p1 = UTHBuildPacketSrcDst((uint8_t*)"lalala", 6, IPPROTO_TCP,
|
|
"172.26.0.2", "172.26.0.11");
|
|
FAIL_IF_NULL(p1);
|
|
Packet *p2 = UTHBuildPacketSrcDst((uint8_t*)"lalala", 6, IPPROTO_TCP,
|
|
"172.26.0.1", "172.26.0.10");
|
|
FAIL_IF_NULL(p2);
|
|
|
|
ThreadVars th_v;
|
|
memset(&th_v, 0, sizeof(th_v));
|
|
|
|
DetectEngineCtx *de_ctx = DetectEngineCtxInit();
|
|
FAIL_IF_NULL(de_ctx);
|
|
de_ctx->flags |= DE_QUIET;
|
|
DetectEngineThreadCtx *det_ctx = NULL;
|
|
|
|
Signature *s = DetectEngineAppendSig(de_ctx,
|
|
"alert tcp any any -> any any (msg:\"ratefilter test\"; gid:1; sid:10;)");
|
|
FAIL_IF_NULL(s);
|
|
|
|
FAIL_IF_NOT_NULL(g_ut_threshold_fp);
|
|
g_ut_threshold_fp = SCThresholdConfGenerateValidDummyFD08();
|
|
FAIL_IF_NULL(g_ut_threshold_fp);
|
|
SCThresholdConfInitContext(de_ctx);
|
|
|
|
SigGroupBuild(de_ctx);
|
|
DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
|
|
TimeGet(&p1->ts);
|
|
p2->ts = p1->ts;
|
|
|
|
/* All should be alerted, none dropped */
|
|
SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
|
|
FAIL_IF(PACKET_TEST_ACTION(p1, ACTION_DROP));
|
|
FAIL_IF(PacketAlertCheck(p1, 10) != 1);
|
|
p1->action = 0;
|
|
SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
|
|
FAIL_IF(PACKET_TEST_ACTION(p2, ACTION_DROP));
|
|
FAIL_IF(PacketAlertCheck(p2, 10) != 1);
|
|
p2->action = 0;
|
|
SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
|
|
FAIL_IF(PACKET_TEST_ACTION(p1, ACTION_DROP));
|
|
FAIL_IF(PacketAlertCheck(p1, 10) != 1);
|
|
p1->action = 0;
|
|
|
|
/* Match #4 should be dropped*/
|
|
SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
|
|
FAIL_IF_NOT(PACKET_TEST_ACTION(p2, ACTION_DROP));
|
|
FAIL_IF(PacketAlertCheck(p2, 10) != 1);
|
|
p2->action = 0;
|
|
|
|
TimeSetIncrementTime(2);
|
|
TimeGet(&p1->ts);
|
|
|
|
/* Still dropped because timeout not expired */
|
|
SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
|
|
FAIL_IF_NOT(PACKET_TEST_ACTION(p1, ACTION_DROP));
|
|
FAIL_IF(PacketAlertCheck(p1, 10) != 1);
|
|
p1->action = 0;
|
|
|
|
TimeSetIncrementTime(10);
|
|
TimeGet(&p1->ts);
|
|
|
|
/* Not dropped because timeout expired */
|
|
SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
|
|
FAIL_IF(PACKET_TEST_ACTION(p1, ACTION_DROP));
|
|
FAIL_IF(PacketAlertCheck(p1, 10) != 1);
|
|
|
|
/* Ensure that a Threshold entry was installed at the sig */
|
|
FAIL_IF_NULL(de_ctx->ths_ctx.th_entry[s->num]);
|
|
|
|
UTHFreePacket(p1);
|
|
UTHFreePacket(p2);
|
|
DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
|
|
DetectEngineCtxFree(de_ctx);
|
|
HostShutdown();
|
|
PASS;
|
|
}
|
|
|
|
/**
|
|
* \test Check if the rate_filter rules work
|
|
*
|
|
* \retval 1 on succces
|
|
* \retval 0 on failure
|
|
*/
|
|
static int SCThresholdConfTest11(void)
|
|
{
|
|
HostInitConfig(HOST_QUIET);
|
|
|
|
struct timeval ts;
|
|
memset (&ts, 0, sizeof(struct timeval));
|
|
TimeGet(&ts);
|
|
|
|
Packet *p = UTHBuildPacket((uint8_t*)"lalala", 6, IPPROTO_TCP);
|
|
FAIL_IF_NULL(p);
|
|
|
|
ThreadVars th_v;
|
|
memset(&th_v, 0, sizeof(th_v));
|
|
|
|
DetectEngineCtx *de_ctx = DetectEngineCtxInit();
|
|
FAIL_IF_NULL(de_ctx);
|
|
de_ctx->flags |= DE_QUIET;
|
|
DetectEngineThreadCtx *det_ctx = NULL;
|
|
|
|
Signature *s = DetectEngineAppendSig(de_ctx,
|
|
"alert tcp any any -> any any (msg:\"event_filter test limit\"; gid:1; sid:10;)");
|
|
FAIL_IF_NULL(s);
|
|
s = DetectEngineAppendSig(de_ctx,
|
|
"alert tcp any any -> any any (msg:\"event_filter test threshold\"; gid:1; sid:11;)");
|
|
FAIL_IF_NULL(s);
|
|
s = DetectEngineAppendSig(de_ctx,
|
|
"alert tcp any any -> any any (msg:\"event_filter test both\"; gid:1; sid:12;)");
|
|
FAIL_IF_NULL(s);
|
|
|
|
FAIL_IF_NOT_NULL(g_ut_threshold_fp);
|
|
g_ut_threshold_fp = SCThresholdConfGenerateValidDummyFD09();
|
|
FAIL_IF_NULL(g_ut_threshold_fp);
|
|
SCThresholdConfInitContext(de_ctx);
|
|
|
|
SigGroupBuild(de_ctx);
|
|
DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
|
|
|
|
TimeGet(&p->ts);
|
|
|
|
int alerts10 = 0;
|
|
int alerts11 = 0;
|
|
int alerts12 = 0;
|
|
|
|
SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
|
|
alerts10 += PacketAlertCheck(p, 10);
|
|
alerts11 += PacketAlertCheck(p, 11);
|
|
alerts12 += PacketAlertCheck(p, 12);
|
|
SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
|
|
alerts10 += PacketAlertCheck(p, 10);
|
|
alerts11 += PacketAlertCheck(p, 11);
|
|
alerts12 += PacketAlertCheck(p, 12);
|
|
SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
|
|
alerts10 += PacketAlertCheck(p, 10);
|
|
alerts11 += PacketAlertCheck(p, 11);
|
|
alerts12 += PacketAlertCheck(p, 12);
|
|
SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
|
|
alerts10 += PacketAlertCheck(p, 10);
|
|
alerts11 += PacketAlertCheck(p, 11);
|
|
alerts12 += PacketAlertCheck(p, 12);
|
|
SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
|
|
alerts10 += PacketAlertCheck(p, 10);
|
|
alerts11 += PacketAlertCheck(p, 11);
|
|
alerts12 += PacketAlertCheck(p, 12);
|
|
|
|
TimeSetIncrementTime(100);
|
|
TimeGet(&p->ts);
|
|
|
|
SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
|
|
alerts10 += PacketAlertCheck(p, 10);
|
|
alerts11 += PacketAlertCheck(p, 11);
|
|
|
|
TimeSetIncrementTime(10);
|
|
TimeGet(&p->ts);
|
|
|
|
SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
|
|
alerts10 += PacketAlertCheck(p, 10);
|
|
alerts11 += PacketAlertCheck(p, 11);
|
|
alerts12 += PacketAlertCheck(p, 12);
|
|
SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
|
|
alerts10 += PacketAlertCheck(p, 10);
|
|
alerts11 += PacketAlertCheck(p, 11);
|
|
alerts12 += PacketAlertCheck(p, 12);
|
|
SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
|
|
alerts10 += PacketAlertCheck(p, 10);
|
|
alerts11 += PacketAlertCheck(p, 11);
|
|
alerts12 += PacketAlertCheck(p, 12);
|
|
SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
|
|
alerts10 += PacketAlertCheck(p, 10);
|
|
alerts11 += PacketAlertCheck(p, 11);
|
|
alerts12 += PacketAlertCheck(p, 12);
|
|
|
|
FAIL_IF_NOT(alerts10 == 4);
|
|
/* One on the first interval, another on the second */
|
|
FAIL_IF_NOT(alerts11 == 2);
|
|
FAIL_IF_NOT(alerts12 == 2);
|
|
|
|
UTHFreePacket(p);
|
|
DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
|
|
DetectEngineCtxFree(de_ctx);
|
|
HostShutdown();
|
|
PASS;
|
|
}
|
|
|
|
/**
|
|
* \test Check if the rate_filter rules work
|
|
*
|
|
* \retval 1 on succces
|
|
* \retval 0 on failure
|
|
*/
|
|
static int SCThresholdConfTest12(void)
|
|
{
|
|
HostInitConfig(HOST_QUIET);
|
|
|
|
struct timeval ts;
|
|
memset (&ts, 0, sizeof(struct timeval));
|
|
TimeGet(&ts);
|
|
|
|
Packet *p = UTHBuildPacket((uint8_t*)"lalala", 6, IPPROTO_TCP);
|
|
FAIL_IF_NULL(p);
|
|
|
|
ThreadVars th_v;
|
|
memset(&th_v, 0, sizeof(th_v));
|
|
|
|
DetectEngineCtx *de_ctx = DetectEngineCtxInit();
|
|
FAIL_IF_NULL(de_ctx);
|
|
de_ctx->flags |= DE_QUIET;
|
|
DetectEngineThreadCtx *det_ctx = NULL;
|
|
|
|
Signature *s = DetectEngineAppendSig(de_ctx,
|
|
"alert tcp any any -> any any (msg:\"event_filter test limit\"; gid:1; sid:10;)");
|
|
FAIL_IF_NULL(s);
|
|
s = DetectEngineAppendSig(de_ctx,
|
|
"alert tcp any any -> any any (msg:\"event_filter test threshold\"; gid:1; sid:11;)");
|
|
FAIL_IF_NULL(s);
|
|
s = DetectEngineAppendSig(de_ctx,
|
|
"alert tcp any any -> any any (msg:\"event_filter test both\"; gid:1; sid:12;)");
|
|
FAIL_IF_NULL(s);
|
|
|
|
FAIL_IF_NOT_NULL(g_ut_threshold_fp);
|
|
g_ut_threshold_fp = SCThresholdConfGenerateValidDummyFD10();
|
|
FAIL_IF_NULL(g_ut_threshold_fp);
|
|
SCThresholdConfInitContext(de_ctx);
|
|
|
|
SigGroupBuild(de_ctx);
|
|
DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
|
|
|
|
TimeGet(&p->ts);
|
|
|
|
int alerts10 = 0;
|
|
int alerts11 = 0;
|
|
int alerts12 = 0;
|
|
|
|
SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
|
|
alerts10 += PacketAlertCheck(p, 10);
|
|
alerts11 += PacketAlertCheck(p, 11);
|
|
alerts12 += PacketAlertCheck(p, 12);
|
|
SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
|
|
alerts10 += PacketAlertCheck(p, 10);
|
|
alerts11 += PacketAlertCheck(p, 11);
|
|
alerts12 += PacketAlertCheck(p, 12);
|
|
SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
|
|
alerts10 += PacketAlertCheck(p, 10);
|
|
alerts11 += PacketAlertCheck(p, 11);
|
|
alerts12 += PacketAlertCheck(p, 12);
|
|
SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
|
|
alerts10 += PacketAlertCheck(p, 10);
|
|
alerts11 += PacketAlertCheck(p, 11);
|
|
alerts12 += PacketAlertCheck(p, 12);
|
|
SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
|
|
alerts10 += PacketAlertCheck(p, 10);
|
|
alerts11 += PacketAlertCheck(p, 11);
|
|
alerts12 += PacketAlertCheck(p, 12);
|
|
|
|
TimeSetIncrementTime(100);
|
|
TimeGet(&p->ts);
|
|
|
|
SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
|
|
alerts10 += PacketAlertCheck(p, 10);
|
|
alerts11 += PacketAlertCheck(p, 11);
|
|
|
|
TimeSetIncrementTime(10);
|
|
TimeGet(&p->ts);
|
|
|
|
SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
|
|
alerts10 += PacketAlertCheck(p, 10);
|
|
alerts11 += PacketAlertCheck(p, 11);
|
|
alerts12 += PacketAlertCheck(p, 12);
|
|
SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
|
|
alerts10 += PacketAlertCheck(p, 10);
|
|
alerts11 += PacketAlertCheck(p, 11);
|
|
alerts12 += PacketAlertCheck(p, 12);
|
|
SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
|
|
alerts10 += PacketAlertCheck(p, 10);
|
|
alerts11 += PacketAlertCheck(p, 11);
|
|
alerts12 += PacketAlertCheck(p, 12);
|
|
SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
|
|
alerts10 += PacketAlertCheck(p, 10);
|
|
alerts11 += PacketAlertCheck(p, 11);
|
|
alerts12 += PacketAlertCheck(p, 12);
|
|
|
|
FAIL_IF_NOT(alerts10 == 10);
|
|
/* One on the first interval, another on the second */
|
|
FAIL_IF_NOT(alerts11 == 1);
|
|
FAIL_IF_NOT(alerts12 == 1);
|
|
|
|
UTHFreePacket(p);
|
|
DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
|
|
DetectEngineCtxFree(de_ctx);
|
|
HostShutdown();
|
|
PASS;
|
|
}
|
|
|
|
/**
|
|
* \test Check if the threshold file is loaded and well parsed
|
|
*
|
|
* \retval 1 on succces
|
|
* \retval 0 on failure
|
|
*/
|
|
static int SCThresholdConfTest13(void)
|
|
{
|
|
DetectEngineCtx *de_ctx = DetectEngineCtxInit();
|
|
FAIL_IF_NULL(de_ctx);
|
|
de_ctx->flags |= DE_QUIET;
|
|
|
|
Signature *sig = DetectEngineAppendSig(de_ctx,
|
|
"alert tcp any any -> any any (msg:\"Threshold limit\"; gid:1; sid:1000;)");
|
|
FAIL_IF_NULL(sig);
|
|
|
|
FAIL_IF_NOT_NULL(g_ut_threshold_fp);
|
|
g_ut_threshold_fp = SCThresholdConfGenerateValidDummyFD11();
|
|
FAIL_IF_NULL(g_ut_threshold_fp);
|
|
SCThresholdConfInitContext(de_ctx);
|
|
|
|
SigMatch *m = DetectGetLastSMByListId(sig,
|
|
DETECT_SM_LIST_SUPPRESS, DETECT_THRESHOLD, -1);
|
|
FAIL_IF_NULL(m);
|
|
|
|
DetectThresholdData *de = (DetectThresholdData *)m->ctx;
|
|
FAIL_IF_NULL(de);
|
|
FAIL_IF_NOT(de->type == TYPE_SUPPRESS && de->track == TRACK_SRC);
|
|
|
|
DetectEngineCtxFree(de_ctx);
|
|
PASS;
|
|
}
|
|
|
|
/**
|
|
* \test Check if the suppress rules work
|
|
*
|
|
* \retval 1 on succces
|
|
* \retval 0 on failure
|
|
*/
|
|
static int SCThresholdConfTest14(void)
|
|
{
|
|
HostInitConfig(HOST_QUIET);
|
|
|
|
Packet *p1 = UTHBuildPacketReal((uint8_t*)"lalala", 6, IPPROTO_TCP, "192.168.0.10",
|
|
"192.168.0.100", 1234, 24);
|
|
FAIL_IF_NULL(p1);
|
|
Packet *p2 = UTHBuildPacketReal((uint8_t*)"lalala", 6, IPPROTO_TCP, "192.168.1.1",
|
|
"192.168.0.100", 1234, 24);
|
|
FAIL_IF_NULL(p2);
|
|
|
|
DetectEngineThreadCtx *det_ctx = NULL;
|
|
DetectEngineCtx *de_ctx = DetectEngineCtxInit();
|
|
FAIL_IF_NULL(de_ctx);
|
|
de_ctx->flags |= DE_QUIET;
|
|
|
|
Signature *sig = DetectEngineAppendSig(de_ctx,
|
|
"alert tcp any any -> any any (msg:\"suppress test\"; gid:1; sid:10000;)");
|
|
FAIL_IF_NULL(sig);
|
|
sig = DetectEngineAppendSig(de_ctx,
|
|
"alert tcp any any -> any any (msg:\"suppress test 2\"; gid:1; sid:10;)");
|
|
FAIL_IF_NULL(sig);
|
|
sig = DetectEngineAppendSig(de_ctx,
|
|
"alert tcp any any -> any any (msg:\"suppress test 3\"; gid:1; sid:1000;)");
|
|
FAIL_IF_NULL(sig);
|
|
|
|
ThreadVars th_v;
|
|
memset(&th_v, 0, sizeof(th_v));
|
|
|
|
struct timeval ts;
|
|
memset (&ts, 0, sizeof(struct timeval));
|
|
TimeGet(&ts);
|
|
|
|
FAIL_IF_NOT_NULL(g_ut_threshold_fp);
|
|
g_ut_threshold_fp = SCThresholdConfGenerateValidDummyFD11();
|
|
FAIL_IF_NULL(g_ut_threshold_fp);
|
|
SCThresholdConfInitContext(de_ctx);
|
|
|
|
SigGroupBuild(de_ctx);
|
|
DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
|
|
|
|
SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
|
|
SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
|
|
|
|
FAIL_IF_NOT(PacketAlertCheck(p1, 10000) == 0);
|
|
FAIL_IF_NOT(PacketAlertCheck(p1, 10) == 1);
|
|
FAIL_IF_NOT(PacketAlertCheck(p1, 1000) == 1);
|
|
FAIL_IF_NOT(PacketAlertCheck(p2, 1000) == 0);
|
|
|
|
UTHFreePacket(p1);
|
|
UTHFreePacket(p2);
|
|
|
|
DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
|
|
DetectEngineCtxFree(de_ctx);
|
|
|
|
HostShutdown();
|
|
PASS;
|
|
}
|
|
|
|
/**
|
|
* \test Check if the suppress rules work
|
|
*
|
|
* \retval 1 on succces
|
|
* \retval 0 on failure
|
|
*/
|
|
static int SCThresholdConfTest15(void)
|
|
{
|
|
HostInitConfig(HOST_QUIET);
|
|
|
|
Packet *p = UTHBuildPacketReal((uint8_t*)"lalala", 6, IPPROTO_TCP, "192.168.0.10",
|
|
"192.168.0.100", 1234, 24);
|
|
FAIL_IF_NULL(p);
|
|
|
|
ThreadVars th_v;
|
|
memset(&th_v, 0, sizeof(th_v));
|
|
|
|
DetectEngineThreadCtx *det_ctx = NULL;
|
|
DetectEngineCtx *de_ctx = DetectEngineCtxInit();
|
|
FAIL_IF_NULL(de_ctx);
|
|
de_ctx->flags |= DE_QUIET;
|
|
|
|
struct timeval ts;
|
|
memset (&ts, 0, sizeof(struct timeval));
|
|
TimeGet(&ts);
|
|
|
|
Signature *sig = DetectEngineAppendSig(de_ctx,
|
|
"drop tcp any any -> any any (msg:\"suppress test\"; content:\"lalala\"; gid:1; sid:10000;)");
|
|
FAIL_IF_NULL(sig);
|
|
|
|
FAIL_IF_NOT_NULL(g_ut_threshold_fp);
|
|
g_ut_threshold_fp = SCThresholdConfGenerateValidDummyFD11();
|
|
FAIL_IF_NULL(g_ut_threshold_fp);
|
|
SCThresholdConfInitContext(de_ctx);
|
|
|
|
SigGroupBuild(de_ctx);
|
|
DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
|
|
|
|
SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
|
|
|
|
/* 10000 shouldn't match */
|
|
FAIL_IF(PacketAlertCheck(p, 10000) != 0);
|
|
/* however, it should have set the drop flag */
|
|
FAIL_IF(!(PACKET_TEST_ACTION(p, ACTION_DROP)));
|
|
|
|
UTHFreePacket(p);
|
|
DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
|
|
DetectEngineCtxFree(de_ctx);
|
|
HostShutdown();
|
|
PASS;
|
|
}
|
|
|
|
/**
|
|
* \test Check if the suppress rules work
|
|
*
|
|
* \retval 1 on succces
|
|
* \retval 0 on failure
|
|
*/
|
|
static int SCThresholdConfTest16(void)
|
|
{
|
|
HostInitConfig(HOST_QUIET);
|
|
|
|
Packet *p = UTHBuildPacketReal((uint8_t*)"lalala", 6, IPPROTO_TCP, "192.168.1.1",
|
|
"192.168.0.100", 1234, 24);
|
|
FAIL_IF_NULL(p);
|
|
|
|
ThreadVars th_v;
|
|
memset(&th_v, 0, sizeof(th_v));
|
|
|
|
DetectEngineThreadCtx *det_ctx = NULL;
|
|
DetectEngineCtx *de_ctx = DetectEngineCtxInit();
|
|
FAIL_IF_NULL(de_ctx);
|
|
de_ctx->flags |= DE_QUIET;
|
|
|
|
struct timeval ts;
|
|
memset (&ts, 0, sizeof(struct timeval));
|
|
TimeGet(&ts);
|
|
|
|
Signature *sig = DetectEngineAppendSig(de_ctx,
|
|
"drop tcp any any -> any any (msg:\"suppress test\"; gid:1; sid:1000;)");
|
|
FAIL_IF_NULL(sig);
|
|
|
|
FAIL_IF_NOT_NULL(g_ut_threshold_fp);
|
|
g_ut_threshold_fp = SCThresholdConfGenerateValidDummyFD11();
|
|
FAIL_IF_NULL(g_ut_threshold_fp);
|
|
SCThresholdConfInitContext(de_ctx);
|
|
|
|
SigGroupBuild(de_ctx);
|
|
DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
|
|
|
|
SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
|
|
|
|
FAIL_IF(PacketAlertCheck(p, 1000) != 0);
|
|
/* however, it should have set the drop flag */
|
|
FAIL_IF(!(PACKET_TEST_ACTION(p, ACTION_DROP)));
|
|
|
|
UTHFreePacket(p);
|
|
DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
|
|
DetectEngineCtxFree(de_ctx);
|
|
HostShutdown();
|
|
PASS;
|
|
}
|
|
|
|
/**
|
|
* \test Check if the suppress rules work - ip only rule
|
|
*
|
|
* \retval 1 on succces
|
|
* \retval 0 on failure
|
|
*/
|
|
static int SCThresholdConfTest17(void)
|
|
{
|
|
HostInitConfig(HOST_QUIET);
|
|
|
|
Packet *p = UTHBuildPacketReal((uint8_t*)"lalala", 6, IPPROTO_TCP, "192.168.0.10",
|
|
"192.168.0.100", 1234, 24);
|
|
FAIL_IF_NULL(p);
|
|
|
|
ThreadVars th_v;
|
|
memset(&th_v, 0, sizeof(th_v));
|
|
|
|
DetectEngineThreadCtx *det_ctx = NULL;
|
|
DetectEngineCtx *de_ctx = DetectEngineCtxInit();
|
|
FAIL_IF_NULL(de_ctx);
|
|
de_ctx->flags |= DE_QUIET;
|
|
|
|
struct timeval ts;
|
|
memset (&ts, 0, sizeof(struct timeval));
|
|
TimeGet(&ts);
|
|
|
|
Signature *sig = DetectEngineAppendSig(de_ctx,
|
|
"drop tcp 192.168.0.10 any -> 192.168.0.100 any (msg:\"suppress test\"; gid:1; sid:10000;)");
|
|
FAIL_IF_NULL(sig);
|
|
|
|
FAIL_IF_NOT_NULL(g_ut_threshold_fp);
|
|
g_ut_threshold_fp = SCThresholdConfGenerateValidDummyFD11();
|
|
FAIL_IF_NULL(g_ut_threshold_fp);
|
|
SCThresholdConfInitContext(de_ctx);
|
|
|
|
SigGroupBuild(de_ctx);
|
|
DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
|
|
|
|
SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
|
|
|
|
/* 10000 shouldn't match */
|
|
FAIL_IF(PacketAlertCheck(p, 10000) != 0);
|
|
/* however, it should have set the drop flag */
|
|
FAIL_IF(!(PACKET_TEST_ACTION(p, ACTION_DROP)));
|
|
|
|
UTHFreePacket(p);
|
|
DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
|
|
DetectEngineCtxFree(de_ctx);
|
|
HostShutdown();
|
|
PASS;
|
|
}
|
|
|
|
/**
|
|
* \brief Creates a dummy threshold file, with all valid options, for testing purposes.
|
|
*
|
|
* \retval fd Pointer to file descriptor.
|
|
*/
|
|
static FILE *SCThresholdConfGenerateInvalidDummyFD12(void)
|
|
{
|
|
FILE *fd = NULL;
|
|
const char *buffer =
|
|
"suppress gen_id 1, sig_id 2200029, track by_dst, ip fe80::/16\n"
|
|
"suppress gen_id 1, sig_id 2200029, track by_stc, ip fe80::/16\n";
|
|
|
|
fd = SCFmemopen((void *)buffer, strlen(buffer), "r");
|
|
if (fd == NULL)
|
|
SCLogDebug("Error with SCFmemopen() called by Threshold Config test code");
|
|
|
|
return fd;
|
|
}
|
|
|
|
/**
|
|
* \test Check if the suppress rule parsing handles errors correctly
|
|
*
|
|
* \retval 1 on succces
|
|
* \retval 0 on failure
|
|
*/
|
|
static int SCThresholdConfTest18(void)
|
|
{
|
|
HostInitConfig(HOST_QUIET);
|
|
DetectEngineCtx *de_ctx = DetectEngineCtxInit();
|
|
FAIL_IF_NULL(de_ctx);
|
|
de_ctx->flags |= DE_QUIET;
|
|
|
|
Signature *s = DetectEngineAppendSig(de_ctx,
|
|
"alert tcp 192.168.0.10 any -> 192.168.0.100 any (msg:\"suppress test\"; gid:1; sid:2200029;)");
|
|
FAIL_IF_NULL(s);
|
|
FAIL_IF_NOT_NULL(g_ut_threshold_fp);
|
|
g_ut_threshold_fp = SCThresholdConfGenerateInvalidDummyFD12();
|
|
FAIL_IF_NULL(g_ut_threshold_fp);
|
|
SCThresholdConfInitContext(de_ctx);
|
|
SigGroupBuild(de_ctx);
|
|
|
|
FAIL_IF_NULL(s->sm_arrays[DETECT_SM_LIST_SUPPRESS]);
|
|
SigMatchData *smd = s->sm_arrays[DETECT_SM_LIST_SUPPRESS];
|
|
DetectThresholdData *de = (DetectThresholdData *)smd->ctx;
|
|
FAIL_IF_NULL(de);
|
|
FAIL_IF_NOT(de->type == TYPE_SUPPRESS && de->track == TRACK_DST);
|
|
|
|
DetectEngineCtxFree(de_ctx);
|
|
HostShutdown();
|
|
PASS;
|
|
}
|
|
|
|
/**
|
|
* \brief Creates a dummy threshold file, with all valid options, for testing purposes.
|
|
*
|
|
* \retval fd Pointer to file descriptor.
|
|
*/
|
|
static FILE *SCThresholdConfGenerateInvalidDummyFD13(void)
|
|
{
|
|
FILE *fd = NULL;
|
|
const char *buffer =
|
|
"suppress gen_id 1, sig_id 2200029, track by_stc, ip fe80::/16\n"
|
|
"suppress gen_id 1, sig_id 2200029, track by_dst, ip fe80::/16\n";
|
|
|
|
fd = SCFmemopen((void *)buffer, strlen(buffer), "r");
|
|
if (fd == NULL)
|
|
SCLogDebug("Error with SCFmemopen() called by Threshold Config test code");
|
|
|
|
return fd;
|
|
}
|
|
|
|
/**
|
|
* \test Check if the suppress rule parsing handles errors correctly
|
|
*
|
|
* \retval 1 on succces
|
|
* \retval 0 on failure
|
|
*/
|
|
static int SCThresholdConfTest19(void)
|
|
{
|
|
HostInitConfig(HOST_QUIET);
|
|
DetectEngineCtx *de_ctx = DetectEngineCtxInit();
|
|
FAIL_IF_NULL(de_ctx);
|
|
de_ctx->flags |= DE_QUIET;
|
|
Signature *s = DetectEngineAppendSig(de_ctx,
|
|
"alert tcp 192.168.0.10 any -> 192.168.0.100 any (msg:\"suppress test\"; gid:1; sid:2200029;)");
|
|
FAIL_IF_NULL(s);
|
|
FAIL_IF_NOT_NULL(g_ut_threshold_fp);
|
|
g_ut_threshold_fp = SCThresholdConfGenerateInvalidDummyFD13();
|
|
FAIL_IF_NULL(g_ut_threshold_fp);
|
|
SCThresholdConfInitContext(de_ctx);
|
|
SigGroupBuild(de_ctx);
|
|
FAIL_IF_NULL(s->sm_arrays[DETECT_SM_LIST_SUPPRESS]);
|
|
SigMatchData *smd = s->sm_arrays[DETECT_SM_LIST_SUPPRESS];
|
|
DetectThresholdData *de = (DetectThresholdData *)smd->ctx;
|
|
FAIL_IF_NULL(de);
|
|
FAIL_IF_NOT(de->type == TYPE_SUPPRESS && de->track == TRACK_DST);
|
|
DetectEngineCtxFree(de_ctx);
|
|
HostShutdown();
|
|
PASS;
|
|
}
|
|
|
|
/**
|
|
* \brief Creates a dummy threshold file, with all valid options, for testing purposes.
|
|
*
|
|
* \retval fd Pointer to file descriptor.
|
|
*/
|
|
static FILE *SCThresholdConfGenerateValidDummyFD20(void)
|
|
{
|
|
FILE *fd = NULL;
|
|
const char *buffer =
|
|
"suppress gen_id 1, sig_id 1000, track by_src, ip 2.2.3.4\n"
|
|
"suppress gen_id 1, sig_id 1000, track by_src, ip 1.2.3.4\n"
|
|
"suppress gen_id 1, sig_id 1000, track by_src, ip 192.168.1.1\n";
|
|
|
|
fd = SCFmemopen((void *)buffer, strlen(buffer), "r");
|
|
if (fd == NULL)
|
|
SCLogDebug("Error with SCFmemopen() called by Threshold Config test code");
|
|
|
|
return fd;
|
|
}
|
|
|
|
/**
|
|
* \test Check if the threshold file is loaded and well parsed
|
|
*
|
|
* \retval 1 on succces
|
|
* \retval 0 on failure
|
|
*/
|
|
static int SCThresholdConfTest20(void)
|
|
{
|
|
HostInitConfig(HOST_QUIET);
|
|
DetectEngineCtx *de_ctx = DetectEngineCtxInit();
|
|
FAIL_IF_NULL(de_ctx);
|
|
de_ctx->flags |= DE_QUIET;
|
|
Signature *s = DetectEngineAppendSig(de_ctx,
|
|
"alert tcp any any -> any any (msg:\"Threshold limit\"; content:\"abc\"; sid:1000;)");
|
|
FAIL_IF_NULL(s);
|
|
FAIL_IF_NOT_NULL(g_ut_threshold_fp);
|
|
g_ut_threshold_fp = SCThresholdConfGenerateValidDummyFD20();
|
|
FAIL_IF_NULL(g_ut_threshold_fp);
|
|
SCThresholdConfInitContext(de_ctx);
|
|
SigGroupBuild(de_ctx);
|
|
FAIL_IF_NULL(s->sm_arrays[DETECT_SM_LIST_SUPPRESS]);
|
|
|
|
SigMatchData *smd = s->sm_arrays[DETECT_SM_LIST_SUPPRESS];
|
|
DetectThresholdData *de = (DetectThresholdData *)smd->ctx;
|
|
FAIL_IF_NULL(de);
|
|
FAIL_IF_NOT(de->type == TYPE_SUPPRESS && de->track == TRACK_SRC);
|
|
FAIL_IF(smd->is_last);
|
|
|
|
smd++;
|
|
de = (DetectThresholdData *)smd->ctx;
|
|
FAIL_IF_NULL(de);
|
|
FAIL_IF_NOT(de->type == TYPE_SUPPRESS && de->track == TRACK_SRC);
|
|
FAIL_IF(smd->is_last);
|
|
|
|
smd++;
|
|
de = (DetectThresholdData *)smd->ctx;
|
|
FAIL_IF_NULL(de);
|
|
FAIL_IF_NOT(de->type == TYPE_SUPPRESS && de->track == TRACK_SRC);
|
|
FAIL_IF_NOT(smd->is_last);
|
|
|
|
DetectEngineCtxFree(de_ctx);
|
|
HostShutdown();
|
|
PASS;
|
|
}
|
|
|
|
/**
|
|
* \test Check if the threshold file is loaded and well parsed, and applied
|
|
* correctly to a rule with thresholding
|
|
*
|
|
* \retval 1 on succces
|
|
* \retval 0 on failure
|
|
*/
|
|
static int SCThresholdConfTest21(void)
|
|
{
|
|
HostInitConfig(HOST_QUIET);
|
|
DetectEngineCtx *de_ctx = DetectEngineCtxInit();
|
|
FAIL_IF_NULL(de_ctx);
|
|
de_ctx->flags |= DE_QUIET;
|
|
Signature *s = DetectEngineAppendSig(de_ctx,
|
|
"alert tcp any any -> any any (msg:\"Threshold limit\"; content:\"abc\"; threshold: type limit, track by_dst, count 5, seconds 60; sid:1000;)");
|
|
FAIL_IF_NULL(s);
|
|
g_ut_threshold_fp = SCThresholdConfGenerateValidDummyFD20();
|
|
FAIL_IF_NULL(g_ut_threshold_fp);
|
|
SCThresholdConfInitContext(de_ctx);
|
|
SigGroupBuild(de_ctx);
|
|
FAIL_IF_NULL(s->sm_arrays[DETECT_SM_LIST_SUPPRESS]);
|
|
|
|
SigMatchData *smd = s->sm_arrays[DETECT_SM_LIST_SUPPRESS];
|
|
DetectThresholdData *de = (DetectThresholdData *)smd->ctx;
|
|
FAIL_IF_NULL(de);
|
|
FAIL_IF_NOT(de->type == TYPE_SUPPRESS && de->track == TRACK_SRC);
|
|
FAIL_IF(smd->is_last);
|
|
|
|
smd++;
|
|
de = (DetectThresholdData *)smd->ctx;
|
|
FAIL_IF_NULL(de);
|
|
FAIL_IF_NOT(de->type == TYPE_SUPPRESS && de->track == TRACK_SRC);
|
|
FAIL_IF(smd->is_last);
|
|
|
|
smd++;
|
|
de = (DetectThresholdData *)smd->ctx;
|
|
FAIL_IF_NULL(de);
|
|
FAIL_IF_NOT(de->type == TYPE_SUPPRESS && de->track == TRACK_SRC);
|
|
FAIL_IF_NOT(smd->is_last);
|
|
|
|
DetectEngineCtxFree(de_ctx);
|
|
HostShutdown();
|
|
PASS;
|
|
}
|
|
|
|
/**
|
|
* \brief Creates a dummy rate_filter file, for testing rate filtering by_both source and destination
|
|
*
|
|
* \retval fd Pointer to file descriptor.
|
|
*/
|
|
static FILE *SCThresholdConfGenerateValidDummyFD22(void)
|
|
{
|
|
FILE *fd = NULL;
|
|
const char *buffer =
|
|
"rate_filter gen_id 1, sig_id 10, track by_both, count 2, seconds 5, new_action drop, timeout 6\n";
|
|
|
|
fd = SCFmemopen((void *)buffer, strlen(buffer), "r");
|
|
if (fd == NULL)
|
|
SCLogDebug("Error with SCFmemopen() called by Threshold Config test code");
|
|
|
|
return fd;
|
|
}
|
|
|
|
/**
|
|
* \test Check if the rate_filter rules work with track by_both
|
|
*
|
|
* \retval 1 on succces
|
|
* \retval 0 on failure
|
|
*/
|
|
static int SCThresholdConfTest22(void)
|
|
{
|
|
ThreadVars th_v;
|
|
memset(&th_v, 0, sizeof(th_v));
|
|
|
|
IPPairInitConfig(IPPAIR_QUIET);
|
|
|
|
struct timeval ts;
|
|
memset(&ts, 0, sizeof(struct timeval));
|
|
TimeGet(&ts);
|
|
|
|
/* This packet will cause rate_filter */
|
|
Packet *p1 = UTHBuildPacketSrcDst((uint8_t*)"lalala", 6, IPPROTO_TCP, "172.26.0.1", "172.26.0.10");
|
|
FAIL_IF_NULL(p1);
|
|
|
|
/* Should not be filtered for different destination */
|
|
Packet *p2 = UTHBuildPacketSrcDst((uint8_t*)"lalala", 6, IPPROTO_TCP, "172.26.0.1", "172.26.0.2");
|
|
FAIL_IF_NULL(p2);
|
|
|
|
/* Should not be filtered when both src and dst the same */
|
|
Packet *p3 = UTHBuildPacketSrcDst((uint8_t*)"lalala", 6, IPPROTO_TCP, "172.26.0.1", "172.26.0.1");
|
|
FAIL_IF_NULL(p3);
|
|
|
|
DetectEngineThreadCtx *det_ctx = NULL;
|
|
|
|
DetectEngineCtx *de_ctx = DetectEngineCtxInit();
|
|
FAIL_IF_NULL(de_ctx);
|
|
de_ctx->flags |= DE_QUIET;
|
|
|
|
Signature *sig = DetectEngineAppendSig(de_ctx,
|
|
"alert tcp any any -> any any (msg:\"ratefilter by_both test\"; gid:1; sid:10;)");
|
|
FAIL_IF_NULL(sig);
|
|
|
|
FAIL_IF_NOT_NULL(g_ut_threshold_fp);
|
|
g_ut_threshold_fp = SCThresholdConfGenerateValidDummyFD22();
|
|
FAIL_IF_NULL(g_ut_threshold_fp);
|
|
SCThresholdConfInitContext(de_ctx);
|
|
|
|
SigGroupBuild(de_ctx);
|
|
DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
|
|
|
|
TimeGet(&p1->ts);
|
|
p2->ts = p3->ts = p1->ts;
|
|
|
|
/* All should be alerted, none dropped */
|
|
SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
|
|
FAIL_IF(PACKET_TEST_ACTION(p1, ACTION_DROP));
|
|
FAIL_IF(PacketAlertCheck(p1, 10) != 1);
|
|
|
|
SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
|
|
FAIL_IF(PACKET_TEST_ACTION(p2, ACTION_DROP));
|
|
FAIL_IF(PacketAlertCheck(p2, 10) != 1);
|
|
|
|
SigMatchSignatures(&th_v, de_ctx, det_ctx, p3);
|
|
FAIL_IF(PACKET_TEST_ACTION(p3, ACTION_DROP));
|
|
FAIL_IF(PacketAlertCheck(p3, 10) != 1);
|
|
|
|
p1->action = p2->action = p3->action = 0;
|
|
|
|
TimeSetIncrementTime(2);
|
|
TimeGet(&p1->ts);
|
|
p2->ts = p3->ts = p1->ts;
|
|
|
|
/* p1 still shouldn't be dropped after 2nd alert */
|
|
SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
|
|
FAIL_IF(PACKET_TEST_ACTION(p1, ACTION_DROP));
|
|
FAIL_IF(PacketAlertCheck(p1, 10) != 1);
|
|
|
|
p1->action = 0;
|
|
|
|
TimeSetIncrementTime(2);
|
|
TimeGet(&p1->ts);
|
|
p2->ts = p3->ts = p1->ts;
|
|
|
|
/* All should be alerted, only p1 must be dropped due to rate_filter*/
|
|
SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
|
|
FAIL_IF_NOT(PACKET_TEST_ACTION(p1, ACTION_DROP));
|
|
FAIL_IF(PacketAlertCheck(p1, 10) != 1);
|
|
|
|
SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
|
|
FAIL_IF(PACKET_TEST_ACTION(p2, ACTION_DROP));
|
|
FAIL_IF(PacketAlertCheck(p2, 10) != 1);
|
|
|
|
SigMatchSignatures(&th_v, de_ctx, det_ctx, p3);
|
|
FAIL_IF(PACKET_TEST_ACTION(p3, ACTION_DROP));
|
|
FAIL_IF(PacketAlertCheck(p3, 10) != 1);
|
|
|
|
p1->action = p2->action = p3->action = 0;
|
|
|
|
TimeSetIncrementTime(7);
|
|
TimeGet(&p1->ts);
|
|
p2->ts = p3->ts = p1->ts;
|
|
|
|
/* All should be alerted, none dropped (because timeout expired) */
|
|
SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
|
|
FAIL_IF(PACKET_TEST_ACTION(p1, ACTION_DROP));
|
|
FAIL_IF(PacketAlertCheck(p1, 10) != 1);
|
|
|
|
SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
|
|
FAIL_IF(PACKET_TEST_ACTION(p2, ACTION_DROP));
|
|
FAIL_IF(PacketAlertCheck(p2, 10) != 1);
|
|
|
|
SigMatchSignatures(&th_v, de_ctx, det_ctx, p3);
|
|
FAIL_IF(PACKET_TEST_ACTION(p3, ACTION_DROP));
|
|
FAIL_IF(PacketAlertCheck(p3, 10) != 1);
|
|
|
|
UTHFreePacket(p3);
|
|
UTHFreePacket(p2);
|
|
UTHFreePacket(p1);
|
|
|
|
DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
|
|
DetectEngineCtxFree(de_ctx);
|
|
IPPairShutdown();
|
|
PASS;
|
|
}
|
|
|
|
/**
|
|
* \brief Creates a dummy rate_filter file, for testing rate filtering by_both source and destination
|
|
*
|
|
* \retval fd Pointer to file descriptor.
|
|
*/
|
|
static FILE *SCThresholdConfGenerateValidDummyFD23(void)
|
|
{
|
|
FILE *fd = NULL;
|
|
const char *buffer =
|
|
"rate_filter gen_id 1, sig_id 10, track by_both, count 1, seconds 5, new_action drop, timeout 6\n";
|
|
|
|
fd = SCFmemopen((void *)buffer, strlen(buffer), "r");
|
|
if (fd == NULL)
|
|
SCLogDebug("Error with SCFmemopen() called by Threshold Config test code");
|
|
|
|
return fd;
|
|
}
|
|
|
|
/**
|
|
* \test Check if the rate_filter by_both work when similar packets
|
|
* going in opposite direction
|
|
*
|
|
* \retval 1 on succces
|
|
* \retval 0 on failure
|
|
*/
|
|
static int SCThresholdConfTest23(void)
|
|
{
|
|
ThreadVars th_v;
|
|
memset(&th_v, 0, sizeof(th_v));
|
|
|
|
IPPairInitConfig(IPPAIR_QUIET);
|
|
|
|
struct timeval ts;
|
|
memset(&ts, 0, sizeof(struct timeval));
|
|
TimeGet(&ts);
|
|
|
|
/* Create two packets between same addresses in opposite direction */
|
|
Packet *p1 = UTHBuildPacketSrcDst((uint8_t*)"lalala", 6, IPPROTO_TCP, "172.26.0.1", "172.26.0.10");
|
|
FAIL_IF_NULL(p1);
|
|
|
|
Packet *p2 = UTHBuildPacketSrcDst((uint8_t*)"lalala", 6, IPPROTO_TCP, "172.26.0.10", "172.26.0.1");
|
|
FAIL_IF_NULL(p2);
|
|
|
|
DetectEngineThreadCtx *det_ctx = NULL;
|
|
|
|
DetectEngineCtx *de_ctx = DetectEngineCtxInit();
|
|
FAIL_IF_NULL(de_ctx);
|
|
de_ctx->flags |= DE_QUIET;
|
|
|
|
Signature *sig = DetectEngineAppendSig(de_ctx,
|
|
"alert tcp any any -> any any (msg:\"ratefilter by_both test\"; gid:1; sid:10;)");
|
|
FAIL_IF_NULL(sig);
|
|
|
|
FAIL_IF_NOT_NULL(g_ut_threshold_fp);
|
|
g_ut_threshold_fp = SCThresholdConfGenerateValidDummyFD23();
|
|
FAIL_IF_NULL(g_ut_threshold_fp);
|
|
SCThresholdConfInitContext(de_ctx);
|
|
|
|
SigGroupBuild(de_ctx);
|
|
DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
|
|
|
|
TimeGet(&p1->ts);
|
|
SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
|
|
/* First packet should be alerted, not dropped */
|
|
FAIL_IF(PACKET_TEST_ACTION(p1, ACTION_DROP));
|
|
FAIL_IF(PacketAlertCheck(p1, 10) != 1);
|
|
|
|
TimeSetIncrementTime(2);
|
|
TimeGet(&p2->ts);
|
|
SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
|
|
|
|
/* Second packet should be dropped because it considered as "the same pair"
|
|
and rate_filter count reached*/
|
|
FAIL_IF_NOT(PACKET_TEST_ACTION(p2, ACTION_DROP));
|
|
FAIL_IF(PacketAlertCheck(p2, 10) != 1);
|
|
|
|
UTHFreePacket(p2);
|
|
UTHFreePacket(p1);
|
|
|
|
DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
|
|
DetectEngineCtxFree(de_ctx);
|
|
IPPairShutdown();
|
|
PASS;
|
|
}
|
|
#endif /* UNITTESTS */
|
|
|
|
/**
|
|
* \brief This function registers unit tests for Classification Config API.
|
|
*/
|
|
void SCThresholdConfRegisterTests(void)
|
|
{
|
|
#ifdef UNITTESTS
|
|
UtRegisterTest("SCThresholdConfTest01", SCThresholdConfTest01);
|
|
UtRegisterTest("SCThresholdConfTest02", SCThresholdConfTest02);
|
|
UtRegisterTest("SCThresholdConfTest03", SCThresholdConfTest03);
|
|
UtRegisterTest("SCThresholdConfTest04", SCThresholdConfTest04);
|
|
UtRegisterTest("SCThresholdConfTest05", SCThresholdConfTest05);
|
|
UtRegisterTest("SCThresholdConfTest06", SCThresholdConfTest06);
|
|
UtRegisterTest("SCThresholdConfTest07", SCThresholdConfTest07);
|
|
UtRegisterTest("SCThresholdConfTest08", SCThresholdConfTest08);
|
|
UtRegisterTest("SCThresholdConfTest09 - rate_filter",
|
|
SCThresholdConfTest09);
|
|
UtRegisterTest("SCThresholdConfTest10 - rate_filter",
|
|
SCThresholdConfTest10);
|
|
UtRegisterTest("SCThresholdConfTest11 - event_filter",
|
|
SCThresholdConfTest11);
|
|
UtRegisterTest("SCThresholdConfTest12 - event_filter",
|
|
SCThresholdConfTest12);
|
|
UtRegisterTest("SCThresholdConfTest13", SCThresholdConfTest13);
|
|
UtRegisterTest("SCThresholdConfTest14 - suppress", SCThresholdConfTest14);
|
|
UtRegisterTest("SCThresholdConfTest15 - suppress drop",
|
|
SCThresholdConfTest15);
|
|
UtRegisterTest("SCThresholdConfTest16 - suppress drop",
|
|
SCThresholdConfTest16);
|
|
UtRegisterTest("SCThresholdConfTest17 - suppress drop",
|
|
SCThresholdConfTest17);
|
|
|
|
UtRegisterTest("SCThresholdConfTest18 - suppress parsing",
|
|
SCThresholdConfTest18);
|
|
UtRegisterTest("SCThresholdConfTest19 - suppress parsing",
|
|
SCThresholdConfTest19);
|
|
UtRegisterTest("SCThresholdConfTest20 - suppress parsing",
|
|
SCThresholdConfTest20);
|
|
UtRegisterTest("SCThresholdConfTest21 - suppress parsing",
|
|
SCThresholdConfTest21);
|
|
UtRegisterTest("SCThresholdConfTest22 - rate_filter by_both",
|
|
SCThresholdConfTest22);
|
|
UtRegisterTest("SCThresholdConfTest23 - rate_filter by_both opposite",
|
|
SCThresholdConfTest23);
|
|
|
|
#endif /* UNITTESTS */
|
|
}
|
|
|
|
/**
|
|
* @}
|
|
*/
|