From eac315fcf61059359a29a19f326d85f26be6595f Mon Sep 17 00:00:00 2001 From: Shivani Bhardwaj Date: Fri, 29 Nov 2024 14:01:12 +0530 Subject: [PATCH 1/3] flow/pkts: make syntax cleaner and compact Currently, the syntax includes direction as a part of the keyword which is against how usually keywords are done. By making direction as a mandatory argument, it is possible to make the syntax cleaner and the implementation more compact and easily extendable. Pros: - Registration table sees lesser entries - If the options have to be extended, it can be done trivially - In accordance w existing keyword implementations Note that this commit also retains the existing direction specific keywords. --- src/detect-engine-register.c | 6 +- src/detect-engine-register.h | 6 +- src/detect-flow-pkts.c | 419 ++++++++++++++++++++++++----------- src/detect-flow-pkts.h | 8 +- 4 files changed, 302 insertions(+), 137 deletions(-) diff --git a/src/detect-engine-register.c b/src/detect-engine-register.c index 9bddf0fd8437..82153807ad58 100644 --- a/src/detect-engine-register.c +++ b/src/detect-engine-register.c @@ -581,10 +581,12 @@ void SigTableSetup(void) DetectReplaceRegister(); DetectFlowRegister(); DetectFlowAgeRegister(); - DetectFlowPktsToClientRegister(); + DetectFlowPktsRegister(); DetectFlowPktsToServerRegister(); - DetectFlowBytesToClientRegister(); + DetectFlowPktsToClientRegister(); + DetectFlowBytesRegister(); DetectFlowBytesToServerRegister(); + DetectFlowBytesToClientRegister(); DetectRequiresRegister(); DetectWindowRegister(); DetectRpcRegister(); diff --git a/src/detect-engine-register.h b/src/detect-engine-register.h index b7a029998555..f46bf688f0f8 100644 --- a/src/detect-engine-register.h +++ b/src/detect-engine-register.h @@ -127,10 +127,12 @@ enum DetectKeywordId { DETECT_FRAME, DETECT_FLOW_AGE, - DETECT_FLOW_PKTS_TO_CLIENT, + DETECT_FLOW_PKTS, DETECT_FLOW_PKTS_TO_SERVER, - DETECT_FLOW_BYTES_TO_CLIENT, + DETECT_FLOW_PKTS_TO_CLIENT, + DETECT_FLOW_BYTES, DETECT_FLOW_BYTES_TO_SERVER, + DETECT_FLOW_BYTES_TO_CLIENT, DETECT_REQUIRES, diff --git a/src/detect-flow-pkts.c b/src/detect-flow-pkts.c index ef8fed369cd9..842c78c993d4 100644 --- a/src/detect-flow-pkts.c +++ b/src/detect-flow-pkts.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2023 Open Information Security Foundation +/* Copyright (C) 2023-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 @@ -23,32 +23,58 @@ #include "detect-engine-uint.h" #include "detect-parse.h" -static int DetectFlowPktsToClientMatch( +enum FlowDirection { + DETECT_FLOW_TOSERVER = 1, + DETECT_FLOW_TOCLIENT, +}; + +typedef struct DetectFlow_ { + DetectU32Data *pkt_data; + DetectU64Data *byte_data; + enum FlowDirection dir; +} DetectFlow; + +static int DetectFlowPktsMatch( DetectEngineThreadCtx *det_ctx, Packet *p, const Signature *s, const SigMatchCtx *ctx) { if (p->flow == NULL) { return 0; } - uint32_t nb = p->flow->tosrcpktcnt; - const DetectU32Data *du32 = (const DetectU32Data *)ctx; - return DetectU32Match(nb, du32); + const DetectFlow *df = (const DetectFlow *)ctx; + if (df->dir == DETECT_FLOW_TOSERVER) { + return DetectU32Match(p->flow->todstpktcnt, df->pkt_data); + } else if (df->dir == DETECT_FLOW_TOCLIENT) { + return DetectU32Match(p->flow->tosrcpktcnt, df->pkt_data); + } + return 0; } -static void DetectFlowPktsToClientFree(DetectEngineCtx *de_ctx, void *ptr) +static void DetectFlowPktsFree(DetectEngineCtx *de_ctx, void *ptr) { - rs_detect_u32_free(ptr); + DetectFlow *df = (DetectFlow *)ptr; + if (df != NULL) { + rs_detect_u32_free(df->pkt_data); + SCFree(df); + } } -static int DetectFlowPktsToClientSetup(DetectEngineCtx *de_ctx, Signature *s, const char *rawstr) +static int DetectFlowPktsToServerSetup(DetectEngineCtx *de_ctx, Signature *s, const char *rawstr) { DetectU32Data *du32 = DetectU32Parse(rawstr); if (du32 == NULL) return -1; - if (SigMatchAppendSMToList(de_ctx, s, DETECT_FLOW_PKTS_TO_CLIENT, (SigMatchCtx *)du32, - DETECT_SM_LIST_MATCH) == NULL) { - DetectFlowPktsToClientFree(de_ctx, du32); + DetectFlow *df = SCCalloc(1, sizeof(DetectFlow)); + if (df == NULL) + return -1; + + df->pkt_data = du32; + df->dir = DETECT_FLOW_TOSERVER; + + if (SigMatchAppendSMToList( + de_ctx, s, DETECT_FLOW_PKTS, (SigMatchCtx *)df, DETECT_SM_LIST_MATCH) == NULL) { + DetectFlowPktsFree(de_ctx, df); return -1; } s->flags |= SIG_FLAG_REQUIRE_PACKET; @@ -56,147 +82,220 @@ static int DetectFlowPktsToClientSetup(DetectEngineCtx *de_ctx, Signature *s, co return 0; } -static void PrefilterPacketFlowPktsToClientMatch( - DetectEngineThreadCtx *det_ctx, Packet *p, const void *pectx) +static int DetectFlowPktsToClientSetup(DetectEngineCtx *de_ctx, Signature *s, const char *rawstr) { - const PrefilterPacketHeaderCtx *ctx = pectx; - if (!PrefilterPacketHeaderExtraMatch(ctx, p)) - return; + DetectU32Data *du32 = DetectU32Parse(rawstr); + if (du32 == NULL) + return -1; - DetectU32Data du32; - du32.mode = ctx->v1.u8[0]; - du32.arg1 = ctx->v1.u32[1]; - du32.arg2 = ctx->v1.u32[2]; - if (DetectFlowPktsToClientMatch(det_ctx, p, NULL, (const SigMatchCtx *)&du32)) { - PrefilterAddSids(&det_ctx->pmq, ctx->sigs_array, ctx->sigs_cnt); - } -} + DetectFlow *df = SCCalloc(1, sizeof(DetectFlow)); + if (df == NULL) + return -1; -static int PrefilterSetupFlowPktsToClient(DetectEngineCtx *de_ctx, SigGroupHead *sgh) -{ - return PrefilterSetupPacketHeader(de_ctx, sgh, DETECT_FLOW_PKTS_TO_CLIENT, - SIG_MASK_REQUIRE_FLOW, PrefilterPacketU32Set, PrefilterPacketU32Compare, - PrefilterPacketFlowPktsToClientMatch); -} + df->pkt_data = du32; + df->dir = DETECT_FLOW_TOCLIENT; -static bool PrefilterFlowPktsToClientIsPrefilterable(const Signature *s) -{ - return PrefilterIsPrefilterableById(s, DETECT_FLOW_PKTS_TO_CLIENT); -} + if (SigMatchAppendSMToList( + de_ctx, s, DETECT_FLOW_PKTS, (SigMatchCtx *)df, DETECT_SM_LIST_MATCH) == NULL) { + DetectFlowPktsFree(de_ctx, df); + return -1; + } + s->flags |= SIG_FLAG_REQUIRE_PACKET; -void DetectFlowPktsToClientRegister(void) -{ - sigmatch_table[DETECT_FLOW_PKTS_TO_CLIENT].name = "flow.pkts_toclient"; - sigmatch_table[DETECT_FLOW_PKTS_TO_CLIENT].desc = "match flow number of packets to client"; - sigmatch_table[DETECT_FLOW_PKTS_TO_CLIENT].url = "/rules/flow-keywords.html#flow-pkts_toclient"; - sigmatch_table[DETECT_FLOW_PKTS_TO_CLIENT].Match = DetectFlowPktsToClientMatch; - sigmatch_table[DETECT_FLOW_PKTS_TO_CLIENT].Setup = DetectFlowPktsToClientSetup; - sigmatch_table[DETECT_FLOW_PKTS_TO_CLIENT].Free = DetectFlowPktsToClientFree; - sigmatch_table[DETECT_FLOW_PKTS_TO_CLIENT].SupportsPrefilter = - PrefilterFlowPktsToClientIsPrefilterable; - sigmatch_table[DETECT_FLOW_PKTS_TO_CLIENT].SetupPrefilter = PrefilterSetupFlowPktsToClient; + return 0; } -static int DetectFlowPktsToServerMatch( - DetectEngineThreadCtx *det_ctx, Packet *p, const Signature *s, const SigMatchCtx *ctx) +static int DetectFlowPktsSetup(DetectEngineCtx *de_ctx, Signature *s, const char *rawstr) { - if (p->flow == NULL) { - return 0; + DetectFlow *df = NULL; + char copy[strlen(rawstr) + 1]; + strlcpy(copy, rawstr, sizeof(copy)); + char *context = NULL; + char *token = strtok_r(copy, ",", &context); + uint8_t num_tokens = 1; + uint8_t dir = 0; + char *pkt_data = NULL; + + while (token != NULL) { + if (num_tokens > 2) + goto error; + + while (*token != '\0' && isblank(*token)) { + token++; + } + if (strlen(token) == 0) { + goto next; + } + + num_tokens++; + + if (strcmp(token, "toserver") == 0) { + dir = DETECT_FLOW_TOSERVER; + } else if (strcmp(token, "toclient") == 0) { + dir = DETECT_FLOW_TOCLIENT; + } + + if (dir) { + pkt_data = token; + } + next: + token = strtok_r(NULL, ",", &context); } - uint32_t nb = p->flow->todstpktcnt; - const DetectU32Data *du32 = (const DetectU32Data *)ctx; - return DetectU32Match(nb, du32); + if (dir) { + DetectU32Data *du32 = DetectU32Parse(pkt_data); + if (du32 == NULL) + goto error; + df = SCCalloc(1, sizeof(DetectFlow)); + if (df == NULL) + return -1; + df->dir = dir; + df->pkt_data = du32; + if (SigMatchAppendSMToList( + de_ctx, s, DETECT_FLOW_PKTS, (SigMatchCtx *)df, DETECT_SM_LIST_MATCH) == NULL) { + goto error; + } + } + + s->flags |= SIG_FLAG_REQUIRE_PACKET; + + return 0; +error: + if (df != NULL) + DetectFlowPktsFree(de_ctx, df); + return -1; } -static void DetectFlowPktsToServerFree(DetectEngineCtx *de_ctx, void *ptr) +static void PrefilterPacketFlowPktsSet(PrefilterPacketHeaderValue *v, void *smctx) { - rs_detect_u32_free(ptr); + const DetectFlow *df = smctx; + const DetectUintData_u32 *data = df->pkt_data; + v->u8[0] = data->mode; + v->u8[1] = (uint8_t)df->dir; + v->u32[1] = data->arg1; + v->u32[2] = data->arg2; } -static int DetectFlowPktsToServerSetup(DetectEngineCtx *de_ctx, Signature *s, const char *rawstr) +static bool PrefilterPacketFlowPktsCompare(PrefilterPacketHeaderValue v, void *smctx) { - DetectU32Data *du32 = DetectU32Parse(rawstr); - if (du32 == NULL) - return -1; - - if (SigMatchAppendSMToList(de_ctx, s, DETECT_FLOW_PKTS_TO_SERVER, (SigMatchCtx *)du32, - DETECT_SM_LIST_MATCH) == NULL) { - DetectFlowPktsToServerFree(de_ctx, du32); - return -1; + const DetectFlow *df = smctx; + if (v.u8[0] == df->pkt_data->mode && v.u8[1] == df->dir && v.u32[1] == df->pkt_data->arg1 && + v.u32[2] == df->pkt_data->arg2) { + return true; } - s->flags |= SIG_FLAG_REQUIRE_PACKET; - - return 0; + return false; } -static void PrefilterPacketFlowPktsToServerMatch( +static void PrefilterPacketFlowPktsMatch( DetectEngineThreadCtx *det_ctx, Packet *p, const void *pectx) { const PrefilterPacketHeaderCtx *ctx = pectx; if (!PrefilterPacketHeaderExtraMatch(ctx, p)) return; - DetectU32Data du32; - du32.mode = ctx->v1.u8[0]; - du32.arg1 = ctx->v1.u32[1]; - du32.arg2 = ctx->v1.u32[2]; - if (DetectFlowPktsToServerMatch(det_ctx, p, NULL, (const SigMatchCtx *)&du32)) { + DetectFlow df; + DetectUintData_u32 data = { + .mode = ctx->v1.u8[0], .arg1 = ctx->v1.u32[1], .arg2 = ctx->v1.u32[2] + }; + df.pkt_data = &data; + df.dir = ctx->v1.u8[1]; + + if (DetectFlowPktsMatch(det_ctx, p, NULL, (const SigMatchCtx *)&df)) { PrefilterAddSids(&det_ctx->pmq, ctx->sigs_array, ctx->sigs_cnt); } } -static int PrefilterSetupFlowPktsToServer(DetectEngineCtx *de_ctx, SigGroupHead *sgh) +static int PrefilterSetupFlowPkts(DetectEngineCtx *de_ctx, SigGroupHead *sgh) { - return PrefilterSetupPacketHeader(de_ctx, sgh, DETECT_FLOW_PKTS_TO_SERVER, - SIG_MASK_REQUIRE_FLOW, PrefilterPacketU32Set, PrefilterPacketU32Compare, - PrefilterPacketFlowPktsToServerMatch); + return PrefilterSetupPacketHeader(de_ctx, sgh, DETECT_FLOW_PKTS, SIG_MASK_REQUIRE_FLOW, + PrefilterPacketFlowPktsSet, PrefilterPacketFlowPktsCompare, + PrefilterPacketFlowPktsMatch); } -static bool PrefilterFlowPktsToServerIsPrefilterable(const Signature *s) +static bool PrefilterFlowPktsIsPrefilterable(const Signature *s) { - return PrefilterIsPrefilterableById(s, DETECT_FLOW_PKTS_TO_SERVER); + return PrefilterIsPrefilterableById(s, DETECT_FLOW_PKTS); +} + +void DetectFlowPktsRegister(void) +{ + sigmatch_table[DETECT_FLOW_PKTS].name = "flow.pkts"; + sigmatch_table[DETECT_FLOW_PKTS].desc = "match number of packets in a flow"; + sigmatch_table[DETECT_FLOW_PKTS].url = "/rules/flow-keywords.html#flow-pkts"; + sigmatch_table[DETECT_FLOW_PKTS].Match = DetectFlowPktsMatch; + sigmatch_table[DETECT_FLOW_PKTS].Setup = DetectFlowPktsSetup; + sigmatch_table[DETECT_FLOW_PKTS].Free = DetectFlowPktsFree; + sigmatch_table[DETECT_FLOW_PKTS].SupportsPrefilter = PrefilterFlowPktsIsPrefilterable; + sigmatch_table[DETECT_FLOW_PKTS].SetupPrefilter = PrefilterSetupFlowPkts; } void DetectFlowPktsToServerRegister(void) { sigmatch_table[DETECT_FLOW_PKTS_TO_SERVER].name = "flow.pkts_toserver"; - sigmatch_table[DETECT_FLOW_PKTS_TO_SERVER].desc = "match flow number of packets to server"; - sigmatch_table[DETECT_FLOW_PKTS_TO_SERVER].url = "/rules/flow-keywords.html#flow-pkts_toserver"; - sigmatch_table[DETECT_FLOW_PKTS_TO_SERVER].Match = DetectFlowPktsToServerMatch; + sigmatch_table[DETECT_FLOW_PKTS_TO_SERVER].desc = + "match number of packets in a flow in to server direction"; + sigmatch_table[DETECT_FLOW_PKTS_TO_SERVER].url = "/rules/flow-keywords.html#flow-pkts"; + sigmatch_table[DETECT_FLOW_PKTS_TO_SERVER].Match = DetectFlowPktsMatch; sigmatch_table[DETECT_FLOW_PKTS_TO_SERVER].Setup = DetectFlowPktsToServerSetup; - sigmatch_table[DETECT_FLOW_PKTS_TO_SERVER].Free = DetectFlowPktsToServerFree; - sigmatch_table[DETECT_FLOW_PKTS_TO_SERVER].SupportsPrefilter = - PrefilterFlowPktsToServerIsPrefilterable; - sigmatch_table[DETECT_FLOW_PKTS_TO_SERVER].SetupPrefilter = PrefilterSetupFlowPktsToServer; + sigmatch_table[DETECT_FLOW_PKTS_TO_SERVER].Free = DetectFlowPktsFree; + sigmatch_table[DETECT_FLOW_PKTS_TO_SERVER].SupportsPrefilter = PrefilterFlowPktsIsPrefilterable; + sigmatch_table[DETECT_FLOW_PKTS_TO_SERVER].SetupPrefilter = PrefilterSetupFlowPkts; } -static int DetectFlowBytesToClientMatch( +void DetectFlowPktsToClientRegister(void) +{ + sigmatch_table[DETECT_FLOW_PKTS_TO_CLIENT].name = "flow.pkts_toclient"; + sigmatch_table[DETECT_FLOW_PKTS_TO_CLIENT].desc = + "match number of packets in a flow in to client direction"; + sigmatch_table[DETECT_FLOW_PKTS_TO_CLIENT].url = "/rules/flow-keywords.html#flow-pkts"; + sigmatch_table[DETECT_FLOW_PKTS_TO_CLIENT].Match = DetectFlowPktsMatch; + sigmatch_table[DETECT_FLOW_PKTS_TO_CLIENT].Setup = DetectFlowPktsToClientSetup; + sigmatch_table[DETECT_FLOW_PKTS_TO_CLIENT].Free = DetectFlowPktsFree; + sigmatch_table[DETECT_FLOW_PKTS_TO_CLIENT].SupportsPrefilter = PrefilterFlowPktsIsPrefilterable; + sigmatch_table[DETECT_FLOW_PKTS_TO_CLIENT].SetupPrefilter = PrefilterSetupFlowPkts; +} + +static int DetectFlowBytesMatch( DetectEngineThreadCtx *det_ctx, Packet *p, const Signature *s, const SigMatchCtx *ctx) { if (p->flow == NULL) { return 0; } - uint64_t nb = p->flow->tosrcbytecnt; - const DetectU64Data *du64 = (const DetectU64Data *)ctx; - return DetectU64Match(nb, du64); + const DetectFlow *df = (const DetectFlow *)ctx; + if (df->dir == DETECT_FLOW_TOSERVER) { + return DetectU64Match(p->flow->todstbytecnt, df->byte_data); + } else if (df->dir == DETECT_FLOW_TOCLIENT) { + return DetectU64Match(p->flow->tosrcbytecnt, df->byte_data); + } + return 0; } -static void DetectFlowBytesToClientFree(DetectEngineCtx *de_ctx, void *ptr) +static void DetectFlowBytesFree(DetectEngineCtx *de_ctx, void *ptr) { - rs_detect_u64_free(ptr); + DetectFlow *df = (DetectFlow *)ptr; + if (df != NULL) { + rs_detect_u64_free(df->byte_data); + SCFree(df); + } } -static int DetectFlowBytesToClientSetup(DetectEngineCtx *de_ctx, Signature *s, const char *rawstr) +static int DetectFlowBytesToServerSetup(DetectEngineCtx *de_ctx, Signature *s, const char *rawstr) { DetectU64Data *du64 = DetectU64Parse(rawstr); if (du64 == NULL) return -1; - if (SigMatchAppendSMToList(de_ctx, s, DETECT_FLOW_BYTES_TO_CLIENT, (SigMatchCtx *)du64, - DETECT_SM_LIST_MATCH) == NULL) { - DetectFlowBytesToClientFree(de_ctx, du64); + DetectFlow *df = SCCalloc(1, sizeof(DetectFlow)); + if (df == NULL) + return -1; + + df->byte_data = du64; + df->dir = DETECT_FLOW_TOSERVER; + + if (SigMatchAppendSMToList( + de_ctx, s, DETECT_FLOW_BYTES, (SigMatchCtx *)df, DETECT_SM_LIST_MATCH) == NULL) { + DetectFlowBytesFree(de_ctx, df); return -1; } s->flags |= SIG_FLAG_REQUIRE_PACKET; @@ -204,57 +303,117 @@ static int DetectFlowBytesToClientSetup(DetectEngineCtx *de_ctx, Signature *s, c return 0; } -void DetectFlowBytesToClientRegister(void) +static int DetectFlowBytesToClientSetup(DetectEngineCtx *de_ctx, Signature *s, const char *rawstr) { - sigmatch_table[DETECT_FLOW_BYTES_TO_CLIENT].name = "flow.bytes_toclient"; - sigmatch_table[DETECT_FLOW_BYTES_TO_CLIENT].desc = "match flow number of bytes to client"; - sigmatch_table[DETECT_FLOW_BYTES_TO_CLIENT].url = - "/rules/flow-keywords.html#flow-bytes_toclient"; - sigmatch_table[DETECT_FLOW_BYTES_TO_CLIENT].Match = DetectFlowBytesToClientMatch; - sigmatch_table[DETECT_FLOW_BYTES_TO_CLIENT].Setup = DetectFlowBytesToClientSetup; - sigmatch_table[DETECT_FLOW_BYTES_TO_CLIENT].Free = DetectFlowBytesToClientFree; -} + DetectU64Data *du64 = DetectU64Parse(rawstr); + if (du64 == NULL) + return -1; -static int DetectFlowBytesToServerMatch( - DetectEngineThreadCtx *det_ctx, Packet *p, const Signature *s, const SigMatchCtx *ctx) -{ - if (p->flow == NULL) { - return 0; - } - uint64_t nb = p->flow->todstbytecnt; + DetectFlow *df = SCCalloc(1, sizeof(DetectFlow)); + if (df == NULL) + return -1; - const DetectU64Data *du64 = (const DetectU64Data *)ctx; - return DetectU64Match(nb, du64); -} + df->byte_data = du64; + df->dir = DETECT_FLOW_TOCLIENT; -static void DetectFlowBytesToServerFree(DetectEngineCtx *de_ctx, void *ptr) -{ - rs_detect_u64_free(ptr); + if (SigMatchAppendSMToList( + de_ctx, s, DETECT_FLOW_BYTES, (SigMatchCtx *)df, DETECT_SM_LIST_MATCH) == NULL) { + DetectFlowBytesFree(de_ctx, df); + return -1; + } + s->flags |= SIG_FLAG_REQUIRE_PACKET; + + return 0; } -static int DetectFlowBytesToServerSetup(DetectEngineCtx *de_ctx, Signature *s, const char *rawstr) +static int DetectFlowBytesSetup(DetectEngineCtx *de_ctx, Signature *s, const char *rawstr) { - DetectU64Data *du64 = DetectU64Parse(rawstr); - if (du64 == NULL) - return -1; + DetectFlow *df = NULL; + char copy[strlen(rawstr) + 1]; + strlcpy(copy, rawstr, sizeof(copy)); + char *context = NULL; + char *token = strtok_r(copy, ",", &context); + uint8_t num_tokens = 1; + uint8_t dir = 0; + char *byte_data = NULL; + + while (token != NULL) { + if (num_tokens > 2) + goto error; + + while (*token != '\0' && isblank(*token)) { + token++; + } + if (strlen(token) == 0) { + goto next; + } + + num_tokens++; + + if (strcmp(token, "toserver") == 0) { + dir = DETECT_FLOW_TOSERVER; + } else if (strcmp(token, "toclient") == 0) { + dir = DETECT_FLOW_TOCLIENT; + } + + if (dir) { + byte_data = token; + } + next: + token = strtok_r(NULL, ",", &context); + } - if (SigMatchAppendSMToList(de_ctx, s, DETECT_FLOW_BYTES_TO_SERVER, (SigMatchCtx *)du64, - DETECT_SM_LIST_MATCH) == NULL) { - DetectFlowBytesToServerFree(de_ctx, du64); - return -1; + if (dir) { + DetectU64Data *du64 = DetectU64Parse(byte_data); + if (du64 == NULL) + goto error; + df = SCCalloc(1, sizeof(DetectFlow)); + if (df == NULL) + return -1; + df->dir = dir; + df->byte_data = du64; + if (SigMatchAppendSMToList(de_ctx, s, DETECT_FLOW_BYTES, (SigMatchCtx *)df, + DETECT_SM_LIST_MATCH) == NULL) { + goto error; + } } s->flags |= SIG_FLAG_REQUIRE_PACKET; return 0; +error: + if (df) + DetectFlowBytesFree(de_ctx, df); + return -1; +} + +void DetectFlowBytesRegister(void) +{ + sigmatch_table[DETECT_FLOW_BYTES].name = "flow.bytes"; + sigmatch_table[DETECT_FLOW_BYTES].desc = "match number of bytes in a flow"; + sigmatch_table[DETECT_FLOW_BYTES].url = "/rules/flow-keywords.html#flow-bytes"; + sigmatch_table[DETECT_FLOW_BYTES].Match = DetectFlowBytesMatch; + sigmatch_table[DETECT_FLOW_BYTES].Setup = DetectFlowBytesSetup; + sigmatch_table[DETECT_FLOW_BYTES].Free = DetectFlowBytesFree; } void DetectFlowBytesToServerRegister(void) { sigmatch_table[DETECT_FLOW_BYTES_TO_SERVER].name = "flow.bytes_toserver"; - sigmatch_table[DETECT_FLOW_BYTES_TO_SERVER].desc = "match flow number of bytes to server"; - sigmatch_table[DETECT_FLOW_BYTES_TO_SERVER].url = - "/rules/flow-keywords.html#flow-bytes_toserver"; - sigmatch_table[DETECT_FLOW_BYTES_TO_SERVER].Match = DetectFlowBytesToServerMatch; + sigmatch_table[DETECT_FLOW_BYTES_TO_SERVER].desc = + "match number of bytes in a flow in to server dir"; + sigmatch_table[DETECT_FLOW_BYTES_TO_SERVER].url = "/rules/flow-keywords.html#flow-bytes"; + sigmatch_table[DETECT_FLOW_BYTES_TO_SERVER].Match = DetectFlowBytesMatch; sigmatch_table[DETECT_FLOW_BYTES_TO_SERVER].Setup = DetectFlowBytesToServerSetup; - sigmatch_table[DETECT_FLOW_BYTES_TO_SERVER].Free = DetectFlowBytesToServerFree; + sigmatch_table[DETECT_FLOW_BYTES_TO_SERVER].Free = DetectFlowBytesFree; +} + +void DetectFlowBytesToClientRegister(void) +{ + sigmatch_table[DETECT_FLOW_BYTES_TO_CLIENT].name = "flow.bytes_toclient"; + sigmatch_table[DETECT_FLOW_BYTES_TO_CLIENT].desc = + "match number of bytes in a flow in to client dir"; + sigmatch_table[DETECT_FLOW_BYTES_TO_CLIENT].url = "/rules/flow-keywords.html#flow-bytes"; + sigmatch_table[DETECT_FLOW_BYTES_TO_CLIENT].Match = DetectFlowBytesMatch; + sigmatch_table[DETECT_FLOW_BYTES_TO_CLIENT].Setup = DetectFlowBytesToClientSetup; + sigmatch_table[DETECT_FLOW_BYTES_TO_CLIENT].Free = DetectFlowBytesFree; } diff --git a/src/detect-flow-pkts.h b/src/detect-flow-pkts.h index da1e0eb5a6aa..0bf47e1b7318 100644 --- a/src/detect-flow-pkts.h +++ b/src/detect-flow-pkts.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2023 Open Information Security Foundation +/* Copyright (C) 2023-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 @@ -18,9 +18,11 @@ #ifndef SURICATA_DETECT_FLOW_PKTS_H #define SURICATA_DETECT_FLOW_PKTS_H -void DetectFlowPktsToClientRegister(void); +void DetectFlowPktsRegister(void); void DetectFlowPktsToServerRegister(void); -void DetectFlowBytesToClientRegister(void); +void DetectFlowPktsToClientRegister(void); +void DetectFlowBytesRegister(void); void DetectFlowBytesToServerRegister(void); +void DetectFlowBytesToClientRegister(void); #endif /* SURICATA_DETECT_FLOW_PKTS_H */ From f1e76fcd1c5dacc9f453d129fc803453fb45dbb2 Mon Sep 17 00:00:00 2001 From: Shivani Bhardwaj Date: Tue, 8 Oct 2024 12:43:27 +0530 Subject: [PATCH 2/3] doc: update syntax for flow.pkts & flow.bytes --- doc/userguide/rules/flow-keywords.rst | 83 ++++++++------------------- 1 file changed, 24 insertions(+), 59 deletions(-) diff --git a/doc/userguide/rules/flow-keywords.rst b/doc/userguide/rules/flow-keywords.rst index 00801352303e..358c110496fe 100644 --- a/doc/userguide/rules/flow-keywords.rst +++ b/doc/userguide/rules/flow-keywords.rst @@ -318,90 +318,55 @@ Signature example:: In this example, we combine `flow.age` and `flowbits` to get an alert on the first packet after the flow's age is older than one hour. -flow.pkts_toclient ------------------- +flow.pkts +--------- -Flow number of packets to client (integer) +Flow number of packets (integer) This keyword does not wait for the end of the flow, but will be checked at each packet. -flow.pkts_toclient uses an :ref:`unsigned 32-bit integer `. +flow.pkts uses an :ref:`unsigned 32-bit integer ` and supports +following directions: -Syntax:: - - flow.pkts_toclient: [op] - -The number of packets can be matched exactly, or compared using the _op_ setting:: - - flow.pkts_toclient:3 # exactly 3 - flow.pkts_toclient:<3 # smaller than 3 - flow.pkts_toclient:>=2 # greater than or equal to 2 - -Signature example:: - - alert ip any any -> any any (msg:"Flow has 20 packets"; flow.pkts_toclient:20; sid:1;) - -flow.pkts_toserver ------------------- +* toclient -Flow number of packets to server (integer) -This keyword does not wait for the end of the flow, but will be checked at each packet. - -flow.pkts_toserver uses an :ref:`unsigned 32-bit integer `. +* toserver Syntax:: - flow.pkts_toserver: [op] + flow.pkts:,[op] The number of packets can be matched exactly, or compared using the _op_ setting:: - flow.pkts_toserver:3 # exactly 3 - flow.pkts_toserver:<3 # smaller than 3 - flow.pkts_toserver:>=2 # greater than or equal to 2 + flow.pkts:toclient,3 # exactly 3 + flow.pkts:toserver,<3 # smaller than 3 Signature example:: - alert ip any any -> any any (msg:"Flow has 20 packets"; flow.pkts_toserver:20; sid:1;) - -flow.bytes_toclient -------------------- + alert ip any any -> any any (msg:"Flow has 20 packets in toclient dir"; flow.pkts:toclient,20; sid:1;) -Flow number of bytes to client (integer) -This keyword does not wait for the end of the flow, but will be checked at each packet. - -flow.bytes_toclient uses an :ref:`unsigned 64-bit integer `. - -Syntax:: - - flow.bytes_toclient: [op] - -The number of packets can be matched exactly, or compared using the _op_ setting:: - - flow.bytes_toclient:3 # exactly 3 - flow.bytes_toclient:<3 # smaller than 3 - flow.bytes_toclient:>=2 # greater than or equal to 2 -Signature example:: +flow.bytes +---------- - alert ip any any -> any any (msg:"Flow has less than 2000 bytes"; flow.bytes_toclient:<2000; sid:1;) +Flow number of bytes (integer) +This keyword does not wait for the end of the flow, but will be checked at each packet. -flow.bytes_toserver -------------------- +flow.bytes uses an :ref:`unsigned 64-bit integer ` and supports +following directions: -Flow number of bytes to server (integer) -This keyword does not wait for the end of the flow, but will be checked at each packet. +* toclient -flow.bytes_toserver uses an :ref:`unsigned 64-bit integer `. +* toserver Syntax:: - flow.bytes_toserver: [op] + flow.bytes:,[op] -The number of packets can be matched exactly, or compared using the _op_ setting:: +The number of bytes can be matched exactly, or compared using the _op_ setting:: - flow.bytes_toserver:3 # exactly 3 - flow.bytes_toserver:<3 # smaller than 3 - flow.bytes_toserver:>=2 # greater than or equal to 2 + flow.bytes:toclient,3 # exactly 3 + flow.bytes:toserver,<3 # smaller than 3 Signature example:: - alert ip any any -> any any (msg:"Flow has less than 2000 bytes"; flow.bytes_toserver:<2000; sid:1;) + alert ip any any -> any any (msg:"Flow has less than 2000 bytes in toserver dir"; flow.bytes:toserver,<2000; sid:1;) From 5320e79530e7b30b485d5e7b6a87e3addb20f37e Mon Sep 17 00:00:00 2001 From: Shivani Bhardwaj Date: Fri, 23 Aug 2024 12:27:35 +0530 Subject: [PATCH 3/3] flow/pkts: allow matching on either direction For flow.bytes and flow.pkts keywords, allow matching in either direction. Feature 5646 --- doc/userguide/rules/flow-keywords.rst | 6 ++++++ src/detect-flow-pkts.c | 15 +++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/doc/userguide/rules/flow-keywords.rst b/doc/userguide/rules/flow-keywords.rst index 358c110496fe..089c8ba8cc9e 100644 --- a/doc/userguide/rules/flow-keywords.rst +++ b/doc/userguide/rules/flow-keywords.rst @@ -331,6 +331,8 @@ following directions: * toserver +* either + Syntax:: flow.pkts:,[op] @@ -339,6 +341,7 @@ The number of packets can be matched exactly, or compared using the _op_ setting flow.pkts:toclient,3 # exactly 3 flow.pkts:toserver,<3 # smaller than 3 + flow.pkts:either,>=2 # greater than or equal to 2 Signature example:: @@ -358,6 +361,8 @@ following directions: * toserver +* either + Syntax:: flow.bytes:,[op] @@ -366,6 +371,7 @@ The number of bytes can be matched exactly, or compared using the _op_ setting:: flow.bytes:toclient,3 # exactly 3 flow.bytes:toserver,<3 # smaller than 3 + flow.bytes:either,>=2 # greater than or equal to 2 Signature example:: diff --git a/src/detect-flow-pkts.c b/src/detect-flow-pkts.c index 842c78c993d4..f602260df748 100644 --- a/src/detect-flow-pkts.c +++ b/src/detect-flow-pkts.c @@ -26,6 +26,7 @@ enum FlowDirection { DETECT_FLOW_TOSERVER = 1, DETECT_FLOW_TOCLIENT, + DETECT_FLOW_TOEITHER, }; typedef struct DetectFlow_ { @@ -46,6 +47,11 @@ static int DetectFlowPktsMatch( return DetectU32Match(p->flow->todstpktcnt, df->pkt_data); } else if (df->dir == DETECT_FLOW_TOCLIENT) { return DetectU32Match(p->flow->tosrcpktcnt, df->pkt_data); + } else if (df->dir == DETECT_FLOW_TOEITHER) { + if (DetectU32Match(p->flow->tosrcpktcnt, df->pkt_data)) { + return 1; + } + return DetectU32Match(p->flow->todstpktcnt, df->pkt_data); } return 0; } @@ -133,6 +139,8 @@ static int DetectFlowPktsSetup(DetectEngineCtx *de_ctx, Signature *s, const char dir = DETECT_FLOW_TOSERVER; } else if (strcmp(token, "toclient") == 0) { dir = DETECT_FLOW_TOCLIENT; + } else if (strcmp(token, "either") == 0) { + dir = DETECT_FLOW_TOEITHER; } if (dir) { @@ -267,6 +275,11 @@ static int DetectFlowBytesMatch( return DetectU64Match(p->flow->todstbytecnt, df->byte_data); } else if (df->dir == DETECT_FLOW_TOCLIENT) { return DetectU64Match(p->flow->tosrcbytecnt, df->byte_data); + } else if (df->dir == DETECT_FLOW_TOEITHER) { + if (DetectU64Match(p->flow->tosrcbytecnt, df->byte_data)) { + return 1; + } + return DetectU64Match(p->flow->todstbytecnt, df->byte_data); } return 0; } @@ -354,6 +367,8 @@ static int DetectFlowBytesSetup(DetectEngineCtx *de_ctx, Signature *s, const cha dir = DETECT_FLOW_TOSERVER; } else if (strcmp(token, "toclient") == 0) { dir = DETECT_FLOW_TOCLIENT; + } else if (strcmp(token, "either") == 0) { + dir = DETECT_FLOW_TOEITHER; } if (dir) {