From 33a3448f45a9874bd3424ee27e3702b82b6870ba Mon Sep 17 00:00:00 2001 From: Shivani Bhardwaj Date: Tue, 2 Dec 2025 13:44:12 +0530 Subject: [PATCH] detect/flow: add support for elephant flow detection Expose elephant flow flags on a flow via rule language. The rule keyword is defined as "flow.elephant: ;" where the can expand to: - toserver - toclient - either - both Feature 8117 --- src/Makefile.am | 2 + src/detect-engine-analyzer.c | 20 +++++ src/detect-engine-register.c | 2 + src/detect-engine-register.h | 1 + src/detect-flow-elephant.c | 144 +++++++++++++++++++++++++++++++++++ src/detect-flow-elephant.h | 23 ++++++ 6 files changed, 192 insertions(+) create mode 100644 src/detect-flow-elephant.c create mode 100644 src/detect-flow-elephant.h diff --git a/src/Makefile.am b/src/Makefile.am index 66da2d1df0..6a717a48d7 100755 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -171,6 +171,7 @@ noinst_HEADERS = \ detect-flow-age.h \ detect-flow-pkts.h \ detect-flow.h \ + detect-flow-elephant.h \ detect-flowbits.h \ detect-flowint.h \ detect-flowvar.h \ @@ -771,6 +772,7 @@ libsuricata_c_a_SOURCES = \ detect-flow-age.c \ detect-flow-pkts.c \ detect-flow.c \ + detect-flow-elephant.c \ detect-flowbits.c \ detect-flowint.c \ detect-flowvar.c \ diff --git a/src/detect-engine-analyzer.c b/src/detect-engine-analyzer.c index 960e6b6d0e..ec8c82f031 100644 --- a/src/detect-engine-analyzer.c +++ b/src/detect-engine-analyzer.c @@ -1076,6 +1076,26 @@ static void DumpMatches(RuleAnalyzer *ctx, SCJsonBuilder *js, const SigMatchData SCJbClose(js); break; } + case DETECT_FLOW_ELEPHANT: { + const uint8_t *dfd = (const uint8_t *)smd->ctx; + SCJbOpenObject(js, "flow_elephant"); + switch (*dfd) { + case DETECT_FLOW_TOSERVER: + SCJbSetString(js, "dir", "toserver"); + break; + case DETECT_FLOW_TOCLIENT: + SCJbSetString(js, "dir", "toclient"); + break; + case DETECT_FLOW_TOEITHER: + SCJbSetString(js, "dir", "either"); + break; + case DETECT_FLOW_TOBOTH: + SCJbSetString(js, "dir", "both"); + break; + } + SCJbClose(js); + break; + } } SCJbClose(js); diff --git a/src/detect-engine-register.c b/src/detect-engine-register.c index 30d7d04a20..36de385c3e 100644 --- a/src/detect-engine-register.c +++ b/src/detect-engine-register.c @@ -120,6 +120,7 @@ #include "detect-flow.h" #include "detect-flow-age.h" #include "detect-flow-pkts.h" +#include "detect-flow-elephant.h" #include "detect-requires.h" #include "detect-tcp-window.h" #include "detect-tcp-wscale.h" @@ -660,6 +661,7 @@ void SigTableSetup(void) DetectFlowBytesRegister(); DetectFlowBytesToServerRegister(); DetectFlowBytesToClientRegister(); + DetectFlowElephantRegister(); DetectRequiresRegister(); DetectWindowRegister(); DetectRpcRegister(); diff --git a/src/detect-engine-register.h b/src/detect-engine-register.h index bce512dca2..24ada0d1c0 100644 --- a/src/detect-engine-register.h +++ b/src/detect-engine-register.h @@ -132,6 +132,7 @@ enum DetectKeywordId { DETECT_FLOW_BYTES, DETECT_FLOW_BYTES_TO_SERVER, DETECT_FLOW_BYTES_TO_CLIENT, + DETECT_FLOW_ELEPHANT, DETECT_REQUIRES, diff --git a/src/detect-flow-elephant.c b/src/detect-flow-elephant.c new file mode 100644 index 0000000000..8e8025b5ec --- /dev/null +++ b/src/detect-flow-elephant.c @@ -0,0 +1,144 @@ +/* Copyright (C) 2025 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. + */ + +/** + * \file + * + * \author Shivani Bhardwaj + * + * Elephant flow detection. + */ + +#include "suricata-common.h" +#include "rust.h" +#include "flow.h" +#include "detect-flow-elephant.h" +#include "detect-engine.h" +#include "detect-engine-prefilter.h" +#include "detect-engine-prefilter-common.h" +#include "detect-parse.h" + +static int DetectFlowElephantMatchAux(Packet *p, const DetectFlowDir *fdir) +{ + switch (*fdir) { + case DETECT_FLOW_TOSERVER: + return (p->flow->flags & FLOW_IS_ELEPHANT_TOSERVER); + case DETECT_FLOW_TOCLIENT: + return (p->flow->flags & FLOW_IS_ELEPHANT_TOCLIENT); + case DETECT_FLOW_TOEITHER: + return ((p->flow->flags & FLOW_IS_ELEPHANT_TOSERVER) || + (p->flow->flags & FLOW_IS_ELEPHANT_TOCLIENT)); + case DETECT_FLOW_TOBOTH: + return ((p->flow->flags & (FLOW_IS_ELEPHANT_TOSERVER | FLOW_IS_ELEPHANT_TOCLIENT)) == + (FLOW_IS_ELEPHANT_TOSERVER | FLOW_IS_ELEPHANT_TOCLIENT)); + } + + DEBUG_VALIDATE_BUG_ON(1); + return -1; +} + +static int DetectFlowElephantMatch( + DetectEngineThreadCtx *det_ctx, Packet *p, const Signature *s, const SigMatchCtx *ctx) +{ + if (p->flow == NULL) { + return 0; + } + + DetectFlowDir *fdir = (DetectFlowDir *)ctx; + + return DetectFlowElephantMatchAux(p, fdir); +} + +static int DetectFlowElephantSetup(DetectEngineCtx *de_ctx, Signature *s, const char *rawstr) +{ + DetectFlowDir *fdir = SCDetectFlowDir(rawstr); + if (fdir == NULL) + return -1; + + if (SCSigMatchAppendSMToList(de_ctx, s, DETECT_FLOW_ELEPHANT, (SigMatchCtx *)fdir, + DETECT_SM_LIST_MATCH) == NULL) { + return -1; + } + s->flags |= SIG_FLAG_REQUIRE_PACKET; + + return 0; +} + +static void DetectFlowElephantFree(DetectEngineCtx *de_ctx, void *dfd) +{ + SCDetectFlowDirFree(dfd); +} + +/* prefilter code */ + +static void PrefilterPacketFlowElephantMatch( + DetectEngineThreadCtx *det_ctx, Packet *p, const void *pectx) +{ + if (p->flow == NULL) + return; + + /* during setup Suricata will automatically see if there is another + * check that can be added: alproto, sport or dport */ + const PrefilterPacketHeaderCtx *ctx = pectx; + if (!PrefilterPacketHeaderExtraMatch(ctx, p)) + return; + + DetectFlowDir dfd = ctx->v1.u8[0]; + /* if we match, add all the sigs that use this prefilter. This means + * that these will be inspected further */ + if (DetectFlowElephantMatchAux(p, &dfd) > 0) { + PrefilterAddSids(&det_ctx->pmq, ctx->sigs_array, ctx->sigs_cnt); + } +} + +static void PrefilterPacketFlowElephantSet(PrefilterPacketHeaderValue *v, void *smctx) +{ + const DetectFlowDir *a = smctx; + v->u8[0] = *a; +} + +static bool PrefilterPacketFlowElephantCompare(PrefilterPacketHeaderValue v, void *smctx) +{ + const DetectFlowDir *a = smctx; + if (v.u8[0] == *a) + return true; + return false; +} + +static int PrefilterSetupFlowElephant(DetectEngineCtx *de_ctx, SigGroupHead *sgh) +{ + return PrefilterSetupPacketHeader(de_ctx, sgh, DETECT_FLOW_ELEPHANT, SIG_MASK_REQUIRE_FLOW, + PrefilterPacketFlowElephantSet, PrefilterPacketFlowElephantCompare, + PrefilterPacketFlowElephantMatch); +} + +static bool PrefilterFlowElephantIsPrefilterable(const Signature *s) +{ + return PrefilterIsPrefilterableById(s, DETECT_FLOW_ELEPHANT); +} + +void DetectFlowElephantRegister(void) +{ + sigmatch_table[DETECT_FLOW_ELEPHANT].name = "flow.elephant"; + sigmatch_table[DETECT_FLOW_ELEPHANT].desc = "match elephant flow"; + sigmatch_table[DETECT_FLOW_ELEPHANT].url = "/rules/flow-keywords.html#flow-elephant"; + sigmatch_table[DETECT_FLOW_ELEPHANT].Match = DetectFlowElephantMatch; + sigmatch_table[DETECT_FLOW_ELEPHANT].Setup = DetectFlowElephantSetup; + sigmatch_table[DETECT_FLOW_ELEPHANT].Free = DetectFlowElephantFree; + sigmatch_table[DETECT_FLOW_ELEPHANT].SupportsPrefilter = PrefilterFlowElephantIsPrefilterable; + sigmatch_table[DETECT_FLOW_ELEPHANT].SetupPrefilter = PrefilterSetupFlowElephant; +} diff --git a/src/detect-flow-elephant.h b/src/detect-flow-elephant.h new file mode 100644 index 0000000000..78f1af9450 --- /dev/null +++ b/src/detect-flow-elephant.h @@ -0,0 +1,23 @@ +/* Copyright (C) 2025 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. + */ + +#ifndef SURICATA_DETECT_FLOW_ELEPHANT_H +#define SURICATA_DETECT_FLOW_ELEPHANT_H + +void DetectFlowElephantRegister(void); + +#endif /* SURICATA_DETECT_FLOW_ELEPHANT_H */