]> git.ipfire.org Git - thirdparty/suricata.git/commitdiff
detect/engine: support frames
authorVictor Julien <vjulien@oisf.net>
Fri, 3 Dec 2021 06:53:32 +0000 (07:53 +0100)
committerVictor Julien <vjulien@oisf.net>
Tue, 18 Jan 2022 11:21:51 +0000 (12:21 +0100)
Implement the low level detect engine support for inspecting frames,
including MPM, transforms and inspect API's.

16 files changed:
src/Makefile.am
src/detect-engine-build.c
src/detect-engine-content-inspection.h
src/detect-engine-frame.c [new file with mode: 0644]
src/detect-engine-frame.h [new file with mode: 0644]
src/detect-engine-mpm.c
src/detect-engine-mpm.h
src/detect-engine-prefilter.c
src/detect-engine-prefilter.h
src/detect-engine-siggroup.c
src/detect-engine.c
src/detect-engine.h
src/detect.c
src/detect.h
src/suricata-common.h
src/util-profiling.c

index 32dd12a2d6e68cda5f335de52a5534ca54eb4b06..3ac3f07b06bb82289b660ebb603a9d7ba2d654e0 100755 (executable)
@@ -139,6 +139,7 @@ noinst_HEADERS = \
        detect-engine-enip.h \
        detect-engine-event.h \
        detect-engine-file.h \
+       detect-engine-frame.h \
        detect-engine.h \
        detect-engine-iponly.h \
        detect-engine-loader.h \
@@ -725,6 +726,7 @@ libsuricata_c_a_SOURCES = \
        detect-engine-enip.c \
        detect-engine-event.c \
        detect-engine-file.c \
+       detect-engine-frame.c \
        detect-engine-iponly.c \
        detect-engine-loader.c \
        detect-engine-mpm.c \
index 9c9f7992a296b7723b514ceda2f5ae3327a8e178..108514c96230743e6138df3e9ebd234f6c7039f5 100644 (file)
@@ -1968,6 +1968,7 @@ int SigGroupBuild(DetectEngineCtx *de_ctx)
     int r = DetectMpmPrepareBuiltinMpms(de_ctx);
     r |= DetectMpmPrepareAppMpms(de_ctx);
     r |= DetectMpmPreparePktMpms(de_ctx);
+    r |= DetectMpmPrepareFrameMpms(de_ctx);
     if (r != 0) {
         FatalError(SC_ERR_FATAL, "initializing the detection engine failed");
     }
index 6d9f87d98b9cd8a2928d98f90cde696827e8c4b4..149109e60e081ea2ebd481862c09ce759ad737ff 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (C) 2007-2011 Open Information Security Foundation
+/* Copyright (C) 2007-2021 Open Information Security Foundation
  *
  * You can copy, redistribute or modify this Program under the terms of
  * the GNU General Public License version 2 as published by the Free
  *  we're inspecting
  */
 enum {
-    DETECT_ENGINE_CONTENT_INSPECTION_MODE_PAYLOAD = 0,  /* enables 'replace' logic */
+    DETECT_ENGINE_CONTENT_INSPECTION_MODE_PAYLOAD = 0, /* enables 'replace' logic */
     DETECT_ENGINE_CONTENT_INSPECTION_MODE_HEADER,
     DETECT_ENGINE_CONTENT_INSPECTION_MODE_STREAM,
+    DETECT_ENGINE_CONTENT_INSPECTION_MODE_FRAME,
     DETECT_ENGINE_CONTENT_INSPECTION_MODE_STATE,
 };
 
