-/* Copyright (C) 2007-2021 Open Information Security Foundation
+/* Copyright (C) 2007-2024 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
#include "tm-threads.h"
#include "action-globals.h"
+#include "util-validate.h"
static HostStorageId host_threshold_id = { .id = -1 }; /**< host storage id for thresholds */
static IPPairStorageId ippair_threshold_id = { .id = -1 }; /**< ip pair storage id for thresholds */
return e;
}
+/** struct for storing per flow thresholds. This will be stored in the Flow::flowvar list, so it
+ * needs to follow the GenericVar header format. */
+typedef struct FlowVarThreshold_ {
+ uint8_t type;
+ uint8_t pad[7];
+ struct GenericVar_ *next;
+ DetectThresholdEntry *thresholds;
+} FlowVarThreshold;
+
+void FlowThresholdVarFree(void *ptr)
+{
+ FlowVarThreshold *t = ptr;
+ ThresholdListFree(t->thresholds);
+ SCFree(t);
+}
+
+static FlowVarThreshold *FlowThresholdVarGet(Flow *f)
+{
+ if (f == NULL)
+ return NULL;
+
+ for (GenericVar *gv = f->flowvar; gv != NULL; gv = gv->next) {
+ if (gv->type == DETECT_THRESHOLD)
+ return (FlowVarThreshold *)gv;
+ }
+
+ return NULL;
+}
+
+static DetectThresholdEntry *ThresholdFlowLookupEntry(Flow *f, uint32_t sid, uint32_t gid)
+{
+ FlowVarThreshold *t = FlowThresholdVarGet(f);
+ if (t == NULL)
+ return NULL;
+
+ for (DetectThresholdEntry *e = t->thresholds; e != NULL; e = e->next) {
+ if (e->sid == sid && e->gid == gid) {
+ return e;
+ }
+ }
+ return NULL;
+}
+
+static int AddEntryToFlow(Flow *f, DetectThresholdEntry *e, SCTime_t packet_time)
+{
+ DEBUG_VALIDATE_BUG_ON(e == NULL);
+
+ FlowVarThreshold *t = FlowThresholdVarGet(f);
+ if (t == NULL) {
+ t = SCCalloc(1, sizeof(*t));
+ if (t == NULL) {
+ return -1;
+ }
+ t->type = DETECT_THRESHOLD;
+ GenericVarAppend(&f->flowvar, (GenericVar *)t);
+ }
+
+ e->current_count = 1;
+ e->tv1 = packet_time;
+ e->tv_timeout = 0;
+ e->next = t->thresholds;
+ t->thresholds = e;
+ return 0;
+}
+
static DetectThresholdEntry *ThresholdIPPairLookupEntry(IPPair *pair,
uint32_t sid, uint32_t gid)
{
return ret;
}
+/**
+ * \retval 2 silent match (no alert but apply actions)
+ * \retval 1 normal match
+ * \retval 0 no match
+ */
+static int ThresholdHandlePacketFlow(Flow *f, Packet *p, const DetectThresholdData *td,
+ uint32_t sid, uint32_t gid, PacketAlert *pa)
+{
+ int ret = 0;
+ DetectThresholdEntry *lookup_tsh = ThresholdFlowLookupEntry(f, 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) {
+ if (AddEntryToFlow(f, new_tsh, p->ts) == -1) {
+ SCFree(new_tsh);
+ }
+ }
+ return ret;
+}
+
/**
* \brief Make the threshold logic for signatures
*
SCMutexLock(&de_ctx->ths_ctx.threshold_table_lock);
ret = ThresholdHandlePacketRule(de_ctx,p,td,s,pa);
SCMutexUnlock(&de_ctx->ths_ctx.threshold_table_lock);
+ } else if (td->track == TRACK_FLOW) {
+ if (p->flow) {
+ ret = ThresholdHandlePacketFlow(p->flow, p, td, s->id, s->gid, pa);
+ }
}
SCReturnInt(ret);
#include "util-cpu.h"
#endif
-#define PARSE_REGEX "^\\s*(track|type|count|seconds)\\s+(limit|both|threshold|by_dst|by_src|by_both|by_rule|\\d+)\\s*,\\s*(track|type|count|seconds)\\s+(limit|both|threshold|by_dst|by_src|by_both|by_rule|\\d+)\\s*,\\s*(track|type|count|seconds)\\s+(limit|both|threshold|by_dst|by_src|by_both|by_rule|\\d+)\\s*,\\s*(track|type|count|seconds)\\s+(limit|both|threshold|by_dst|by_src|by_both|by_rule|\\d+)\\s*"
+#define PARSE_REGEX \
+ "^\\s*(track|type|count|seconds)\\s+(limit|both|threshold|by_dst|by_src|by_both|by_rule|by_" \
+ "flow|\\d+)\\s*,\\s*(track|type|count|seconds)\\s+(limit|both|threshold|by_dst|by_src|by_" \
+ "both|by_rule|by_flow|\\d+)\\s*,\\s*(track|type|count|seconds)\\s+(limit|both|threshold|by_" \
+ "dst|by_src|by_both|by_rule|by_flow|\\d+)\\s*,\\s*(track|type|count|seconds)\\s+(limit|both|" \
+ "threshold|by_dst|by_src|by_both|by_rule|by_flow|\\d+)\\s*"
static DetectParseRegex parse_regex;
de->track = TRACK_BOTH;
if (strncasecmp(args[i],"by_rule",strlen("by_rule")) == 0)
de->track = TRACK_RULE;
+ if (strncasecmp(args[i], "by_flow", strlen("by_flow")) == 0)
+ de->track = TRACK_FLOW;
if (strncasecmp(args[i],"count",strlen("count")) == 0)
count_pos = i+1;
if (strncasecmp(args[i],"seconds",strlen("seconds")) == 0)
#include "util-hashlist.h"
#include "packet.h"
#include "action-globals.h"
+
/**
* \test ThresholdTestParse01 is a test for a valid threshold options
*
return 0;
}
+static int ThresholdTestParseByFlow01(void)
+{
+ DetectThresholdData *de = DetectThresholdParse("type limit,track by_flow,count 1,seconds 60");
+ FAIL_IF_NULL(de);
+ FAIL_IF_NOT(de->type == TYPE_LIMIT);
+ FAIL_IF_NOT(de->track == TRACK_FLOW);
+ FAIL_IF_NOT(de->count == 1);
+ FAIL_IF_NOT(de->seconds == 60);
+ DetectThresholdFree(NULL, de);
+ PASS;
+}
+
/**
* \test ThresholdTestParse02 is a test for a invalid threshold options
*
static void ThresholdRegisterTests(void)
{
UtRegisterTest("ThresholdTestParse01", ThresholdTestParse01);
+ UtRegisterTest("ThresholdTestParseByFlow01", ThresholdTestParseByFlow01);
UtRegisterTest("ThresholdTestParse02", ThresholdTestParse02);
UtRegisterTest("ThresholdTestParse03", ThresholdTestParse03);
UtRegisterTest("ThresholdTestParse04", ThresholdTestParse04);