]> git.ipfire.org Git - thirdparty/suricata.git/commitdiff
flow/pkts: make syntax cleaner and compact
authorShivani Bhardwaj <shivani@oisf.net>
Fri, 29 Nov 2024 08:31:12 +0000 (14:01 +0530)
committerVictor Julien <victor@inliniac.net>
Fri, 10 Jan 2025 21:32:40 +0000 (22:32 +0100)
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 newer options are added
- 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
src/detect-engine-register.h
src/detect-flow-pkts.c
src/detect-flow-pkts.h

index 9bddf0fd84372117b982b8bbd26ac2bac487d7bc..82153807ad5823d6aa7e433cde41befd7b9602f6 100644 (file)
@@ -581,10 +581,12 @@ void SigTableSetup(void)
     DetectReplaceRegister();
     DetectFlowRegister();
     DetectFlowAgeRegister();
-    DetectFlowPktsToClientRegister();
+    DetectFlowPktsRegister();
     DetectFlowPktsToServerRegister();
-    DetectFlowBytesToClientRegister();
+    DetectFlowPktsToClientRegister();
+    DetectFlowBytesRegister();
     DetectFlowBytesToServerRegister();
+    DetectFlowBytesToClientRegister();
     DetectRequiresRegister();
     DetectWindowRegister();
     DetectRpcRegister();
index b7a029998555901596b1a580e4628d3c0e28aa6c..f46bf688f0f873c0f48c3230beed03c76573bbf3 100644 (file)
@@ -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,
 
