Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Threshold flow/v2 #10550

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
94 changes: 93 additions & 1 deletion src/detect-engine-threshold.c
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/* 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
Expand Down Expand Up @@ -69,6 +69,7 @@
#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 */
Expand Down Expand Up @@ -241,6 +242,70 @@ static DetectThresholdEntry *ThresholdHostLookupEntry(Host *h,
return e;
}

typedef struct FlowVarThreshold_ {
uint8_t type;
uint8_t pad[3];
uint32_t idx;
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 && gv->idx == 0)
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)
{
Expand Down Expand Up @@ -277,6 +342,7 @@ static int ThresholdHandlePacketSuppress(Packet *p,
}
break;
case TRACK_RULE:
case TRACK_FLOW:
default:
SCLogError("track mode %d is not supported", td->track);
break;
Expand Down Expand Up @@ -587,6 +653,28 @@ static int ThresholdHandlePacketRule(DetectEngineCtx *de_ctx, Packet *p,
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
*
Expand Down Expand Up @@ -633,6 +721,10 @@ int PacketAlertThreshold(DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx
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);
Expand Down
2 changes: 2 additions & 0 deletions src/detect-engine-threshold.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,4 +51,6 @@ int ThresholdHostTimeoutCheck(Host *, SCTime_t);
int ThresholdIPPairTimeoutCheck(IPPair *, SCTime_t);
void ThresholdListFree(void *ptr);

void FlowThresholdVarFree(void *ptr);

#endif /* __DETECT_ENGINE_THRESHOLD_H__ */
23 changes: 22 additions & 1 deletion src/detect-threshold.c
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,12 @@
#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;

Expand Down Expand Up @@ -183,6 +188,8 @@ static DetectThresholdData *DetectThresholdParse(const char *rawstr)
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)
Expand Down Expand Up @@ -342,6 +349,7 @@ DetectThresholdData *DetectThresholdDataCopy(DetectThresholdData *de)
#include "util-hashlist.h"
#include "packet.h"
#include "action-globals.h"

/**
* \test ThresholdTestParse01 is a test for a valid threshold options
*
Expand All @@ -360,6 +368,18 @@ static int ThresholdTestParse01(void)
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
*
Expand Down Expand Up @@ -1692,6 +1712,7 @@ static int DetectThresholdTestSig14(void)
static void ThresholdRegisterTests(void)
{
UtRegisterTest("ThresholdTestParse01", ThresholdTestParse01);
UtRegisterTest("ThresholdTestParseByFlow01", ThresholdTestParseByFlow01);
UtRegisterTest("ThresholdTestParse02", ThresholdTestParse02);
UtRegisterTest("ThresholdTestParse03", ThresholdTestParse03);
UtRegisterTest("ThresholdTestParse04", ThresholdTestParse04);
Expand Down
1 change: 1 addition & 0 deletions src/detect-threshold.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
#define TRACK_RULE 3
#define TRACK_EITHER 4 /**< either src or dst: only used by suppress */
#define TRACK_BOTH 5 /* used by rate_filter to match detections by both src and dst addresses */
#define TRACK_FLOW 6 /**< track by flow */

/* Get the new action to take */
#define TH_ACTION_ALERT 0x01
Expand Down
10 changes: 8 additions & 2 deletions src/util-threshold-config.c
Original file line number Diff line number Diff line change
Expand Up @@ -67,11 +67,15 @@ static FILE *g_ut_threshold_fp = NULL;
#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|by_both|by_rule)\\s*," \
"^,\\s*type\\s*(limit|both|threshold)\\s*,\\s*track\\s*(by_dst|by_src|by_both|by_rule|by_" \
"flow)\\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*$"
#define DETECT_RATE_REGEX \
"^,\\s*track\\s*(by_dst|by_src|by_both|by_rule|by_flow)\\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:
Expand Down Expand Up @@ -796,6 +800,8 @@ static int ParseThresholdRule(const DetectEngineCtx *de_ctx, char *rawstr, uint3
}
else if (strcasecmp(th_track,"by_rule") == 0)
parsed_track = TRACK_RULE;
else if (strcasecmp(th_track, "by_flow") == 0)
parsed_track = TRACK_FLOW;
else {
SCLogError("Invalid track parameter %s in %s", th_track, rawstr);
goto error;
Expand Down
12 changes: 9 additions & 3 deletions src/util-var.c
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/* Copyright (C) 2007-2013 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
Expand All @@ -25,6 +25,7 @@

#include "suricata-common.h"
#include "detect.h"
#include "detect-engine-threshold.h"

#include "util-var.h"

Expand All @@ -33,7 +34,7 @@
#include "pkt-var.h"
#include "host-bit.h"
#include "ippair-bit.h"

#include "util-validate.h"
#include "util-debug.h"

void XBitFree(XBit *fb)
Expand Down Expand Up @@ -67,6 +68,10 @@ void GenericVarFree(GenericVar *gv)
XBitFree(fb);
break;
}
case DETECT_THRESHOLD: {
FlowThresholdVarFree(gv);
break;
}
case DETECT_FLOWVAR:
{
FlowVar *fv = (FlowVar *)gv;
Expand All @@ -81,7 +86,8 @@ void GenericVarFree(GenericVar *gv)
}
default:
{
printf("ERROR: GenericVarFree unknown type %" PRIu32 "\n", gv->type);
SCLogDebug("GenericVarFree unknown type %" PRIu32, gv->type);
DEBUG_VALIDATE_BUG_ON(1);
break;
}
}
Expand Down
1 change: 1 addition & 0 deletions src/util-var.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ enum VarTypes {
VAR_TYPE_FLOW_BIT,
VAR_TYPE_FLOW_INT,
VAR_TYPE_FLOW_VAR,
VAR_TYPE_FLOW_THRESHOLD,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was expecting VarNameStore* ops corresponding to this but I see nothing. Could you please tell why was this type added?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good question, I will look into it. It seems the current use of these types is a bit inconsistent.


VAR_TYPE_HOST_BIT,
VAR_TYPE_HOST_INT,
Expand Down
Loading