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.
pull/13410/head
Victor Julien 1 year ago committed by Victor Julien
parent ecbcccf355
commit 8f2b925e09

@ -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"

@ -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;

@ -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,
};

@ -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) {

@ -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++) {

@ -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 {

@ -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

@ -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);

@ -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);

Loading…
Cancel
Save