detect: start of pkt hooks

New facility to allow a user to specify where to hook a rule
into the engine. This patch adds this for packets, adding two
hooks:

- `all`: to let a rule be evaluated by all rules
- `flow_start`: to have a rule be evaluated only for the first
  packet in both directions

Implemented by adding a hook flags field in the packet.
pull/12979/head
Victor Julien 10 months ago committed by Victor Julien
parent 95ed975cca
commit f60e1efc8a

@ -512,8 +512,13 @@ typedef struct Packet_
/* coccinelle: Packet:flowflags:FLOW_PKT_ */
uint8_t app_update_direction; // enum StreamUpdateDir
/** sig mask flags this packet has, used in signature matching */
SignatureMask sig_mask;
/** bit flags of SignatureHookPkt values this packet should trigger */
uint16_t pkt_hooks;
/* Pkt Flags */
uint32_t flags;

@ -208,6 +208,10 @@ static bool SignatureInspectsBuffers(const Signature *s)
*/
int SignatureIsIPOnly(DetectEngineCtx *de_ctx, const Signature *s)
{
/* explicit hook means no IP-only */
if (s->init_data->hook.type != SIGNATURE_HOOK_TYPE_NOT_SET)
return 0;
if (s->alproto != ALPROTO_UNKNOWN)
return 0;
@ -276,6 +280,10 @@ int SignatureIsIPOnly(DetectEngineCtx *de_ctx, const Signature *s)
*/
static int SignatureIsPDOnly(const DetectEngineCtx *de_ctx, const Signature *s)
{
/* explicit hook means no PD-only */
if (s->init_data->hook.type != SIGNATURE_HOOK_TYPE_NOT_SET)
return 0;
if (s->alproto != ALPROTO_UNKNOWN)
return 0;
@ -354,6 +362,10 @@ static int SignatureIsInspectingPayload(DetectEngineCtx *de_ctx, const Signature
*/
static int SignatureIsDEOnly(DetectEngineCtx *de_ctx, const Signature *s)
{
/* explicit hook means no DE-only */
if (s->init_data->hook.type != SIGNATURE_HOOK_TYPE_NOT_SET)
SCReturnInt(0);
if (s->alproto != ALPROTO_UNKNOWN) {
SCReturnInt(0);
}

@ -142,8 +142,9 @@ static int SetupEngineForPacketHeader(DetectEngineCtx *de_ctx, SigGroupHead *sgh
SCLogDebug("%s: ctx %p extra type %u extra value %u, sig cnt %u",
sigmatch_table[sm_type].name, ctx, ctx->type, ctx->value,
ctx->sigs_cnt);
PrefilterAppendEngine(
de_ctx, sgh, Match, mask, ctx, PrefilterPacketHeaderFree, sigmatch_table[sm_type].name);
enum SignatureHookPkt hook = SIGNATURE_HOOK_PKT_NOT_SET; // TODO review
PrefilterAppendEngine(de_ctx, sgh, Match, mask, hook, ctx, PrefilterPacketHeaderFree,
sigmatch_table[sm_type].name);
return 0;
}
@ -245,7 +246,8 @@ static int SetupEngineForPacketHeaderPrefilterPacketU8HashCtx(DetectEngineCtx *d
}
if (cnt) {
PrefilterAppendEngine(de_ctx, sgh, Match, mask, ctx, PrefilterPacketU8HashCtxFree,
enum SignatureHookPkt hook = SIGNATURE_HOOK_PKT_NOT_SET; // TODO review
PrefilterAppendEngine(de_ctx, sgh, Match, mask, hook, ctx, PrefilterPacketU8HashCtxFree,
sigmatch_table[sm_type].name);
} else {
PrefilterPacketU8HashCtxFree(ctx);

@ -205,7 +205,12 @@ void Prefilter(DetectEngineThreadCtx *det_ctx, const SigGroupHead *sgh, Packet *
/* run packet engines */
PrefilterEngine *engine = sgh->pkt_engines;
do {
if ((engine->ctx.pkt_mask & mask) == engine->ctx.pkt_mask) {
/* run engine if:
* mask matches
* no hook is used OR hook matches
*/
if (((engine->ctx.pkt.mask & mask) == engine->ctx.pkt.mask) &&
(engine->ctx.pkt.hook == 0 || (p->pkt_hooks & BIT_U16(engine->ctx.pkt.hook)))) {
PREFILTER_PROFILING_START(det_ctx);
engine->cb.Prefilter(det_ctx, p, engine->pectx);
PREFILTER_PROFILING_END(det_ctx, engine->gid);
@ -248,7 +253,8 @@ void Prefilter(DetectEngineThreadCtx *det_ctx, const SigGroupHead *sgh, Packet *
}
int PrefilterAppendEngine(DetectEngineCtx *de_ctx, SigGroupHead *sgh, PrefilterPktFn PrefilterFunc,
SignatureMask mask, void *pectx, void (*FreeFunc)(void *pectx), const char *name)
SignatureMask mask, enum SignatureHookPkt hook, void *pectx, void (*FreeFunc)(void *pectx),
const char *name)
{
if (sgh == NULL || PrefilterFunc == NULL || pectx == NULL)
return -1;
@ -258,10 +264,14 @@ int PrefilterAppendEngine(DetectEngineCtx *de_ctx, SigGroupHead *sgh, PrefilterP
return -1;
memset(e, 0x00, sizeof(*e));
// TODO right now we represent the hook in a u8 in the prefilter engine for space reasons.
BUG_ON(hook >= 8);
e->Prefilter = PrefilterFunc;
e->pectx = pectx;
e->Free = FreeFunc;
e->pkt_mask = mask;
e->pkt_hook = hook;
if (sgh->init->pkt_engines == NULL) {
sgh->init->pkt_engines = e;
@ -609,6 +619,14 @@ static void PrefilterPktNonPF(DetectEngineThreadCtx *det_ctx, Packet *p, const v
}
}
static void PrefilterPktNonPFHookFlowStart(
DetectEngineThreadCtx *det_ctx, Packet *p, const void *pectx)
{
if (p->flowflags & (FLOW_PKT_TOSERVER_FIRST | FLOW_PKT_TOCLIENT_FIRST)) {
PrefilterPktNonPF(det_ctx, p, pectx);
}
}
/** \internal
* \brief engine to select the non-prefilter rules for frames
* Checks the alproto and type as well.
@ -785,10 +803,22 @@ static int SetupNonPrefilter(DetectEngineCtx *de_ctx, SigGroupHead *sgh)
}
uint32_t frame_non_pf_array_size = 0;
struct PrefilterNonPFDataSig *pkt_hook_flow_start_non_pf_array =
SCCalloc(max_sids, sizeof(*pkt_hook_flow_start_non_pf_array));
if (pkt_hook_flow_start_non_pf_array == NULL) {
SCFree(pkt_non_pf_array);
SCFree(frame_non_pf_array);
return -1;
}
uint32_t pkt_hook_flow_start_non_pf_array_size = 0;
SignatureMask pkt_hook_flow_start_mask = 0;
bool pkt_hook_flow_start_mask_init = false;
HashListTable *tx_engines_hash =
HashListTableInit(256, TxNonPFHash, TxNonPFCompare, TxNonPFFree);
if (tx_engines_hash == NULL) {
SCFree(pkt_non_pf_array);
SCFree(pkt_hook_flow_start_non_pf_array);
SCFree(frame_non_pf_array);
return -1;
}
@ -798,6 +828,7 @@ static int SetupNonPrefilter(DetectEngineCtx *de_ctx, SigGroupHead *sgh)
HashTableInit(512, NonPFNamesHash, NonPFNamesCompare, NonPFNamesFree);
if (de_ctx->non_pf_engine_names == NULL) {
SCFree(pkt_non_pf_array);
SCFree(pkt_hook_flow_start_non_pf_array);
SCFree(frame_non_pf_array);
HashListTableFree(tx_engines_hash);
return -1;
@ -830,6 +861,44 @@ static int SetupNonPrefilter(DetectEngineCtx *de_ctx, SigGroupHead *sgh)
bool tx_non_pf = false;
bool frame_non_pf = false;
bool pkt_non_pf = false;
if (s->init_data->hook.type == SIGNATURE_HOOK_TYPE_PKT &&
s->init_data->hook.t.pkt.ph == SIGNATURE_HOOK_PKT_FLOW_START) {
// TODO code duplication with regular pkt case below
/* for pkt non prefilter, we have some space in the structure,
* so we can squeeze another filter */
uint8_t type;
uint16_t value;
if ((s->flags & SIG_FLAG_DSIZE) && s->dsize_mode == DETECT_UINT_EQ) {
SCLogDebug("dsize extra match");
type = 2;
value = s->dsize_low;
} else if (s->dp != NULL && s->dp->next == NULL && s->dp->port == s->dp->port2) {
type = 1;
value = s->dp->port;
} else {
type = 0;
value = s->alproto;
}
pkt_hook_flow_start_non_pf_array[pkt_hook_flow_start_non_pf_array_size].sid = s->num;
pkt_hook_flow_start_non_pf_array[pkt_hook_flow_start_non_pf_array_size].value = value;
pkt_hook_flow_start_non_pf_array[pkt_hook_flow_start_non_pf_array_size].type = type;
pkt_hook_flow_start_non_pf_array[pkt_hook_flow_start_non_pf_array_size].pkt.sig_mask =
s->mask;
pkt_hook_flow_start_non_pf_array_size++;
if (pkt_hook_flow_start_mask_init) {
pkt_hook_flow_start_mask &= s->mask;
} else {
pkt_hook_flow_start_mask = s->mask;
pkt_hook_flow_start_mask_init = true;
}
SCLogDebug("flow_start hook");
continue; // done for this sig
}
for (uint32_t x = 0; x < s->init_data->buffer_index; x++) {
const int list_id = s->init_data->buffers[x].id;
const DetectBufferType *buf = DetectEngineBufferTypeGetById(de_ctx, list_id);
@ -984,12 +1053,32 @@ static int SetupNonPrefilter(DetectEngineCtx *de_ctx, SigGroupHead *sgh)
data->size = pkt_non_pf_array_size;
memcpy((uint8_t *)&data->array, pkt_non_pf_array,
pkt_non_pf_array_size * sizeof(data->array[0]));
if (PrefilterAppendEngine(de_ctx, sgh, PrefilterPktNonPF, pkt_mask, (void *)data,
enum SignatureHookPkt hook = SIGNATURE_HOOK_PKT_NOT_SET; // TODO review
if (PrefilterAppendEngine(de_ctx, sgh, PrefilterPktNonPF, pkt_mask, hook, (void *)data,
PrefilterNonPFDataFree, "packet:non_pf") < 0) {
SCFree(data);
goto error;
}
}
if (pkt_hook_flow_start_non_pf_array_size) {
struct PrefilterNonPFData *data = SCCalloc(
1, sizeof(*data) + pkt_hook_flow_start_non_pf_array_size * sizeof(data->array[0]));
if (data == NULL)
goto error;
data->size = pkt_hook_flow_start_non_pf_array_size;
memcpy((uint8_t *)&data->array, pkt_hook_flow_start_non_pf_array,
pkt_hook_flow_start_non_pf_array_size * sizeof(data->array[0]));
SCLogDebug("packet:flow_start:non_pf added with %u rules", data->size);
enum SignatureHookPkt hook = SIGNATURE_HOOK_PKT_FLOW_START;
if (PrefilterAppendEngine(de_ctx, sgh,
PrefilterPktNonPFHookFlowStart, // TODO no longer needed to have a dedicated
// callback
pkt_hook_flow_start_mask, hook, (void *)data, PrefilterNonPFDataFree,
"packet:flow_start:non_pf") < 0) {
SCFree(data);
goto error;
}
}
if (frame_non_pf_array_size) {
SCLogDebug("%u frame non-pf sigs", frame_non_pf_array_size);
struct PrefilterNonPFData *data =
@ -1006,6 +1095,8 @@ static int SetupNonPrefilter(DetectEngineCtx *de_ctx, SigGroupHead *sgh)
}
}
SCFree(pkt_hook_flow_start_non_pf_array);
pkt_hook_flow_start_non_pf_array = NULL;
SCFree(pkt_non_pf_array);
pkt_non_pf_array = NULL;
SCFree(frame_non_pf_array);
@ -1016,6 +1107,7 @@ error:
if (tx_engines_hash) {
HashListTableFree(tx_engines_hash);
}
SCFree(pkt_hook_flow_start_non_pf_array);
SCFree(pkt_non_pf_array);
SCFree(frame_non_pf_array);
return -1;
@ -1061,7 +1153,11 @@ int PrefilterSetupRuleGroup(DetectEngineCtx *de_ctx, SigGroupHead *sgh)
for (el = sgh->init->pkt_engines ; el != NULL; el = el->next) {
e->local_id = el->id;
e->cb.Prefilter = el->Prefilter;
e->ctx.pkt_mask = el->pkt_mask;
e->ctx.pkt.mask = el->pkt_mask;
// TODO right now we represent the hook in a u8 in the prefilter engine for space
// reasons.
BUG_ON(el->pkt_hook >= 8);
e->ctx.pkt.hook = (uint8_t)el->pkt_hook;
e->pectx = el->pectx;
el->pectx = NULL; // e now owns the ctx
e->gid = el->gid;
@ -1086,7 +1182,11 @@ int PrefilterSetupRuleGroup(DetectEngineCtx *de_ctx, SigGroupHead *sgh)
for (el = sgh->init->payload_engines ; el != NULL; el = el->next) {
e->local_id = el->id;
e->cb.Prefilter = el->Prefilter;
e->ctx.pkt_mask = el->pkt_mask;
e->ctx.pkt.mask = el->pkt_mask;
// TODO right now we represent the hook in a u8 in the prefilter engine for space
// reasons.
BUG_ON(el->pkt_hook >= 8);
e->ctx.pkt.hook = (uint8_t)el->pkt_hook;
e->pectx = el->pectx;
el->pectx = NULL; // e now owns the ctx
e->gid = el->gid;
@ -1518,8 +1618,9 @@ int PrefilterGenericMpmPktRegister(DetectEngineCtx *de_ctx, SigGroupHead *sgh, M
pectx->mpm_ctx = mpm_ctx;
pectx->transforms = &mpm_reg->transforms;
enum SignatureHookPkt hook = SIGNATURE_HOOK_PKT_NOT_SET; // TODO review
int r = PrefilterAppendEngine(
de_ctx, sgh, PrefilterMpmPkt, 0, pectx, PrefilterMpmPktFree, mpm_reg->pname);
de_ctx, sgh, PrefilterMpmPkt, 0, hook, pectx, PrefilterMpmPktFree, mpm_reg->pname);
if (r != 0) {
SCFree(pectx);
}

@ -54,7 +54,8 @@ void Prefilter(DetectEngineThreadCtx *, const SigGroupHead *, Packet *p, const u
const SignatureMask mask);
int PrefilterAppendEngine(DetectEngineCtx *de_ctx, SigGroupHead *sgh, PrefilterPktFn PrefilterFunc,
SignatureMask mask, void *pectx, void (*FreeFunc)(void *pectx), const char *name);
SignatureMask mask, enum SignatureHookPkt hook, void *pectx, void (*FreeFunc)(void *pectx),
const char *name);
void PrefilterPostRuleMatch(
DetectEngineThreadCtx *det_ctx, const SigGroupHead *sgh, Packet *p, Flow *f);

@ -1332,8 +1332,9 @@ static int PrefilterSetupFlowbits(DetectEngineCtx *de_ctx, SigGroupHead *sgh)
/* finally, register the states with their engines */
static const char *g_prefilter_flowbits_isset = "flowbits:isset";
if (isset_ctx != NULL) {
PrefilterAppendEngine(de_ctx, sgh, PrefilterFlowbitMatch, SIG_MASK_REQUIRE_FLOW, isset_ctx,
PrefilterFlowbitFree, g_prefilter_flowbits_isset);
enum SignatureHookPkt hook = SIGNATURE_HOOK_PKT_NOT_SET; // TODO review
PrefilterAppendEngine(de_ctx, sgh, PrefilterFlowbitMatch, SIG_MASK_REQUIRE_FLOW, hook,
isset_ctx, PrefilterFlowbitFree, g_prefilter_flowbits_isset);
SCLogDebug("isset: added prefilter engine");
if (set_ctx != NULL && !RB_EMPTY(&set_ctx->fb_tree)) {

@ -1198,6 +1198,7 @@ void DetectRegisterAppLayerHookLists(void)
}
}
#ifdef DEBUG
static const char *SignatureHookTypeToString(enum SignatureHookType t)
{
switch (t) {
@ -1205,11 +1206,68 @@ static const char *SignatureHookTypeToString(enum SignatureHookType t)
return "not_set";
case SIGNATURE_HOOK_TYPE_APP:
return "app";
// case SIGNATURE_HOOK_TYPE_PKT:
// return "pkt";
case SIGNATURE_HOOK_TYPE_PKT:
return "pkt";
}
return "unknown";
}
#endif
static enum SignatureHookPkt HookPktFromString(const char *str)
{
if (strcmp(str, "flow_start") == 0) {
return SIGNATURE_HOOK_PKT_FLOW_START;
} else if (strcmp(str, "all") == 0) {
return SIGNATURE_HOOK_PKT_ALL;
}
return SIGNATURE_HOOK_PKT_NOT_SET;
}
#ifdef DEBUG
static const char *HookPktToString(const enum SignatureHookPkt ph)
{
switch (ph) {
case SIGNATURE_HOOK_PKT_NOT_SET:
return "not set";
case SIGNATURE_HOOK_PKT_FLOW_START:
return "flow_start";
case SIGNATURE_HOOK_PKT_ALL:
return "all";
}
return "error";
}
#endif
static SignatureHook SetPktHook(const char *hook_str)
{
SignatureHook h = {
.type = SIGNATURE_HOOK_TYPE_PKT,
.t.pkt.ph = HookPktFromString(hook_str),
};
return h;
}
/**
* \param proto_hook string of protocol and hook, e.g. dns:request_complete
*/
static int SigParseProtoHookPkt(Signature *s, const char *proto_hook, const char *p, const char *h)
{
enum SignatureHookPkt hook = HookPktFromString(h);
if (hook != SIGNATURE_HOOK_PKT_NOT_SET) {
s->init_data->hook = SetPktHook(h);
if (s->init_data->hook.t.pkt.ph == SIGNATURE_HOOK_PKT_NOT_SET) {
return -1; // TODO unreachable?
}
} else {
SCLogError("unknown pkt hook %s", h);
return -1;
}
SCLogDebug("protocol:%s hook:%s: type:%s parsed hook:%s", p, h,
SignatureHookTypeToString(s->init_data->hook.type),
HookPktToString(s->init_data->hook.t.pkt.ph));
return 0;
}
static SignatureHook SetAppHook(const AppProto alproto, int progress)
{
@ -1334,6 +1392,13 @@ static int SigParseProto(Signature *s, const char *protostr)
p, p);
SCReturnInt(-1);
}
} else if (h != NULL) {
SCLogDebug("non-app-layer rule with %s:%s", p, h);
if (SigParseProtoHookPkt(s, protostr, p, h) < 0) {
SCLogError("protocol \"%s\" does not support hook \"%s\"", p, h);
SCReturnInt(-1);
}
}
/* if any of these flags are set they are set in a mutually exclusive
@ -2702,6 +2767,14 @@ static Signature *SigInitHelper(DetectEngineCtx *de_ctx, const char *sigstr,
}
}
if (sig->init_data->hook.type == SIGNATURE_HOOK_TYPE_PKT) {
if (sig->init_data->hook.t.pkt.ph == SIGNATURE_HOOK_PKT_FLOW_START) {
if ((sig->flags & SIG_FLAG_TOSERVER) != 0) {
sig->init_data->init_flags |= SIG_FLAG_INIT_FLOW;
}
}
}
if (!(sig->init_data->init_flags & SIG_FLAG_INIT_FLOW)) {
if ((sig->flags & (SIG_FLAG_TOSERVER|SIG_FLAG_TOCLIENT)) == 0) {
sig->flags |= SIG_FLAG_TOSERVER;

@ -547,9 +547,15 @@ typedef struct SignatureInitDataBuffer_ {
SigMatch *tail;
} SignatureInitDataBuffer;
enum SignatureHookPkt {
SIGNATURE_HOOK_PKT_NOT_SET,
SIGNATURE_HOOK_PKT_FLOW_START,
SIGNATURE_HOOK_PKT_ALL, /**< match each packet */
};
enum SignatureHookType {
SIGNATURE_HOOK_TYPE_NOT_SET,
// SIGNATURE_HOOK_TYPE_PKT,
SIGNATURE_HOOK_TYPE_PKT,
SIGNATURE_HOOK_TYPE_APP,
};
@ -565,6 +571,9 @@ typedef struct SignatureHook_ {
* specific progress value. */
int app_progress;
} app;
struct {
enum SignatureHookPkt ph;
} pkt;
} t;
} SignatureHook;
@ -1452,6 +1461,8 @@ typedef struct PrefilterEngineList_ {
SignatureMask pkt_mask; /**< mask for pkt engines */
enum SignatureHookPkt pkt_hook;
/** Context for matching. Might be MpmCtx for MPM engines, other ctx'
* for other engines. */
void *pectx;
@ -1479,13 +1490,19 @@ typedef struct PrefilterEngine_ {
AppProto alproto;
union {
SignatureMask pkt_mask; /**< mask for pkt engines */
struct {
SignatureMask mask; /**< mask for pkt engines */
uint8_t hook; /**< enum SignatureHookPkt */
} pkt;
/** Minimal Tx progress we need before running the engine. Only used
* with Tx Engine */
uint8_t tx_min_progress;
uint8_t frame_type;
} ctx;
bool is_last;
bool is_last_for_progress;
/** Context for matching. Might be MpmCtx for MPM engines, other ctx'
* for other engines. */
void *pectx;
@ -1500,8 +1517,6 @@ typedef struct PrefilterEngine_ {
/* global id for this prefilter */
uint32_t gid;
bool is_last;
bool is_last_for_progress;
} PrefilterEngine;
typedef struct SigGroupHeadInitData_ {

@ -438,6 +438,7 @@ void FlowHandlePacketUpdate(Flow *f, Packet *p, ThreadVars *tv, DecodeThreadVars
if (FlowUpdateSeenFlag(p)) {
f->flags |= FLOW_TO_DST_SEEN;
p->flowflags |= FLOW_PKT_TOSERVER_FIRST;
p->pkt_hooks |= BIT_U16(SIGNATURE_HOOK_PKT_FLOW_START);
}
}
/* xfer proto detect ts flag to first packet in ts dir */
@ -463,6 +464,7 @@ void FlowHandlePacketUpdate(Flow *f, Packet *p, ThreadVars *tv, DecodeThreadVars
if (FlowUpdateSeenFlag(p)) {
f->flags |= FLOW_TO_SRC_SEEN;
p->flowflags |= FLOW_PKT_TOCLIENT_FIRST;
p->pkt_hooks |= BIT_U16(SIGNATURE_HOOK_PKT_FLOW_START);
}
}
/* xfer proto detect tc flag to first packet in tc dir */

@ -98,6 +98,7 @@ void PacketReinit(Packet *p)
PACKET_FREE_EXTDATA(p);
p->app_update_direction = 0;
p->sig_mask = 0;
p->pkt_hooks = 0;
p->flags = 0;
p->flowflags = 0;
p->pkt_src = 0;

Loading…
Cancel
Save