index ef8fed369cd93ce7f075bca7a69f1d4af0b495b5..884ba2f1cba218870e29ecdd998bcc3c85dac4dc 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (C) 2023 Open Information Security Foundation
+/* Copyright (C) 2023-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
 #include "detect-engine-uint.h"
 #include "detect-parse.h"
 
-static int DetectFlowPktsToClientMatch(
+enum FlowDirection {
+    DETECT_FLOW_TOSERVER = 1,
+    DETECT_FLOW_TOCLIENT,
+};
+
+typedef struct DetectFlowPkts_ {
+    DetectU32Data *pkt_data;
+    enum FlowDirection dir;
+} DetectFlowPkts;
+
+typedef struct DetectFlowBytes_ {
+    DetectU64Data *byte_data;
+    enum FlowDirection dir;
+} DetectFlowBytes;
+
+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 DetectFlowPkts *df = (const DetectFlowPkts *)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);
+    DetectFlowPkts *df = (DetectFlowPkts *)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);
+    DetectFlowPkts *df = SCCalloc(1, sizeof(DetectFlowPkts));
+    if (df == NULL) {
+        rs_detect_u32_free(du32);
+        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 +88,225 @@ 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);
+    DetectFlowPkts *df = SCCalloc(1, sizeof(DetectFlowPkts));
+    if (df == NULL) {
+        rs_detect_u32_free(du32);
+        return -1;
     }
-}
+    df->pkt_data = du32;
+    df->dir = DETECT_FLOW_TOCLIENT;
 
-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);
-}
+    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;
 
-static bool PrefilterFlowPktsToClientIsPrefilterable(const Signature *s)
-{
-    return PrefilterIsPrefilterableById(s, DETECT_FLOW_PKTS_TO_CLIENT);
+    return 0;
 }
 
-void DetectFlowPktsToClientRegister(void)
+static int DetectFlowPktsSetup(DetectEngineCtx *de_ctx, Signature *s, const char *rawstr)
 {
-    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;
-}
+    DetectFlowPkts *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 = 0;
+    uint8_t dir = 0;
+    char *pkt_data = NULL;
+
+    while (token != NULL) {
+        if (num_tokens > 1)
+            return -1;
+
+        while (*token != '\0' && isblank(*token)) {
+            token++;
+        }
+        if (strlen(token) == 0) {
+            goto next;
+        }
+
+        num_tokens++;
+
+        if (dir == 0 && num_tokens == 1) {
+            if (strcmp(token, "toserver") == 0) {
+                dir = DETECT_FLOW_TOSERVER;
+            } else if (strcmp(token, "toclient") == 0) {
+                dir = DETECT_FLOW_TOCLIENT;
+            } else {
+                SCLogError("Invalid direction given: %s", token);
+                return -1;
+            }
+        }
+
+        if (dir && num_tokens == 2) {
+            pkt_data = token;
+        }
+
+    next:
+        token = strtok_r(NULL, ",", &context);
+    }
 
-static int DetectFlowPktsToServerMatch(
-        DetectEngineThreadCtx *det_ctx, Packet *p, const Signature *s, const SigMatchCtx *ctx)
-{
-    if (p->flow == NULL) {
-        return 0;
+    DetectU32Data *du32 = DetectU32Parse(pkt_data);
+    if (du32 == NULL)
+        return -1;
+    df = SCCalloc(1, sizeof(DetectFlowPkts));
+    if (df == NULL) {
+        rs_detect_u32_free(du32);
+        return -1;
     }
-    uint32_t nb = p->flow->todstpktcnt;
+    df->dir = dir;
+    df->pkt_data = du32;
+    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;
 
-    const DetectU32Data *du32 = (const DetectU32Data *)ctx;
-    return DetectU32Match(nb, du32);
+    return 0;
 }
 
-static void DetectFlowPktsToServerFree(DetectEngineCtx *de_ctx, void *ptr)
+static void PrefilterPacketFlowPktsSet(PrefilterPacketHeaderValue *v, void *smctx)
 {
-    rs_detect_u32_free(ptr);
+    const DetectFlowPkts *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 DetectFlowPkts *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)) {
+    DetectFlowPkts 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;
+}
+
+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 DetectFlowBytesToClientMatch(
+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 DetectFlowBytes *df = (const DetectFlowBytes *)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);
+    DetectFlowBytes *df = (DetectFlowBytes *)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);
+    DetectFlowBytes *df = SCCalloc(1, sizeof(DetectFlowBytes));
+    if (df == NULL) {
+        rs_detect_u64_free(du64);
+        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,43 +314,87 @@ 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;
+    DetectFlowBytes *df = SCCalloc(1, sizeof(DetectFlowBytes));
+    if (df == NULL) {
+        rs_detect_u64_free(du64);
+        return -1;
     }
-    uint64_t nb = p->flow->todstbytecnt;
 
-    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);
+    DetectFlowBytes *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 = 0;
+    uint8_t dir = 0;
+    char *byte_data = NULL;
+
+    while (token != NULL) {
+        if (num_tokens > 1)
+            return -1;
+
+        while (*token != '\0' && isblank(*token)) {
+            token++;
+        }
+        if (strlen(token) == 0) {
+            goto next;
+        }
+
+        num_tokens++;
+
+        if (dir == 0 && num_tokens == 1) {
+            if (strcmp(token, "toserver") == 0) {
+                dir = DETECT_FLOW_TOSERVER;
+            } else if (strcmp(token, "toclient") == 0) {
+                dir = DETECT_FLOW_TOCLIENT;
+            } else {
+                SCLogError("Invalid direction given: %s", token);
+                return -1;
+            }
+        }
+
+        if (dir && num_tokens == 2) {
+            byte_data = token;
+        }
+
+    next:
+        token = strtok_r(NULL, ",", &context);
+    }
+
+    DetectU64Data *du64 = DetectU64Parse(byte_data);
     if (du64 == NULL)
         return -1;
-
-    if (SigMatchAppendSMToList(de_ctx, s, DETECT_FLOW_BYTES_TO_SERVER, (SigMatchCtx *)du64,
-                DETECT_SM_LIST_MATCH) == NULL) {
-        DetectFlowBytesToServerFree(de_ctx, du64);
+    df = SCCalloc(1, sizeof(DetectFlowBytes));
+    if (df == NULL) {
+        rs_detect_u64_free(du64);
+        return -1;
+    }
+    df->dir = dir;
+    df->byte_data = du64;
+    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;
@@ -248,13 +402,34 @@ static int DetectFlowBytesToServerSetup(DetectEngineCtx *de_ctx, Signature *s, c
     return 0;
 }
 
+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;
 }
index da1e0eb5a6aaa95388dfaba7123ca7e463bd173e..076e6ddd7b67eaa8f0582b35d0772523a56d075a 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (C) 2023 Open Information Security Foundation
+/* Copyright (C) 2023-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
 #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 */