From: Victor Julien Date: Thu, 23 Jan 2025 15:11:30 +0000 (+0100) Subject: detect: start of pkt hooks X-Git-Tag: suricata-8.0.0-beta1~25 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=f60e1efc8aa5cfd8d95eec345339507772a6233b;p=thirdparty%2Fsuricata.git 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. --- diff --git a/src/decode.h b/src/decode.h index ed4882314b..4753497298 100644 --- a/src/decode.h +++ b/src/decode.h @@ -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; diff --git a/src/detect-engine-build.c b/src/detect-engine-build.c index c36e54db1a..7c7429fdb4 100644 --- a/src/detect-engine-build.c +++ b/src/detect-engine-build.c @@ -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); } diff --git a/src/detect-engine-prefilter-common.c b/src/detect-engine-prefilter-common.c index 3c3321b8f5..6c5ff62548 100644 --- a/src/detect-engine-prefilter-common.c +++ b/src/detect-engine-prefilter-common.c @@ -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); diff --git a/src/detect-engine-prefilter.c b/src/detect-engine-prefilter.c index 55f6c74163..ea000cc04d 100644 --- a/src/detect-engine-prefilter.c +++ b/src/detect-engine-prefilter.c @@ -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); } diff --git a/src/detect-engine-prefilter.h b/src/detect-engine-prefilter.h index 222f5a1528..3dbd2a45e4 100644 --- a/src/detect-engine-prefilter.h +++ b/src/detect-engine-prefilter.h @@ -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); diff --git a/src/detect-flowbits.c b/src/detect-flowbits.c index face78c902..79f3bb8806 100644 --- a/src/detect-flowbits.c +++ b/src/detect-flowbits.c @@ -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)) { diff --git a/src/detect-parse.c b/src/detect-parse.c index 15b2cc3677..4647aa4f68 100644 --- a/src/detect-parse.c +++ b/src/detect-parse.c @@ -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; diff --git a/src/detect.h b/src/detect.h index 8eb1d94af6..4c353c1a6d 100644 --- a/src/detect.h +++ b/src/detect.h @@ -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_ { diff --git a/src/flow.c b/src/flow.c index a07ea762a1..04a8ff5f93 100644 --- a/src/flow.c +++ b/src/flow.c @@ -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 */ diff --git a/src/packet.c b/src/packet.c index 558d366e6e..5254c8cde9 100644 --- a/src/packet.c +++ b/src/packet.c @@ -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;