diff --git a/src/detect-engine-frame.c b/src/detect-engine-frame.c
new file mode 100644 (file)
index 0000000..5ca268a
--- /dev/null
@@ -0,0 +1,357 @@
+/* Copyright (C) 2021 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ */
+
+#include "suricata-common.h"
+#include "suricata.h"
+
+#include "app-layer-parser.h"
+#include "app-layer-frames.h"
+
+#include "detect-engine.h"
+#include "detect-engine-prefilter.h"
+#include "detect-engine-content-inspection.h"
+#include "detect-engine-mpm.h"
+#include "detect-engine-frame.h"
+
+#include "stream-tcp.h"
+
+#include "util-profiling.h"
+#include "util-validate.h"
+#include "util-print.h"
+
+void DetectRunPrefilterFrame(DetectEngineThreadCtx *det_ctx, const SigGroupHead *sgh, Packet *p,
+        const Frames *frames, const Frame *frame, const AppProto alproto, const uint32_t idx)
+{
+    SCLogDebug("pcap_cnt %" PRIu64, p->pcap_cnt);
+    PrefilterEngine *engine = sgh->frame_engines;
+    do {
+        BUG_ON(engine->alproto == ALPROTO_UNKNOWN);
+        if (engine->alproto == alproto && engine->ctx.frame_type == frame->type) {
+            SCLogDebug("frame %p engine %p", frame, engine);
+            PREFILTER_PROFILING_START;
+            engine->cb.PrefilterFrame(det_ctx, engine->pectx, p, frames, frame, idx);
+            PREFILTER_PROFILING_END(det_ctx, engine->gid);
+        }
+        if (engine->is_last)
+            break;
+        engine++;
+    } while (1);
+}
+
+/* generic mpm for frame engines */
+
+// TODO same as Generic?
+typedef struct PrefilterMpmFrameCtx {
+    int list_id;
+    const MpmCtx *mpm_ctx;
+    const DetectEngineTransforms *transforms;
+} PrefilterMpmFrameCtx;
+
+/** \brief Generic Mpm prefilter callback
+ *
+ *  \param det_ctx detection engine thread ctx
+ *  \param frames container for the frames
+ *  \param frame frame to inspect
+ *  \param pectx inspection context
+ */
+static void PrefilterMpmFrame(DetectEngineThreadCtx *det_ctx, const void *pectx, Packet *p,
+        const Frames *frames, const Frame *frame, const uint32_t idx)
+{
+    SCEnter();
+
+    const PrefilterMpmFrameCtx *ctx = (const PrefilterMpmFrameCtx *)pectx;
+    const MpmCtx *mpm_ctx = ctx->mpm_ctx;
+    SCLogDebug("running on list %d -> frame field type %u", ctx->list_id, frame->type);
+    // BUG_ON(frame->type != ctx->type);
+
+    InspectionBuffer *buffer = DetectFrame2InspectBuffer(
+            det_ctx, ctx->transforms, p, frames, frame, ctx->list_id, idx, true);
+    if (buffer == NULL)
+        return;
+
+    const uint32_t data_len = buffer->inspect_len;
+    const uint8_t *data = buffer->inspect;
+
+    SCLogDebug("mpm'ing buffer:");
+    // SCLogDebug("frame: %p", frame);
+    // PrintRawDataFp(stdout, data, MIN(32, data_len));
+
+    if (data != NULL && data_len >= mpm_ctx->minlen) {
+        (void)mpm_table[mpm_ctx->mpm_type].Search(
+                mpm_ctx, &det_ctx->mtcu, &det_ctx->pmq, data, data_len);
+        SCLogDebug("det_ctx->pmq.rule_id_array_cnt %u", det_ctx->pmq.rule_id_array_cnt);
+    }
+}
+
+static void PrefilterMpmFrameFree(void *ptr)
+{
+    SCFree(ptr);
+}
+
+int PrefilterGenericMpmFrameRegister(DetectEngineCtx *de_ctx, SigGroupHead *sgh, MpmCtx *mpm_ctx,
+        const DetectBufferMpmRegistery *mpm_reg, int list_id)
+{
+    SCEnter();
+    PrefilterMpmFrameCtx *pectx = SCCalloc(1, sizeof(*pectx));
+    if (pectx == NULL)
+        return -1;
+    pectx->list_id = list_id;
+    BUG_ON(mpm_reg->frame_v1.alproto == ALPROTO_UNKNOWN);
+    pectx->mpm_ctx = mpm_ctx;
+    pectx->transforms = &mpm_reg->transforms;
+
+    int r = PrefilterAppendFrameEngine(de_ctx, sgh, PrefilterMpmFrame, mpm_reg->frame_v1.alproto,
+            mpm_reg->frame_v1.type, pectx, PrefilterMpmFrameFree, mpm_reg->pname);
+    if (r != 0) {
+        SCFree(pectx);
+    }
+    return r;
+}
+
+int DetectRunFrameInspectRule(ThreadVars *tv, DetectEngineThreadCtx *det_ctx, const Signature *s,
+        Flow *f, Packet *p, const Frames *frames, const Frame *frame, const uint32_t idx)
+{
+    BUG_ON(s->frame_inspect == NULL);
+
+    SCLogDebug("inspecting rule %u against frame %p/%" PRIi64 "/%s", s->id, frame, frame->id,
+            AppLayerParserGetFrameNameById(f->proto, f->alproto, frame->type));
+
+    for (DetectEngineFrameInspectionEngine *e = s->frame_inspect; e != NULL; e = e->next) {
+        if (frame->type == e->type) {
+            // TODO check alproto, direction?
+
+            // TODO there should be only one inspect engine for this frame, ever?
+
+            if (e->v1.Callback(det_ctx, e, s, p, frames, frame, idx) == true) {
+                SCLogDebug("sid %u: e %p Callback returned true", s->id, e);
+                return true;
+            }
+            SCLogDebug("sid %u: e %p Callback returned false", s->id, e);
+        } else {
+            SCLogDebug(
+                    "sid %u: e %p not for frame type %u (want %u)", s->id, e, frame->type, e->type);
+        }
+    }
+    return false;
+}
+
+InspectionBuffer *DetectFrame2InspectBuffer(DetectEngineThreadCtx *det_ctx,
+        const DetectEngineTransforms *transforms, Packet *p, const Frames *frames,
+        const Frame *frame, const int list_id, const uint32_t idx, const bool first)
+{
+    // TODO do we really need multiple buffer support here?
+    InspectionBuffer *buffer = InspectionBufferMultipleForListGet(det_ctx, list_id, idx);
+    if (buffer == NULL)
+        return NULL;
+    if (!first && buffer->inspect != NULL)
+        return buffer;
+
+    BUG_ON(p->flow == NULL);
+    BUG_ON(p->flow->protoctx == NULL);
+    TcpSession *ssn = p->flow->protoctx;
+    TcpStream *stream;
+    if (PKT_IS_TOSERVER(p)) {
+        stream = &ssn->client;
+    } else {
+        stream = &ssn->server;
+    }
+
+    /*
+        stream:   [s                                           ]
+        frame:          [r               ]
+        progress:        |>p
+            rel_offset: 10, len 100
+            progress: 20
+            avail: 90 (complete)
+
+        stream:   [s            ]
+        frame:          [r               ]
+        progress:        |>p
+            stream: 0, len 59
+            rel_offset: 10, len 100
+            progress: 20
+            avail: 30 (incomplete)
+
+        stream:          [s                                           ]
+        frame:        [r               ]
+        progress:              |>p
+            stream: 0, len 200
+            rel_offset: -30, len 100
+            progress: 20
+            avail: 50 (complete)
+     */
+
+    SCLogDebug("frame %" PRIi64 ", len %" PRIi64, frame->id, frame->len);
+
+    uint32_t data_len = 0;
+    const uint8_t *data = NULL;
+
+    uint64_t offset = STREAM_BASE_OFFSET(stream);
+    if (frame->rel_offset > 0 || frames->progress_rel) {
+        uint64_t frame_offset = 0;
+        if (frame->rel_offset >= 0) {
+            frame_offset = MAX((uint64_t)frame->rel_offset, (uint64_t)frames->progress_rel);
+        } else {
+            frame_offset = (uint64_t)frames->progress_rel;
+        }
+        offset += frame_offset;
+    }
+
+    const bool eof = ssn->state == TCP_CLOSED || PKT_IS_PSEUDOPKT(p);
+
+    const uint64_t usable = StreamTcpGetUsable(stream, eof);
+    if (usable <= offset)
+        return NULL;
+
+    // TODO GAP handling
+    if (StreamingBufferGetDataAtOffset(&stream->sb, &data, &data_len, offset) == 0) {
+        return NULL;
+    }
+
+    const uint64_t data_right_edge = offset + data_len;
+    if (data_right_edge > usable)
+        data_len = usable - offset;
+
+    const int64_t frame_start_abs_offset = frame->rel_offset + (int64_t)STREAM_BASE_OFFSET(stream);
+    const uint64_t usable_right_edge = MIN(data_right_edge, usable);
+
+    bool have_end = false;
+
+    if (frame->len > 0) {
+        const int64_t frame_avail_data_abs = (int64_t)usable_right_edge;
+        const int64_t frame_end_abs_offset = frame_start_abs_offset + frame->len;
+        have_end = (int64_t)usable_right_edge >= frame_end_abs_offset;
+
+        SCLogDebug("frame_end_abs_offset %" PRIi64 ", usable_right_edge %" PRIu64,
+                frame_end_abs_offset, usable_right_edge);
+
+        const int64_t avail_from_frame = MIN(frame_end_abs_offset, frame_avail_data_abs) - offset;
+        if (avail_from_frame < (int64_t)data_len) {
+            SCLogDebug("adjusted data len from %u to %" PRIi64, data_len, avail_from_frame);
+            data_len = (uint32_t)avail_from_frame;
+        }
+    }
+    const bool have_start = frame_start_abs_offset == (int64_t)offset;
+
+    if (data == NULL || data_len == 0) {
+        return NULL;
+    }
+
+    // TODO use eof too?
+    SCLogDebug("stream->min_inspect_depth %u", stream->min_inspect_depth);
+    if (data_len < frame->len && data_len < stream->min_inspect_depth) {
+        if (ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED || ssn->state == TCP_CLOSED ||
+                stream->flags & STREAMTCP_STREAM_FLAG_DEPTH_REACHED) {
+            SCLogDebug("EOF use available data: %u bytes", data_len);
+        } else {
+            SCLogDebug("not enough data to inspect now: have %u, want %u", data_len,
+                    stream->min_inspect_depth);
+            return NULL;
+        }
+    }
+
+    const uint8_t ci_flags =
+            (have_start ? DETECT_CI_FLAGS_START : 0) | (have_end ? DETECT_CI_FLAGS_END : 0);
+    SCLogDebug("packet %" PRIu64 " -> frame %p/%" PRIi64 "/%s rel_offset %" PRIi64
+               " type %u len %" PRIi64 " ci_flags %02x (start:%s, end:%s)",
+            p->pcap_cnt, frame, frame->id,
+            AppLayerParserGetFrameNameById(p->flow->proto, p->flow->alproto, frame->type),
+            frame->rel_offset, frame->type, frame->len, ci_flags,
+            (ci_flags & DETECT_CI_FLAGS_START) ? "true" : "false",
+            (ci_flags & DETECT_CI_FLAGS_END) ? "true" : "false");
+    // PrintRawDataFp(stdout, data, MIN(32,data_len));
+
+    InspectionBufferSetupMulti(buffer, transforms, data, data_len);
+    buffer->inspect_offset = frame->rel_offset < 0 ? -1 * frame->rel_offset : 0; // TODO review/test
+    buffer->flags = ci_flags;
+    return buffer;
+}
+
+/**
+ * \brief Do the content inspection & validation for a signature
+ *
+ * \param de_ctx Detection engine context
+ * \param det_ctx Detection engine thread context
+ * \param s Signature to inspect
+ * \param p Packet
+ * \param frame stream frame to inspect
+ *
+ * \retval 0 no match.
+ * \retval 1 match.
+ */
+int DetectEngineInspectFrameBufferGeneric(DetectEngineThreadCtx *det_ctx,
+        const DetectEngineFrameInspectionEngine *engine, const Signature *s, Packet *p,
+        const Frames *frames, const Frame *frame, const uint32_t idx)
+{
+    const int list_id = engine->sm_list;
+    SCLogDebug("running inspect on %d", list_id);
+
+    SCLogDebug("list %d transforms %p", engine->sm_list, engine->v1.transforms);
+
+    /* if prefilter didn't already run, we need to consider transformations */
+    const DetectEngineTransforms *transforms = NULL;
+    if (!engine->mpm) {
+        transforms = engine->v1.transforms;
+    }
+
+    const InspectionBuffer *buffer =
+            DetectFrame2InspectBuffer(det_ctx, transforms, p, frames, frame, list_id, idx, false);
+    if (unlikely(buffer == NULL)) {
+        return DETECT_ENGINE_INSPECT_SIG_NO_MATCH;
+    }
+
+    const uint32_t data_len = buffer->inspect_len;
+    const uint8_t *data = buffer->inspect;
+    const uint64_t offset = buffer->inspect_offset;
+
+    det_ctx->discontinue_matching = 0;
+    det_ctx->buffer_offset = 0;
+    det_ctx->inspection_recursion_counter = 0;
+#ifdef DEBUG
+    const uint8_t ci_flags = buffer->flags;
+    SCLogDebug("frame %p rel_offset %" PRIi64 " type %u len %" PRIi64
+               " ci_flags %02x (start:%s, end:%s)",
+            frame, frame->rel_offset, frame->type, frame->len, ci_flags,
+            (ci_flags & DETECT_CI_FLAGS_START) ? "true" : "false",
+            (ci_flags & DETECT_CI_FLAGS_END) ? "true" : "false");
+    SCLogDebug("buffer %p offset %" PRIu64 " len %u ci_flags %02x (start:%s, end:%s)", buffer,
+            buffer->inspect_offset, buffer->inspect_len, ci_flags,
+            (ci_flags & DETECT_CI_FLAGS_START) ? "true" : "false",
+            (ci_flags & DETECT_CI_FLAGS_END) ? "true" : "false");
+    // PrintRawDataFp(stdout, data, data_len);
+    // PrintRawDataFp(stdout, data, MIN(64, data_len));
+#endif
+    BUG_ON((int64_t)data_len > frame->len);
+
+    // TODO don't call if matching needs frame end and DETECT_CI_FLAGS_END not set
+    // TODO same for start
+    int r = DetectEngineContentInspection(det_ctx->de_ctx, det_ctx, s, engine->smd, p, p->flow,
+            (uint8_t *)data, data_len, offset, buffer->flags,
+            DETECT_ENGINE_CONTENT_INSPECTION_MODE_FRAME);
+    if (r == 1) {
+        return DETECT_ENGINE_INSPECT_SIG_MATCH;
+    } else {
+        return DETECT_ENGINE_INSPECT_SIG_NO_MATCH;
+    }
+}
diff --git a/src/detect-engine-frame.h b/src/detect-engine-frame.h
new file mode 100644 (file)
index 0000000..82dc144
--- /dev/null
@@ -0,0 +1,39 @@
+/* Copyright (C) 2021 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ */
+
+#include "app-layer-frames.h"
+
+void DetectRunPrefilterFrame(DetectEngineThreadCtx *det_ctx, const SigGroupHead *sgh, Packet *p,
+        const Frames *frames, const Frame *frame, const AppProto alproto, const uint32_t idx);
+int DetectRunFrameInspectRule(ThreadVars *tv, DetectEngineThreadCtx *det_ctx, const Signature *s,
+        Flow *f, Packet *p, const Frames *frames, const Frame *frame, const uint32_t idx);
+
+int PrefilterGenericMpmFrameRegister(DetectEngineCtx *de_ctx, SigGroupHead *sgh, MpmCtx *mpm_ctx,
+        const DetectBufferMpmRegistery *mpm_reg, int list_id);
+InspectionBuffer *DetectFrame2InspectBuffer(DetectEngineThreadCtx *det_ctx,
+        const DetectEngineTransforms *transforms, Packet *p, const Frames *frames,
+        const Frame *frame, const int list_id, const uint32_t idx, const bool first);
+int DetectEngineInspectFrameBufferGeneric(DetectEngineThreadCtx *det_ctx,
+        const DetectEngineFrameInspectionEngine *engine, const Signature *s, Packet *p,
+        const Frames *frames, const Frame *frame, const uint32_t idx);
index 1efe53baea92e70780584c42ad2f4e40879a7f24..c55c13f2f7fd3be86ab3e01c164a7a13e0d16843 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (C) 2007-2014 Open Information Security Foundation
+/* Copyright (C) 2007-2021 Open Information Security Foundation
  *
  * You can copy, redistribute or modify this Program under the terms of
  * the GNU General Public License version 2 as published by the Free
@@ -79,8 +79,8 @@ const char *builtin_mpms[] = {
  * Keywords are registered at engine start up
  */
 
