/* 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;
*/
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;
*/
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;
*/
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);
}
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;
}
}
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);
/* 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);
}
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;
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;
}
}
+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.
}
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;
}
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;
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);
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 =
}
}
+ 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);
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;
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;
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;
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);
}
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);
/* 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)) {
}
}
+#ifdef DEBUG
static const char *SignatureHookTypeToString(enum SignatureHookType t)
{
switch (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)
{
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
}
}
+ 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;
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,
};
* specific progress value. */
int app_progress;
} app;
+ struct {
+ enum SignatureHookPkt ph;
+ } pkt;
} t;
} SignatureHook;
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;
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;
/* global id for this prefilter */
uint32_t gid;
- bool is_last;
- bool is_last_for_progress;
} PrefilterEngine;
typedef struct SigGroupHeadInitData_ {
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 */
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 */
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;