From: Victor Julien Date: Wed, 14 May 2025 10:16:46 +0000 (+0200) Subject: detect: add pre_stream hook X-Git-Tag: suricata-8.0.0-rc1~112 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=8f2b925e093178aa5fbe2d3199ea4542ce640205;p=thirdparty%2Fsuricata.git detect: add pre_stream hook Meant to be used from the detection engine, to allow rules to drop traffic before it modifies the stream state. Ticket: #7712. --- diff --git a/etc/schema.json b/etc/schema.json index dad1e1dfe3..e3bbbeff11 100644 --- a/etc/schema.json +++ b/etc/schema.json @@ -6792,6 +6792,11 @@ "type": "integer", "description": "Number of packets dropped due to no NFQ verdict" }, + "pre_stream_hook": { + "description": + "Number of packets dropped in the pre_stream hook ", + "type": "integer" + }, "rules": { "type": "integer", "description": "Number of packets dropped due to rule actions" diff --git a/src/decode.c b/src/decode.c index 42c6363d88..f3ef6c22b3 100644 --- a/src/decode.c +++ b/src/decode.c @@ -911,6 +911,8 @@ const char *PacketDropReasonToString(enum PacketDropReason r) return "default packet policy"; case PKT_DROP_REASON_DEFAULT_APP_POLICY: return "default app policy"; + case PKT_DROP_REASON_STREAM_PRE_HOOK: + return "pre stream hook"; case PKT_DROP_REASON_NOT_SET: case PKT_DROP_REASON_MAX: return NULL; @@ -957,6 +959,8 @@ static const char *PacketDropReasonToJsonString(enum PacketDropReason r) return "ips.drop_reason.default_packet_policy"; case PKT_DROP_REASON_DEFAULT_APP_POLICY: 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_NOT_SET: case PKT_DROP_REASON_MAX: return NULL; diff --git a/src/decode.h b/src/decode.h index deee5a63f8..1224f75a85 100644 --- a/src/decode.h +++ b/src/decode.h @@ -390,6 +390,7 @@ enum PacketDropReason { PKT_DROP_REASON_INNER_PACKET, /**< drop issued by inner (tunnel) packet */ 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_MAX, }; diff --git a/src/detect-engine-alert.c b/src/detect-engine-alert.c index e7f1f847c3..d00514fe8e 100644 --- a/src/detect-engine-alert.c +++ b/src/detect-engine-alert.c @@ -191,11 +191,16 @@ 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; + /* PacketDrop will update the packet action, too */ PacketDrop(p, pa->action, (pa->flags & PACKET_ALERT_FLAG_RATE_FILTER_MODIFIED) ? PKT_DROP_REASON_RULES_THRESHOLD - : PKT_DROP_REASON_RULES); + : drop_reason); SCLogDebug("[packet %p][DROP sid %u]", p, s->id); if (p->alerts.drop.action == 0) { diff --git a/src/detect-engine-build.c b/src/detect-engine-build.c index 3915aff01f..9a524cd2af 100644 --- a/src/detect-engine-build.c +++ b/src/detect-engine-build.c @@ -37,6 +37,7 @@ #include "detect-flow.h" #include "detect-config.h" #include "detect-flowbits.h" + #include "app-layer-events.h" #include "util-port-interval-tree.h" @@ -1853,6 +1854,21 @@ static void DetectEngineAddDecoderEventSig(DetectEngineCtx *de_ctx, Signature *s SigGroupHeadAppendSig(de_ctx, &de_ctx->decoder_event_sgh, s); } +static void DetectEngineAddSigToPreStreamHook(DetectEngineCtx *de_ctx, Signature *s) +{ + SCLogDebug("adding signature %" PRIu32 " to the pre_stream hook sgh", s->id); + + if ((s->flags & (SIG_FLAG_TOSERVER | SIG_FLAG_TOCLIENT)) == + (SIG_FLAG_TOSERVER | SIG_FLAG_TOCLIENT)) { + SigGroupHeadAppendSig(de_ctx, &de_ctx->pre_stream_sgh[0], s); + SigGroupHeadAppendSig(de_ctx, &de_ctx->pre_stream_sgh[1], s); + } else if (s->flags & SIG_FLAG_TOSERVER) { + SigGroupHeadAppendSig(de_ctx, &de_ctx->pre_stream_sgh[0], s); + } else if (s->flags & SIG_FLAG_TOCLIENT) { + SigGroupHeadAppendSig(de_ctx, &de_ctx->pre_stream_sgh[1], s); + } +} + /** * \brief Fill the global src group head, with the sigs included * @@ -1884,6 +1900,9 @@ int SigPrepareStage2(DetectEngineCtx *de_ctx) IPOnlyAddSignature(de_ctx, &de_ctx->io_ctx, s); } else if (s->type == SIG_TYPE_DEONLY) { DetectEngineAddDecoderEventSig(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_STREAM) { + DetectEngineAddSigToPreStreamHook(de_ctx, s); } } @@ -1902,10 +1921,33 @@ static void DetectEngineBuildDecoderEventSgh(DetectEngineCtx *de_ctx) SigGroupHeadBuildMatchArray(de_ctx, de_ctx->decoder_event_sgh, max_idx); } +static void DetectEngineBuildPreStreamHookSghs(DetectEngineCtx *de_ctx) +{ + uint32_t max_idx = DetectEngineGetMaxSigId(de_ctx); + if (de_ctx->pre_stream_sgh[0] != NULL) { + SigGroupHeadSetSigCnt(de_ctx->pre_stream_sgh[0], max_idx); + SigGroupHeadBuildMatchArray(de_ctx, de_ctx->pre_stream_sgh[0], max_idx); + PrefilterSetupRuleGroup(de_ctx, de_ctx->pre_stream_sgh[0]); + } + if (de_ctx->pre_stream_sgh[1] != NULL) { + SigGroupHeadSetSigCnt(de_ctx->pre_stream_sgh[1], max_idx); + SigGroupHeadBuildMatchArray(de_ctx, de_ctx->pre_stream_sgh[1], max_idx); + PrefilterSetupRuleGroup(de_ctx, de_ctx->pre_stream_sgh[1]); + } + + if (de_ctx->pre_stream_sgh[0] != NULL || de_ctx->pre_stream_sgh[1] != NULL) { + de_ctx->PreStreamHook = DetectPreStream; + } +} + int SigPrepareStage3(DetectEngineCtx *de_ctx) { /* prepare the decoder event sgh */ DetectEngineBuildDecoderEventSgh(de_ctx); + + /* pre_stream hook sghs */ + DetectEngineBuildPreStreamHookSghs(de_ctx); + return 0; } @@ -1918,6 +1960,12 @@ 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_stream_sgh[0]) + SigGroupHeadFree(de_ctx, de_ctx->pre_stream_sgh[0]); + de_ctx->pre_stream_sgh[0] = NULL; + if (de_ctx->pre_stream_sgh[1]) + SigGroupHeadFree(de_ctx, de_ctx->pre_stream_sgh[1]); + de_ctx->pre_stream_sgh[1] = NULL; for (int f = 0; f < FLOW_STATES; f++) { for (int p = 0; p < 256; p++) { diff --git a/src/detect-parse.c b/src/detect-parse.c index c78e5fcd47..1ae277c20a 100644 --- a/src/detect-parse.c +++ b/src/detect-parse.c @@ -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_stream") == 0) { + return SIGNATURE_HOOK_PKT_PRE_STREAM; } else if (strcmp(str, "all") == 0) { return SIGNATURE_HOOK_PKT_ALL; } @@ -1254,6 +1256,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_STREAM: + return "pre_stream"; case SIGNATURE_HOOK_PKT_ALL: return "all"; } @@ -2454,7 +2458,11 @@ static void DetectFirewallRuleSetTable(Signature *s) enum FirewallTable table; if (s->flags & SIG_FLAG_FIREWALL) { if (s->type == SIG_TYPE_PKT) { - table = FIREWALL_TABLE_PACKET_FILTER; + 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 + table = FIREWALL_TABLE_PACKET_FILTER; } else if (s->type == SIG_TYPE_APP_TX) { table = FIREWALL_TABLE_APP_FILTER; } else { diff --git a/src/detect.c b/src/detect.c index 4fcb84e008..c181072e8e 100644 --- a/src/detect.c +++ b/src/detect.c @@ -198,6 +198,49 @@ end: SCReturn; } +/** \internal + */ +static void DetectRunPacketHook(ThreadVars *th_v, const DetectEngineCtx *de_ctx, + DetectEngineThreadCtx *det_ctx, const SigGroupHead *sgh, Packet *p) +{ + SCEnter(); + SCLogDebug("p->pcap_cnt %" PRIu64 " direction %s pkt_src %s", p->pcap_cnt, + p->flow ? (FlowGetPacketDirection(p->flow, p) == TOSERVER ? "toserver" : "toclient") + : "noflow", + PktSrcToString(p->pkt_src)); + + /* Load the Packet's flow early, even though it might not be needed. + * Mark as a constant pointer, although the flow itself can change. */ + Flow *const pflow = p->flow; + + DetectRunScratchpad scratch = DetectRunSetup(de_ctx, det_ctx, p, pflow); + scratch.sgh = sgh; + + /* if we didn't get a sig group head, we + * have nothing to do.... */ + if (scratch.sgh == NULL) { + SCLogDebug("no sgh for this packet, nothing to match against"); + goto end; + } + + /* run the prefilters for packets */ + DetectRunPrefilterPkt(th_v, de_ctx, det_ctx, p, &scratch); + + // PACKET_PROFILING_DETECT_START(p, PROF_DETECT_RULES); // TODO + /* inspect the rules against the packet */ + const uint8_t pkt_policy = DetectRulePacketRules(th_v, de_ctx, det_ctx, p, pflow, &scratch); + // PACKET_PROFILING_DETECT_END(p, PROF_DETECT_RULES); + if (pkt_policy & (ACTION_DROP | ACTION_ACCEPT)) { + goto end; + } + +end: + DetectRunPostRules(th_v, de_ctx, det_ctx, p, pflow, &scratch); + + DetectRunCleanup(det_ctx, p, pflow); + SCReturn; +} + static void DetectRunPostMatch(ThreadVars *tv, DetectEngineThreadCtx *det_ctx, Packet *p, const Signature *s) @@ -2252,6 +2295,17 @@ static void DetectNoFlow(ThreadVars *tv, DetectRun(tv, de_ctx, det_ctx, p); } +uint8_t DetectPreStream(ThreadVars *tv, DetectEngineThreadCtx *det_ctx, Packet *p) +{ + const DetectEngineCtx *de_ctx = det_ctx->de_ctx; + const int direction = (PKT_IS_TOCLIENT(p) != 0); + const SigGroupHead *sgh = de_ctx->pre_stream_sgh[direction]; + + 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; +} + /** \brief Detection engine thread wrapper. * \param tv thread vars * \param p packet to inspect diff --git a/src/detect.h b/src/detect.h index da613ff55e..86f1f235ea 100644 --- a/src/detect.h +++ b/src/detect.h @@ -539,6 +539,7 @@ typedef struct SignatureInitDataBuffer_ { enum SignatureHookPkt { SIGNATURE_HOOK_PKT_NOT_SET, SIGNATURE_HOOK_PKT_FLOW_START, + SIGNATURE_HOOK_PKT_PRE_STREAM, SIGNATURE_HOOK_PKT_ALL, /**< match each packet */ }; @@ -549,6 +550,7 @@ enum SignatureHookType { }; enum FirewallTable { + FIREWALL_TABLE_PACKET_PRE_STREAM, FIREWALL_TABLE_PACKET_FILTER, FIREWALL_TABLE_PACKET_TD, FIREWALL_TABLE_APP_FILTER, @@ -905,6 +907,8 @@ typedef struct { uint32_t content_inspect_min_size; } DetectFileDataCfg; +typedef uint8_t (*DetectPacketHookFunc)(ThreadVars *tv, DetectEngineThreadCtx *det_ctx, Packet *p); + /** * \brief Function type for rate filter callback. * @@ -1135,6 +1139,12 @@ typedef struct DetectEngineCtx_ { /* use provided data to be passed to rate_filter_callback. */ void *rate_filter_callback_arg; + + /* Hook for pre_stream engine if it is used. */ + DetectPacketHookFunc PreStreamHook; + /** TCP pre_stream hook rule groups. One per direction. */ + struct SigGroupHead_ *pre_stream_sgh[2]; + } DetectEngineCtx; /** @@ -1698,6 +1708,7 @@ extern SigTableElmt *sigmatch_table; /* detection api */ TmEcode Detect(ThreadVars *tv, Packet *p, void *data); +uint8_t DetectPreStream(ThreadVars *tv, DetectEngineThreadCtx *det_ctx, Packet *p); SigMatch *SigMatchAlloc(void); Signature *SigFindSignatureBySidGid(DetectEngineCtx *, uint32_t, uint32_t); diff --git a/src/flow-worker.c b/src/flow-worker.c index b4bd9a96dd..da64e0753a 100644 --- a/src/flow-worker.c +++ b/src/flow-worker.c @@ -366,6 +366,14 @@ static inline void UpdateCounters(ThreadVars *tv, static inline void FlowWorkerStreamTCPUpdate(ThreadVars *tv, FlowWorkerThreadData *fw, Packet *p, DetectEngineThreadCtx *det_ctx, const bool timeout) { + if (det_ctx != NULL && det_ctx->de_ctx->PreStreamHook != NULL) { + const uint8_t action = det_ctx->de_ctx->PreStreamHook(tv, det_ctx, p); + if (action & ACTION_DROP) { + PacketDrop(p, ACTION_DROP, PKT_DROP_REASON_STREAM_PRE_HOOK); + return; + } + } + FLOWWORKER_PROFILING_START(p, PROFILE_FLOWWORKER_STREAM); StreamTcp(tv, p, fw->stream_thread, &fw->pq); FLOWWORKER_PROFILING_END(p, PROFILE_FLOWWORKER_STREAM);