-static DetectBufferMpmRegistery *g_mpm_list[DETECT_BUFFER_MPM_TYPE_SIZE] = { NULL, NULL };
-static int g_mpm_list_cnt[DETECT_BUFFER_MPM_TYPE_SIZE] = { 0, 0 };
+static DetectBufferMpmRegistery *g_mpm_list[DETECT_BUFFER_MPM_TYPE_SIZE] = { NULL, NULL, NULL };
+static int g_mpm_list_cnt[DETECT_BUFFER_MPM_TYPE_SIZE] = { 0, 0, 0 };
 
 /** \brief register a MPM engine
  *
@@ -287,6 +287,237 @@ int DetectMpmPrepareAppMpms(DetectEngineCtx *de_ctx)
     return r;
 }
 
+/** \brief register a MPM engine
+ *
+ *  \note to be used at start up / registration only. Errors are fatal.
+ */
+void DetectFrameMpmRegister(const char *name, int direction, int priority,
+        int (*PrefilterRegister)(DetectEngineCtx *de_ctx, SigGroupHead *sgh, MpmCtx *mpm_ctx,
+                const DetectBufferMpmRegistery *mpm_reg, int list_id),
+        AppProto alproto, uint8_t type)
+{
+    SCLogDebug("registering %s/%d/%p/%s/%u", name, priority, PrefilterRegister,
+            AppProtoToString(alproto), type);
+
+    DetectBufferTypeSupportsMpm(name);
+    DetectBufferTypeSupportsFrames(name);
+    DetectBufferTypeSupportsTransformations(name);
+    int sm_list = DetectBufferTypeGetByName(name);
+    if (sm_list == -1) {
+        FatalError(SC_ERR_INITIALIZATION, "MPM engine registration for %s failed", name);
+    }
+
+    DetectBufferMpmRegistery *am = SCCalloc(1, sizeof(*am));
+    BUG_ON(am == NULL);
+    am->name = name;
+    snprintf(am->pname, sizeof(am->pname), "%s", am->name);
+    am->sm_list = sm_list;
+    am->direction = direction;
+    am->priority = priority;
+    am->type = DETECT_BUFFER_MPM_TYPE_FRAME;
+
+    am->PrefilterRegisterWithListId = PrefilterRegister;
+    am->frame_v1.alproto = alproto;
+    am->frame_v1.type = type;
+    SCLogDebug("type %u", type);
+    SCLogDebug("am type %u", am->frame_v1.type);
+
+    if (g_mpm_list[DETECT_BUFFER_MPM_TYPE_FRAME] == NULL) {
+        g_mpm_list[DETECT_BUFFER_MPM_TYPE_FRAME] = am;
+    } else {
+        DetectBufferMpmRegistery *t = g_mpm_list[DETECT_BUFFER_MPM_TYPE_FRAME];
+        while (t->next != NULL) {
+            t = t->next;
+        }
+        t->next = am;
+        am->id = t->id + 1;
+    }
+    g_mpm_list_cnt[DETECT_BUFFER_MPM_TYPE_FRAME]++;
+
+    SupportFastPatternForSigMatchList(sm_list, priority);
+    SCLogDebug("%s/%d done", name, sm_list);
+}
+
+/** \brief copy a mpm engine from parent_id, add in transforms */
+void DetectFrameMpmRegisterByParentId(DetectEngineCtx *de_ctx, const int id, const int parent_id,
+        DetectEngineTransforms *transforms)
+{
+    SCLogDebug("registering %d/%d", id, parent_id);
+
+    DetectBufferMpmRegistery *t = de_ctx->frame_mpms_list;
+    while (t) {
+        if (t->sm_list == parent_id) {
+            DetectBufferMpmRegistery *am = SCCalloc(1, sizeof(*am));
+            BUG_ON(am == NULL);
+            am->name = t->name;
+            snprintf(am->pname, sizeof(am->pname), "%s#%d", am->name, id);
+            am->sm_list = id; // use new id
+            am->sm_list_base = t->sm_list;
+            am->type = DETECT_BUFFER_MPM_TYPE_FRAME;
+            am->PrefilterRegisterWithListId = t->PrefilterRegisterWithListId;
+            am->frame_v1 = t->frame_v1;
+            SCLogDebug("am type %u", am->frame_v1.type);
+            am->priority = t->priority;
+            am->direction = t->direction;
+            am->sgh_mpm_context = t->sgh_mpm_context;
+            am->next = t->next;
+            if (transforms) {
+                memcpy(&am->transforms, transforms, sizeof(*transforms));
+            }
+            am->id = de_ctx->frame_mpms_list_cnt++;
+
+            DetectEngineRegisterFastPatternForId(de_ctx, am->sm_list, am->priority);
+            t->next = am;
+            SCLogDebug("copied mpm registration for %s id %u "
+                       "with parent %u",
+                    t->name, id, parent_id);
+            t = am;
+        }
+        t = t->next;
+    }
+}
+
+void DetectEngineFrameMpmRegister(DetectEngineCtx *de_ctx, const char *name, int direction,
+        int priority,
+        int (*PrefilterRegister)(DetectEngineCtx *de_ctx, SigGroupHead *sgh, MpmCtx *mpm_ctx,
+                const DetectBufferMpmRegistery *mpm_reg, int list_id),
+        AppProto alproto, uint8_t type)
+{
+    SCLogDebug("registering %s/%d/%p/%s/%u", name, priority, PrefilterRegister,
+            AppProtoToString(alproto), type);
+
+    const int sm_list = DetectEngineBufferTypeRegister(de_ctx, name);
+    if (sm_list < 0) {
+        FatalError(SC_ERR_INITIALIZATION, "MPM engine registration for %s failed", name);
+    }
+
+    DetectEngineBufferTypeSupportsMpm(de_ctx, name);
+    DetectEngineBufferTypeSupportsFrames(de_ctx, name);
+    DetectEngineBufferTypeSupportsTransformations(de_ctx, name);
+
+    DetectBufferMpmRegistery *am = SCCalloc(1, sizeof(*am));
+    BUG_ON(am == NULL);
+    am->name = name;
+    snprintf(am->pname, sizeof(am->pname), "%s", am->name);
+    am->sm_list = sm_list;
+    am->direction = direction;
+    am->priority = priority;
+    am->type = DETECT_BUFFER_MPM_TYPE_FRAME;
+
+    am->PrefilterRegisterWithListId = PrefilterRegister;
+    am->frame_v1.alproto = alproto;
+    am->frame_v1.type = type;
+
+    // TODO is it ok to do this here?
+
+    /* default to whatever the global setting is */
+    int shared = (de_ctx->sgh_mpm_ctx_cnf == ENGINE_SGH_MPM_FACTORY_CONTEXT_SINGLE);
+    /* see if we use a unique or shared mpm ctx for this type */
+    int confshared = 0;
+    if (ConfGetBool("detect.mpm.frame.shared", &confshared) == 1)
+        shared = confshared;
+
+    if (shared == 0) {
+        am->sgh_mpm_context = MPM_CTX_FACTORY_UNIQUE_CONTEXT;
+    } else {
+        am->sgh_mpm_context = MpmFactoryRegisterMpmCtxProfile(de_ctx, am->name, am->sm_list);
+    }
+
+    if (de_ctx->frame_mpms_list == NULL) {
+        de_ctx->frame_mpms_list = am;
+    } else {
+        DetectBufferMpmRegistery *t = de_ctx->frame_mpms_list;
+        while (t->next != NULL) {
+            t = t->next;
+        }
+
+        t->next = am;
+    }
+    de_ctx->frame_mpms_list_cnt++;
+
+    DetectEngineRegisterFastPatternForId(de_ctx, sm_list, priority);
+    SCLogDebug("%s/%d done", name, sm_list);
+}
+
+void DetectMpmInitializeFrameMpms(DetectEngineCtx *de_ctx)
+{
+    const DetectBufferMpmRegistery *list = g_mpm_list[DETECT_BUFFER_MPM_TYPE_FRAME];
+    while (list != NULL) {
+        DetectBufferMpmRegistery *n = SCCalloc(1, sizeof(*n));
+        BUG_ON(n == NULL);
+
+        *n = *list;
+        n->next = NULL;
+
+        if (de_ctx->frame_mpms_list == NULL) {
+            de_ctx->frame_mpms_list = n;
+        } else {
+            DetectBufferMpmRegistery *t = de_ctx->frame_mpms_list;
+            while (t->next != NULL) {
+                t = t->next;
+            }
+
+            t->next = n;
+        }
+
+        /* default to whatever the global setting is */
+        int shared = (de_ctx->sgh_mpm_ctx_cnf == ENGINE_SGH_MPM_FACTORY_CONTEXT_SINGLE);
+
+        /* see if we use a unique or shared mpm ctx for this type */
+        int confshared = 0;
+        char confstring[256] = "detect.mpm.";
+        strlcat(confstring, n->name, sizeof(confstring));
+        strlcat(confstring, ".shared", sizeof(confstring));
+        if (ConfGetBool(confstring, &confshared) == 1)
+            shared = confshared;
+
+        if (shared == 0) {
+            if (!(de_ctx->flags & DE_QUIET)) {
+                SCLogPerf("using unique mpm ctx' for %s", n->name);
+            }
+            n->sgh_mpm_context = MPM_CTX_FACTORY_UNIQUE_CONTEXT;
+        } else {
+            if (!(de_ctx->flags & DE_QUIET)) {
+                SCLogPerf("using shared mpm ctx' for %s", n->name);
+            }
+            n->sgh_mpm_context = MpmFactoryRegisterMpmCtxProfile(de_ctx, n->name, n->sm_list);
+        }
+
+        list = list->next;
+    }
+    de_ctx->frame_mpms_list_cnt = g_mpm_list_cnt[DETECT_BUFFER_MPM_TYPE_FRAME];
+    SCLogDebug("mpm: de_ctx frame_mpms_list %p %u", de_ctx->frame_mpms_list,
+            de_ctx->frame_mpms_list_cnt);
+}
+
+/**
+ *  \brief initialize mpm contexts for applayer buffers that are in
+ *         "single or "shared" mode.
+ */
+int DetectMpmPrepareFrameMpms(DetectEngineCtx *de_ctx)
+{
+    SCLogDebug("preparing frame mpm");
+    int r = 0;
+    const DetectBufferMpmRegistery *am = de_ctx->frame_mpms_list;
+    while (am != NULL) {
+        SCLogDebug("am %p %s sgh_mpm_context %d", am, am->name, am->sgh_mpm_context);
+        SCLogDebug("%s", am->name);
+        if (am->sgh_mpm_context != MPM_CTX_FACTORY_UNIQUE_CONTEXT) {
+            int dir = (am->direction == SIG_FLAG_TOSERVER) ? 1 : 0;
+            MpmCtx *mpm_ctx = MpmFactoryGetMpmCtxForProfile(de_ctx, am->sgh_mpm_context, dir);
+            SCLogDebug("%s: %d mpm_Ctx %p", am->name, r, mpm_ctx);
+            if (mpm_ctx != NULL) {
+                if (mpm_table[de_ctx->mpm_matcher].Prepare != NULL) {
+                    r |= mpm_table[de_ctx->mpm_matcher].Prepare(mpm_ctx);
+                    SCLogDebug("%s: %d", am->name, r);
+                }
+            }
+        }
+        am = am->next;
+    }
+    return r;
+}
+
 /** \brief register a MPM engine
  *
  *  \note to be used at start up / registration only. Errors are fatal.
@@ -1119,6 +1350,9 @@ void MpmStoreReportStats(const DetectEngineCtx *de_ctx)
     int pkt_mpms_cnt = de_ctx->buffer_type_id;
     uint32_t pktstats[pkt_mpms_cnt + 1];    // +1 to silence scan-build
     memset(&pktstats, 0x00, sizeof(pktstats));
+    int frame_mpms_cnt = de_ctx->buffer_type_id;
+    uint32_t framestats[frame_mpms_cnt + 1]; // +1 to silence scan-build
+    memset(&framestats, 0x00, sizeof(framestats));
 
     for (htb = HashListTableGetListHead(de_ctx->mpm_hash_table);
             htb != NULL;
@@ -1151,6 +1385,12 @@ void MpmStoreReportStats(const DetectEngineCtx *de_ctx)
                                 ms->mpm_ctx);
                         appstats[am->sm_list]++;
                         break;
+                    case DETECT_BUFFER_MPM_TYPE_FRAME:
+                        SCLogDebug("%s: %u patterns. Min %u, Max %u. Ctx %p", am->name,
+                                ms->mpm_ctx->pattern_cnt, ms->mpm_ctx->minlen, ms->mpm_ctx->maxlen,
+                                ms->mpm_ctx);
+                        framestats[am->sm_list]++;
+                        break;
                     case DETECT_BUFFER_MPM_TYPE_SIZE:
                         break;
                 }
@@ -1180,6 +1420,14 @@ void MpmStoreReportStats(const DetectEngineCtx *de_ctx)
             }
             pm = pm->next;
         }
+        const DetectBufferMpmRegistery *um = de_ctx->frame_mpms_list;
+        while (um != NULL) {
+            if (framestats[um->sm_list] > 0) {
+                const char *name = um->name;
+                SCLogPerf("Frame MPM \"%s\": %u", name, framestats[um->sm_list]);
+            }
+            um = um->next;
+        }
     }
 }
 
@@ -1235,8 +1483,9 @@ static void MpmStoreSetup(const DetectEngineCtx *de_ctx, MpmStore *ms)
     }
 
     ms->mpm_ctx = MpmFactoryGetMpmCtxForProfile(de_ctx, ms->sgh_mpm_context, dir);
-    if (ms->mpm_ctx == NULL)
+    if (ms->mpm_ctx == NULL) {
         return;
+    }
 
     MpmInitCtx(ms->mpm_ctx, de_ctx->mpm_matcher);
 
@@ -1246,8 +1495,10 @@ static void MpmStoreSetup(const DetectEngineCtx *de_ctx, MpmStore *ms)
             s = de_ctx->sig_array[sig];
             if (s == NULL)
                 continue;
-            if ((s->flags & ms->direction) == 0)
+            if ((s->flags & ms->direction) == 0) {
+                SCLogDebug("s->flags %x ms->direction %x", s->flags, ms->direction);
                 continue;
+            }
             if (s->init_data->mpm_sm == NULL)
                 continue;
             int list = SigMatchListSMBelongsTo(s, s->init_data->mpm_sm);
@@ -1578,6 +1829,77 @@ static MpmStore *MpmStorePrepareBufferPkt(DetectEngineCtx *de_ctx,
     return NULL;
 }
 
+static MpmStore *MpmStorePrepareBufferFrame(
+        DetectEngineCtx *de_ctx, SigGroupHead *sgh, const DetectBufferMpmRegistery *am)
+{
+    const Signature *s = NULL;
+    uint32_t sig;
+    uint32_t cnt = 0;
+    uint32_t max_sid = DetectEngineGetMaxSigId(de_ctx) / 8 + 1;
+    uint8_t sids_array[max_sid];
+    memset(sids_array, 0x00, max_sid);
+
+    SCLogDebug("handling %s for list %d", am->name, am->sm_list);
+
+    for (sig = 0; sig < sgh->init->sig_cnt; sig++) {
+        s = sgh->init->match_array[sig];
+        if (s == NULL)
+            continue;
+
+        if (s->init_data->mpm_sm == NULL)
+            continue;
+
+        if ((s->flags & am->direction) == 0)
+            continue;
+
+        int list = SigMatchListSMBelongsTo(s, s->init_data->mpm_sm);
+        if (list < 0)
+            continue;
+
+        if (list != am->sm_list)
+            continue;
+
+        sids_array[s->num / 8] |= 1 << (s->num % 8);
+        cnt++;
+    }
+
+    if (cnt == 0)
+        return NULL;
+
+    MpmStore lookup = { sids_array, max_sid, am->direction, MPMB_MAX, am->sm_list, 0, NULL };
+    SCLogDebug("am->sm_list %d", am->sm_list);
+
+    MpmStore *result = MpmStoreLookup(de_ctx, &lookup);
+    if (result == NULL) {
+        SCLogDebug("new unique mpm for %s: %u patterns", am->name, cnt);
+
+        MpmStore *copy = SCCalloc(1, sizeof(MpmStore));
+        if (copy == NULL)
+            return NULL;
+        uint8_t *sids = SCCalloc(1, max_sid);
+        if (sids == NULL) {
+            SCFree(copy);
+            return NULL;
+        }
+
+        memcpy(sids, sids_array, max_sid);
+        copy->sid_array = sids;
+        copy->sid_array_size = max_sid;
+        copy->buffer = MPMB_MAX;
+        copy->direction = am->direction;
+        copy->sm_list = am->sm_list;
+        copy->sgh_mpm_context = am->sgh_mpm_context;
+
+        MpmStoreSetup(de_ctx, copy);
+        MpmStoreAdd(de_ctx, copy);
+        return copy;
+    } else {
+        SCLogDebug("using existing mpm %p", result);
+        return result;
+    }
+    return NULL;
+}
+
 static void SetRawReassemblyFlag(DetectEngineCtx *de_ctx, SigGroupHead *sgh)
 {
     const Signature *s = NULL;
@@ -1663,6 +1985,39 @@ static void PreparePktMpms(DetectEngineCtx *de_ctx, SigGroupHead *sh)
     }
 }
 
+static void PrepareFrameMpms(DetectEngineCtx *de_ctx, SigGroupHead *sh)
+{
+    if (de_ctx->frame_mpms_list_cnt == 0)
+        return;
+
+    sh->init->frame_mpms = SCCalloc(de_ctx->frame_mpms_list_cnt, sizeof(MpmCtx *));
+    BUG_ON(sh->init->frame_mpms == NULL);
+
+    DetectBufferMpmRegistery *a = de_ctx->frame_mpms_list;
+    while (a != NULL) {
+        SCLogDebug("a %s direction %d PrefilterRegisterWithListId %p", a->name, a->direction,
+                a->PrefilterRegisterWithListId);
+        MpmStore *mpm_store = MpmStorePrepareBufferFrame(de_ctx, sh, a);
+        if (mpm_store != NULL) {
+            sh->init->frame_mpms[a->id] = mpm_store->mpm_ctx;
+
+            SCLogDebug("a %p a->name %s a->reg->PrefilterRegisterWithListId %p "
+                       "mpm_store->mpm_ctx %p",
+                    a, a->name, a->PrefilterRegisterWithListId, mpm_store->mpm_ctx);
+
+            /* if we have just certain types of negated patterns,
+             * mpm_ctx can be NULL */
+            SCLogDebug("mpm_store %p mpm_ctx %p", mpm_store, mpm_store->mpm_ctx);
+            if (a->PrefilterRegisterWithListId && mpm_store->mpm_ctx) {
+                BUG_ON(a->PrefilterRegisterWithListId(
+                               de_ctx, sh, mpm_store->mpm_ctx, a, a->sm_list) != 0);
+                SCLogDebug("mpm %s %d set up", a->name, a->sm_list);
+            }
+        }
+        a = a->next;
+    }
+}
+
 /** \brief Prepare the pattern matcher ctx in a sig group head.
  *
  */
