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.
720 lines
21 KiB
C
720 lines
21 KiB
C
/* Copyright (C) 2007-2015 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.
|
|
*/
|
|
|
|
/**
|
|
* \defgroup threshold Thresholding
|
|
*
|
|
* This feature is used to reduce the number of logged alerts for noisy rules.
|
|
* This can be tuned to significantly reduce false alarms, and it can also be
|
|
* used to write a newer breed of rules. Thresholding commands limit the number
|
|
* of times a particular event is logged during a specified time interval.
|
|
*
|
|
* @{
|
|
*/
|
|
|
|
/**
|
|
* \file
|
|
*
|
|
* \author Breno Silva <breno.silva@gmail.com>
|
|
* \author Victor Julien <victor@inliniac.net>
|
|
*
|
|
* Threshold part of the detection engine.
|
|
*/
|
|
|
|
#include "suricata-common.h"
|
|
#include "debug.h"
|
|
#include "detect.h"
|
|
#include "flow.h"
|
|
|
|
#include "host.h"
|
|
#include "host-storage.h"
|
|
|
|
#include "ippair.h"
|
|
#include "ippair-storage.h"
|
|
|
|
#include "detect-parse.h"
|
|
#include "detect-engine-sigorder.h"
|
|
|
|
#include "detect-engine-siggroup.h"
|
|
#include "detect-engine-address.h"
|
|
#include "detect-engine-port.h"
|
|
#include "detect-engine-mpm.h"
|
|
#include "detect-engine-iponly.h"
|
|
|
|
#include "detect-engine.h"
|
|
#include "detect-engine-threshold.h"
|
|
|
|
#include "detect-content.h"
|
|
#include "detect-uricontent.h"
|
|
|
|
#include "util-hash.h"
|
|
#include "util-time.h"
|
|
#include "util-error.h"
|
|
#include "util-debug.h"
|
|
|
|
#include "util-var-name.h"
|
|
#include "tm-threads.h"
|
|
|
|
static int host_threshold_id = -1; /**< host storage id for thresholds */
|
|
static int ippair_threshold_id = -1; /**< ip pair storage id for thresholds */
|
|
|
|
int ThresholdHostStorageId(void)
|
|
{
|
|
return host_threshold_id;
|
|
}
|
|
|
|
void ThresholdInit(void)
|
|
{
|
|
host_threshold_id = HostStorageRegister("threshold", sizeof(void *), NULL, ThresholdListFree);
|
|
if (host_threshold_id == -1) {
|
|
FatalError(SC_ERR_FATAL,
|
|
"Can't initiate host storage for thresholding");
|
|
}
|
|
ippair_threshold_id = IPPairStorageRegister("threshold", sizeof(void *), NULL, ThresholdListFree);
|
|
if (ippair_threshold_id == -1) {
|
|
FatalError(SC_ERR_FATAL,
|
|
"Can't initiate IP pair storage for thresholding");
|
|
}
|
|
}
|
|
|
|
int ThresholdHostHasThreshold(Host *host)
|
|
{
|
|
return HostGetStorageById(host, host_threshold_id) ? 1 : 0;
|
|
}
|
|
|
|
int ThresholdIPPairHasThreshold(IPPair *pair)
|
|
{
|
|
return IPPairGetStorageById(pair, ippair_threshold_id) ? 1 : 0;
|
|
}
|
|
|
|
/**
|
|
* \brief Return next DetectThresholdData for signature
|
|
*
|
|
* \param sig Signature pointer
|
|
* \param p Packet structure
|
|
* \param sm Pointer to a Signature Match pointer
|
|
*
|
|
* \retval tsh Return the threshold data from signature or NULL if not found
|
|
*
|
|
*
|
|
*/
|
|
const DetectThresholdData *SigGetThresholdTypeIter(const Signature *sig,
|
|
Packet *p, const SigMatchData **psm, int list)
|
|
{
|
|
const SigMatchData *smd = NULL;
|
|
const DetectThresholdData *tsh = NULL;
|
|
|
|
if (sig == NULL)
|
|
return NULL;
|
|
|
|
if (*psm == NULL) {
|
|
smd = sig->sm_arrays[list];
|
|
} else {
|
|
/* Iteration in progress, using provided value */
|
|
smd = *psm;
|
|
}
|
|
|
|
if (p == NULL)
|
|
return NULL;
|
|
|
|
while (1) {
|
|
if (smd->type == DETECT_THRESHOLD ||
|
|
smd->type == DETECT_DETECTION_FILTER)
|
|
{
|
|
tsh = (DetectThresholdData *)smd->ctx;
|
|
|
|
if (smd->is_last) {
|
|
*psm = NULL;
|
|
} else {
|
|
*psm = smd + 1;
|
|
}
|
|
return tsh;
|
|
}
|
|
|
|
if (smd->is_last) {
|
|
break;
|
|
}
|
|
smd++;
|
|
}
|
|
*psm = NULL;
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* \brief Remove timeout threshold hash elements
|
|
*
|
|
* \param head Current head element of storage
|
|
* \param tv Current time
|
|
*
|
|
* \retval DetectThresholdEntry Return new head element or NULL if all expired
|
|
*
|
|
*/
|
|
|
|
static DetectThresholdEntry* ThresholdTimeoutCheck(DetectThresholdEntry *head, struct timeval *tv)
|
|
{
|
|
DetectThresholdEntry *tmp = head;
|
|
DetectThresholdEntry *prev = NULL;
|
|
DetectThresholdEntry *new_head = head;
|
|
|
|
while (tmp != NULL) {
|
|
/* check if the 'check' timestamp is not before the creation ts.
|
|
* This can happen due to the async nature of the host timeout
|
|
* code that also calls this code from a management thread. */
|
|
if (TIMEVAL_EARLIER(*tv, tmp->tv1) || TIMEVAL_DIFF_SEC(*tv, tmp->tv1) <= tmp->seconds) {
|
|
prev = tmp;
|
|
tmp = tmp->next;
|
|
continue;
|
|
}
|
|
|
|
/* timed out */
|
|
|
|
DetectThresholdEntry *tde = tmp;
|
|
if (prev != NULL) {
|
|
prev->next = tmp->next;
|
|
}
|
|
else {
|
|
new_head = tmp->next;
|
|
}
|
|
tmp = tde->next;
|
|
SCFree(tde);
|
|
}
|
|
|
|
return new_head;
|
|
}
|
|
|
|
int ThresholdHostTimeoutCheck(Host *host, struct timeval *tv)
|
|
{
|
|
DetectThresholdEntry* head = HostGetStorageById(host, host_threshold_id);
|
|
DetectThresholdEntry* new_head = ThresholdTimeoutCheck(head, tv);
|
|
if (new_head != head) {
|
|
HostSetStorageById(host, host_threshold_id, new_head);
|
|
}
|
|
return new_head == NULL;
|
|
}
|
|
|
|
|
|
int ThresholdIPPairTimeoutCheck(IPPair *pair, struct timeval *tv)
|
|
{
|
|
DetectThresholdEntry* head = IPPairGetStorageById(pair, ippair_threshold_id);
|
|
DetectThresholdEntry* new_head = ThresholdTimeoutCheck(head, tv);
|
|
if (new_head != head) {
|
|
IPPairSetStorageById(pair, ippair_threshold_id, new_head);
|
|
}
|
|
return new_head == NULL;
|
|
}
|
|
|
|
static DetectThresholdEntry *
|
|
DetectThresholdEntryAlloc(const DetectThresholdData *td, Packet *p,
|
|
uint32_t sid, uint32_t gid)
|
|
{
|
|
SCEnter();
|
|
|
|
DetectThresholdEntry *ste = SCCalloc(1, sizeof(DetectThresholdEntry));
|
|
if (unlikely(ste == NULL)) {
|
|
SCReturnPtr(NULL, "DetectThresholdEntry");
|
|
}
|
|
|
|
ste->sid = sid;
|
|
ste->gid = gid;
|
|
ste->track = td->track;
|
|
ste->seconds = td->seconds;
|
|
|
|
SCReturnPtr(ste, "DetectThresholdEntry");
|
|
}
|
|
|
|
static DetectThresholdEntry *ThresholdHostLookupEntry(Host *h,
|
|
uint32_t sid, uint32_t gid)
|
|
{
|
|
DetectThresholdEntry *e;
|
|
|
|
for (e = HostGetStorageById(h, host_threshold_id); e != NULL; e = e->next) {
|
|
if (e->sid == sid && e->gid == gid)
|
|
break;
|
|
}
|
|
|
|
return e;
|
|
}
|
|
|
|
static DetectThresholdEntry *ThresholdIPPairLookupEntry(IPPair *pair,
|
|
uint32_t sid, uint32_t gid)
|
|
{
|
|
DetectThresholdEntry *e;
|
|
|
|
for (e = IPPairGetStorageById(pair, ippair_threshold_id); e != NULL; e = e->next) {
|
|
if (e->sid == sid && e->gid == gid)
|
|
break;
|
|
}
|
|
|
|
return e;
|
|
}
|
|
|
|
static int ThresholdHandlePacketSuppress(Packet *p,
|
|
const DetectThresholdData *td, uint32_t sid, uint32_t gid)
|
|
{
|
|
int ret = 0;
|
|
DetectAddress *m = NULL;
|
|
switch (td->track) {
|
|
case TRACK_DST:
|
|
m = DetectAddressLookupInHead(&td->addrs, &p->dst);
|
|
SCLogDebug("TRACK_DST");
|
|
break;
|
|
case TRACK_SRC:
|
|
m = DetectAddressLookupInHead(&td->addrs, &p->src);
|
|
SCLogDebug("TRACK_SRC");
|
|
break;
|
|
/* suppress if either src or dst is a match on the suppress
|
|
* address list */
|
|
case TRACK_EITHER:
|
|
m = DetectAddressLookupInHead(&td->addrs, &p->src);
|
|
if (m == NULL) {
|
|
m = DetectAddressLookupInHead(&td->addrs, &p->dst);
|
|
}
|
|
break;
|
|
case TRACK_RULE:
|
|
default:
|
|
SCLogError(SC_ERR_INVALID_VALUE,
|
|
"track mode %d is not supported", td->track);
|
|
break;
|
|
}
|
|
if (m == NULL)
|
|
ret = 1;
|
|
else
|
|
ret = 2; /* suppressed but still need actions */
|
|
|
|
return ret;
|
|
}
|
|
|
|
static inline void RateFilterSetAction(Packet *p, PacketAlert *pa, uint8_t new_action)
|
|
{
|
|
switch (new_action) {
|
|
case TH_ACTION_ALERT:
|
|
PACKET_ALERT(p);
|
|
pa->flags |= PACKET_ALERT_RATE_FILTER_MODIFIED;
|
|
break;
|
|
case TH_ACTION_DROP:
|
|
PACKET_DROP(p);
|
|
pa->flags |= PACKET_ALERT_RATE_FILTER_MODIFIED;
|
|
break;
|
|
case TH_ACTION_REJECT:
|
|
PACKET_REJECT(p);
|
|
pa->flags |= PACKET_ALERT_RATE_FILTER_MODIFIED;
|
|
break;
|
|
case TH_ACTION_PASS:
|
|
PACKET_PASS(p);
|
|
pa->flags |= PACKET_ALERT_RATE_FILTER_MODIFIED;
|
|
break;
|
|
default:
|
|
/* Weird, leave the default action */
|
|
break;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* \brief Check if the entry reached threshold count limit
|
|
*
|
|
* \param lookup_tsh Current threshold entry
|
|
* \param td Threshold settings
|
|
* \param packet_time used to compare against previous detection and to set timeouts
|
|
*
|
|
* \retval int 1 if threshold reached for this entry
|
|
*
|
|
*/
|
|
static int IsThresholdReached(DetectThresholdEntry* lookup_tsh, const DetectThresholdData *td, struct timeval packet_time)
|
|
{
|
|
int ret = 0;
|
|
|
|
/* Check if we have a timeout enabled, if so,
|
|
* we still matching (and enabling the new_action) */
|
|
if (lookup_tsh->tv_timeout != 0) {
|
|
if ((packet_time.tv_sec - lookup_tsh->tv_timeout) > td->timeout) {
|
|
/* Ok, we are done, timeout reached */
|
|
lookup_tsh->tv_timeout = 0;
|
|
}
|
|
else {
|
|
/* Already matching */
|
|
ret = 1;
|
|
} /* else - if ((packet_time - lookup_tsh->tv_timeout) > td->timeout) */
|
|
|
|
}
|
|
else {
|
|
/* Update the matching state with the timeout interval */
|
|
if (TIMEVAL_DIFF_SEC(packet_time, lookup_tsh->tv1) < td->seconds) {
|
|
lookup_tsh->current_count++;
|
|
if (lookup_tsh->current_count > td->count) {
|
|
/* Then we must enable the new action by setting a
|
|
* timeout */
|
|
lookup_tsh->tv_timeout = packet_time.tv_sec;
|
|
ret = 1;
|
|
}
|
|
}
|
|
else {
|
|
lookup_tsh->tv1 = packet_time;
|
|
lookup_tsh->current_count = 1;
|
|
}
|
|
} /* else - if (lookup_tsh->tv_timeout != 0) */
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void AddEntryToHostStorage(Host *h, DetectThresholdEntry *e, struct timeval packet_time)
|
|
{
|
|
if (h && e) {
|
|
e->current_count = 1;
|
|
e->tv1 = packet_time;
|
|
e->tv_timeout = 0;
|
|
e->next = HostGetStorageById(h, host_threshold_id);
|
|
HostSetStorageById(h, host_threshold_id, e);
|
|
}
|
|
}
|
|
|
|
static void AddEntryToIPPairStorage(IPPair *pair, DetectThresholdEntry *e, struct timeval packet_time)
|
|
{
|
|
if (pair && e) {
|
|
e->current_count = 1;
|
|
e->tv1 = packet_time;
|
|
e->tv_timeout = 0;
|
|
e->next = IPPairGetStorageById(pair, ippair_threshold_id);
|
|
IPPairSetStorageById(pair, ippair_threshold_id, e);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* \retval 2 silent match (no alert but apply actions)
|
|
* \retval 1 normal match
|
|
* \retval 0 no match
|
|
*
|
|
* If a new DetectThresholdEntry is generated to track the threshold
|
|
* for this rule, then it will be returned in new_tsh.
|
|
*/
|
|
static int ThresholdHandlePacket(Packet *p, DetectThresholdEntry *lookup_tsh,
|
|
DetectThresholdEntry **new_tsh, const DetectThresholdData *td,
|
|
uint32_t sid, uint32_t gid, PacketAlert *pa)
|
|
{
|
|
int ret = 0;
|
|
|
|
switch(td->type) {
|
|
case TYPE_LIMIT:
|
|
{
|
|
SCLogDebug("limit");
|
|
|
|
if (lookup_tsh != NULL) {
|
|
if (TIMEVAL_DIFF_SEC(p->ts, lookup_tsh->tv1) < td->seconds) {
|
|
lookup_tsh->current_count++;
|
|
|
|
if (lookup_tsh->current_count <= td->count) {
|
|
ret = 1;
|
|
} else {
|
|
ret = 2;
|
|
}
|
|
} else {
|
|
lookup_tsh->tv1 = p->ts;
|
|
lookup_tsh->current_count = 1;
|
|
|
|
ret = 1;
|
|
}
|
|
} else {
|
|
*new_tsh = DetectThresholdEntryAlloc(td, p, sid, gid);
|
|
|
|
ret = 1;
|
|
}
|
|
break;
|
|
}
|
|
case TYPE_THRESHOLD:
|
|
{
|
|
SCLogDebug("threshold");
|
|
|
|
if (lookup_tsh != NULL) {
|
|
if (TIMEVAL_DIFF_SEC(p->ts, lookup_tsh->tv1) < td->seconds) {
|
|
lookup_tsh->current_count++;
|
|
|
|
if (lookup_tsh->current_count >= td->count) {
|
|
ret = 1;
|
|
lookup_tsh->current_count = 0;
|
|
}
|
|
} else {
|
|
lookup_tsh->tv1 = p->ts;
|
|
lookup_tsh->current_count = 1;
|
|
}
|
|
} else {
|
|
if (td->count == 1) {
|
|
ret = 1;
|
|
} else {
|
|
*new_tsh = DetectThresholdEntryAlloc(td, p, sid, gid);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case TYPE_BOTH:
|
|
{
|
|
SCLogDebug("both");
|
|
|
|
if (lookup_tsh != NULL) {
|
|
if (TIMEVAL_DIFF_SEC(p->ts, lookup_tsh->tv1) < td->seconds) {
|
|
/* within time limit */
|
|
|
|
lookup_tsh->current_count++;
|
|
if (lookup_tsh->current_count == td->count) {
|
|
ret = 1;
|
|
} else if (lookup_tsh->current_count > td->count) {
|
|
/* silent match */
|
|
ret = 2;
|
|
}
|
|
} else {
|
|
/* expired, so reset */
|
|
lookup_tsh->tv1 = p->ts;
|
|
lookup_tsh->current_count = 1;
|
|
|
|
/* if we have a limit of 1, this is a match */
|
|
if (lookup_tsh->current_count == td->count) {
|
|
ret = 1;
|
|
}
|
|
}
|
|
} else {
|
|
*new_tsh = DetectThresholdEntryAlloc(td, p, sid, gid);
|
|
|
|
/* for the first match we return 1 to
|
|
* indicate we should alert */
|
|
if (td->count == 1) {
|
|
ret = 1;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
/* detection_filter */
|
|
case TYPE_DETECTION:
|
|
{
|
|
SCLogDebug("detection_filter");
|
|
|
|
if (lookup_tsh != NULL) {
|
|
if (TIMEVAL_DIFF_SEC(p->ts, lookup_tsh->tv1) < td->seconds) {
|
|
/* within timeout */
|
|
lookup_tsh->current_count++;
|
|
if (lookup_tsh->current_count > td->count) {
|
|
ret = 1;
|
|
}
|
|
} else {
|
|
/* expired, reset */
|
|
lookup_tsh->tv1 = p->ts;
|
|
lookup_tsh->current_count = 1;
|
|
}
|
|
} else {
|
|
*new_tsh = DetectThresholdEntryAlloc(td, p, sid, gid);
|
|
}
|
|
break;
|
|
}
|
|
/* rate_filter */
|
|
case TYPE_RATE:
|
|
{
|
|
SCLogDebug("rate_filter");
|
|
ret = 1;
|
|
if (lookup_tsh && IsThresholdReached(lookup_tsh, td, p->ts)) {
|
|
RateFilterSetAction(p, pa, td->new_action);
|
|
} else if (!lookup_tsh) {
|
|
*new_tsh = DetectThresholdEntryAlloc(td, p, sid, gid);
|
|
}
|
|
break;
|
|
}
|
|
/* case TYPE_SUPPRESS: is not handled here */
|
|
default:
|
|
SCLogError(SC_ERR_INVALID_VALUE, "type %d is not supported", td->type);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static int ThresholdHandlePacketIPPair(IPPair *pair, Packet *p, const DetectThresholdData *td,
|
|
uint32_t sid, uint32_t gid, PacketAlert *pa)
|
|
{
|
|
int ret = 0;
|
|
|
|
DetectThresholdEntry *lookup_tsh = ThresholdIPPairLookupEntry(pair, sid, gid);
|
|
SCLogDebug("ippair lookup_tsh %p sid %u gid %u", lookup_tsh, sid, gid);
|
|
|
|
DetectThresholdEntry *new_tsh = NULL;
|
|
ret = ThresholdHandlePacket(p, lookup_tsh, &new_tsh, td, sid, gid, pa);
|
|
if (new_tsh != NULL) {
|
|
AddEntryToIPPairStorage(pair, new_tsh, p->ts);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* \retval 2 silent match (no alert but apply actions)
|
|
* \retval 1 normal match
|
|
* \retval 0 no match
|
|
*/
|
|
static int ThresholdHandlePacketHost(Host *h, Packet *p, const DetectThresholdData *td,
|
|
uint32_t sid, uint32_t gid, PacketAlert *pa)
|
|
{
|
|
int ret = 0;
|
|
DetectThresholdEntry *lookup_tsh = ThresholdHostLookupEntry(h, sid, gid);
|
|
SCLogDebug("lookup_tsh %p sid %u gid %u", lookup_tsh, sid, gid);
|
|
|
|
DetectThresholdEntry *new_tsh = NULL;
|
|
ret = ThresholdHandlePacket(p, lookup_tsh, &new_tsh, td, sid, gid, pa);
|
|
if (new_tsh != NULL) {
|
|
AddEntryToHostStorage(h, new_tsh, p->ts);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static int ThresholdHandlePacketRule(DetectEngineCtx *de_ctx, Packet *p,
|
|
const DetectThresholdData *td, const Signature *s, PacketAlert *pa)
|
|
{
|
|
int ret = 0;
|
|
|
|
DetectThresholdEntry* lookup_tsh = (DetectThresholdEntry *)de_ctx->ths_ctx.th_entry[s->num];
|
|
SCLogDebug("by_rule lookup_tsh %p num %u", lookup_tsh, s->num);
|
|
|
|
DetectThresholdEntry *new_tsh = NULL;
|
|
ret = ThresholdHandlePacket(p, lookup_tsh, &new_tsh, td, s->id, s->gid, pa);
|
|
if (new_tsh != NULL) {
|
|
new_tsh->tv1 = p->ts;
|
|
new_tsh->current_count = 1;
|
|
new_tsh->tv_timeout = 0;
|
|
de_ctx->ths_ctx.th_entry[s->num] = new_tsh;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* \brief Make the threshold logic for signatures
|
|
*
|
|
* \param de_ctx Dectection Context
|
|
* \param tsh_ptr Threshold element
|
|
* \param p Packet structure
|
|
* \param s Signature structure
|
|
*
|
|
* \retval 2 silent match (no alert but apply actions)
|
|
* \retval 1 alert on this event
|
|
* \retval 0 do not alert on this event
|
|
*/
|
|
int PacketAlertThreshold(DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx,
|
|
const DetectThresholdData *td, Packet *p, const Signature *s, PacketAlert *pa)
|
|
{
|
|
SCEnter();
|
|
|
|
int ret = 0;
|
|
if (td == NULL) {
|
|
SCReturnInt(0);
|
|
}
|
|
|
|
if (td->type == TYPE_SUPPRESS) {
|
|
ret = ThresholdHandlePacketSuppress(p,td,s->id,s->gid);
|
|
} else if (td->track == TRACK_SRC) {
|
|
Host *src = HostGetHostFromHash(&p->src);
|
|
if (src) {
|
|
ret = ThresholdHandlePacketHost(src,p,td,s->id,s->gid,pa);
|
|
HostRelease(src);
|
|
}
|
|
} else if (td->track == TRACK_DST) {
|
|
Host *dst = HostGetHostFromHash(&p->dst);
|
|
if (dst) {
|
|
ret = ThresholdHandlePacketHost(dst,p,td,s->id,s->gid,pa);
|
|
HostRelease(dst);
|
|
}
|
|
} else if (td->track == TRACK_BOTH) {
|
|
IPPair *pair = IPPairGetIPPairFromHash(&p->src, &p->dst);
|
|
if (pair) {
|
|
ret = ThresholdHandlePacketIPPair(pair, p, td, s->id, s->gid, pa);
|
|
IPPairRelease(pair);
|
|
}
|
|
} else if (td->track == TRACK_RULE) {
|
|
SCMutexLock(&de_ctx->ths_ctx.threshold_table_lock);
|
|
ret = ThresholdHandlePacketRule(de_ctx,p,td,s,pa);
|
|
SCMutexUnlock(&de_ctx->ths_ctx.threshold_table_lock);
|
|
}
|
|
|
|
SCReturnInt(ret);
|
|
}
|
|
|
|
/**
|
|
* \brief Init threshold context hash tables
|
|
*
|
|
* \param de_ctx Dectection Context
|
|
*
|
|
*/
|
|
void ThresholdHashInit(DetectEngineCtx *de_ctx)
|
|
{
|
|
if (SCMutexInit(&de_ctx->ths_ctx.threshold_table_lock, NULL) != 0) {
|
|
FatalError(SC_ERR_FATAL,
|
|
"Threshold: Failed to initialize hash table mutex.");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* \brief Realloc threshold context hash tables
|
|
*
|
|
* \param de_ctx Detection Context
|
|
*/
|
|
void ThresholdHashRealloc(DetectEngineCtx *de_ctx)
|
|
{
|
|
/* Return if we are already big enough */
|
|
uint32_t num = de_ctx->signum + 1;
|
|
if (num <= de_ctx->ths_ctx.th_size)
|
|
return;
|
|
|
|
void *ptmp = SCRealloc(de_ctx->ths_ctx.th_entry, num * sizeof(DetectThresholdEntry *));
|
|
if (ptmp == NULL) {
|
|
SCLogWarning(SC_ERR_MEM_ALLOC, "Error allocating memory for rule thresholds"
|
|
" (tried to allocate %"PRIu32" th_entrys for rule tracking)", num);
|
|
} else {
|
|
de_ctx->ths_ctx.th_entry = ptmp;
|
|
for (uint32_t i = de_ctx->ths_ctx.th_size; i < num; ++i) {
|
|
de_ctx->ths_ctx.th_entry[i] = NULL;
|
|
}
|
|
de_ctx->ths_ctx.th_size = num;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* \brief Destroy threshold context hash tables
|
|
*
|
|
* \param de_ctx Dectection Context
|
|
*
|
|
*/
|
|
void ThresholdContextDestroy(DetectEngineCtx *de_ctx)
|
|
{
|
|
if (de_ctx->ths_ctx.th_entry != NULL)
|
|
SCFree(de_ctx->ths_ctx.th_entry);
|
|
SCMutexDestroy(&de_ctx->ths_ctx.threshold_table_lock);
|
|
}
|
|
|
|
/**
|
|
* \brief this function will free all the entries of a list
|
|
* DetectTagDataEntry
|
|
*
|
|
* \param td pointer to DetectTagDataEntryList
|
|
*/
|
|
void ThresholdListFree(void *ptr)
|
|
{
|
|
if (ptr != NULL) {
|
|
DetectThresholdEntry *entry = ptr;
|
|
|
|
while (entry != NULL) {
|
|
DetectThresholdEntry *next_entry = entry->next;
|
|
SCFree(entry);
|
|
entry = next_entry;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @}
|
|
*/
|