]> git.ipfire.org Git - thirdparty/suricata.git/commitdiff
detect: add pre_flow hook
authorVictor Julien <vjulien@oisf.net>
Tue, 20 May 2025 14:15:15 +0000 (16:15 +0200)
committerVictor Julien <victor@inliniac.net>
Tue, 10 Jun 2025 06:36:36 +0000 (08:36 +0200)
Allows dropping of packets before a flow is created/updated.
Directionless as direction is inferred from the flow.

Ticket: #7714.

etc/schema.json
src/decode.c
src/decode.h
src/detect-engine-alert.c
src/detect-engine-build.c
src/detect-parse.c
src/detect.c
src/detect.h
src/flow-worker.c

index e3bbbeff11df6c38e6ce7b4c5d2b5e577b59182f..598cfac1816c6808fae7bd786db8b439b6fcf2e1 100644 (file)
                                     "type": "integer",
                                     "description": "Number of packets dropped due to no NFQ verdict"
                                 },
+                                "pre_flow_hook": {
+                                    "description":
+                                            "Number of packets dropped in the pre_flow hook ",
+                                    "type": "integer"
+                                },
                                 "pre_stream_hook": {
                                     "description":
                                             "Number of packets dropped in the pre_stream hook ",
index f3ef6c22b3730cb97b367cc198d62bc1eb4fee02..d55d282242f738c60d9f002626cae9722695071d 100644 (file)
@@ -913,6 +913,8 @@ const char *PacketDropReasonToString(enum PacketDropReason r)
             return "default app policy";
         case PKT_DROP_REASON_STREAM_PRE_HOOK:
             return "pre stream hook";
+        case PKT_DROP_REASON_FLOW_PRE_HOOK:
+            return "pre flow hook";
         case PKT_DROP_REASON_NOT_SET:
         case PKT_DROP_REASON_MAX:
             return NULL;
@@ -961,6 +963,8 @@ static const char *PacketDropReasonToJsonString(enum PacketDropReason r)
             return "ips.drop_reason.default_app_policy";
         case PKT_DROP_REASON_STREAM_PRE_HOOK:
             return "ips.drop_reason.pre_stream_hook";
+        case PKT_DROP_REASON_FLOW_PRE_HOOK:
+            return "ips.drop_reason.pre_flow_hook";
         case PKT_DROP_REASON_NOT_SET:
         case PKT_DROP_REASON_MAX:
             return NULL;
index 1224f75a8599889d9d4ba415136efd8007f3b8e1..5a65805d4f776b57c401a1d39005b344ba76044e 100644 (file)
@@ -391,6 +391,7 @@ enum PacketDropReason {
     PKT_DROP_REASON_DEFAULT_PACKET_POLICY, /**< drop issued by default packet policy */
     PKT_DROP_REASON_DEFAULT_APP_POLICY,    /**< drop issued by default app policy */
     PKT_DROP_REASON_STREAM_PRE_HOOK,       /**< drop issued in the pre_stream hook */
+    PKT_DROP_REASON_FLOW_PRE_HOOK,         /**< drop issued in the pre_flow hook */
     PKT_DROP_REASON_MAX,
 };
 
index d00514fe8e41e4f8fb486a64cab5e09d470a28f9..571b8aede88ff1e9032b7913504a9413bf253f9f 100644 (file)
@@ -191,10 +191,14 @@ static void PacketApplySignatureActions(Packet *p, const Signature *s, const Pac
 
     /* REJECT also sets ACTION_DROP, just make it more visible with this check */
     if (pa->action & ACTION_DROP_REJECT) {
-        const uint8_t drop_reason = ((s->flags & SIG_FLAG_FIREWALL) &&
-                                            s->firewall_table == FIREWALL_TABLE_PACKET_PRE_STREAM)
-                                            ? PKT_DROP_REASON_STREAM_PRE_HOOK
-                                            : PKT_DROP_REASON_RULES;
+        uint8_t drop_reason = PKT_DROP_REASON_RULES;
+        if (s->flags & SIG_FLAG_FIREWALL) {
+            if (s->firewall_table == FIREWALL_TABLE_PACKET_PRE_STREAM) {
+                drop_reason = PKT_DROP_REASON_STREAM_PRE_HOOK;
+            } else if (s->firewall_table == FIREWALL_TABLE_PACKET_PRE_FLOW) {
+                drop_reason = PKT_DROP_REASON_FLOW_PRE_HOOK;
+            }
+        }
 
         /* PacketDrop will update the packet action, too */
         PacketDrop(p, pa->action,
index 9a524cd2af3544648dfff9cacd0ea01c26104814..519c695775fcb6388a749f303fc9b15b0168f670 100644 (file)
@@ -1869,6 +1869,12 @@ static void DetectEngineAddSigToPreStreamHook(DetectEngineCtx *de_ctx, Signature
     }
 }
 
+static void DetectEngineAddSigToPreFlowHook(DetectEngineCtx *de_ctx, Signature *s)
+{
+    SCLogDebug("adding signature %" PRIu32 " to the pre_flow hook sgh", s->id);
+    SigGroupHeadAppendSig(de_ctx, &de_ctx->pre_flow_sgh, s);
+}
+
 /**
  * \brief Fill the global src group head, with the sigs included
  *
@@ -1903,6 +1909,9 @@ int SigPrepareStage2(DetectEngineCtx *de_ctx)
         } else if (s->type == SIG_TYPE_PKT && s->init_data->hook.type == SIGNATURE_HOOK_TYPE_PKT &&
                    s->init_data->hook.t.pkt.ph == SIGNATURE_HOOK_PKT_PRE_STREAM) {
             DetectEngineAddSigToPreStreamHook(de_ctx, s);
+        } else if (s->type == SIG_TYPE_PKT && s->init_data->hook.type == SIGNATURE_HOOK_TYPE_PKT &&
+                   s->init_data->hook.t.pkt.ph == SIGNATURE_HOOK_PKT_PRE_FLOW) {
+            DetectEngineAddSigToPreFlowHook(de_ctx, s);
         }
     }
 
@@ -1940,11 +1949,25 @@ static void DetectEngineBuildPreStreamHookSghs(DetectEngineCtx *de_ctx)
     }
 }
 
+static void DetectEngineBuildPreFlowHookSghs(DetectEngineCtx *de_ctx)
+{
+    if (de_ctx->pre_flow_sgh != NULL) {
+        uint32_t max_idx = DetectEngineGetMaxSigId(de_ctx);
+        SigGroupHeadSetSigCnt(de_ctx->pre_flow_sgh, max_idx);
+        SigGroupHeadBuildMatchArray(de_ctx, de_ctx->pre_flow_sgh, max_idx);
+        PrefilterSetupRuleGroup(de_ctx, de_ctx->pre_flow_sgh);
+        de_ctx->PreFlowHook = DetectPreFlow;
+    }
+}
+
 int SigPrepareStage3(DetectEngineCtx *de_ctx)
 {
     /* prepare the decoder event sgh */
     DetectEngineBuildDecoderEventSgh(de_ctx);
 
+    /* pre_flow hook sgh */
+    DetectEngineBuildPreFlowHookSghs(de_ctx);
+
     /* pre_stream hook sghs */
     DetectEngineBuildPreStreamHookSghs(de_ctx);
 
@@ -1960,6 +1983,9 @@ int SigAddressCleanupStage1(DetectEngineCtx *de_ctx)
     if (de_ctx->decoder_event_sgh)
         SigGroupHeadFree(de_ctx, de_ctx->decoder_event_sgh);
     de_ctx->decoder_event_sgh = NULL;
+    if (de_ctx->pre_flow_sgh)
+        SigGroupHeadFree(de_ctx, de_ctx->pre_flow_sgh);
+    de_ctx->pre_flow_sgh = NULL;
     if (de_ctx->pre_stream_sgh[0])
         SigGroupHeadFree(de_ctx, de_ctx->pre_stream_sgh[0]);
     de_ctx->pre_stream_sgh[0] = NULL;
index 1ae277c20a289eb75eacf5a3690e9ab6bac3104a..d526d582194e9ec112a4dcd33f598f1fc7f3ea28 100644 (file)
@@ -1240,6 +1240,8 @@ static enum SignatureHookPkt HookPktFromString(const char *str)
 {
     if (strcmp(str, "flow_start") == 0) {
         return SIGNATURE_HOOK_PKT_FLOW_START;
+    } else if (strcmp(str, "pre_flow") == 0) {
+        return SIGNATURE_HOOK_PKT_PRE_FLOW;
     } else if (strcmp(str, "pre_stream") == 0) {
         return SIGNATURE_HOOK_PKT_PRE_STREAM;
     } else if (strcmp(str, "all") == 0) {
@@ -1256,6 +1258,8 @@ static const char *HookPktToString(const enum SignatureHookPkt ph)
             return "not set";
         case SIGNATURE_HOOK_PKT_FLOW_START:
             return "flow_start";
+        case SIGNATURE_HOOK_PKT_PRE_FLOW:
+            return "pre_flow";
         case SIGNATURE_HOOK_PKT_PRE_STREAM:
             return "pre_stream";
         case SIGNATURE_HOOK_PKT_ALL:
@@ -2461,6 +2465,9 @@ static void DetectFirewallRuleSetTable(Signature *s)
             if (s->init_data->hook.type == SIGNATURE_HOOK_TYPE_PKT &&
                     s->init_data->hook.t.pkt.ph == SIGNATURE_HOOK_PKT_PRE_STREAM)
                 table = FIREWALL_TABLE_PACKET_PRE_STREAM;
+            else if (s->init_data->hook.type == SIGNATURE_HOOK_TYPE_PKT &&
+                     s->init_data->hook.t.pkt.ph == SIGNATURE_HOOK_PKT_PRE_FLOW)
+                table = FIREWALL_TABLE_PACKET_PRE_FLOW;
             else
                 table = FIREWALL_TABLE_PACKET_FILTER;
         } else if (s->type == SIG_TYPE_APP_TX) {
index c181072e8eb6e01a660246b9f853f292b32a449e..96ef76a9b714fd6625ef78f73bdb8a690c0371af 100644 (file)
@@ -2295,6 +2295,16 @@ static void DetectNoFlow(ThreadVars *tv,
     DetectRun(tv, de_ctx, det_ctx, p);
 }
 
+uint8_t DetectPreFlow(ThreadVars *tv, DetectEngineThreadCtx *det_ctx, Packet *p)
+{
+    const DetectEngineCtx *de_ctx = det_ctx->de_ctx;
+    const SigGroupHead *sgh = de_ctx->pre_flow_sgh;
+
+    SCLogDebug("thread id: %u, packet %" PRIu64 ", sgh %p", tv->id, p->pcap_cnt, sgh);
+    DetectRunPacketHook(tv, de_ctx, det_ctx, sgh, p);
+    return p->action;
+}
+
 uint8_t DetectPreStream(ThreadVars *tv, DetectEngineThreadCtx *det_ctx, Packet *p)
 {
     const DetectEngineCtx *de_ctx = det_ctx->de_ctx;
index 86f1f235ea5470fc13e7edc6308d7b6715e73e3d..477e89f699fd65bc89a6f08858119a7664f33ca3 100644 (file)
@@ -539,6 +539,7 @@ typedef struct SignatureInitDataBuffer_ {
 enum SignatureHookPkt {
     SIGNATURE_HOOK_PKT_NOT_SET,
     SIGNATURE_HOOK_PKT_FLOW_START,
+    SIGNATURE_HOOK_PKT_PRE_FLOW,
     SIGNATURE_HOOK_PKT_PRE_STREAM,
     SIGNATURE_HOOK_PKT_ALL, /**< match each packet */
 };
@@ -550,6 +551,7 @@ enum SignatureHookType {
 };
 
 enum FirewallTable {
+    FIREWALL_TABLE_PACKET_PRE_FLOW,
     FIREWALL_TABLE_PACKET_PRE_STREAM,
     FIREWALL_TABLE_PACKET_FILTER,
     FIREWALL_TABLE_PACKET_TD,
@@ -1145,6 +1147,10 @@ typedef struct DetectEngineCtx_ {
     /** TCP pre_stream hook rule groups. One per direction. */
     struct SigGroupHead_ *pre_stream_sgh[2];
 
+    /* Hook for pre_flow engine if it is used. */
+    DetectPacketHookFunc PreFlowHook;
+    /** pre_flow hook rule groups. Before flow we don't know a direction yet. */
+    struct SigGroupHead_ *pre_flow_sgh;
 } DetectEngineCtx;
 
 /**
@@ -1708,6 +1714,7 @@ extern SigTableElmt *sigmatch_table;
 
 /* detection api */
 TmEcode Detect(ThreadVars *tv, Packet *p, void *data);
+uint8_t DetectPreFlow(ThreadVars *tv, DetectEngineThreadCtx *det_ctx, Packet *p);
 uint8_t DetectPreStream(ThreadVars *tv, DetectEngineThreadCtx *det_ctx, Packet *p);
 
 SigMatch *SigMatchAlloc(void);
index da64e0753adb85a5e8da358a587aa78d4756d8b5..585dfa755b93259cd2f794d0be35874142d531af 100644 (file)
@@ -574,6 +574,14 @@ static TmEcode FlowWorker(ThreadVars *tv, Packet *p, void *data)
     }
 
     /* handle Flow */
+    if (det_ctx != NULL && det_ctx->de_ctx->PreFlowHook != NULL) {
+        const uint8_t action = det_ctx->de_ctx->PreFlowHook(tv, det_ctx, p);
+        if (action & ACTION_DROP) {
+            PacketDrop(p, ACTION_DROP, PKT_DROP_REASON_FLOW_PRE_HOOK);
+            goto pre_flow_drop;
+        }
+    }
+
     if (p->flags & PKT_WANTS_FLOW) {
         FLOWWORKER_PROFILING_START(p, PROFILE_FLOWWORKER_FLOW);
 
@@ -660,6 +668,7 @@ static TmEcode FlowWorker(ThreadVars *tv, Packet *p, void *data)
         FLOWWORKER_PROFILING_END(p, PROFILE_FLOWWORKER_DETECT);
     }
 
+pre_flow_drop:
     // Outputs.
     OutputLoggerLog(tv, p, fw->output_thread);