|
|
|
|
@ -43,6 +43,9 @@
|
|
|
|
|
#include "host.h"
|
|
|
|
|
#include "host-storage.h"
|
|
|
|
|
|
|
|
|
|
#include "ippair.h"
|
|
|
|
|
#include "ippair-storage.h"
|
|
|
|
|
|
|
|
|
|
#include "detect-parse.h"
|
|
|
|
|
#include "detect-engine-sigorder.h"
|
|
|
|
|
|
|
|
|
|
@ -66,25 +69,36 @@
|
|
|
|
|
#include "util-var-name.h"
|
|
|
|
|
#include "tm-threads.h"
|
|
|
|
|
|
|
|
|
|
static int threshold_id = -1; /**< host storage id for thresholds */
|
|
|
|
|
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 threshold_id;
|
|
|
|
|
return host_threshold_id;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ThresholdInit(void)
|
|
|
|
|
{
|
|
|
|
|
threshold_id = HostStorageRegister("threshold", sizeof(void *), NULL, ThresholdListFree);
|
|
|
|
|
if (threshold_id == -1) {
|
|
|
|
|
host_threshold_id = HostStorageRegister("threshold", sizeof(void *), NULL, ThresholdListFree);
|
|
|
|
|
if (host_threshold_id == -1) {
|
|
|
|
|
SCLogError(SC_ERR_HOST_INIT, "Can't initiate host storage for thresholding");
|
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
|
}
|
|
|
|
|
ippair_threshold_id = IPPairStorageRegister("threshold", sizeof(void *), NULL, ThresholdListFree);
|
|
|
|
|
if (ippair_threshold_id == -1) {
|
|
|
|
|
SCLogError(SC_ERR_HOST_INIT, "Can't initiate IP pair storage for thresholding");
|
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int ThresholdHostHasThreshold(Host *host)
|
|
|
|
|
{
|
|
|
|
|
return HostGetStorageById(host, threshold_id) ? 1 : 0;
|
|
|
|
|
return HostGetStorageById(host, host_threshold_id) ? 1 : 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int ThresholdIPPairHasThreshold(IPPair *pair)
|
|
|
|
|
{
|
|
|
|
|
return IPPairGetStorageById(pair, ippair_threshold_id) ? 1 : 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
@ -143,49 +157,61 @@ const DetectThresholdData *SigGetThresholdTypeIter(const Signature *sig,
|
|
|
|
|
/**
|
|
|
|
|
* \brief Remove timeout threshold hash elements
|
|
|
|
|
*
|
|
|
|
|
* \param de_ctx Dectection Context
|
|
|
|
|
* \param head Current head element of storage
|
|
|
|
|
* \param tv Current time
|
|
|
|
|
*
|
|
|
|
|
* \retval DetectThresholdEntry Return new head element or NULL if all expired
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
int ThresholdTimeoutCheck(Host *host, struct timeval *tv)
|
|
|
|
|
static DetectThresholdEntry* ThresholdTimeoutCheck(DetectThresholdEntry *head, struct timeval *tv)
|
|
|
|
|
{
|
|
|
|
|
DetectThresholdEntry *tde = NULL;
|
|
|
|
|
DetectThresholdEntry *tmp = NULL;
|
|
|
|
|
DetectThresholdEntry *tmp = head;
|
|
|
|
|
DetectThresholdEntry *prev = NULL;
|
|
|
|
|
int retval = 1;
|
|
|
|
|
|
|
|
|
|
tmp = HostGetStorageById(host, threshold_id);
|
|
|
|
|
if (tmp == NULL)
|
|
|
|
|
return 1;
|
|
|
|
|
DetectThresholdEntry *new_head = head;
|
|
|
|
|
|
|
|
|
|
prev = NULL;
|
|
|
|
|
while (tmp != NULL) {
|
|
|
|
|
if ((tv->tv_sec - tmp->tv_sec1) <= tmp->seconds) {
|
|
|
|
|
prev = tmp;
|
|
|
|
|
tmp = tmp->next;
|
|
|
|
|
retval = 0;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* timed out */
|
|
|
|
|
|
|
|
|
|
DetectThresholdEntry *tde = tmp;
|
|
|
|
|
if (prev != NULL) {
|
|
|
|
|
prev->next = tmp->next;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
new_head = tmp->next;
|
|
|
|
|
}
|
|
|
|
|
tmp = tde->next;
|
|
|
|
|
SCFree(tde);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
tde = tmp;
|
|
|
|
|
tmp = tde->next;
|
|
|
|
|
|
|
|
|
|
SCFree(tde);
|
|
|
|
|
} else {
|
|
|
|
|
HostSetStorageById(host, threshold_id, tmp->next);
|
|
|
|
|
tde = tmp;
|
|
|
|
|
tmp = tde->next;
|
|
|
|
|
return new_head;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SCFree(tde);
|
|
|
|
|
}
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return retval;
|
|
|
|
|
|
|
|
|
|
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 inline DetectThresholdEntry *
|
|
|
|
|
@ -213,7 +239,19 @@ static DetectThresholdEntry *ThresholdHostLookupEntry(Host *h, uint32_t sid, uin
|
|
|
|
|
{
|
|
|
|
|
DetectThresholdEntry *e;
|
|
|
|
|
|
|
|
|
|
for (e = HostGetStorageById(h, threshold_id); e != NULL; e = e->next) {
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
@ -281,6 +319,106 @@ static inline void RateFilterSetAction(Packet *p, PacketAlert *pa, uint8_t new_a
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* \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, uint32_t 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 - 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 ((packet_time - lookup_tsh->tv_sec1) < 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;
|
|
|
|
|
ret = 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
lookup_tsh->tv_sec1 = packet_time;
|
|
|
|
|
lookup_tsh->current_count = 1;
|
|
|
|
|
}
|
|
|
|
|
} /* else - if (lookup_tsh->tv_timeout != 0) */
|
|
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void AddEntryToHostStorage(Host *h, DetectThresholdEntry *e, uint32_t packet_time)
|
|
|
|
|
{
|
|
|
|
|
if (h && e) {
|
|
|
|
|
e->current_count = 1;
|
|
|
|
|
e->tv_sec1 = 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, uint32_t packet_time)
|
|
|
|
|
{
|
|
|
|
|
if (pair && e) {
|
|
|
|
|
e->current_count = 1;
|
|
|
|
|
e->tv_sec1 = packet_time;
|
|
|
|
|
e->tv_timeout = 0;
|
|
|
|
|
e->next = IPPairGetStorageById(pair, ippair_threshold_id);
|
|
|
|
|
IPPairSetStorageById(pair, ippair_threshold_id, e);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
|
|
|
|
|
switch (td->type) {
|
|
|
|
|
case TYPE_RATE:
|
|
|
|
|
{
|
|
|
|
|
SCLogDebug("rate_filter");
|
|
|
|
|
ret = 1;
|
|
|
|
|
if (lookup_tsh && IsThresholdReached(lookup_tsh, td, p->ts.tv_sec)) {
|
|
|
|
|
RateFilterSetAction(p, pa, td->new_action);
|
|
|
|
|
} else if (!lookup_tsh) {
|
|
|
|
|
DetectThresholdEntry *e = DetectThresholdEntryAlloc(td, p, sid, gid);
|
|
|
|
|
AddEntryToIPPairStorage(pair, e, p->ts.tv_sec);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
default:
|
|
|
|
|
{
|
|
|
|
|
SCLogError(SC_ERR_INVALID_VALUE, "type %d is not supported", td->type);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* \retval 2 silent match (no alert but apply actions)
|
|
|
|
|
* \retval 1 normal match
|
|
|
|
|
@ -325,8 +463,8 @@ static int ThresholdHandlePacketHost(Host *h, Packet *p, const DetectThresholdDa
|
|
|
|
|
|
|
|
|
|
ret = 1;
|
|
|
|
|
|
|
|
|
|
e->next = HostGetStorageById(h, threshold_id);
|
|
|
|
|
HostSetStorageById(h, threshold_id, e);
|
|
|
|
|
e->next = HostGetStorageById(h, host_threshold_id);
|
|
|
|
|
HostSetStorageById(h, host_threshold_id, e);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
@ -358,8 +496,8 @@ static int ThresholdHandlePacketHost(Host *h, Packet *p, const DetectThresholdDa
|
|
|
|
|
e->current_count = 1;
|
|
|
|
|
e->tv_sec1 = p->ts.tv_sec;
|
|
|
|
|
|
|
|
|
|
e->next = HostGetStorageById(h, threshold_id);
|
|
|
|
|
HostSetStorageById(h, threshold_id, e);
|
|
|
|
|
e->next = HostGetStorageById(h, host_threshold_id);
|
|
|
|
|
HostSetStorageById(h, host_threshold_id, e);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
@ -398,8 +536,8 @@ static int ThresholdHandlePacketHost(Host *h, Packet *p, const DetectThresholdDa
|
|
|
|
|
e->current_count = 1;
|
|
|
|
|
e->tv_sec1 = p->ts.tv_sec;
|
|
|
|
|
|
|
|
|
|
e->next = HostGetStorageById(h, threshold_id);
|
|
|
|
|
HostSetStorageById(h, threshold_id, e);
|
|
|
|
|
e->next = HostGetStorageById(h, host_threshold_id);
|
|
|
|
|
HostSetStorageById(h, host_threshold_id, e);
|
|
|
|
|
|
|
|
|
|
/* for the first match we return 1 to
|
|
|
|
|
* indicate we should alert */
|
|
|
|
|
@ -442,8 +580,8 @@ static int ThresholdHandlePacketHost(Host *h, Packet *p, const DetectThresholdDa
|
|
|
|
|
e->tv_sec1 = p->ts.tv_sec;
|
|
|
|
|
e->tv_usec1 = p->ts.tv_usec;
|
|
|
|
|
|
|
|
|
|
e->next = HostGetStorageById(h, threshold_id);
|
|
|
|
|
HostSetStorageById(h, threshold_id, e);
|
|
|
|
|
e->next = HostGetStorageById(h, host_threshold_id);
|
|
|
|
|
HostSetStorageById(h, host_threshold_id, e);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
@ -451,56 +589,12 @@ static int ThresholdHandlePacketHost(Host *h, Packet *p, const DetectThresholdDa
|
|
|
|
|
case TYPE_RATE:
|
|
|
|
|
{
|
|
|
|
|
SCLogDebug("rate_filter");
|
|
|
|
|
|
|
|
|
|
ret = 1;
|
|
|
|
|
|
|
|
|
|
if (lookup_tsh != NULL) {
|
|
|
|
|
/* Check if we have a timeout enabled, if so,
|
|
|
|
|
* we still matching (and enabling the new_action) */
|
|
|
|
|
if (lookup_tsh->tv_timeout != 0) {
|
|
|
|
|
if ((p->ts.tv_sec - lookup_tsh->tv_timeout) > td->timeout) {
|
|
|
|
|
/* Ok, we are done, timeout reached */
|
|
|
|
|
lookup_tsh->tv_timeout = 0;
|
|
|
|
|
} else {
|
|
|
|
|
/* Already matching */
|
|
|
|
|
/* Take the action to perform */
|
|
|
|
|
RateFilterSetAction(p, pa, td->new_action);
|
|
|
|
|
ret = 1;
|
|
|
|
|
} /* else - if ((p->ts.tv_sec - lookup_tsh->tv_timeout) > td->timeout) */
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
/* Update the matching state with the timeout interval */
|
|
|
|
|
if ( (p->ts.tv_sec - lookup_tsh->tv_sec1) < 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 = p->ts.tv_sec;
|
|
|
|
|
/* Take the action to perform */
|
|
|
|
|
RateFilterSetAction(p, pa, td->new_action);
|
|
|
|
|
ret = 1;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
lookup_tsh->tv_sec1 = p->ts.tv_sec;
|
|
|
|
|
lookup_tsh->current_count = 1;
|
|
|
|
|
}
|
|
|
|
|
} /* else - if (lookup_tsh->tv_timeout != 0) */
|
|
|
|
|
} else {
|
|
|
|
|
if (td->count == 1) {
|
|
|
|
|
ret = 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (lookup_tsh && IsThresholdReached(lookup_tsh, td, p->ts.tv_sec)) {
|
|
|
|
|
RateFilterSetAction(p, pa, td->new_action);
|
|
|
|
|
} else if (!lookup_tsh) {
|
|
|
|
|
DetectThresholdEntry *e = DetectThresholdEntryAlloc(td, p, sid, gid);
|
|
|
|
|
if (e == NULL) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
e->current_count = 1;
|
|
|
|
|
e->tv_sec1 = p->ts.tv_sec;
|
|
|
|
|
e->tv_timeout = 0;
|
|
|
|
|
|
|
|
|
|
e->next = HostGetStorageById(h, threshold_id);
|
|
|
|
|
HostSetStorageById(h, threshold_id, e);
|
|
|
|
|
AddEntryToHostStorage(h, e, p->ts.tv_sec);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
@ -603,6 +697,12 @@ int PacketAlertThreshold(DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx
|
|
|
|
|
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);
|
|
|
|
|
|