@@ -1718,6 +2073,7 @@ int PatternMatchPrepareGroup(DetectEngineCtx *de_ctx, SigGroupHead *sh)
 
     PrepareAppMpms(de_ctx, sh);
     PreparePktMpms(de_ctx, sh);
+    PrepareFrameMpms(de_ctx, sh);
     return 0;
 }
 
index e9ed4078ccb7b4187b57f7ef4f125c0302409de8..566287f8c11a551e155f12411347d6f816022c3f 100644 (file)
@@ -32,6 +32,8 @@
 
 #include "stream.h"
 
+void DetectMpmInitializeFrameMpms(DetectEngineCtx *de_ctx);
+int DetectMpmPrepareFrameMpms(DetectEngineCtx *de_ctx);
 void DetectMpmInitializePktMpms(DetectEngineCtx *de_ctx);
 int DetectMpmPreparePktMpms(DetectEngineCtx *de_ctx);
 void DetectMpmInitializeAppMpms(DetectEngineCtx *de_ctx);
@@ -112,11 +114,24 @@ void DetectPktMpmRegisterByParentId(DetectEngineCtx *de_ctx,
         const int id, const int parent_id,
         DetectEngineTransforms *transforms);
 
+void DetectFrameMpmRegister(const char *name, int direction, int priority,
+        int (*PrefilterRegister)(DetectEngineCtx *de_ctx, SigGroupHead *sgh, MpmCtx *mpm_ctx,
+                const DetectBufferMpmRegistery *mpm_reg, int list_id),
+        AppProto alproto, uint8_t type);
+void DetectFrameMpmRegisterByParentId(DetectEngineCtx *de_ctx, const int id, const int parent_id,
+        DetectEngineTransforms *transforms);
+void DetectEngineFrameMpmRegister(DetectEngineCtx *de_ctx, const char *name, int direction,
+        int priority,
+        int (*PrefilterRegister)(DetectEngineCtx *de_ctx, SigGroupHead *sgh, MpmCtx *mpm_ctx,
+                const DetectBufferMpmRegistery *mpm_reg, int list_id),
+        AppProto alproto, uint8_t type);
 
 int PrefilterGenericMpmPktRegister(DetectEngineCtx *de_ctx,
          SigGroupHead *sgh, MpmCtx *mpm_ctx,
          const DetectBufferMpmRegistery *mpm_reg, int list_id);
 
+int PrefilterGenericMpmFrameRegister(DetectEngineCtx *de_ctx, SigGroupHead *sgh, MpmCtx *mpm_ctx,
+        const DetectBufferMpmRegistery *mpm_reg, int list_id);
 
 typedef struct PrefilterMpmListId {
     int list_id;
index 3b23ba98f68f8cb5ebf4ab93bb926f5d03c095b9..6cf0fbed5dbe3dfcbdfc4b550e3d75182de68e8a 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (C) 2016 Open Information Security Foundation
+/* Copyright (C) 2016-2021 Open Information Security Foundation
  *
  * You can copy, redistribute or modify this Program under the terms of
  * the GNU General Public License version 2 as published by the Free
 #include "suricata-common.h"
 #include "suricata.h"
 
+#include "detect-engine.h"
 #include "detect-engine-prefilter.h"
 #include "detect-engine-mpm.h"
+#include "detect-engine-frame.h"
 
 #include "app-layer-parser.h"
 #include "app-layer-htp.h"
@@ -107,10 +109,10 @@ void DetectRunPrefilterTx(DetectEngineThreadCtx *det_ctx,
     do {
         if (engine->alproto != alproto)
             goto next;
-        if (engine->tx_min_progress > tx->tx_progress)
+        if (engine->ctx.tx_min_progress > tx->tx_progress)
             break;
-        if (tx->tx_progress > engine->tx_min_progress) {
-            if (tx->prefilter_flags & BIT_U64(engine->tx_min_progress)) {
+        if (tx->tx_progress > engine->ctx.tx_min_progress) {
+            if (tx->prefilter_flags & BIT_U64(engine->ctx.tx_min_progress)) {
                 goto next;
             }
         }
@@ -120,8 +122,8 @@ void DetectRunPrefilterTx(DetectEngineThreadCtx *det_ctx,
                 p, p->flow, tx->tx_ptr, tx->tx_id, flow_flags);
         PREFILTER_PROFILING_END(det_ctx, engine->gid);
 
-        if (tx->tx_progress > engine->tx_min_progress && engine->is_last_for_progress) {
-            tx->prefilter_flags |= BIT_U64(engine->tx_min_progress);
+        if (tx->tx_progress > engine->ctx.tx_min_progress && engine->is_last_for_progress) {
+            tx->prefilter_flags |= BIT_U64(engine->ctx.tx_min_progress);
         }
     next:
         if (engine->is_last)
@@ -142,7 +144,16 @@ void Prefilter(DetectEngineThreadCtx *det_ctx, const SigGroupHead *sgh,
         Packet *p, const uint8_t flags)
 {
     SCEnter();
-
+#if 0
+    /* TODO review this check */
+    SCLogDebug("sgh %p frame_engines %p", sgh, sgh->frame_engines);
+    if (p->proto == IPPROTO_TCP && sgh->frame_engines && p->flow &&
+            p->flow->alproto != ALPROTO_UNKNOWN && p->flow->alparser != NULL) {
+        PACKET_PROFILING_DETECT_START(p, PROF_DETECT_PF_RECORD);
+        PrefilterFrames(det_ctx, sgh, p, flags, p->flow->alproto);
+        PACKET_PROFILING_DETECT_END(p, PROF_DETECT_PF_RECORD);
+    }
+#endif
     if (sgh->pkt_engines) {
         PACKET_PROFILING_DETECT_START(p, PROF_DETECT_PF_PKT);
         /* run packet engines */
@@ -295,6 +306,41 @@ int PrefilterAppendTxEngine(DetectEngineCtx *de_ctx, SigGroupHead *sgh,
     return 0;
 }
 
+int PrefilterAppendFrameEngine(DetectEngineCtx *de_ctx, SigGroupHead *sgh,
+        PrefilterFrameFn PrefilterFrameFunc, AppProto alproto, uint8_t frame_type, void *pectx,
+        void (*FreeFunc)(void *pectx), const char *name)
+{
+    if (sgh == NULL || PrefilterFrameFunc == NULL || pectx == NULL)
+        return -1;
+
+    PrefilterEngineList *e = SCMallocAligned(sizeof(*e), CLS);
+    if (e == NULL)
+        return -1;
+    memset(e, 0x00, sizeof(*e));
+
+    e->frame_type = frame_type;
+    e->alproto = alproto;
+    e->PrefilterFrame = PrefilterFrameFunc;
+    e->pectx = pectx;
+    e->Free = FreeFunc;
+
+    if (sgh->init->frame_engines == NULL) {
+        sgh->init->frame_engines = e;
+    } else {
+        PrefilterEngineList *t = sgh->init->frame_engines;
+        while (t->next != NULL) {
+            t = t->next;
+        }
+
+        t->next = e;
+        e->id = t->id + 1;
+    }
+
+    e->name = name;
+    e->gid = PrefilterStoreGetId(de_ctx, e->name, e->Free);
+    return 0;
+}
+
 static void PrefilterFreeEngineList(PrefilterEngineList *e)
 {
     if (e->Free && e->pectx) {
@@ -345,20 +391,24 @@ void PrefilterCleanupRuleGroup(const DetectEngineCtx *de_ctx, SigGroupHead *sgh)
         PrefilterFreeEngines(de_ctx, sgh->tx_engines);
         sgh->tx_engines = NULL;
     }
+    if (sgh->frame_engines) {
+        PrefilterFreeEngines(de_ctx, sgh->frame_engines);
+        sgh->frame_engines = NULL;
+    }
 }
 
 static int PrefilterSetupRuleGroupSortHelper(const void *a, const void *b)
 {
     const PrefilterEngine *s0 = a;
     const PrefilterEngine *s1 = b;
-    if (s1->tx_min_progress == s0->tx_min_progress) {
+    if (s1->ctx.tx_min_progress == s0->ctx.tx_min_progress) {
         if (s1->alproto == s0->alproto) {
             return s0->local_id > s1->local_id ? 1 : -1;
         } else {
             return s0->alproto > s1->alproto ? 1 : -1;
         }
     } else {
-        return s0->tx_min_progress > s1->tx_min_progress ? 1 : -1;
+        return s0->ctx.tx_min_progress > s1->ctx.tx_min_progress ? 1 : -1;
     }
 }
 
@@ -453,7 +503,7 @@ void PrefilterSetupRuleGroup(DetectEngineCtx *de_ctx, SigGroupHead *sgh)
         for (el = sgh->init->tx_engines ; el != NULL; el = el->next) {
             e->local_id = local_id++;
             e->alproto = el->alproto;
-            e->tx_min_progress = el->tx_min_progress;
+            e->ctx.tx_min_progress = el->tx_min_progress;
             e->cb.PrefilterTx = el->PrefilterTx;
             e->pectx = el->pectx;
             el->pectx = NULL; // e now owns the ctx
@@ -477,9 +527,9 @@ void PrefilterSetupRuleGroup(DetectEngineCtx *de_ctx, SigGroupHead *sgh)
             PrefilterEngine *prev_engine = NULL;
             engine = sgh->tx_engines;
             do {
-                BUG_ON(engine->tx_min_progress < last_tx_progress);
+                BUG_ON(engine->ctx.tx_min_progress < last_tx_progress);
                 if (engine->alproto == a) {
-                    if (last_tx_progress_set && engine->tx_min_progress > last_tx_progress) {
+                    if (last_tx_progress_set && engine->ctx.tx_min_progress > last_tx_progress) {
                         if (prev_engine) {
                             prev_engine->is_last_for_progress = true;
                         }
@@ -492,7 +542,7 @@ void PrefilterSetupRuleGroup(DetectEngineCtx *de_ctx, SigGroupHead *sgh)
                         prev_engine->is_last_for_progress = true;
                     }
                 }
-                last_tx_progress = engine->tx_min_progress;
+                last_tx_progress = engine->ctx.tx_min_progress;
                 if (engine->is_last)
                     break;
                 engine++;
@@ -504,7 +554,7 @@ void PrefilterSetupRuleGroup(DetectEngineCtx *de_ctx, SigGroupHead *sgh)
         do {
             SCLogDebug("engine: gid %u alproto %s tx_min_progress %d is_last %s "
                        "is_last_for_progress %s",
-                    engine->gid, AppProtoToString(engine->alproto), engine->tx_min_progress,
+                    engine->gid, AppProtoToString(engine->alproto), engine->ctx.tx_min_progress,
                     engine->is_last ? "true" : "false",
                     engine->is_last_for_progress ? "true" : "false");
             if (engine->is_last)
@@ -513,6 +563,33 @@ void PrefilterSetupRuleGroup(DetectEngineCtx *de_ctx, SigGroupHead *sgh)
         } while (1);
 #endif
     }
+    if (sgh->init->frame_engines != NULL) {
+        uint32_t cnt = 0;
+        for (el = sgh->init->frame_engines; el != NULL; el = el->next) {
+            cnt++;
+            de_ctx->prefilter_maxid = MAX(de_ctx->prefilter_maxid, el->gid);
+        }
+        sgh->frame_engines = SCMallocAligned(cnt * sizeof(PrefilterEngine), CLS);
+        if (sgh->frame_engines == NULL) {
+            return;
+        }
+        memset(sgh->frame_engines, 0x00, (cnt * sizeof(PrefilterEngine)));
+
+        PrefilterEngine *e = sgh->frame_engines;
+        for (el = sgh->init->frame_engines; el != NULL; el = el->next) {
+            e->local_id = el->id;
+            e->ctx.frame_type = el->frame_type;
+            e->cb.PrefilterFrame = el->PrefilterFrame;
+            e->alproto = el->alproto;
+            e->pectx = el->pectx;
+            el->pectx = NULL; // e now owns the ctx
+            e->gid = el->gid;
+            if (el->next == NULL) {
+                e->is_last = TRUE;
+            }
+            e++;
+        }
+    }
 }
 
 /* hash table for assigning a unique id to each engine type. */
index cb0e6eb267f69373f5bd98346d35130c67077117..5ad6d1b62386caf91378b3d61bdcc6c020427f15 100644 (file)
@@ -44,12 +44,13 @@ int PrefilterAppendPayloadEngine(DetectEngineCtx *de_ctx, SigGroupHead *sgh,
         void *pectx, void (*FreeFunc)(void *pectx),
         const char *name);
 int PrefilterAppendTxEngine(DetectEngineCtx *de_ctx, SigGroupHead *sgh,
-        void (*PrefilterTx)(DetectEngineThreadCtx *det_ctx, const void *pectx,
-            Packet *p, Flow *f, void *tx,
-            const uint64_t idx, const uint8_t flags),
-        const AppProto alproto, const int tx_min_progress,
-        void *pectx, void (*FreeFunc)(void *pectx),
-        const char *name);
+        void (*PrefilterTx)(DetectEngineThreadCtx *det_ctx, const void *pectx, Packet *p, Flow *f,
+                void *tx, const uint64_t idx, const uint8_t flags),
+        const AppProto alproto, const int tx_min_progress, void *pectx,
+        void (*FreeFunc)(void *pectx), const char *name);
+int PrefilterAppendFrameEngine(DetectEngineCtx *de_ctx, SigGroupHead *sgh,
+        PrefilterFrameFn PrefilterFrameFunc, AppProto alproto, uint8_t frame_type, void *pectx,
+        void (*FreeFunc)(void *pectx), const char *name);
 
 void DetectRunPrefilterTx(DetectEngineThreadCtx *det_ctx,
         const SigGroupHead *sgh,
index b1d0057cce5a3a5e5e3c80bdb1d5ee1438b682d7..b9e00f777ebee03dc46de730f3541303e6b31eca 100644 (file)
@@ -71,10 +71,14 @@ void SigGroupHeadInitDataFree(SigGroupHeadInitData *sghid)
     if (sghid->pkt_mpms != NULL) {
         SCFree(sghid->pkt_mpms);
     }
+    if (sghid->frame_mpms != NULL) {
+        SCFree(sghid->frame_mpms);
+    }
 
     PrefilterFreeEnginesList(sghid->tx_engines);
     PrefilterFreeEnginesList(sghid->pkt_engines);
     PrefilterFreeEnginesList(sghid->payload_engines);
+    PrefilterFreeEnginesList(sghid->frame_engines);
 
     SCFree(sghid);
 }
index 9a405a42f0c11105c7862c16ceedccb35b7ca17c..0db3967b9e13cd4daec2a45900e8ba75b5ad5af5 100644 (file)
@@ -100,6 +100,7 @@ static uint32_t DetectEngineTentantGetIdFromPcap(const void *ctx, const Packet *
 
 static DetectEngineAppInspectionEngine *g_app_inspect_engines = NULL;
 static DetectEnginePktInspectionEngine *g_pkt_inspect_engines = NULL;
+static DetectEngineFrameInspectionEngine *g_frame_inspect_engines = NULL;
 
 SCEnumCharMap det_ctx_event_table[] = {
 #ifdef UNITTESTS
@@ -169,6 +170,54 @@ void DetectPktInspectEngineRegister(const char *name,
     }
 }
 
+/** \brief register inspect engine at start up time
+ *
+ *  \note errors are fatal */
+void DetectFrameInspectEngineRegister(const char *name, int dir,
+        InspectionBufferFrameInspectFunc Callback, AppProto alproto, uint8_t type)
+{
+    DetectBufferTypeRegister(name);
+    const int sm_list = DetectBufferTypeGetByName(name);
+    if (sm_list == -1) {
+        FatalError(SC_ERR_INITIALIZATION, "failed to register inspect engine %s", name);
+    }
+
+    if ((sm_list < DETECT_SM_LIST_MATCH) || (sm_list >= SHRT_MAX) || (Callback == NULL)) {
+        SCLogError(SC_ERR_INVALID_ARGUMENTS, "Invalid arguments");
+        BUG_ON(1);
+    }
+
+    int direction;
+    if (dir == SIG_FLAG_TOSERVER) {
+        direction = 0;
+    } else {
+        direction = 1;
+    }
+
+    DetectEngineFrameInspectionEngine *new_engine = SCCalloc(1, sizeof(*new_engine));
+    if (unlikely(new_engine == NULL)) {
+        FatalError(SC_ERR_INITIALIZATION, "failed to register inspect engine %s: %s", name,
+                strerror(errno));
+    }
+    new_engine->sm_list = sm_list;
+    new_engine->sm_list_base = sm_list;
+    new_engine->dir = direction;
+    new_engine->v1.Callback = Callback;
+    new_engine->alproto = alproto;
+    new_engine->type = type;
+
+    if (g_frame_inspect_engines == NULL) {
+        g_frame_inspect_engines = new_engine;
+    } else {
+        DetectEngineFrameInspectionEngine *t = g_frame_inspect_engines;
+        while (t->next != NULL) {
+            t = t->next;
+        }
+
+        t->next = new_engine;
+    }
+}
+
 /** \brief register inspect engine at start up time
  *
  *  \note errors are fatal */
@@ -361,6 +410,123 @@ static void DetectPktInspectEngineCopyListToDetectCtx(DetectEngineCtx *de_ctx)
     }
 }
 
+/** \brief register inspect engine at start up time
+ *
+ *  \note errors are fatal */
+void DetectEngineFrameInspectEngineRegister(DetectEngineCtx *de_ctx, const char *name, int dir,
+        InspectionBufferFrameInspectFunc Callback, AppProto alproto, uint8_t type)
+{
+    const int sm_list = DetectEngineBufferTypeRegister(de_ctx, name);
+    if (sm_list < 0) {
+        FatalError(SC_ERR_INITIALIZATION, "failed to register inspect engine %s", name);
+    }
+
+    if ((sm_list < DETECT_SM_LIST_MATCH) || (sm_list >= SHRT_MAX) || (Callback == NULL)) {
+        SCLogError(SC_ERR_INVALID_ARGUMENTS, "Invalid arguments");
+        BUG_ON(1);
+    }
+
+    int direction;
+    if (dir == SIG_FLAG_TOSERVER) {
+        direction = 0;
+    } else {
+        direction = 1;
+    }
+
+    DetectEngineFrameInspectionEngine *new_engine = SCCalloc(1, sizeof(*new_engine));
+    if (unlikely(new_engine == NULL)) {
+        FatalError(SC_ERR_INITIALIZATION, "failed to register inspect engine %s: %s", name,
+                strerror(errno));
+    }
+    new_engine->sm_list = sm_list;
+    new_engine->sm_list_base = sm_list;
+    new_engine->dir = direction;
+    new_engine->v1.Callback = Callback;
+    new_engine->alproto = alproto;
+    new_engine->type = type;
+
+    if (de_ctx->frame_inspect_engines == NULL) {
+        de_ctx->frame_inspect_engines = new_engine;
+    } else {
+        DetectEngineFrameInspectionEngine *list = de_ctx->frame_inspect_engines;
+        while (list->next != NULL) {
+            list = list->next;
+        }
+
+        list->next = new_engine;
+    }
+}
+
+/* copy an inspect engine with transforms to a new list id. */
+static void DetectFrameInspectEngineCopy(DetectEngineCtx *de_ctx, int sm_list, int new_list,
+        const DetectEngineTransforms *transforms)
+{
+    /* take the list from the detect engine as the buffers can be registered
+     * dynamically. */
+    const DetectEngineFrameInspectionEngine *t = de_ctx->frame_inspect_engines;
+    while (t) {
+        if (t->sm_list == sm_list) {
+            DetectEngineFrameInspectionEngine *new_engine =
+                    SCCalloc(1, sizeof(DetectEngineFrameInspectionEngine));
+            if (unlikely(new_engine == NULL)) {
+                exit(EXIT_FAILURE);
+            }
+            new_engine->sm_list = new_list; /* use new list id */
+            new_engine->sm_list_base = sm_list;
+            new_engine->dir = t->dir;
+            new_engine->alproto = t->alproto;
+            new_engine->type = t->type;
+            new_engine->v1 = t->v1;
+            new_engine->v1.transforms = transforms; /* assign transforms */
+
+            if (de_ctx->frame_inspect_engines == NULL) {
+                de_ctx->frame_inspect_engines = new_engine;
+            } else {
+                DetectEngineFrameInspectionEngine *list = de_ctx->frame_inspect_engines;
+                while (list->next != NULL) {
+                    list = list->next;
+                }
+
+                list->next = new_engine;
+            }
+        }
+        t = t->next;
+    }
+}
+
+/* copy inspect engines from global registrations to de_ctx list */
+static void DetectFrameInspectEngineCopyListToDetectCtx(DetectEngineCtx *de_ctx)
+{
+    const DetectEngineFrameInspectionEngine *t = g_frame_inspect_engines;
+    while (t) {
+        SCLogDebug("engine %p", t);
+        DetectEngineFrameInspectionEngine *new_engine =
+                SCCalloc(1, sizeof(DetectEngineFrameInspectionEngine));
+        if (unlikely(new_engine == NULL)) {
+            exit(EXIT_FAILURE);
+        }
+        new_engine->sm_list = t->sm_list;
+        new_engine->sm_list_base = t->sm_list;
+        new_engine->dir = t->dir;
+        new_engine->alproto = t->alproto;
+        new_engine->type = t->type;
+        new_engine->v1 = t->v1;
+
+        if (de_ctx->frame_inspect_engines == NULL) {
+            de_ctx->frame_inspect_engines = new_engine;
+        } else {
+            DetectEngineFrameInspectionEngine *list = de_ctx->frame_inspect_engines;
+            while (list->next != NULL) {
+                list = list->next;
+            }
+
+            list->next = new_engine;
+        }
+
+        t = t->next;
+    }
+}
+
 /** \internal
  *  \brief append the stream inspection
  *
@@ -435,6 +601,63 @@ int DetectEngineAppInspectionEngine2Signature(DetectEngineCtx *de_ctx, Signature
         SCLogDebug("ptrs[%d] is set", i);
     }
 
+    /* set up inspect engines */
+    const DetectEngineFrameInspectionEngine *u = de_ctx->frame_inspect_engines;
+    while (u != NULL) {
+        SCLogDebug("u %p sm_list %u nlists %u ptrs[] %p", u, u->sm_list, nlists,
+                u->sm_list < nlists ? ptrs[u->sm_list] : NULL);
+        if (u->sm_list < nlists && ptrs[u->sm_list] != NULL) {
+            bool prepend = false;
+
+            if (u->alproto == ALPROTO_UNKNOWN) {
+                /* special case, inspect engine applies to all protocols */
+            } else if (s->alproto != ALPROTO_UNKNOWN && !AppProtoEquals(s->alproto, u->alproto))
+                goto next_engine;
+
+            if (s->flags & SIG_FLAG_TOSERVER && !(s->flags & SIG_FLAG_TOCLIENT)) {
+                if (u->dir == 1)
+                    goto next_engine;
+            } else if (s->flags & SIG_FLAG_TOCLIENT && !(s->flags & SIG_FLAG_TOSERVER)) {
+                if (u->dir == 0)
+                    goto next_engine;
+            }
+            DetectEngineFrameInspectionEngine *new_engine =
+                    SCCalloc(1, sizeof(DetectEngineFrameInspectionEngine));
+            if (unlikely(new_engine == NULL)) {
+                exit(EXIT_FAILURE);
+            }
+            if (mpm_list == u->sm_list) {
+                SCLogDebug("%s is mpm", DetectEngineBufferTypeGetNameById(de_ctx, u->sm_list));
+                prepend = true;
+                new_engine->mpm = true;
+            }
+
+            new_engine->type = u->type;
+            new_engine->sm_list = u->sm_list;
+            new_engine->sm_list_base = u->sm_list_base;
+            new_engine->smd = ptrs[new_engine->sm_list];
+            new_engine->v1 = u->v1;
+            SCLogDebug("sm_list %d new_engine->v1 %p/%p", new_engine->sm_list,
+                    new_engine->v1.Callback, new_engine->v1.transforms);
+
+            if (s->frame_inspect == NULL) {
+                s->frame_inspect = new_engine;
+            } else if (prepend) {
+                new_engine->next = s->frame_inspect;
+                s->frame_inspect = new_engine;
+            } else {
+                DetectEngineFrameInspectionEngine *a = s->frame_inspect;
+                while (a->next != NULL) {
+                    a = a->next;
+                }
+                new_engine->next = a->next;
+                a->next = new_engine;
+            }
+        }
+    next_engine:
+        u = u->next;
+    }
+
     /* set up pkt inspect engines */
     const DetectEnginePktInspectionEngine *e = de_ctx->pkt_inspect_engines;
     while (e != NULL) {
@@ -628,8 +851,14 @@ void DetectEngineAppInspectionEngineSignatureFree(DetectEngineCtx *de_ctx, Signa
         nlists = MAX(e->sm_list + 1, nlists);
         e = e->next;
     }
+    DetectEngineFrameInspectionEngine *u = s->frame_inspect;
+    while (u) {
+        nlists = MAX(u->sm_list + 1, nlists);
+        u = u->next;
+    }
     if (nlists == 0) {
         BUG_ON(s->pkt_inspect);
+        BUG_ON(s->frame_inspect);
         return;
     }
 
@@ -652,6 +881,13 @@ void DetectEngineAppInspectionEngineSignatureFree(DetectEngineCtx *de_ctx, Signa
         SCFree(e);
         e = next;
     }
+    u = s->frame_inspect;
+    while (u) {
+        DetectEngineFrameInspectionEngine *next = u->next;
+        ptrs[u->sm_list] = u->smd;
+        SCFree(u);
+        u = next;
+    }
 
     /* free the smds */
     for (int i = 0; i < nlists; i++)
@@ -804,6 +1040,16 @@ int DetectBufferTypeRegister(const char *name)
     }
 }
 
+void DetectBufferTypeSupportsFrames(const char *name)
+{
+    BUG_ON(g_buffer_type_reg_closed);
+    DetectBufferTypeRegister(name);
+    DetectBufferType *exists = DetectBufferTypeLookupByName(name);
+    BUG_ON(!exists);
+    exists->frame = true;
+    SCLogDebug("%p %s -- %d supports frame inspection", exists, name, exists->id);
+}
+
 void DetectBufferTypeSupportsPacket(const char *name)
 {
     BUG_ON(g_buffer_type_reg_closed);
@@ -887,6 +1133,40 @@ static int DetectEngineBufferTypeAdd(DetectEngineCtx *de_ctx, const char *string
     return map->id;
 }
 
+int DetectEngineBufferTypeRegisterWithFrameEngines(DetectEngineCtx *de_ctx, const char *name,
+        const int direction, const AppProto alproto, const uint8_t frame_type)
+{
+    DetectBufferType *exists = DetectEngineBufferTypeLookupByName(de_ctx, name);
+    if (exists) {
+        return exists->id;
+    }
+
+    const int buffer_id = DetectEngineBufferTypeAdd(de_ctx, name);
+    if (buffer_id < 0) {
+        return -1;
+    }
+
+    /* TODO hack we need the map to get the name. Should we return the map at reg? */
+    const DetectBufferType *map = DetectEngineBufferTypeGetById(de_ctx, buffer_id);
+    BUG_ON(!map);
+
+    /* register MPM/inspect engines */
+    if (direction & SIG_FLAG_TOSERVER) {
+        DetectEngineFrameMpmRegister(de_ctx, map->name, SIG_FLAG_TOSERVER, 2,
+                PrefilterGenericMpmFrameRegister, alproto, frame_type);
+        DetectEngineFrameInspectEngineRegister(de_ctx, map->name, SIG_FLAG_TOSERVER,
+                DetectEngineInspectFrameBufferGeneric, alproto, frame_type);
+    }
+    if (direction & SIG_FLAG_TOCLIENT) {
+        DetectEngineFrameMpmRegister(de_ctx, map->name, SIG_FLAG_TOCLIENT, 2,
+                PrefilterGenericMpmFrameRegister, alproto, frame_type);
+        DetectEngineFrameInspectEngineRegister(de_ctx, map->name, SIG_FLAG_TOCLIENT,
+                DetectEngineInspectFrameBufferGeneric, alproto, frame_type);
+    }
+
+    return buffer_id;
+}
+
 int DetectEngineBufferTypeRegister(DetectEngineCtx *de_ctx, const char *name)
 {
     DetectBufferType *exists = DetectEngineBufferTypeLookupByName(de_ctx, name);
@@ -926,6 +1206,14 @@ const char *DetectBufferTypeGetDescriptionByName(const char *name)
     return exists->description;
 }
 
+void DetectEngineBufferTypeSupportsFrames(DetectEngineCtx *de_ctx, const char *name)
+{
+    DetectBufferType *exists = DetectEngineBufferTypeLookupByName(de_ctx, name);
+    BUG_ON(!exists);
+    exists->frame = true;
+    SCLogDebug("%p %s -- %d supports frame inspection", exists, name, exists->id);
+}
+
 void DetectEngineBufferTypeSupportsPacket(DetectEngineCtx *de_ctx, const char *name)
 {
     DetectBufferType *exists = DetectEngineBufferTypeLookupByName(de_ctx, name);
@@ -1310,6 +1598,8 @@ static void DetectBufferTypeSetupDetectEngine(DetectEngineCtx *de_ctx)
     PrefilterInit(de_ctx);
     DetectMpmInitializeAppMpms(de_ctx);
     DetectAppLayerInspectEngineCopyListToDetectCtx(de_ctx);
+    DetectMpmInitializeFrameMpms(de_ctx);
+    DetectFrameInspectEngineCopyListToDetectCtx(de_ctx);
     DetectMpmInitializePktMpms(de_ctx);
     DetectPktInspectEngineCopyListToDetectCtx(de_ctx);
 }
@@ -1346,6 +1636,18 @@ static void DetectBufferTypeFreeDetectEngine(DetectEngineCtx *de_ctx)
             SCFree(pmlist);
             pmlist = next;
         }
+        DetectEngineFrameInspectionEngine *framelist = de_ctx->frame_inspect_engines;
+        while (framelist) {
+            DetectEngineFrameInspectionEngine *next = framelist->next;
+            SCFree(framelist);
+            framelist = next;
+        }
+        DetectBufferMpmRegistery *framemlist = de_ctx->frame_mpms_list;
+        while (framemlist) {
+            DetectBufferMpmRegistery *next = framemlist->next;
+            SCFree(framemlist);
+            framemlist = next;
+        }
         PrefilterDeinit(de_ctx);
     }
 }
@@ -1400,9 +1702,12 @@ int DetectEngineBufferTypeGetByIdTransforms(
     map->transforms = t;
     map->mpm = base_map->mpm;
     map->packet = base_map->packet;
+    map->frame = base_map->frame;
     map->SetupCallback = base_map->SetupCallback;
     map->ValidateCallback = base_map->ValidateCallback;
-    if (map->packet) {
+    if (map->frame) {
+        DetectFrameMpmRegisterByParentId(de_ctx, map->id, map->parent_id, &map->transforms);
+    } else if (map->packet) {
         DetectPktMpmRegisterByParentId(de_ctx,
                 map->id, map->parent_id, &map->transforms);
     } else {
@@ -1415,7 +1720,9 @@ int DetectEngineBufferTypeGetByIdTransforms(
     SCLogDebug("buffer %s registered with id %d, parent %d", map->name, map->id, map->parent_id);
     de_ctx->buffer_type_id++;
 
-    if (map->packet) {
+    if (map->frame) {
+        DetectFrameInspectEngineCopy(de_ctx, map->parent_id, map->id, &map->transforms);
+    } else if (map->packet) {
         DetectPktInspectEngineCopy(de_ctx, map->parent_id, map->id, &map->transforms);
     } else {
         DetectAppLayerInspectEngineCopy(de_ctx, map->parent_id, map->id, &map->transforms);
index 933222d3c9243a7db501d8d32f26c80d59b6602d..018149f7d385774cc74d509bb7c53b18cf89a860 100644 (file)
@@ -28,6 +28,8 @@
 #include "tm-threads.h"
 #include "flow-private.h"
 
+#include "detect-engine-frame.h"
+
 void InspectionBufferInit(InspectionBuffer *buffer, uint32_t initial_size);
 void InspectionBufferSetup(DetectEngineThreadCtx *det_ctx, const int list_id,
         InspectionBuffer *buffer, const uint8_t *data, const uint32_t data_len);
@@ -49,6 +51,7 @@ int DetectBufferTypeRegister(const char *name);
 int DetectBufferTypeGetByName(const char *name);
 void DetectBufferTypeSupportsMpm(const char *name);
 void DetectBufferTypeSupportsPacket(const char *name);
+void DetectBufferTypeSupportsFrames(const char *name);
 void DetectBufferTypeSupportsTransformations(const char *name);
 int DetectBufferTypeMaxId(void);
 void DetectBufferTypeCloseRegistration(void);
@@ -61,6 +64,8 @@ void DetectBufferTypeRegisterValidateCallback(const char *name,
 
 /* detect engine related buffer funcs */
 
+int DetectEngineBufferTypeRegisterWithFrameEngines(DetectEngineCtx *de_ctx, const char *name,
+        const int direction, const AppProto alproto, const uint8_t frame_type);
 int DetectEngineBufferTypeRegister(DetectEngineCtx *de_ctx, const char *name);
 const char *DetectEngineBufferTypeGetNameById(const DetectEngineCtx *de_ctx, const int id);
 const DetectBufferType *DetectEngineBufferTypeGetById(const DetectEngineCtx *de_ctx, const int id);
@@ -75,6 +80,7 @@ bool DetectEngineBufferRunValidateCallback(
         const DetectEngineCtx *de_ctx, const int id, const Signature *s, const char **sigerror);
 bool DetectEngineBufferTypeValidateTransform(DetectEngineCtx *de_ctx, int sm_list,
         const uint8_t *content, uint16_t content_len, const char **namestr);
+void DetectEngineBufferTypeSupportsFrames(DetectEngineCtx *de_ctx, const char *name);
 void DetectEngineBufferTypeSupportsPacket(DetectEngineCtx *de_ctx, const char *name);
 void DetectEngineBufferTypeSupportsMpm(DetectEngineCtx *de_ctx, const char *name);
 void DetectEngineBufferTypeSupportsTransformations(DetectEngineCtx *de_ctx, const char *name);
@@ -161,9 +167,16 @@ void DetectPktInspectEngineRegister(const char *name,
         InspectionBufferGetPktDataPtr GetPktData,
         InspectionBufferPktInspectFunc Callback);
 
+void DetectFrameInspectEngineRegister(const char *name, int dir,
+        InspectionBufferFrameInspectFunc Callback, AppProto alproto, uint8_t type);
+void DetectEngineFrameInspectEngineRegister(DetectEngineCtx *de_ctx, const char *name, int dir,
+        InspectionBufferFrameInspectFunc Callback, AppProto alproto, uint8_t type);
+
 int DetectEngineAppInspectionEngine2Signature(DetectEngineCtx *de_ctx, Signature *s);
 void DetectEngineAppInspectionEngineSignatureFree(DetectEngineCtx *, Signature *s);
 
+bool DetectEngineFrameInspectionRun(ThreadVars *tv, DetectEngineThreadCtx *det_ctx,
+        const Signature *s, Flow *f, Packet *p, uint8_t *alert_flags);
 bool DetectEnginePktInspectionRun(ThreadVars *tv,
         DetectEngineThreadCtx *det_ctx, const Signature *s,
         Flow *f, Packet *p,
index 410ccb1f76abe0e89c42b5df4901998cb35a2e53..51b8de23b34794928a1264b17d848452adf6379f 100644 (file)
@@ -84,6 +84,8 @@ static inline void DetectRulePacketRules(ThreadVars * const tv,
 static void DetectRunTx(ThreadVars *tv, DetectEngineCtx *de_ctx,
         DetectEngineThreadCtx *det_ctx, Packet *p,
         Flow *f, DetectRunScratchpad *scratch);
+static void DetectRunFrames(ThreadVars *tv, DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx,
+        Packet *p, Flow *f, DetectRunScratchpad *scratch);
 static inline void DetectRunPostRules(ThreadVars *tv, DetectEngineCtx *de_ctx,
         DetectEngineThreadCtx *det_ctx, Packet * const p, Flow * const pflow,
         DetectRunScratchpad *scratch);
@@ -136,6 +138,15 @@ static void DetectRun(ThreadVars *th_v,
 
     /* run tx/state inspection. Don't call for ICMP error msgs. */
     if (pflow && pflow->alstate && likely(pflow->proto == p->proto)) {
+        if (p->proto == IPPROTO_TCP) {
+            const TcpSession *ssn = p->flow->protoctx;
+            if (ssn && (ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED) == 0) {
+                // PACKET_PROFILING_DETECT_START(p, PROF_DETECT_TX);
+                DetectRunFrames(th_v, de_ctx, det_ctx, p, pflow, &scratch);
+                // PACKET_PROFILING_DETECT_END(p, PROF_DETECT_TX);
+            }
+        }
+
         PACKET_PROFILING_DETECT_START(p, PROF_DETECT_TX);
         DetectRunTx(th_v, de_ctx, det_ctx, p, pflow, &scratch);
         PACKET_PROFILING_DETECT_END(p, PROF_DETECT_TX);
@@ -749,6 +760,9 @@ static inline void DetectRulePacketRules(
         if (s->app_inspect != NULL) {
             goto next; // handle sig in DetectRunTx
         }
+        if (s->frame_inspect != NULL) {
+            goto next; // handle sig in DetectRunFrame
+        }
 
         /* don't run mask check for stateful rules.
          * There we depend on prefilter */
@@ -1517,6 +1531,114 @@ next:
     }
 }
 
+static void DetectRunFrames(ThreadVars *tv, DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx,
+        Packet *p, Flow *f, DetectRunScratchpad *scratch)
+{
+    const SigGroupHead *const sgh = scratch->sgh;
+    const AppProto alproto = f->alproto;
+
+    FramesContainer *frames_container = AppLayerFramesGetContainer(f);
+    if (frames_container == NULL) {
+        return;
+    }
+    Frames *frames;
+    if (PKT_IS_TOSERVER(p)) {
+        frames = &frames_container->toserver;
+    } else {
+        frames = &frames_container->toclient;
+    }
+
+    for (uint32_t idx = 0; idx < frames->cnt; idx++) {
+        SCLogDebug("frame %u", idx);
+        const Frame *frame = FrameGetByIndex(frames, idx);
+        if (frame == NULL) {
+            continue;
+        }
+
+        uint32_t array_idx = 0;
+        uint32_t total_rules = det_ctx->match_array_cnt;
+
+        /* run prefilter engines and merge results into a candidates array */
+        if (sgh->frame_engines) {
+            //            PACKET_PROFILING_DETECT_START(p, PROF_DETECT_PF_TX);
+            DetectRunPrefilterFrame(det_ctx, sgh, p, frames, frame, alproto, idx);
+            //            PACKET_PROFILING_DETECT_END(p, PROF_DETECT_PF_TX);
+            SCLogDebug("%p/%" PRIi64 " rules added from prefilter: %u candidates", frame, frame->id,
+                    det_ctx->pmq.rule_id_array_cnt);
+
+            total_rules += det_ctx->pmq.rule_id_array_cnt;
+
+            if (!(RuleMatchCandidateTxArrayHasSpace(
+                        det_ctx, total_rules))) { // TODO is it safe to overload?
+                RuleMatchCandidateTxArrayExpand(det_ctx, total_rules);
+            }
+
+            for (uint32_t i = 0; i < det_ctx->pmq.rule_id_array_cnt; i++) {
+                const Signature *s = de_ctx->sig_array[det_ctx->pmq.rule_id_array[i]];
+                const SigIntId id = s->num;
+                det_ctx->tx_candidates[array_idx].s = s;
+                det_ctx->tx_candidates[array_idx].id = id;
+                det_ctx->tx_candidates[array_idx].flags = NULL;
+                det_ctx->tx_candidates[array_idx].stream_reset = 0;
+                array_idx++;
+            }
+            PMQ_RESET(&det_ctx->pmq);
+        }
+        /* merge 'state' rules from the regular prefilter */
+        uint32_t x = array_idx;
+        for (uint32_t i = 0; i < det_ctx->match_array_cnt; i++) {
+            const Signature *s = det_ctx->match_array[i];
+            if (s->frame_inspect != NULL) {
+                const SigIntId id = s->num;
+                det_ctx->tx_candidates[array_idx].s = s;
+                det_ctx->tx_candidates[array_idx].id = id;
+                det_ctx->tx_candidates[array_idx].flags = NULL;
+                det_ctx->tx_candidates[array_idx].stream_reset = 0;
+                array_idx++;
+
+                SCLogDebug("%p/%" PRIi64 " rule %u (%u) added from 'match' list", frame, frame->id,
+                        s->id, id);
+            }
+        }
+        SCLogDebug("%p/%" PRIi64 " rules added from 'match' list: %u", frame, frame->id,
+                array_idx - x);
+        (void)x;
+
+        /* run rules: inspect the match candidates */
+        for (uint32_t i = 0; i < array_idx; i++) {
+            const Signature *s = det_ctx->tx_candidates[i].s;
+
+            SCLogDebug("%p/%" PRIi64 " inspecting: sid %u (%u)", frame, frame->id, s->id, s->num);
+
+            /* start new inspection */
+            SCLogDebug("%p/%" PRIi64 " Start sid %u", frame, frame->id, s->id);
+
+            /* call individual rule inspection */
+            RULE_PROFILING_START(p);
+            int r = DetectRunInspectRuleHeader(p, f, s, s->flags, s->proto.flags);
+            if (r == 1) {
+                r = DetectRunFrameInspectRule(tv, det_ctx, s, f, p, frames, frame, idx);
+                if (r == 1) {
+                    /* match */
+                    DetectRunPostMatch(tv, det_ctx, p, s);
+
+                    const uint8_t alert_flags =
+                            (PACKET_ALERT_FLAG_STATE_MATCH | PACKET_ALERT_FLAG_FRAME);
+                    // TODO set tx id if the frame has it
+                    det_ctx->flags |= DETECT_ENGINE_THREAD_CTX_FRAME_ID_SET;
+                    det_ctx->frame_id = frame->id;
+                    SCLogDebug(
+                            "%p/%" PRIi64 " sig %u (%u) matched", frame, frame->id, s->id, s->num);
+                    PacketAlertAppend(det_ctx, s, p, 0, alert_flags); // TODO tx id frame field
+                }
+            }
+            DetectVarProcessList(det_ctx, p->flow, p);
+            RULE_PROFILING_END(det_ctx, s, r, p);
+        }
+        InspectionBufferClean(det_ctx);
+    }
+}
+
 static DetectEngineThreadCtx *GetTenantById(HashTable *h, uint32_t id)
 {
     /* technically we need to pass a DetectEngineThreadCtx struct with the
index 3a3758a35d4abbdea0c31fa98858e79bfa632901..215118db4d6d855e835159cb8b02258feb3c255d 100644 (file)
@@ -279,6 +279,7 @@ typedef struct DetectPort_ {
 /* for now a uint8_t is enough */
 #define SignatureMask uint8_t
 
+#define DETECT_ENGINE_THREAD_CTX_FRAME_ID_SET         0x0001
 #define DETECT_ENGINE_THREAD_CTX_STREAM_CONTENT_MATCH 0x0004
 
 #define FILE_SIG_NEED_FILE          0x01
@@ -423,6 +424,7 @@ typedef struct DetectBufferType_ {
     int parent_id;
     bool mpm;
     bool packet; /**< compat to packet matches */
+    bool frame;  /**< is about Frame inspection */
     bool supports_transforms;
     void (*SetupCallback)(const struct DetectEngineCtx_ *, struct Signature_ *);
     bool (*ValidateCallback)(const struct Signature_ *, const char **sigerror);
@@ -460,6 +462,33 @@ typedef struct DetectEnginePktInspectionEngine {
     struct DetectEnginePktInspectionEngine *next;
 } DetectEnginePktInspectionEngine;
 
+struct Frame;
+struct Frames;
+struct DetectEngineFrameInspectionEngine;
+
+/**
+ *  \param alert_flags[out] for setting PACKET_ALERT_FLAG_*
+ */
+typedef int (*InspectionBufferFrameInspectFunc)(struct DetectEngineThreadCtx_ *,
+        const struct DetectEngineFrameInspectionEngine *engine, const struct Signature_ *s,
+        Packet *p, const struct Frames *frames, const struct Frame *frame, const uint32_t idx);
+
+typedef struct DetectEngineFrameInspectionEngine {
+    AppProto alproto;
+    uint8_t dir;
+    uint8_t type;
+    bool mpm;
+    uint16_t sm_list;
+    uint16_t sm_list_base;
+    struct {
+        InspectionBufferFrameInspectFunc Callback;
+        /** pointer to the transforms in the 'DetectBuffer entry for this list */
+        const DetectEngineTransforms *transforms;
+    } v1;
+    SigMatchData *smd;
+    struct DetectEngineFrameInspectionEngine *next;
+} DetectEngineFrameInspectionEngine;
+
 #ifdef UNITTESTS
 #define sm_lists init_data->smlists
 #define sm_lists_tail init_data->smlists_tail
@@ -565,6 +594,7 @@ typedef struct Signature_ {
 
     DetectEngineAppInspectionEngine *app_inspect;
     DetectEnginePktInspectionEngine *pkt_inspect;
+    DetectEngineFrameInspectionEngine *frame_inspect;
 
     /* Matching structures for the built-ins. The others are in
      * their inspect engines. */
@@ -593,6 +623,7 @@ typedef struct Signature_ {
 enum DetectBufferMpmType {
     DETECT_BUFFER_MPM_TYPE_PKT,
     DETECT_BUFFER_MPM_TYPE_APP,
+    DETECT_BUFFER_MPM_TYPE_FRAME,
     /* must be last */
     DETECT_BUFFER_MPM_TYPE_SIZE,
 };
@@ -629,6 +660,12 @@ typedef struct DetectBufferMpmRegistery_ {
                     const struct DetectBufferMpmRegistery_ *mpm_reg, int list_id);
             InspectionBufferGetPktDataPtr GetData;
         } pkt_v1;
+
+        /* frame matching: use if type == DETECT_BUFFER_MPM_TYPE_FRAME */
+        struct {
+            AppProto alproto;
+            uint8_t type;
+        } frame_v1;
     };
 
     struct DetectBufferMpmRegistery_ *next;
@@ -933,6 +970,9 @@ typedef struct DetectEngineCtx_ {
     DetectEnginePktInspectionEngine *pkt_inspect_engines;
     DetectBufferMpmRegistery *pkt_mpms_list;
     uint32_t pkt_mpms_list_cnt;
+    DetectEngineFrameInspectionEngine *frame_inspect_engines;
+    DetectBufferMpmRegistery *frame_mpms_list;
+    uint32_t frame_mpms_list_cnt;
 
     uint32_t prefilter_id;
     HashListTable *prefilter_hash_table;
@@ -1269,6 +1309,9 @@ typedef struct MpmStore_ {
 
 } MpmStore;
 
+typedef void (*PrefilterFrameFn)(DetectEngineThreadCtx *det_ctx, const void *pectx, Packet *p,
+        const struct Frames *frames, const struct Frame *frame, const uint32_t idx);
+
 typedef struct PrefilterEngineList_ {
     uint16_t id;
 
@@ -1278,6 +1321,8 @@ typedef struct PrefilterEngineList_ {
      *  with Tx Engine */
     uint8_t tx_min_progress;
 
+    uint8_t frame_type;
+
     /** Context for matching. Might be MpmCtx for MPM engines, other ctx'
      *  for other engines. */
     void *pectx;
@@ -1286,6 +1331,7 @@ typedef struct PrefilterEngineList_ {
     void (*PrefilterTx)(DetectEngineThreadCtx *det_ctx, const void *pectx,
             Packet *p, Flow *f, void *tx,
             const uint64_t idx, const uint8_t flags);
+    PrefilterFrameFn PrefilterFrame;
 
     struct PrefilterEngineList_ *next;
 
@@ -1302,9 +1348,13 @@ typedef struct PrefilterEngine_ {
 
     /** App Proto this engine applies to: only used with Tx Engines */
     AppProto alproto;
-    /** Minimal Tx progress we need before running the engine. Only used
-     *  with Tx Engine */
-    uint8_t tx_min_progress;
+
+    union {
+        /** Minimal Tx progress we need before running the engine. Only used
+         *  with Tx Engine */
+        uint8_t tx_min_progress;
+        uint8_t frame_type;
+    } ctx;
 
     /** Context for matching. Might be MpmCtx for MPM engines, other ctx'
      *  for other engines. */
@@ -1315,6 +1365,7 @@ typedef struct PrefilterEngine_ {
         void (*PrefilterTx)(DetectEngineThreadCtx *det_ctx, const void *pectx,
                 Packet *p, Flow *f, void *tx,
                 const uint64_t idx, const uint8_t flags);
+        PrefilterFrameFn PrefilterFrame;
     } cb;
 
     /* global id for this prefilter */
@@ -1335,10 +1386,12 @@ typedef struct SigGroupHeadInitData_ {
 
     MpmCtx **app_mpms;
     MpmCtx **pkt_mpms;
+    MpmCtx **frame_mpms;
 
     PrefilterEngineList *pkt_engines;
     PrefilterEngineList *payload_engines;
     PrefilterEngineList *tx_engines;
+    PrefilterEngineList *frame_engines;
 
     /** number of sigs in this group */
     SigIntId sig_cnt;
@@ -1371,6 +1424,7 @@ typedef struct SigGroupHead_ {
     PrefilterEngine *pkt_engines;
     PrefilterEngine *payload_engines;
     PrefilterEngine *tx_engines;
+    PrefilterEngine *frame_engines;
 
     /* ptr to our init data we only use at... init :) */
     SigGroupHeadInitData *init;
index d1bcd97c123f1c2a591523967cb75575beedb741..84b578d7473b34cbf9037140bbbaef476d5c8743 100644 (file)
@@ -422,6 +422,7 @@ typedef enum PacketProfileDetectId_ {
     PROF_DETECT_PF_PKT,
     PROF_DETECT_PF_PAYLOAD,
     PROF_DETECT_PF_TX,
+    PROF_DETECT_PF_RECORD,
     PROF_DETECT_PF_SORT1,
     PROF_DETECT_PF_SORT2,
     PROF_DETECT_NONMPMLIST,
index d8807f697a0943ff43efb653ffc89943987fa1a3..4be72e58d0ef313c9bd8855e44797bd64e8e1927 100644 (file)
@@ -1267,6 +1267,7 @@ const char * PacketProfileDetectIdToString(PacketProfileDetectId id)
         CASE_CODE (PROF_DETECT_PF_PKT);
         CASE_CODE (PROF_DETECT_PF_PAYLOAD);
         CASE_CODE (PROF_DETECT_PF_TX);
+        CASE_CODE(PROF_DETECT_PF_RECORD);
         CASE_CODE (PROF_DETECT_PF_SORT1);
         CASE_CODE (PROF_DETECT_PF_SORT2);
         CASE_CODE (PROF_DETECT_NONMPMLIST);