Coverage Report

Created: 2026-05-16 07:38

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/suricata7/src/detect.c
Line
Count
Source
1
/* Copyright (C) 2007-2022 Open Information Security Foundation
2
 *
3
 * You can copy, redistribute or modify this Program under the terms of
4
 * the GNU General Public License version 2 as published by the Free
5
 * Software Foundation.
6
 *
7
 * This program is distributed in the hope that it will be useful,
8
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10
 * GNU General Public License for more details.
11
 *
12
 * You should have received a copy of the GNU General Public License
13
 * version 2 along with this program; if not, write to the Free Software
14
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
15
 * 02110-1301, USA.
16
 */
17
18
/**
19
 * \file
20
 *
21
 * \author Victor Julien <victor@inliniac.net>
22
 *
23
 * Basic detection engine
24
 */
25
26
#include "suricata-common.h"
27
#include "suricata.h"
28
#include "conf.h"
29
30
#include "decode.h"
31
#include "packet.h"
32
#include "flow.h"
33
#include "stream-tcp.h"
34
#include "app-layer.h"
35
#include "app-layer-parser.h"
36
#include "app-layer-frames.h"
37
38
#include "detect.h"
39
#include "detect-dsize.h"
40
#include "detect-engine.h"
41
#include "detect-engine-build.h"
42
#include "detect-engine-frame.h"
43
#include "detect-engine-profile.h"
44
45
#include "detect-engine-alert.h"
46
#include "detect-engine-siggroup.h"
47
#include "detect-engine-address.h"
48
#include "detect-engine-proto.h"
49
#include "detect-engine-port.h"
50
#include "detect-engine-mpm.h"
51
#include "detect-engine-iponly.h"
52
#include "detect-engine-threshold.h"
53
#include "detect-engine-prefilter.h"
54
#include "detect-engine-state.h"
55
#include "detect-engine-analyzer.h"
56
57
#include "detect-engine-payload.h"
58
#include "detect-engine-event.h"
59
60
#include "detect-filestore.h"
61
#include "detect-flowvar.h"
62
#include "detect-replace.h"
63
64
#include "util-validate.h"
65
#include "util-detect.h"
66
#include "util-profiling.h"
67
68
#include "action-globals.h"
69
70
typedef struct DetectRunScratchpad {
71
    const AppProto alproto;
72
    const uint8_t flow_flags; /* flow/state flags: STREAM_* */
73
    const bool app_decoder_events;
74
    const SigGroupHead *sgh;
75
    SignatureMask pkt_mask;
76
} DetectRunScratchpad;
77
78
/* prototypes */
79
static DetectRunScratchpad DetectRunSetup(const DetectEngineCtx *de_ctx,
80
        DetectEngineThreadCtx *det_ctx, Packet * const p, Flow * const pflow);
81
static void DetectRunInspectIPOnly(ThreadVars *tv, const DetectEngineCtx *de_ctx,
82
        DetectEngineThreadCtx *det_ctx, Flow * const pflow, Packet * const p);
83
static inline void DetectRunGetRuleGroup(const DetectEngineCtx *de_ctx,
84
        Packet * const p, Flow * const pflow, DetectRunScratchpad *scratch);
85
static inline void DetectRunPrefilterPkt(ThreadVars *tv,
86
        DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx, Packet *p,
87
        DetectRunScratchpad *scratch);
88
static inline void DetectRulePacketRules(ThreadVars * const tv,
89
        DetectEngineCtx * const de_ctx, DetectEngineThreadCtx * const det_ctx,
90
        Packet * const p, Flow * const pflow, const DetectRunScratchpad *scratch);
91
static void DetectRunTx(ThreadVars *tv, DetectEngineCtx *de_ctx,
92
        DetectEngineThreadCtx *det_ctx, Packet *p,
93
        Flow *f, DetectRunScratchpad *scratch);
94
static void DetectRunFrames(ThreadVars *tv, DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx,
95
        Packet *p, Flow *f, DetectRunScratchpad *scratch);
96
static inline void DetectRunPostRules(ThreadVars *tv, DetectEngineCtx *de_ctx,
97
        DetectEngineThreadCtx *det_ctx, Packet * const p, Flow * const pflow,
98
        DetectRunScratchpad *scratch);
99
static void DetectRunCleanup(DetectEngineThreadCtx *det_ctx,
100
        Packet *p, Flow * const pflow);
101
102
/** \internal
103
 */
104
static void DetectRun(ThreadVars *th_v,
105
        DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx,
106
        Packet *p)
107
8.68M
{
108
8.68M
    SCEnter();
109
8.68M
    SCLogDebug("p->pcap_cnt %" PRIu64 " direction %s pkt_src %s", p->pcap_cnt,
110
8.68M
            p->flow ? (FlowGetPacketDirection(p->flow, p) == TOSERVER ? "toserver" : "toclient")
111
8.68M
                    : "noflow",
112
8.68M
            PktSrcToString(p->pkt_src));
113
114
    /* bail early if packet should not be inspected */
115
8.68M
    if (p->flags & PKT_NOPACKET_INSPECTION) {
116
        /* nothing to do */
117
0
        SCReturn;
118
0
    }
119
120
    /* Load the Packet's flow early, even though it might not be needed.
121
     * Mark as a constant pointer, although the flow itself can change. */
122
8.68M
    Flow * const pflow = p->flow;
123
124
8.68M
    DetectRunScratchpad scratch = DetectRunSetup(de_ctx, det_ctx, p, pflow);
125
126
    /* run the IPonly engine */
127
8.68M
    DetectRunInspectIPOnly(th_v, de_ctx, det_ctx, pflow, p);
128
129
    /* get our rule group */
130
8.68M
    DetectRunGetRuleGroup(de_ctx, p, pflow, &scratch);
131
    /* if we didn't get a sig group head, we
132
     * have nothing to do.... */
133
8.68M
    if (scratch.sgh == NULL) {
134
6.16M
        SCLogDebug("no sgh for this packet, nothing to match against");
135
6.16M
        goto end;
136
6.16M
    }
137
138
    /* run the prefilters for packets */
139
2.51M
    DetectRunPrefilterPkt(th_v, de_ctx, det_ctx, p, &scratch);
140
141
2.51M
    PACKET_PROFILING_DETECT_START(p, PROF_DETECT_RULES);
142
    /* inspect the rules against the packet */
143
2.51M
    DetectRulePacketRules(th_v, de_ctx, det_ctx, p, pflow, &scratch);
144
2.51M
    PACKET_PROFILING_DETECT_END(p, PROF_DETECT_RULES);
145
146
    /* run tx/state inspection. Don't call for ICMP error msgs. */
147
2.51M
    if (pflow && pflow->alstate && likely(pflow->proto == p->proto)) {
148
1.70M
        if (p->proto == IPPROTO_TCP) {
149
1.64M
            const TcpSession *ssn = p->flow->protoctx;
150
1.64M
            if (ssn && (ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED) == 0) {
151
                // PACKET_PROFILING_DETECT_START(p, PROF_DETECT_TX);
152
1.48M
                DetectRunFrames(th_v, de_ctx, det_ctx, p, pflow, &scratch);
153
                // PACKET_PROFILING_DETECT_END(p, PROF_DETECT_TX);
154
1.48M
            }
155
            // no update to transactions
156
1.64M
            if (!PKT_IS_PSEUDOPKT(p) && p->app_update_direction == 0 &&
157
1.05M
                    ((PKT_IS_TOSERVER(p) && (p->flow->flags & FLOW_TS_APP_UPDATED) == 0) ||
158
895k
                            (PKT_IS_TOCLIENT(p) && (p->flow->flags & FLOW_TC_APP_UPDATED) == 0))) {
159
895k
                goto end;
160
895k
            }
161
1.64M
        } else if (p->proto == IPPROTO_UDP) {
162
69.3k
            DetectRunFrames(th_v, de_ctx, det_ctx, p, pflow, &scratch);
163
69.3k
        }
164
165
813k
        PACKET_PROFILING_DETECT_START(p, PROF_DETECT_TX);
166
813k
        DetectRunTx(th_v, de_ctx, det_ctx, p, pflow, &scratch);
167
813k
        PACKET_PROFILING_DETECT_END(p, PROF_DETECT_TX);
168
        /* see if we need to increment the inspect_id and reset the de_state */
169
813k
        PACKET_PROFILING_DETECT_START(p, PROF_DETECT_TX_UPDATE);
170
813k
        AppLayerParserSetTransactionInspectId(
171
813k
                pflow, pflow->alparser, pflow->alstate, scratch.flow_flags, (scratch.sgh == NULL));
172
813k
        PACKET_PROFILING_DETECT_END(p, PROF_DETECT_TX_UPDATE);
173
813k
    }
174
175
8.68M
end:
176
8.68M
    DetectRunPostRules(th_v, de_ctx, det_ctx, p, pflow, &scratch);
177
178
8.68M
    DetectRunCleanup(det_ctx, p, pflow);
179
8.68M
    SCReturn;
180
2.51M
}
181
182
static void DetectRunPostMatch(ThreadVars *tv,
183
                               DetectEngineThreadCtx *det_ctx, Packet *p,
184
                               const Signature *s)
185
805k
{
186
    /* run the packet match functions */
187
805k
    const SigMatchData *smd = s->sm_arrays[DETECT_SM_LIST_POSTMATCH];
188
805k
    if (smd != NULL) {
189
98.7k
        KEYWORD_PROFILING_SET_LIST(det_ctx, DETECT_SM_LIST_POSTMATCH);
190
191
98.7k
        SCLogDebug("running match functions, sm %p", smd);
192
193
106k
        while (1) {
194
106k
            KEYWORD_PROFILING_START;
195
106k
            (void)sigmatch_table[smd->type].Match(det_ctx, p, s, smd->ctx);
196
106k
            KEYWORD_PROFILING_END(det_ctx, smd->type, 1);
197
106k
            if (smd->is_last)
198
98.7k
                break;
199
7.91k
            smd++;
200
7.91k
        }
201
98.7k
    }
202
805k
}
203
204
/**
205
 *  \brief Get the SigGroupHead for a packet.
206
 *
207
 *  \param de_ctx detection engine context
208
 *  \param p packet
209
 *
210
 *  \retval sgh the SigGroupHead or NULL if non applies to the packet
211
 */
212
const SigGroupHead *SigMatchSignaturesGetSgh(const DetectEngineCtx *de_ctx,
213
        const Packet *p)
214
1.97M
{
215
1.97M
    SCEnter();
216
217
1.97M
    int f;
218
1.97M
    SigGroupHead *sgh = NULL;
219
220
    /* if the packet proto is 0 (not set), we're inspecting it against
221
     * the decoder events sgh we have. */
222
1.97M
    if (p->proto == 0 && p->events.cnt > 0) {
223
710k
        SCReturnPtr(de_ctx->decoder_event_sgh, "SigGroupHead");
224
1.26M
    } else if (p->proto == 0) {
225
526k
        if (!(PKT_IS_IPV4(p) || PKT_IS_IPV6(p))) {
226
            /* not IP, so nothing to do */
227
515k
            SCReturnPtr(NULL, "SigGroupHead");
228
515k
        }
229
526k
    }
230
231
    /* select the flow_gh */
232
746k
    if (p->flowflags & FLOW_PKT_TOCLIENT)
233
167k
        f = 0;
234
579k
    else
235
579k
        f = 1;
236
237
746k
    int proto = IP_GET_IPPROTO(p);
238
746k
    if (proto == IPPROTO_TCP) {
239
546k
        DetectPort *list = de_ctx->flow_gh[f].tcp;
240
546k
        SCLogDebug("tcp toserver %p, tcp toclient %p: going to use %p",
241
546k
                de_ctx->flow_gh[1].tcp, de_ctx->flow_gh[0].tcp, de_ctx->flow_gh[f].tcp);
242
546k
        uint16_t port = f ? p->dp : p->sp;
243
546k
        SCLogDebug("tcp port %u -> %u:%u", port, p->sp, p->dp);
244
546k
        DetectPort *sghport = DetectPortLookupGroup(list, port);
245
546k
        if (sghport != NULL)
246
204k
            sgh = sghport->sh;
247
546k
        SCLogDebug("TCP list %p, port %u, direction %s, sghport %p, sgh %p",
248
546k
                list, port, f ? "toserver" : "toclient", sghport, sgh);
249
546k
    } else if (proto == IPPROTO_UDP) {
250
39.8k
        DetectPort *list = de_ctx->flow_gh[f].udp;
251
39.8k
        uint16_t port = f ? p->dp : p->sp;
252
39.8k
        DetectPort *sghport = DetectPortLookupGroup(list, port);
253
39.8k
        if (sghport != NULL)
254
16.9k
            sgh = sghport->sh;
255
39.8k
        SCLogDebug("UDP list %p, port %u, direction %s, sghport %p, sgh %p",
256
39.8k
                list, port, f ? "toserver" : "toclient", sghport, sgh);
257
160k
    } else {
258
160k
        sgh = de_ctx->flow_gh[f].sgh[proto];
259
160k
    }
260
261
746k
    SCReturnPtr(sgh, "SigGroupHead");
262
1.97M
}
263
264
static inline void DetectPrefilterMergeSort(DetectEngineCtx *de_ctx,
265
                                            DetectEngineThreadCtx *det_ctx)
266
2.11M
{
267
2.11M
    SigIntId mpm, nonmpm;
268
2.11M
    SigIntId *mpm_ptr = det_ctx->pmq.rule_id_array;
269
2.11M
    SigIntId *nonmpm_ptr = det_ctx->non_pf_id_array;
270
2.11M
    uint32_t m_cnt = det_ctx->pmq.rule_id_array_cnt;
271
2.11M
    uint32_t n_cnt = det_ctx->non_pf_id_cnt;
272
2.11M
    SigIntId *final_ptr;
273
2.11M
    uint32_t final_cnt;
274
2.11M
    SigIntId id;
275
2.11M
    SigIntId previous_id = (SigIntId)-1;
276
2.11M
    Signature **sig_array = de_ctx->sig_array;
277
2.11M
    Signature **match_array = det_ctx->match_array;
278
2.11M
    Signature *s;
279
280
2.11M
    SCLogDebug("PMQ rule id array count %d", det_ctx->pmq.rule_id_array_cnt);
281
282
    /* Load first values. */
283
2.11M
    if (likely(m_cnt)) {
284
27.7k
        mpm = *mpm_ptr;
285
2.08M
    } else {
286
        /* mpm list is empty */
287
2.08M
        final_ptr = nonmpm_ptr;
288
2.08M
        final_cnt = n_cnt;
289
2.08M
        goto final;
290
2.08M
    }
291
27.7k
    if (likely(n_cnt)) {
292
17.0k
        nonmpm = *nonmpm_ptr;
293
17.0k
    } else {
294
        /* non-mpm list is empty. */
295
10.7k
        final_ptr = mpm_ptr;
296
10.7k
        final_cnt = m_cnt;
297
10.7k
        goto final;
298
10.7k
    }
299
30.1k
    while (1) {
300
30.1k
        if (mpm < nonmpm) {
301
            /* Take from mpm list */
302
8.08k
            id = mpm;
303
304
8.08k
            s = sig_array[id];
305
            /* As the mpm list can contain duplicates, check for that here. */
306
8.08k
            if (likely(id != previous_id)) {
307
8.03k
                *match_array++ = s;
308
8.03k
                previous_id = id;
309
8.03k
            }
310
8.08k
            if (unlikely(--m_cnt == 0)) {
311
                /* mpm list is now empty */
312
5.92k
                final_ptr = nonmpm_ptr;
313
5.92k
                final_cnt = n_cnt;
314
5.92k
                goto final;
315
5.92k
             }
316
2.15k
             mpm_ptr++;
317
2.15k
             mpm = *mpm_ptr;
318
22.0k
         } else if (mpm > nonmpm) {
319
21.5k
             id = nonmpm;
320
321
21.5k
             s = sig_array[id];
322
             /* As the mpm list can contain duplicates, check for that here. */
323
21.5k
             if (likely(id != previous_id)) {
324
21.5k
                 *match_array++ = s;
325
21.5k
                 previous_id = id;
326
21.5k
             }
327
21.5k
             if (unlikely(--n_cnt == 0)) {
328
10.6k
                 final_ptr = mpm_ptr;
329
10.6k
                 final_cnt = m_cnt;
330
10.6k
                 goto final;
331
10.6k
             }
332
10.8k
             nonmpm_ptr++;
333
10.8k
             nonmpm = *nonmpm_ptr;
334
335
10.8k
        } else { /* implied mpm == nonmpm */
336
            /* special case: if on both lists, it's a negated mpm pattern */
337
338
            /* mpm list may have dups, so skip past them here */
339
528
            while (--m_cnt != 0) {
340
106
                mpm_ptr++;
341
106
                mpm = *mpm_ptr;
342
106
                if (mpm != nonmpm)
343
105
                    break;
344
106
            }
345
            /* if mpm is done, update nonmpm_ptrs and jump to final */
346
527
            if (unlikely(m_cnt == 0)) {
347
422
                n_cnt--;
348
349
                /* mpm list is now empty */
350
422
                final_ptr = ++nonmpm_ptr;
351
422
                final_cnt = n_cnt;
352
422
                goto final;
353
422
            }
354
            /* otherwise, if nonmpm is done jump to final for mpm
355
             * mpm ptrs already updated */
356
105
            if (unlikely(--n_cnt == 0)) {
357
28
                final_ptr = mpm_ptr;
358
28
                final_cnt = m_cnt;
359
28
                goto final;
360
28
            }
361
362
            /* not at end of the lists, update nonmpm. Mpm already
363
             * updated in while loop above. */
364
77
            nonmpm_ptr++;
365
77
            nonmpm = *nonmpm_ptr;
366
77
        }
367
30.1k
    }
368
369
2.11M
 final: /* Only one list remaining. Just walk that list. */
370
371
4.93M
    while (final_cnt-- > 0) {
372
2.82M
        id = *final_ptr++;
373
2.82M
        s = sig_array[id];
374
375
        /* As the mpm list can contain duplicates, check for that here. */
376
2.82M
        if (likely(id != previous_id)) {
377
2.82M
            *match_array++ = s;
378
2.82M
            previous_id = id;
379
2.82M
        }
380
2.82M
    }
381
382
2.11M
    det_ctx->match_array_cnt = match_array - det_ctx->match_array;
383
2.11M
    DEBUG_VALIDATE_BUG_ON((det_ctx->pmq.rule_id_array_cnt + det_ctx->non_pf_id_cnt) < det_ctx->match_array_cnt);
384
2.11M
    PMQ_RESET(&det_ctx->pmq);
385
2.11M
}
386
387
/** \internal
388
 *  \brief build non-prefilter list based on the rule group list we've set.
389
 */
390
static inline void DetectPrefilterBuildNonPrefilterList(
391
        DetectEngineThreadCtx *det_ctx, const SignatureMask mask, const AppProto alproto)
392
2.10M
{
393
7.52M
    for (uint32_t x = 0; x < det_ctx->non_pf_store_cnt; x++) {
394
        /* only if the mask matches this rule can possibly match,
395
         * so build the non_mpm array only for match candidates */
396
5.41M
        const SignatureMask rule_mask = det_ctx->non_pf_store_ptr[x].mask;
397
5.41M
        const AppProto rule_alproto = det_ctx->non_pf_store_ptr[x].alproto;
398
5.41M
        if ((rule_mask & mask) == rule_mask &&
399
4.01M
                (rule_alproto == 0 || AppProtoEquals(rule_alproto, alproto))) {
400
2.82M
            det_ctx->non_pf_id_array[det_ctx->non_pf_id_cnt++] = det_ctx->non_pf_store_ptr[x].id;
401
2.82M
        }
402
5.41M
    }
403
2.10M
}
404
405
/** \internal
406
 *  \brief select non-mpm list
407
 *  Based on the packet properties, select the non-mpm list to use
408
 *  \todo move non_pf_store* into scratchpad */
409
static inline void
410
DetectPrefilterSetNonPrefilterList(const Packet *p, DetectEngineThreadCtx *det_ctx, DetectRunScratchpad *scratch)
411
2.51M
{
412
2.51M
    if ((p->proto == IPPROTO_TCP) && (p->tcph != NULL) && (p->tcph->th_flags & TH_SYN)) {
413
134k
        det_ctx->non_pf_store_ptr = scratch->sgh->non_pf_syn_store_array;
414
134k
        det_ctx->non_pf_store_cnt = scratch->sgh->non_pf_syn_store_cnt;
415
2.38M
    } else {
416
2.38M
        det_ctx->non_pf_store_ptr = scratch->sgh->non_pf_other_store_array;
417
2.38M
        det_ctx->non_pf_store_cnt = scratch->sgh->non_pf_other_store_cnt;
418
2.38M
    }
419
2.51M
    SCLogDebug("sgh non_pf ptr %p cnt %u (syn %p/%u, other %p/%u)",
420
2.51M
            det_ctx->non_pf_store_ptr, det_ctx->non_pf_store_cnt,
421
2.51M
            scratch->sgh->non_pf_syn_store_array, scratch->sgh->non_pf_syn_store_cnt,
422
2.51M
            scratch->sgh->non_pf_other_store_array, scratch->sgh->non_pf_other_store_cnt);
423
2.51M
}
424
425
/** \internal
426
 *  \brief update flow's file tracking flags based on the detection engine
427
 *         A set of flags is prepared that is sent to the File API. The
428
           File API may reject one or more based on the global force settings.
429
 */
430
static inline void
431
DetectPostInspectFileFlagsUpdate(Flow *f, const SigGroupHead *sgh, uint8_t direction)
432
404k
{
433
404k
    uint16_t flow_file_flags = FLOWFILE_INIT;
434
435
404k
    if (sgh == NULL) {
436
248k
        SCLogDebug("requesting disabling all file features for flow");
437
248k
        flow_file_flags = FLOWFILE_NONE;
438
248k
    } else {
439
156k
        if (sgh->filestore_cnt == 0) {
440
117k
            SCLogDebug("requesting disabling filestore for flow");
441
117k
            flow_file_flags |= (FLOWFILE_NO_STORE_TS|FLOWFILE_NO_STORE_TC);
442
117k
        }
443
#ifdef HAVE_MAGIC
444
        if (!(sgh->flags & SIG_GROUP_HEAD_HAVEFILEMAGIC)) {
445
            SCLogDebug("requesting disabling magic for flow");
446
            flow_file_flags |= (FLOWFILE_NO_MAGIC_TS|FLOWFILE_NO_MAGIC_TC);
447
        }
448
#endif
449
156k
        if (!(sgh->flags & SIG_GROUP_HEAD_HAVEFILEMD5)) {
450
156k
            SCLogDebug("requesting disabling md5 for flow");
451
156k
            flow_file_flags |= (FLOWFILE_NO_MD5_TS|FLOWFILE_NO_MD5_TC);
452
156k
        }
453
156k
        if (!(sgh->flags & SIG_GROUP_HEAD_HAVEFILESHA1)) {
454
156k
            SCLogDebug("requesting disabling sha1 for flow");
455
156k
            flow_file_flags |= (FLOWFILE_NO_SHA1_TS|FLOWFILE_NO_SHA1_TC);
456
156k
        }
457
156k
        if (!(sgh->flags & SIG_GROUP_HEAD_HAVEFILESHA256)) {
458
156k
            SCLogDebug("requesting disabling sha256 for flow");
459
156k
            flow_file_flags |= (FLOWFILE_NO_SHA256_TS|FLOWFILE_NO_SHA256_TC);
460
156k
        }
461
156k
        if (!(sgh->flags & SIG_GROUP_HEAD_HAVEFILESIZE)) {
462
156k
            SCLogDebug("requesting disabling filesize for flow");
463
156k
            flow_file_flags |= (FLOWFILE_NO_SIZE_TS|FLOWFILE_NO_SIZE_TC);
464
156k
        }
465
156k
    }
466
404k
    if (flow_file_flags != 0) {
467
404k
        FileUpdateFlowFileFlags(f, flow_file_flags, direction);
468
404k
    }
469
404k
}
470
471
static inline void
472
DetectRunPostGetFirstRuleGroup(const Packet *p, Flow *pflow, const SigGroupHead *sgh)
473
845k
{
474
845k
    if ((p->flowflags & FLOW_PKT_TOSERVER) && !(pflow->flags & FLOW_SGH_TOSERVER)) {
475
        /* first time we see this toserver sgh, store it */
476
507k
        pflow->sgh_toserver = sgh;
477
507k
        pflow->flags |= FLOW_SGH_TOSERVER;
478
479
507k
        if (p->proto == IPPROTO_TCP && (sgh == NULL || !(sgh->flags & SIG_GROUP_HEAD_HAVERAWSTREAM))) {
480
321k
            if (pflow->protoctx != NULL) {
481
298k
                TcpSession *ssn = pflow->protoctx;
482
298k
                SCLogDebug("STREAMTCP_STREAM_FLAG_DISABLE_RAW ssn.client");
483
298k
                ssn->client.flags |= STREAMTCP_STREAM_FLAG_DISABLE_RAW;
484
298k
            }
485
321k
        }
486
487
507k
        DetectPostInspectFileFlagsUpdate(pflow,
488
507k
                pflow->sgh_toserver, STREAM_TOSERVER);
489
490
507k
    } else if ((p->flowflags & FLOW_PKT_TOCLIENT) && !(pflow->flags & FLOW_SGH_TOCLIENT)) {
491
337k
        pflow->sgh_toclient = sgh;
492
337k
        pflow->flags |= FLOW_SGH_TOCLIENT;
493
494
337k
        if (p->proto == IPPROTO_TCP && (sgh == NULL || !(sgh->flags & SIG_GROUP_HEAD_HAVERAWSTREAM))) {
495
251k
            if (pflow->protoctx != NULL) {
496
250k
                TcpSession *ssn = pflow->protoctx;
497
250k
                SCLogDebug("STREAMTCP_STREAM_FLAG_DISABLE_RAW ssn.server");
498
250k
                ssn->server.flags |= STREAMTCP_STREAM_FLAG_DISABLE_RAW;
499
250k
            }
500
251k
        }
501
502
337k
        DetectPostInspectFileFlagsUpdate(pflow,
503
337k
                pflow->sgh_toclient, STREAM_TOCLIENT);
504
337k
    }
505
845k
}
506
507
static inline void DetectRunGetRuleGroup(
508
    const DetectEngineCtx *de_ctx,
509
    Packet * const p, Flow * const pflow,
510
    DetectRunScratchpad *scratch)
511
8.68M
{
512
8.68M
    const SigGroupHead *sgh = NULL;
513
514
8.68M
    if (pflow) {
515
7.11M
        bool use_flow_sgh = false;
516
        /* Get the stored sgh from the flow (if any). Make sure we're not using
517
         * the sgh for icmp error packets part of the same stream. */
518
7.11M
        if (IP_GET_IPPROTO(p) == pflow->proto) { /* filter out icmp */
519
7.11M
            PACKET_PROFILING_DETECT_START(p, PROF_DETECT_GETSGH);
520
7.11M
            if ((p->flowflags & FLOW_PKT_TOSERVER) && (pflow->flags & FLOW_SGH_TOSERVER)) {
521
3.80M
                sgh = pflow->sgh_toserver;
522
3.80M
                SCLogDebug("sgh = pflow->sgh_toserver; => %p", sgh);
523
3.80M
                use_flow_sgh = true;
524
3.80M
            } else if ((p->flowflags & FLOW_PKT_TOCLIENT) && (pflow->flags & FLOW_SGH_TOCLIENT)) {
525
2.90M
                sgh = pflow->sgh_toclient;
526
2.90M
                SCLogDebug("sgh = pflow->sgh_toclient; => %p", sgh);
527
2.90M
                use_flow_sgh = true;
528
2.90M
            }
529
7.11M
            PACKET_PROFILING_DETECT_END(p, PROF_DETECT_GETSGH);
530
7.11M
        }
531
532
7.11M
        if (!(use_flow_sgh)) {
533
405k
            PACKET_PROFILING_DETECT_START(p, PROF_DETECT_GETSGH);
534
405k
            sgh = SigMatchSignaturesGetSgh(de_ctx, p);
535
405k
            PACKET_PROFILING_DETECT_END(p, PROF_DETECT_GETSGH);
536
537
            /* HACK: prevent the wrong sgh (or NULL) from being stored in the
538
             * flow's sgh pointers */
539
405k
            if (PKT_IS_ICMPV4(p) && ICMPV4_DEST_UNREACH_IS_VALID(p)) {
540
183
                ; /* no-op */
541
404k
            } else {
542
                /* store the found sgh (or NULL) in the flow to save us
543
                 * from looking it up again for the next packet.
544
                 * Also run other tasks */
545
404k
                DetectRunPostGetFirstRuleGroup(p, pflow, sgh);
546
404k
            }
547
405k
        }
548
7.11M
    } else { /* p->flags & PKT_HAS_FLOW */
549
        /* no flow */
550
551
1.56M
        PACKET_PROFILING_DETECT_START(p, PROF_DETECT_GETSGH);
552
1.56M
        sgh = SigMatchSignaturesGetSgh(de_ctx, p);
553
1.56M
        PACKET_PROFILING_DETECT_END(p, PROF_DETECT_GETSGH);
554
1.56M
    }
555
556
8.68M
    scratch->sgh = sgh;
557
8.68M
}
558
559
static void DetectRunInspectIPOnly(ThreadVars *tv, const DetectEngineCtx *de_ctx,
560
        DetectEngineThreadCtx *det_ctx,
561
        Flow * const pflow, Packet * const p)
562
17.6M
{
563
17.6M
    if (pflow) {
564
14.2M
        if (p->flowflags & (FLOW_PKT_TOSERVER_FIRST | FLOW_PKT_TOCLIENT_FIRST)) {
565
777k
            SCLogDebug("testing against \"ip-only\" signatures");
566
567
777k
            PACKET_PROFILING_DETECT_START(p, PROF_DETECT_IPONLY);
568
777k
            IPOnlyMatchPacket(tv, de_ctx, det_ctx, &de_ctx->io_ctx, p);
569
777k
            PACKET_PROFILING_DETECT_END(p, PROF_DETECT_IPONLY);
570
777k
        }
571
14.2M
    } else { /* p->flags & PKT_HAS_FLOW */
572
        /* no flow */
573
574
        /* Even without flow we should match the packet src/dst */
575
3.40M
        PACKET_PROFILING_DETECT_START(p, PROF_DETECT_IPONLY);
576
3.40M
        IPOnlyMatchPacket(tv, de_ctx, det_ctx, &de_ctx->io_ctx, p);
577
3.40M
        PACKET_PROFILING_DETECT_END(p, PROF_DETECT_IPONLY);
578
3.40M
    }
579
17.6M
}
580
581
/* returns 0 if no match, 1 if match */
582
static inline int DetectRunInspectRuleHeader(
583
    const Packet *p,
584
    const Flow *f,
585
    const Signature *s,
586
    const uint32_t sflags,
587
    const uint8_t s_proto_flags)
588
2.54M
{
589
    /* check if this signature has a requirement for flowvars of some type
590
     * and if so, if we actually have any in the flow. If not, the sig
591
     * can't match and we skip it. */
592
2.54M
    if ((p->flags & PKT_HAS_FLOW) && (sflags & SIG_FLAG_REQUIRE_FLOWVAR)) {
593
29.5k
        DEBUG_VALIDATE_BUG_ON(f == NULL);
594
595
29.5k
        int m  = f->flowvar ? 1 : 0;
596
597
        /* no flowvars? skip this sig */
598
29.5k
        if (m == 0) {
599
18.5k
            SCLogDebug("skipping sig as the flow has no flowvars and sig "
600
18.5k
                    "has SIG_FLAG_REQUIRE_FLOWVAR flag set.");
601
18.5k
            return 0;
602
18.5k
        }
603
29.5k
    }
604
605
2.52M
    if ((s_proto_flags & DETECT_PROTO_IPV4) && !PKT_IS_IPV4(p)) {
606
868
        SCLogDebug("ip version didn't match");
607
868
        return 0;
608
868
    }
609
2.52M
    if ((s_proto_flags & DETECT_PROTO_IPV6) && !PKT_IS_IPV6(p)) {
610
31.8k
        SCLogDebug("ip version didn't match");
611
31.8k
        return 0;
612
31.8k
    }
613
614
2.49M
    if (DetectProtoContainsProto(&s->proto, IP_GET_IPPROTO(p)) == 0) {
615
324
        SCLogDebug("proto didn't match");
616
324
        return 0;
617
324
    }
618
619
    /* check the source & dst port in the sig */
620
2.49M
    if (p->proto == IPPROTO_TCP || p->proto == IPPROTO_UDP || p->proto == IPPROTO_SCTP) {
621
2.07M
        if (!(sflags & SIG_FLAG_DP_ANY)) {
622
49.3k
            if (p->flags & PKT_IS_FRAGMENT)
623
0
                return 0;
624
49.3k
            DetectPort *dport = DetectPortLookupGroup(s->dp,p->dp);
625
49.3k
            if (dport == NULL) {
626
27.6k
                SCLogDebug("dport didn't match.");
627
27.6k
                return 0;
628
27.6k
            }
629
49.3k
        }
630
2.04M
        if (!(sflags & SIG_FLAG_SP_ANY)) {
631
34.3k
            if (p->flags & PKT_IS_FRAGMENT)
632
11
                return 0;
633
34.3k
            DetectPort *sport = DetectPortLookupGroup(s->sp,p->sp);
634
34.3k
            if (sport == NULL) {
635
31.3k
                SCLogDebug("sport didn't match.");
636
31.3k
                return 0;
637
31.3k
            }
638
34.3k
        }
639
2.04M
    } else if ((sflags & (SIG_FLAG_DP_ANY|SIG_FLAG_SP_ANY)) != (SIG_FLAG_DP_ANY|SIG_FLAG_SP_ANY)) {
640
3.45k
        SCLogDebug("port-less protocol and sig needs ports");
641
3.45k
        return 0;
642
3.45k
    }
643
644
    /* check the destination address */
645
2.42M
    if (!(sflags & SIG_FLAG_DST_ANY)) {
646
8.61k
        if (PKT_IS_IPV4(p)) {
647
6.98k
            if (DetectAddressMatchIPv4(s->addr_dst_match4, s->addr_dst_match4_cnt, &p->dst) == 0)
648
1.86k
                return 0;
649
6.98k
        } else if (PKT_IS_IPV6(p)) {
650
1.63k
            if (DetectAddressMatchIPv6(s->addr_dst_match6, s->addr_dst_match6_cnt, &p->dst) == 0)
651
81
                return 0;
652
1.63k
        }
653
8.61k
    }
654
    /* check the source address */
655
2.42M
    if (!(sflags & SIG_FLAG_SRC_ANY)) {
656
4.00k
        if (PKT_IS_IPV4(p)) {
657
3.52k
            if (DetectAddressMatchIPv4(s->addr_src_match4, s->addr_src_match4_cnt, &p->src) == 0)
658
1.80k
                return 0;
659
3.52k
        } else if (PKT_IS_IPV6(p)) {
660
483
            if (DetectAddressMatchIPv6(s->addr_src_match6, s->addr_src_match6_cnt, &p->src) == 0)
661
93
                return 0;
662
483
        }
663
4.00k
    }
664
665
2.42M
    return 1;
666
2.42M
}
667
668
/** \internal
669
 *  \brief run packet/stream prefilter engines
670
 */
671
static inline void DetectRunPrefilterPkt(
672
    ThreadVars *tv,
673
    DetectEngineCtx *de_ctx,
674
    DetectEngineThreadCtx *det_ctx,
675
    Packet *p,
676
    DetectRunScratchpad *scratch
677
)
678
2.51M
{
679
2.51M
    DetectPrefilterSetNonPrefilterList(p, det_ctx, scratch);
680
681
    /* create our prefilter mask */
682
2.51M
    PacketCreateMask(p, &scratch->pkt_mask, scratch->alproto, scratch->app_decoder_events);
683
684
    /* build and prefilter non_pf list against the mask of the packet */
685
2.51M
    PACKET_PROFILING_DETECT_START(p, PROF_DETECT_NONMPMLIST);
686
2.51M
    det_ctx->non_pf_id_cnt = 0;
687
2.51M
    if (likely(det_ctx->non_pf_store_cnt > 0)) {
688
2.10M
        DetectPrefilterBuildNonPrefilterList(det_ctx, scratch->pkt_mask, scratch->alproto);
689
2.10M
    }
690
2.51M
    PACKET_PROFILING_DETECT_END(p, PROF_DETECT_NONMPMLIST);
691
692
    /* run the prefilter engines */
693
2.51M
    Prefilter(det_ctx, scratch->sgh, p, scratch->flow_flags);
694
    /* create match list if we have non-pf and/or pf */
695
2.51M
    if (det_ctx->non_pf_store_cnt || det_ctx->pmq.rule_id_array_cnt) {
696
#ifdef PROFILING
697
        if (tv) {
698
            StatsAddUI64(tv, det_ctx->counter_mpm_list, (uint64_t)det_ctx->pmq.rule_id_array_cnt);
699
        }
700
#endif
701
2.11M
        PACKET_PROFILING_DETECT_START(p, PROF_DETECT_PF_SORT2);
702
2.11M
        DetectPrefilterMergeSort(de_ctx, det_ctx);
703
2.11M
        PACKET_PROFILING_DETECT_END(p, PROF_DETECT_PF_SORT2);
704
2.11M
    }
705
706
#ifdef PROFILING
707
    if (tv) {
708
        StatsAddUI64(tv, det_ctx->counter_nonmpm_list,
709
                             (uint64_t)det_ctx->non_pf_store_cnt);
710
        /* non mpm sigs after mask prefilter */
711
        StatsAddUI64(tv, det_ctx->counter_fnonmpm_list,
712
                             (uint64_t)det_ctx->non_pf_id_cnt);
713
    }
714
#endif
715
2.51M
}
716
717
static inline void DetectRulePacketRules(
718
    ThreadVars * const tv,
719
    DetectEngineCtx * const de_ctx,
720
    DetectEngineThreadCtx * const det_ctx,
721
    Packet * const p,
722
    Flow * const pflow,
723
    const DetectRunScratchpad *scratch
724
)
725
2.51M
{
726
2.51M
    const Signature *s = NULL;
727
2.51M
    const Signature *next_s = NULL;
728
729
    /* inspect the sigs against the packet */
730
    /* Prefetch the next signature. */
731
2.51M
    SigIntId match_cnt = det_ctx->match_array_cnt;
732
#ifdef PROFILING
733
    if (tv) {
734
        StatsAddUI64(tv, det_ctx->counter_match_list,
735
                             (uint64_t)match_cnt);
736
    }
737
#endif
738
2.51M
    Signature **match_array = det_ctx->match_array;
739
740
2.51M
    SGH_PROFILING_RECORD(det_ctx, scratch->sgh);
741
#ifdef PROFILING
742
    if (match_cnt >= de_ctx->profile_match_logging_threshold)
743
        RulesDumpMatchArray(det_ctx, scratch->sgh, p);
744
#endif
745
746
2.51M
    uint32_t sflags, next_sflags = 0;
747
2.51M
    if (match_cnt) {
748
1.47M
        next_s = *match_array++;
749
1.47M
        next_sflags = next_s->flags;
750
1.47M
    }
751
5.37M
    while (match_cnt--) {
752
2.85M
        RULE_PROFILING_START(p);
753
2.85M
        uint8_t alert_flags = 0;
754
#ifdef PROFILE_RULES
755
        bool smatch = false; /* signature match */
756
#endif
757
2.85M
        s = next_s;
758
2.85M
        sflags = next_sflags;
759
2.85M
        if (match_cnt) {
760
1.37M
            next_s = *match_array++;
761
1.37M
            next_sflags = next_s->flags;
762
1.37M
        }
763
2.85M
        const uint8_t s_proto_flags = s->proto.flags;
764
765
2.85M
        SCLogDebug("inspecting signature id %"PRIu32"", s->id);
766
767
2.85M
        if (s->app_inspect != NULL) {
768
590k
            goto next; // handle sig in DetectRunTx
769
590k
        }
770
2.26M
        if (s->frame_inspect != NULL) {
771
59.7k
            goto next; // handle sig in DetectRunFrame
772
59.7k
        }
773
774
        /* skip pkt sigs for flow end packets */
775
2.20M
        if ((p->flags & PKT_PSEUDO_STREAM_END) != 0 && s->type == SIG_TYPE_PKT)
776
11.3k
            goto next;
777
778
        /* don't run mask check for stateful rules.
779
         * There we depend on prefilter */
780
2.19M
        if ((s->mask & scratch->pkt_mask) != s->mask) {
781
631
            SCLogDebug("mask mismatch %x & %x != %x", s->mask, scratch->pkt_mask, s->mask);
782
631
            goto next;
783
631
        }
784
785
2.19M
        if (SigDsizePrefilter(p, s, sflags))
786
9.75k
            goto next;
787
788
        /* if the sig has alproto and the session as well they should match */
789
2.18M
        if (likely(sflags & SIG_FLAG_APPLAYER)) {
790
10.0k
            if (s->alproto != ALPROTO_UNKNOWN && !AppProtoEquals(s->alproto, scratch->alproto)) {
791
690
                SCLogDebug("alproto mismatch");
792
690
                goto next;
793
690
            }
794
10.0k
        }
795
796
2.18M
        if (DetectRunInspectRuleHeader(p, pflow, s, sflags, s_proto_flags) == 0) {
797
112k
            goto next;
798
112k
        }
799
800
2.06M
        if (DetectEnginePktInspectionRun(tv, det_ctx, s, pflow, p, &alert_flags) == false) {
801
1.71M
            goto next;
802
1.71M
        }
803
804
#ifdef PROFILE_RULES
805
        smatch = true;
806
#endif
807
356k
        DetectRunPostMatch(tv, det_ctx, p, s);
808
809
356k
        uint64_t txid = PACKET_ALERT_NOTX;
810
356k
        if (pflow && pflow->alstate) {
811
231k
            uint8_t dir = (p->flowflags & FLOW_PKT_TOCLIENT) ? STREAM_TOCLIENT : STREAM_TOSERVER;
812
231k
            txid = AppLayerParserGetTransactionInspectId(pflow->alparser, dir);
813
231k
            if ((s->alproto != ALPROTO_UNKNOWN && pflow->proto == IPPROTO_UDP) ||
814
231k
                    (alert_flags & PACKET_ALERT_FLAG_STREAM_MATCH) ||
815
203k
                    (de_ctx->guess_applayer &&
816
28.1k
                            AppLayerParserGetTxCnt(pflow, pflow->alstate) == txid + 1)) {
817
                // if there is a UDP specific app-layer signature,
818
                // or only one live transaction
819
                // try to use the good tx for the packet direction
820
28.1k
                alert_flags |= PACKET_ALERT_FLAG_TX;
821
28.1k
                if (pflow->proto != IPPROTO_UDP) {
822
28.0k
                    alert_flags |= PACKET_ALERT_FLAG_TX_GUESSED;
823
28.0k
                }
824
28.1k
            }
825
231k
        }
826
356k
        AlertQueueAppend(det_ctx, s, p, txid, alert_flags);
827
2.85M
next:
828
2.85M
        DetectVarProcessList(det_ctx, pflow, p);
829
2.85M
        DetectReplaceFree(det_ctx);
830
2.85M
        RULE_PROFILING_END(det_ctx, s, smatch, p);
831
832
2.85M
        det_ctx->flags = 0;
833
2.85M
        continue;
834
356k
    }
835
2.51M
}
836
837
static DetectRunScratchpad DetectRunSetup(
838
    const DetectEngineCtx *de_ctx,
839
    DetectEngineThreadCtx *det_ctx,
840
    Packet * const p, Flow * const pflow)
841
17.6M
{
842
17.6M
    AppProto alproto = ALPROTO_UNKNOWN;
843
17.6M
    uint8_t flow_flags = 0; /* flow/state flags */
844
17.6M
    bool app_decoder_events = false;
845
846
17.6M
    PACKET_PROFILING_DETECT_START(p, PROF_DETECT_SETUP);
847
848
#ifdef UNITTESTS
849
    p->alerts.cnt = 0;
850
    p->alerts.discarded = 0;
851
    p->alerts.suppressed = 0;
852
#endif
853
17.6M
    det_ctx->filestore_cnt = 0;
854
17.6M
    det_ctx->base64_decoded_len = 0;
855
17.6M
    det_ctx->raw_stream_progress = 0;
856
17.6M
    det_ctx->match_array_cnt = 0;
857
858
17.6M
    det_ctx->alert_queue_size = 0;
859
17.6M
    p->alerts.drop.action = 0;
860
861
#ifdef DEBUG
862
    if (p->flags & PKT_STREAM_ADD) {
863
        det_ctx->pkt_stream_add_cnt++;
864
    }
865
#endif
866
867
    /* grab the protocol state we will detect on */
868
17.6M
    if (p->flags & PKT_HAS_FLOW) {
869
14.2M
        DEBUG_VALIDATE_BUG_ON(pflow == NULL);
870
871
14.2M
        if (p->flowflags & FLOW_PKT_TOSERVER) {
872
8.14M
            flow_flags = STREAM_TOSERVER;
873
8.14M
            SCLogDebug("flag STREAM_TOSERVER set");
874
8.14M
        } else if (p->flowflags & FLOW_PKT_TOCLIENT) {
875
6.08M
            flow_flags = STREAM_TOCLIENT;
876
6.08M
            SCLogDebug("flag STREAM_TOCLIENT set");
877
6.08M
        }
878
14.2M
        SCLogDebug("p->flowflags 0x%02x", p->flowflags);
879
880
14.2M
        if (p->flags & PKT_STREAM_EOF) {
881
255k
            flow_flags |= STREAM_EOF;
882
255k
            SCLogDebug("STREAM_EOF set");
883
255k
        }
884
885
        /* store tenant_id in the flow so that we can use it
886
         * for creating pseudo packets */
887
14.2M
        if (p->tenant_id > 0 && pflow->tenant_id == 0) {
888
0
            pflow->tenant_id = p->tenant_id;
889
0
        }
890
891
        /* live ruleswap check for flow updates */
892
14.2M
        if (pflow->de_ctx_version == 0) {
893
            /* first time this flow is inspected, set id */
894
493k
            pflow->de_ctx_version = de_ctx->version;
895
13.7M
        } else if (pflow->de_ctx_version != de_ctx->version) {
896
            /* first time we inspect flow with this de_ctx, reset */
897
30.7k
            pflow->flags &= ~FLOW_SGH_TOSERVER;
898
30.7k
            pflow->flags &= ~FLOW_SGH_TOCLIENT;
899
30.7k
            pflow->sgh_toserver = NULL;
900
30.7k
            pflow->sgh_toclient = NULL;
901
902
30.7k
            pflow->de_ctx_version = de_ctx->version;
903
30.7k
            GenericVarFree(pflow->flowvar);
904
30.7k
            pflow->flowvar = NULL;
905
906
30.7k
            DetectEngineStateResetTxs(pflow);
907
30.7k
        }
908
909
        /* Retrieve the app layer state and protocol and the tcp reassembled
910
         * stream chunks. */
911
14.2M
        if ((p->proto == IPPROTO_TCP && (p->flags & PKT_STREAM_EST)) ||
912
2.70M
                (p->proto == IPPROTO_UDP) ||
913
1.87M
                (p->proto == IPPROTO_SCTP && (p->flowflags & FLOW_PKT_ESTABLISHED)))
914
12.3M
        {
915
            /* update flow flags with knowledge on disruptions */
916
12.3M
            flow_flags = FlowGetDisruptionFlags(pflow, flow_flags);
917
12.3M
            alproto = FlowGetAppProtocol(pflow);
918
12.3M
            if (p->proto == IPPROTO_TCP && pflow->protoctx &&
919
11.5M
                    StreamReassembleRawHasDataReady(pflow->protoctx, p)) {
920
297k
                p->flags |= PKT_DETECT_HAS_STREAMDATA;
921
297k
            }
922
12.3M
            SCLogDebug("alproto %u", alproto);
923
12.3M
        } else {
924
1.87M
            SCLogDebug("packet doesn't have established flag set (proto %d)", p->proto);
925
1.87M
        }
926
927
14.2M
        app_decoder_events = AppLayerParserHasDecoderEvents(pflow->alparser);
928
14.2M
    }
929
930
17.6M
    DetectRunScratchpad pad = { alproto, flow_flags, app_decoder_events, NULL, 0 };
931
17.6M
    PACKET_PROFILING_DETECT_END(p, PROF_DETECT_SETUP);
932
17.6M
    return pad;
933
17.6M
}
934
935
static inline void DetectRunPostRules(
936
    ThreadVars *tv,
937
    DetectEngineCtx *de_ctx,
938
    DetectEngineThreadCtx *det_ctx,
939
    Packet * const p,
940
    Flow * const pflow,
941
    DetectRunScratchpad *scratch)
942
8.68M
{
943
    /* so now let's iterate the alerts and remove the ones after a pass rule
944
     * matched (if any). This is done inside PacketAlertFinalize() */
945
    /* PR: installed "tag" keywords are handled after the threshold inspection */
946
947
8.68M
    PACKET_PROFILING_DETECT_START(p, PROF_DETECT_ALERT);
948
8.68M
    PacketAlertFinalize(de_ctx, det_ctx, p);
949
8.68M
    if (p->alerts.cnt > 0) {
950
388k
        StatsAddUI64(tv, det_ctx->counter_alerts, (uint64_t)p->alerts.cnt);
951
388k
    }
952
8.68M
    if (p->alerts.discarded > 0) {
953
213
        StatsAddUI64(tv, det_ctx->counter_alerts_overflow, (uint64_t)p->alerts.discarded);
954
213
    }
955
8.68M
    if (p->alerts.suppressed > 0) {
956
6.99k
        StatsAddUI64(tv, det_ctx->counter_alerts_suppressed, (uint64_t)p->alerts.suppressed);
957
6.99k
    }
958
8.68M
    PACKET_PROFILING_DETECT_END(p, PROF_DETECT_ALERT);
959
8.68M
}
960
961
static void DetectRunCleanup(DetectEngineThreadCtx *det_ctx,
962
        Packet *p, Flow * const pflow)
963
17.6M
{
964
17.6M
    PACKET_PROFILING_DETECT_START(p, PROF_DETECT_CLEANUP);
965
17.6M
    InspectionBufferClean(det_ctx);
966
967
17.6M
    if (pflow != NULL) {
968
        /* update inspected tracker for raw reassembly */
969
14.2M
        if (p->proto == IPPROTO_TCP && pflow->protoctx != NULL &&
970
12.9M
                (p->flags & PKT_DETECT_HAS_STREAMDATA)) {
971
297k
            StreamReassembleRawUpdateProgress(pflow->protoctx, p,
972
297k
                    det_ctx->raw_stream_progress);
973
297k
        }
974
14.2M
    }
975
17.6M
    PACKET_PROFILING_DETECT_END(p, PROF_DETECT_CLEANUP);
976
17.6M
    SCReturn;
977
17.6M
}
978
979
void RuleMatchCandidateTxArrayInit(DetectEngineThreadCtx *det_ctx, uint32_t size)
980
52.0k
{
981
52.0k
    DEBUG_VALIDATE_BUG_ON(det_ctx->tx_candidates);
982
52.0k
    det_ctx->tx_candidates = SCCalloc(size, sizeof(RuleMatchCandidateTx));
983
52.0k
    if (det_ctx->tx_candidates == NULL) {
984
0
        FatalError("failed to allocate %" PRIu64 " bytes",
985
0
                (uint64_t)(size * sizeof(RuleMatchCandidateTx)));
986
0
    }
987
52.0k
    det_ctx->tx_candidates_size = size;
988
52.0k
    SCLogDebug("array initialized to %u elements (%"PRIu64" bytes)",
989
52.0k
            size, (uint64_t)(size * sizeof(RuleMatchCandidateTx)));
990
52.0k
}
991
992
void RuleMatchCandidateTxArrayFree(DetectEngineThreadCtx *det_ctx)
993
122k
{
994
122k
    SCFree(det_ctx->tx_candidates);
995
122k
    det_ctx->tx_candidates_size = 0;
996
122k
}
997
998
/* if size >= cur_space */
999
static inline bool RuleMatchCandidateTxArrayHasSpace(const DetectEngineThreadCtx *det_ctx,
1000
        const uint32_t need)
1001
2.21M
{
1002
2.21M
    if (det_ctx->tx_candidates_size >= need)
1003
2.20M
        return 1;
1004
1.48k
    return 0;
1005
2.21M
}
1006
1007
/* realloc */
1008
static int RuleMatchCandidateTxArrayExpand(DetectEngineThreadCtx *det_ctx, const uint32_t needed)
1009
1.48k
{
1010
1.48k
    const uint32_t old_size = det_ctx->tx_candidates_size;
1011
1.48k
    uint32_t new_size = needed;
1012
1.48k
    void *ptmp = SCRealloc(det_ctx->tx_candidates, (new_size * sizeof(RuleMatchCandidateTx)));
1013
1.48k
    if (ptmp == NULL) {
1014
0
        FatalError("failed to expand to %" PRIu64 " bytes",
1015
0
                (uint64_t)(new_size * sizeof(RuleMatchCandidateTx)));
1016
        // TODO can this be handled more gracefully?
1017
0
    }
1018
1.48k
    det_ctx->tx_candidates = ptmp;
1019
1.48k
    det_ctx->tx_candidates_size = new_size;
1020
1.48k
    SCLogDebug("array expanded from %u to %u elements (%"PRIu64" bytes -> %"PRIu64" bytes)",
1021
1.48k
            old_size, new_size, (uint64_t)(old_size * sizeof(RuleMatchCandidateTx)),
1022
1.48k
            (uint64_t)(new_size * sizeof(RuleMatchCandidateTx))); (void)old_size;
1023
1.48k
    return 1;
1024
1.48k
}
1025
1026
/** \internal
1027
 *  \brief sort helper for sorting match candidates by id: ascending
1028
 *
1029
 *  The id field is set from Signature::num, so we sort the candidates to match the signature
1030
 *  sort order (ascending), where candidates that have flags go first.
1031
 */
1032
static int
1033
DetectRunTxSortHelper(const void *a, const void *b)
1034
90.6k
{
1035
90.6k
    const RuleMatchCandidateTx *s0 = a;
1036
90.6k
    const RuleMatchCandidateTx *s1 = b;
1037
90.6k
    if (s1->id == s0->id) {
1038
75.1k
        if (s1->flags && !s0->flags)
1039
74.1k
            return 1;
1040
954
        else if (!s1->flags && s0->flags)
1041
0
            return -1;
1042
954
        return 0;
1043
75.1k
    } else
1044
15.5k
        return s0->id > s1->id ? 1 : -1;
1045
90.6k
}
1046
1047
#if 0
1048
#define TRACE_SID_TXS(sid,txs,...)          \
1049
    do {                                    \
1050
        char _trace_buf[2048];              \
1051
        snprintf(_trace_buf, sizeof(_trace_buf), __VA_ARGS__);  \
1052
        SCLogNotice("%p/%"PRIu64"/%u: %s", txs->tx_ptr, txs->tx_id, sid, _trace_buf);   \
1053
    } while(0)
1054
#else
1055
#define TRACE_SID_TXS(sid,txs,...)
1056
#endif
1057
1058
/** \internal
1059
 *  \brief inspect a rule against a transaction
1060
 *
1061
 *  Inspect a rule. New detection or continued stateful
1062
 *  detection.
1063
 *
1064
 *  \param stored_flags pointer to stored flags or NULL.
1065
 *         If stored_flags is set it means we're continuing
1066
 *         inspection from an earlier run.
1067
 *
1068
 *  \retval bool true sig matched, false didn't match
1069
 */
1070
static bool DetectRunTxInspectRule(ThreadVars *tv,
1071
        DetectEngineCtx *de_ctx,
1072
        DetectEngineThreadCtx *det_ctx,
1073
        Packet *p,
1074
        Flow *f,
1075
        const uint8_t in_flow_flags,   // direction, EOF, etc
1076
        void *alstate,
1077
        DetectTransaction *tx,
1078
        const Signature *s,
1079
        uint32_t *stored_flags,
1080
        RuleMatchCandidateTx *can,
1081
        DetectRunScratchpad *scratch)
1082
288k
{
1083
288k
    uint8_t flow_flags = in_flow_flags;
1084
288k
    const int direction = (flow_flags & STREAM_TOSERVER) ? 0 : 1;
1085
288k
    uint32_t inspect_flags = stored_flags ? *stored_flags : 0;
1086
288k
    int total_matches = 0;
1087
288k
    uint16_t file_no_match = 0;
1088
288k
    bool retval = false;
1089
288k
    bool mpm_before_progress = false;   // is mpm engine before progress?
1090
288k
    bool mpm_in_progress = false;       // is mpm engine in a buffer we will revisit?
1091
1092
288k
    TRACE_SID_TXS(s->id, tx, "starting %s", direction ? "toclient" : "toserver");
1093
1094
    /* for a new inspection we inspect pkt header and packet matches */
1095
288k
    if (likely(stored_flags == NULL)) {
1096
250k
        TRACE_SID_TXS(s->id, tx, "first inspect, run packet matches");
1097
250k
        if (DetectRunInspectRuleHeader(p, f, s, s->flags, s->proto.flags) == 0) {
1098
5.82k
            TRACE_SID_TXS(s->id, tx, "DetectRunInspectRuleHeader() no match");
1099
5.82k
            return false;
1100
5.82k
        }
1101
245k
        if (DetectEnginePktInspectionRun(tv, det_ctx, s, f, p, NULL) == false) {
1102
145
            TRACE_SID_TXS(s->id, tx, "DetectEnginePktInspectionRun no match");
1103
145
            return false;
1104
145
        }
1105
        /* stream mpm and negated mpm sigs can end up here with wrong proto */
1106
244k
        if (!(AppProtoEquals(s->alproto, f->alproto) || s->alproto == ALPROTO_UNKNOWN)) {
1107
1
            TRACE_SID_TXS(s->id, tx, "alproto mismatch");
1108
1
            return false;
1109
1
        }
1110
244k
    }
1111
1112
282k
    const DetectEngineAppInspectionEngine *engine = s->app_inspect;
1113
1.59M
    while (engine != NULL) { // TODO could be do {} while as s->app_inspect cannot be null
1114
1.51M
        TRACE_SID_TXS(s->id, tx, "engine %p inspect_flags %x", engine, inspect_flags);
1115
1.51M
        if (!(inspect_flags & BIT_U32(engine->id)) &&
1116
1.47M
                direction == engine->dir)
1117
836k
        {
1118
836k
            const bool skip_engine = (engine->alproto != 0 && engine->alproto != f->alproto);
1119
            /* special case: file_data on 'alert tcp' will have engines
1120
             * in the list that are not for us. */
1121
836k
            if (unlikely(skip_engine)) {
1122
539k
                engine = engine->next;
1123
539k
                continue;
1124
539k
            }
1125
1126
            /* engines are sorted per progress, except that the one with
1127
             * mpm/prefilter enabled is first */
1128
297k
            if (tx->tx_progress < engine->progress) {
1129
26.1k
                SCLogDebug("tx progress %d < engine progress %d",
1130
26.1k
                        tx->tx_progress, engine->progress);
1131
26.1k
                break;
1132
26.1k
            }
1133
271k
            if (engine->mpm) {
1134
182k
                if (tx->tx_progress > engine->progress) {
1135
119k
                    TRACE_SID_TXS(s->id, tx,
1136
119k
                            "engine->mpm: t->tx_progress %u > engine->progress %u, so set "
1137
119k
                            "mpm_before_progress",
1138
119k
                            tx->tx_progress, engine->progress);
1139
119k
                    mpm_before_progress = true;
1140
119k
                } else if (tx->tx_progress == engine->progress) {
1141
62.8k
                    TRACE_SID_TXS(s->id, tx,
1142
62.8k
                            "engine->mpm: t->tx_progress %u == engine->progress %u, so set "
1143
62.8k
                            "mpm_in_progress",
1144
62.8k
                            tx->tx_progress, engine->progress);
1145
62.8k
                    mpm_in_progress = true;
1146
62.8k
                }
1147
182k
            }
1148
1149
            /* run callback: but bypass stream callback if we can */
1150
271k
            uint8_t match;
1151
271k
            if (unlikely(engine->stream && can->stream_stored)) {
1152
0
                match = can->stream_result;
1153
0
                TRACE_SID_TXS(s->id, tx, "stream skipped, stored result %d used instead", match);
1154
271k
            } else {
1155
271k
                KEYWORD_PROFILING_SET_LIST(det_ctx, engine->sm_list);
1156
271k
                DEBUG_VALIDATE_BUG_ON(engine->v2.Callback == NULL);
1157
271k
                match = engine->v2.Callback(
1158
271k
                        de_ctx, det_ctx, engine, s, f, flow_flags, alstate, tx->tx_ptr, tx->tx_id);
1159
271k
                TRACE_SID_TXS(s->id, tx, "engine %p match %d", engine, match);
1160
271k
                if (engine->stream) {
1161
125k
                    can->stream_stored = true;
1162
125k
                    can->stream_result = match;
1163
125k
                    TRACE_SID_TXS(s->id, tx, "stream ran, store result %d for next tx (if any)", match);
1164
125k
                }
1165
271k
            }
1166
271k
            if (match == DETECT_ENGINE_INSPECT_SIG_MATCH) {
1167
93.6k
                inspect_flags |= BIT_U32(engine->id);
1168
93.6k
                engine = engine->next;
1169
93.6k
                total_matches++;
1170
93.6k
                continue;
1171
177k
            } else if (match == DETECT_ENGINE_INSPECT_SIG_MATCH_MORE_FILES) {
1172
                /* if the file engine matched, but indicated more
1173
                 * files are still in progress, we don't set inspect
1174
                 * flags as these would end inspection for this tx */
1175
0
                engine = engine->next;
1176
0
                total_matches++;
1177
0
                continue;
1178
177k
            } else if (match == DETECT_ENGINE_INSPECT_SIG_CANT_MATCH) {
1179
26.2k
                inspect_flags |= DE_STATE_FLAG_SIG_CANT_MATCH;
1180
26.2k
                inspect_flags |= BIT_U32(engine->id);
1181
151k
            } else if (match == DETECT_ENGINE_INSPECT_SIG_CANT_MATCH_FILES) {
1182
10.8k
                inspect_flags |= DE_STATE_FLAG_SIG_CANT_MATCH;
1183
10.8k
                inspect_flags |= BIT_U32(engine->id);
1184
10.8k
                file_no_match = 1;
1185
10.8k
            }
1186
            /* implied DETECT_ENGINE_INSPECT_SIG_NO_MATCH */
1187
177k
            if (engine->mpm && mpm_before_progress) {
1188
64.7k
                inspect_flags |= DE_STATE_FLAG_SIG_CANT_MATCH;
1189
64.7k
                inspect_flags |= BIT_U32(engine->id);
1190
64.7k
            }
1191
177k
            break;
1192
271k
        }
1193
680k
        engine = engine->next;
1194
680k
    }
1195
282k
    TRACE_SID_TXS(s->id, tx, "inspect_flags %x, total_matches %u, engine %p",
1196
282k
            inspect_flags, total_matches, engine);
1197
1198
282k
    if (engine == NULL && total_matches) {
1199
73.4k
        inspect_flags |= DE_STATE_FLAG_FULL_INSPECT;
1200
73.4k
        TRACE_SID_TXS(s->id, tx, "MATCH");
1201
73.4k
        retval = true;
1202
73.4k
    }
1203
1204
282k
    if (stored_flags) {
1205
37.5k
        *stored_flags = inspect_flags;
1206
37.5k
        TRACE_SID_TXS(s->id, tx, "continue inspect flags %08x", inspect_flags);
1207
244k
    } else {
1208
        // store... or? If tx is done we might not want to come back to this tx
1209
1210
        // also... if mpmid tracking is enabled, we won't do a sig again for this tx...
1211
244k
        TRACE_SID_TXS(s->id, tx, "start inspect flags %08x", inspect_flags);
1212
244k
        if (inspect_flags & DE_STATE_FLAG_SIG_CANT_MATCH) {
1213
85.6k
            if (file_no_match) {
1214
                /* if we have a mismatch on a file sig, we need to keep state.
1215
                 * We may get another file on the same tx (for http and smtp
1216
                 * at least), so for a new file we need to re-eval the sig.
1217
                 * Thoughts / TODO:
1218
                 *  - not for some protos that have 1 file per tx (e.g. nfs)
1219
                 *  - maybe we only need this for file sigs that mix with
1220
                 *    other matches? E.g. 'POST + filename', is different than
1221
                 *    just 'filename'.
1222
                 */
1223
10.8k
                DetectRunStoreStateTx(scratch->sgh, f, tx->tx_ptr, tx->tx_id, s,
1224
10.8k
                        inspect_flags, flow_flags, file_no_match);
1225
10.8k
            }
1226
159k
        } else if ((inspect_flags & DE_STATE_FLAG_FULL_INSPECT) && mpm_before_progress) {
1227
45.3k
            TRACE_SID_TXS(s->id, tx, "no need to store match sig, "
1228
45.3k
                    "mpm won't trigger for it anymore");
1229
1230
45.3k
            if (inspect_flags & DE_STATE_FLAG_FILE_INSPECT) {
1231
3.61k
                TRACE_SID_TXS(s->id, tx, "except that for new files, "
1232
3.61k
                        "we may have to revisit anyway");
1233
3.61k
                DetectRunStoreStateTx(scratch->sgh, f, tx->tx_ptr, tx->tx_id, s,
1234
3.61k
                        inspect_flags, flow_flags, file_no_match);
1235
3.61k
            }
1236
113k
        } else if ((inspect_flags & DE_STATE_FLAG_FULL_INSPECT) == 0 && mpm_in_progress) {
1237
44.4k
            TRACE_SID_TXS(s->id, tx, "no need to store no-match sig, "
1238
44.4k
                    "mpm will revisit it");
1239
69.3k
        } else {
1240
69.3k
            TRACE_SID_TXS(s->id, tx, "storing state: flags %08x", inspect_flags);
1241
69.3k
            DetectRunStoreStateTx(scratch->sgh, f, tx->tx_ptr, tx->tx_id, s,
1242
69.3k
                    inspect_flags, flow_flags, file_no_match);
1243
69.3k
        }
1244
244k
    }
1245
1246
282k
    return retval;
1247
282k
}
1248
1249
#define NO_TX                                                                                      \
1250
28.0M
    {                                                                                              \
1251
28.0M
        NULL, 0, NULL, NULL, 0, 0, 0, 0, 0,                                                        \
1252
28.0M
    }
1253
1254
/** \internal
1255
 *  \brief get a DetectTransaction object
1256
 *  \retval struct filled with relevant info or all nulls/0s
1257
 */
1258
static DetectTransaction GetDetectTx(const uint8_t ipproto, const AppProto alproto,
1259
        void *alstate, const uint64_t tx_id, void *tx_ptr, const int tx_end_state,
1260
        const uint8_t flow_flags)
1261
28.7M
{
1262
28.7M
    AppLayerTxData *txd = AppLayerParserGetTxData(ipproto, alproto, tx_ptr);
1263
28.7M
    if (unlikely(txd == NULL)) {
1264
0
        DetectTransaction no_tx = NO_TX;
1265
0
        return no_tx;
1266
0
    }
1267
28.7M
    const int tx_progress = AppLayerParserGetStateProgress(ipproto, alproto, tx_ptr, flow_flags);
1268
28.7M
    bool updated = (flow_flags & STREAM_TOSERVER) ? txd->updated_ts : txd->updated_tc;
1269
28.7M
    if (!updated && tx_progress < tx_end_state && ((flow_flags & STREAM_EOF) == 0)) {
1270
26.3M
        DetectTransaction no_tx = NO_TX;
1271
26.3M
        return no_tx;
1272
26.3M
    }
1273
2.35M
    uint64_t detect_flags =
1274
2.35M
            (flow_flags & STREAM_TOSERVER) ? txd->detect_flags_ts : txd->detect_flags_tc;
1275
2.35M
    if (detect_flags & APP_LAYER_TX_INSPECTED_FLAG) {
1276
1.64M
        SCLogDebug("%"PRIu64" tx already fully inspected for %s. Flags %016"PRIx64,
1277
1.64M
                tx_id, flow_flags & STREAM_TOSERVER ? "toserver" : "toclient",
1278
1.64M
                detect_flags);
1279
1.64M
        DetectTransaction no_tx = NO_TX;
1280
1.64M
        return no_tx;
1281
1.64M
    }
1282
716k
    if (detect_flags & APP_LAYER_TX_SKIP_INSPECT_FLAG) {
1283
34.7k
        SCLogDebug("%" PRIu64 " tx should not be inspected in direction %s. Flags %016" PRIx64,
1284
34.7k
                tx_id, flow_flags & STREAM_TOSERVER ? "toserver" : "toclient", detect_flags);
1285
34.7k
        DetectTransaction no_tx = NO_TX;
1286
34.7k
        return no_tx;
1287
34.7k
    }
1288
1289
681k
    const int dir_int = (flow_flags & STREAM_TOSERVER) ? 0 : 1;
1290
681k
    DetectEngineState *tx_de_state = txd->de_state;
1291
681k
    DetectEngineStateDirection *tx_dir_state = tx_de_state ? &tx_de_state->dir_state[dir_int] : NULL;
1292
681k
    uint64_t prefilter_flags = detect_flags & APP_LAYER_TX_PREFILTER_MASK;
1293
681k
    DEBUG_VALIDATE_BUG_ON(prefilter_flags & APP_LAYER_TX_RESERVED_FLAGS);
1294
1295
681k
    DetectTransaction tx = {
1296
681k
                            .tx_ptr = tx_ptr,
1297
681k
                            .tx_id = tx_id,
1298
681k
                            .tx_data_ptr = (struct AppLayerTxData *)txd,
1299
681k
                            .de_state = tx_dir_state,
1300
681k
                            .detect_flags = detect_flags,
1301
681k
                            .prefilter_flags = prefilter_flags,
1302
681k
                            .prefilter_flags_orig = prefilter_flags,
1303
681k
                            .tx_progress = tx_progress,
1304
681k
                            .tx_end_state = tx_end_state,
1305
681k
                           };
1306
681k
    return tx;
1307
681k
}
1308
1309
static inline void StoreDetectFlags(DetectTransaction *tx, const uint8_t flow_flags,
1310
        const uint8_t ipproto, const AppProto alproto, const uint64_t detect_flags)
1311
344k
{
1312
344k
    AppLayerTxData *txd = (AppLayerTxData *)tx->tx_data_ptr;
1313
344k
    if (likely(txd != NULL)) {
1314
344k
        if (flow_flags & STREAM_TOSERVER) {
1315
215k
            txd->detect_flags_ts = detect_flags;
1316
215k
        } else {
1317
128k
            txd->detect_flags_tc = detect_flags;
1318
128k
        }
1319
344k
    }
1320
344k
}
1321
1322
// Merge 'state' rules from the regular prefilter
1323
// updates array_idx on the way
1324
static inline void RuleMatchCandidateMergeStateRules(
1325
        DetectEngineThreadCtx *det_ctx, uint32_t *array_idx)
1326
1.44M
{
1327
    // Now, we will merge 2 sorted lists :
1328
    // the one in det_ctx->tx_candidates
1329
    // and the one in det_ctx->match_array
1330
    // For match_array, we take only the relevant elements where s->app_inspect != NULL
1331
1332
    // Basically, we iterate at the same time over the 2 lists
1333
    // comparing and taking an element from either.
1334
1335
    // Trick is to do so in place in det_ctx->tx_candidates,
1336
    // so as to minimize the number of moves in det_ctx->tx_candidates.
1337
    // For this, the algorithm traverses the lists in reverse order.
1338
    // Otherwise, if the first element of match_array was to be put before
1339
    // all tx_candidates, we would need to shift all tx_candidates
1340
1341
    // Retain the number of elements sorted in tx_candidates before merge
1342
1.44M
    uint32_t j = *array_idx;
1343
    // First loop only counting the number of elements to add
1344
2.29M
    for (uint32_t i = 0; i < det_ctx->match_array_cnt; i++) {
1345
850k
        const Signature *s = det_ctx->match_array[i];
1346
850k
        if (s->app_inspect != NULL) {
1347
315k
            (*array_idx)++;
1348
315k
        }
1349
850k
    }
1350
    // Future number of elements in tx_candidates after merge
1351
1.44M
    uint32_t k = *array_idx;
1352
1353
1.44M
    if (k == j) {
1354
        // no new element from match_array to merge in tx_candidates
1355
1.15M
        return;
1356
1.15M
    }
1357
1358
    // variable i is for all elements of match_array (even not relevant ones)
1359
    // variable j is for elements of tx_candidates before merge
1360
    // variable k is for elements of tx_candidates after merge
1361
645k
    for (uint32_t i = det_ctx->match_array_cnt; i > 0;) {
1362
361k
        const Signature *s = det_ctx->match_array[i - 1];
1363
361k
        if (s->app_inspect == NULL) {
1364
            // no relevant element, get the next one from match_array
1365
43.9k
            i--;
1366
43.9k
            continue;
1367
43.9k
        }
1368
        // we have one element from match_array to merge in tx_candidates
1369
317k
        k--;
1370
317k
        if (j > 0) {
1371
            // j > 0 means there is still at least one element in tx_candidates to merge
1372
2.88k
            const RuleMatchCandidateTx *s0 = &det_ctx->tx_candidates[j - 1];
1373
2.88k
            if (s->num <= s0->id) {
1374
                // get next element from previous tx_candidates
1375
2.13k
                j--;
1376
                // take the element from tx_candidates before merge
1377
2.13k
                det_ctx->tx_candidates[k].s = det_ctx->tx_candidates[j].s;
1378
2.13k
                det_ctx->tx_candidates[k].id = det_ctx->tx_candidates[j].id;
1379
2.13k
                det_ctx->tx_candidates[k].flags = det_ctx->tx_candidates[j].flags;
1380
2.13k
                det_ctx->tx_candidates[k].stream_reset = det_ctx->tx_candidates[j].stream_reset;
1381
2.13k
                continue;
1382
2.13k
            }
1383
2.88k
        } // otherwise
1384
        // get next element from match_array
1385
315k
        i--;
1386
        // take the element from match_array
1387
315k
        det_ctx->tx_candidates[k].s = s;
1388
315k
        det_ctx->tx_candidates[k].id = s->num;
1389
315k
        det_ctx->tx_candidates[k].flags = NULL;
1390
315k
        det_ctx->tx_candidates[k].stream_reset = 0;
1391
315k
    }
1392
    // Even if k > 0 or j > 0, the loop is over. (Note that j == k now)
1393
    // The remaining elements in tx_candidates up to k were already sorted
1394
    // and come before any other element later in the list
1395
283k
}
1396
1397
static void DetectRunTx(ThreadVars *tv,
1398
                    DetectEngineCtx *de_ctx,
1399
                    DetectEngineThreadCtx *det_ctx,
1400
                    Packet *p,
1401
                    Flow *f,
1402
                    DetectRunScratchpad *scratch)
1403
813k
{
1404
813k
    const uint8_t flow_flags = scratch->flow_flags;
1405
813k
    const SigGroupHead * const sgh = scratch->sgh;
1406
813k
    void * const alstate = f->alstate;
1407
813k
    const uint8_t ipproto = f->proto;
1408
813k
    const AppProto alproto = f->alproto;
1409
1410
813k
    const uint64_t total_txs = AppLayerParserGetTxCnt(f, alstate);
1411
813k
    uint64_t tx_id_min = AppLayerParserGetTransactionInspectId(f->alparser, flow_flags);
1412
813k
    const int tx_end_state = AppLayerParserGetStateProgressCompletionStatus(alproto, flow_flags);
1413
1414
813k
    AppLayerGetTxIteratorFunc IterFunc = AppLayerGetTxIterator(ipproto, alproto);
1415
813k
    AppLayerGetTxIterState state;
1416
813k
    memset(&state, 0, sizeof(state));
1417
1418
28.8M
    while (1) {
1419
28.8M
        AppLayerGetTxIterTuple ires = IterFunc(ipproto, alproto, alstate, tx_id_min, total_txs, &state);
1420
28.8M
        if (ires.tx_ptr == NULL)
1421
97.5k
            break;
1422
1423
28.7M
        DetectTransaction tx = GetDetectTx(ipproto, alproto,
1424
28.7M
                alstate, ires.tx_id, ires.tx_ptr, tx_end_state, flow_flags);
1425
28.7M
        if (tx.tx_ptr == NULL) {
1426
28.0M
            SCLogDebug("%p/%"PRIu64" no transaction to inspect",
1427
28.0M
                    tx.tx_ptr, tx_id_min);
1428
1429
28.0M
            tx_id_min++; // next (if any) run look for +1
1430
28.0M
            goto next;
1431
28.0M
        }
1432
681k
        tx_id_min = tx.tx_id + 1; // next look for cur + 1
1433
1434
681k
        bool do_sort = false; // do we need to sort the tx candidate list?
1435
681k
        uint32_t array_idx = 0;
1436
681k
        uint32_t total_rules = det_ctx->match_array_cnt;
1437
681k
        total_rules += (tx.de_state ? tx.de_state->cnt : 0);
1438
1439
        /* run prefilter engines and merge results into a candidates array */
1440
681k
        if (sgh->tx_engines) {
1441
189k
            PACKET_PROFILING_DETECT_START(p, PROF_DETECT_PF_TX);
1442
189k
            DetectRunPrefilterTx(det_ctx, sgh, p, ipproto, flow_flags, alproto,
1443
189k
                    alstate, &tx);
1444
189k
            PACKET_PROFILING_DETECT_END(p, PROF_DETECT_PF_TX);
1445
189k
            SCLogDebug("%p/%"PRIu64" rules added from prefilter: %u candidates",
1446
189k
                    tx.tx_ptr, tx.tx_id, det_ctx->pmq.rule_id_array_cnt);
1447
1448
189k
            total_rules += det_ctx->pmq.rule_id_array_cnt;
1449
189k
            if (!(RuleMatchCandidateTxArrayHasSpace(det_ctx, total_rules))) {
1450
485
                RuleMatchCandidateTxArrayExpand(det_ctx, total_rules);
1451
485
            }
1452
1453
194k
            for (uint32_t i = 0; i < det_ctx->pmq.rule_id_array_cnt; i++) {
1454
5.13k
                const Signature *s = de_ctx->sig_array[det_ctx->pmq.rule_id_array[i]];
1455
5.13k
                const SigIntId id = s->num;
1456
5.13k
                det_ctx->tx_candidates[array_idx].s = s;
1457
5.13k
                det_ctx->tx_candidates[array_idx].id = id;
1458
5.13k
                det_ctx->tx_candidates[array_idx].flags = NULL;
1459
5.13k
                det_ctx->tx_candidates[array_idx].stream_reset = 0;
1460
5.13k
                array_idx++;
1461
5.13k
            }
1462
189k
            PMQ_RESET(&det_ctx->pmq);
1463
492k
        } else {
1464
492k
            if (!(RuleMatchCandidateTxArrayHasSpace(det_ctx, total_rules))) {
1465
565
                RuleMatchCandidateTxArrayExpand(det_ctx, total_rules);
1466
565
            }
1467
492k
        }
1468
1469
        /* merge 'state' rules from the regular prefilter */
1470
#ifdef PROFILING
1471
        uint32_t x = array_idx;
1472
#endif
1473
681k
        RuleMatchCandidateMergeStateRules(det_ctx, &array_idx);
1474
1475
        /* merge stored state into results */
1476
681k
        if (tx.de_state != NULL) {
1477
85.0k
            const uint32_t old = array_idx;
1478
1479
            /* if tx.de_state->flags has 'new file' set and sig below has
1480
             * 'file inspected' flag, reset the file part of the state */
1481
85.0k
            const bool have_new_file = (tx.de_state->flags & DETECT_ENGINE_STATE_FLAG_FILE_NEW);
1482
85.0k
            if (have_new_file) {
1483
4.76k
                SCLogDebug("%p/%"PRIu64" destate: need to consider new file",
1484
4.76k
                        tx.tx_ptr, tx.tx_id);
1485
4.76k
                tx.de_state->flags &= ~DETECT_ENGINE_STATE_FLAG_FILE_NEW;
1486
4.76k
            }
1487
1488
85.0k
            SigIntId state_cnt = 0;
1489
85.0k
            DeStateStore *tx_store = tx.de_state->head;
1490
144k
            for (; tx_store != NULL; tx_store = tx_store->next) {
1491
59.1k
                SCLogDebug("tx_store %p", tx_store);
1492
1493
59.1k
                SigIntId store_cnt = 0;
1494
59.1k
                for (store_cnt = 0;
1495
120k
                        store_cnt < DE_STATE_CHUNK_SIZE && state_cnt < tx.de_state->cnt;
1496
61.3k
                        store_cnt++, state_cnt++)
1497
61.3k
                {
1498
61.3k
                    DeStateStoreItem *item = &tx_store->store[store_cnt];
1499
61.3k
                    SCLogDebug("rule id %u, inspect_flags %u", item->sid, item->flags);
1500
61.3k
                    if (have_new_file && (item->flags & DE_STATE_FLAG_FILE_INSPECT)) {
1501
                        /* remove part of the state. File inspect engine will now
1502
                         * be able to run again */
1503
135
                        item->flags &= ~(DE_STATE_FLAG_SIG_CANT_MATCH|DE_STATE_FLAG_FULL_INSPECT|DE_STATE_FLAG_FILE_INSPECT);
1504
135
                        SCLogDebug("rule id %u, post file reset inspect_flags %u", item->sid, item->flags);
1505
135
                    }
1506
61.3k
                    det_ctx->tx_candidates[array_idx].s = de_ctx->sig_array[item->sid];
1507
61.3k
                    det_ctx->tx_candidates[array_idx].id = item->sid;
1508
61.3k
                    det_ctx->tx_candidates[array_idx].flags = &item->flags;
1509
61.3k
                    det_ctx->tx_candidates[array_idx].stream_reset = 0;
1510
61.3k
                    array_idx++;
1511
61.3k
                }
1512
59.1k
            }
1513
85.0k
            do_sort |= (old && old != array_idx); // sort if continue list adds sids
1514
85.0k
            SCLogDebug("%p/%" PRIu64 " rules added from 'continue' list: %u", tx.tx_ptr, tx.tx_id,
1515
85.0k
                    array_idx - old);
1516
85.0k
        }
1517
681k
        if (do_sort) {
1518
56.9k
            qsort(det_ctx->tx_candidates, array_idx, sizeof(RuleMatchCandidateTx),
1519
56.9k
                    DetectRunTxSortHelper);
1520
56.9k
        }
1521
1522
#ifdef PROFILING
1523
        if (array_idx >= de_ctx->profile_match_logging_threshold)
1524
            RulesDumpTxMatchArray(det_ctx, scratch->sgh, p, tx.tx_id, array_idx, x);
1525
#endif
1526
681k
        det_ctx->tx_id = tx.tx_id;
1527
681k
        det_ctx->tx_id_set = true;
1528
681k
        det_ctx->p = p;
1529
1530
#ifdef DEBUG
1531
        for (uint32_t i = 0; i < array_idx; i++) {
1532
            RuleMatchCandidateTx *can = &det_ctx->tx_candidates[i];
1533
            const Signature *s = det_ctx->tx_candidates[i].s;
1534
            SCLogDebug("%u: sid %u flags %p", i, s->id, can->flags);
1535
        }
1536
#endif
1537
        /* run rules: inspect the match candidates */
1538
993k
        for (uint32_t i = 0; i < array_idx; i++) {
1539
312k
            RuleMatchCandidateTx *can = &det_ctx->tx_candidates[i];
1540
312k
            const Signature *s = det_ctx->tx_candidates[i].s;
1541
312k
            uint32_t *inspect_flags = det_ctx->tx_candidates[i].flags;
1542
1543
            /* deduplicate: rules_array is sorted, but not deduplicated:
1544
             * both mpm and stored state could give us the same sid.
1545
             * As they are back to back in that case we can check for it
1546
             * here. We select the stored state one as that comes first
1547
             * in the array. */
1548
372k
            while ((i + 1) < array_idx &&
1549
89.4k
                    det_ctx->tx_candidates[i].s == det_ctx->tx_candidates[i + 1].s) {
1550
60.0k
                SCLogDebug("%p/%" PRIu64 " inspecting SKIP NEXT: sid %u (%u), flags %08x",
1551
60.0k
                        tx.tx_ptr, tx.tx_id, s->id, s->num, inspect_flags ? *inspect_flags : 0);
1552
60.0k
                i++;
1553
60.0k
            }
1554
1555
312k
            SCLogDebug("%p/%"PRIu64" inspecting: sid %u (%u), flags %08x",
1556
312k
                    tx.tx_ptr, tx.tx_id, s->id, s->num, inspect_flags ? *inspect_flags : 0);
1557
1558
312k
            if (inspect_flags) {
1559
61.3k
                if (*inspect_flags & (DE_STATE_FLAG_FULL_INSPECT|DE_STATE_FLAG_SIG_CANT_MATCH)) {
1560
23.7k
                    SCLogDebug("%p/%"PRIu64" inspecting: sid %u (%u), flags %08x ALREADY COMPLETE",
1561
23.7k
                            tx.tx_ptr, tx.tx_id, s->id, s->num, *inspect_flags);
1562
23.7k
                    continue;
1563
23.7k
                }
1564
61.3k
            }
1565
1566
288k
            if (inspect_flags) {
1567
                /* continue previous inspection */
1568
37.5k
                SCLogDebug("%p/%" PRIu64 " Continuing sid %u", tx.tx_ptr, tx.tx_id, s->id);
1569
250k
            } else {
1570
                /* start new inspection */
1571
250k
                SCLogDebug("%p/%"PRIu64" Start sid %u", tx.tx_ptr, tx.tx_id, s->id);
1572
250k
            }
1573
1574
            /* call individual rule inspection */
1575
288k
            RULE_PROFILING_START(p);
1576
288k
            const int r = DetectRunTxInspectRule(tv, de_ctx, det_ctx, p, f, flow_flags,
1577
288k
                    alstate, &tx, s, inspect_flags, can, scratch);
1578
288k
            if (r == 1) {
1579
                /* match */
1580
73.4k
                DetectRunPostMatch(tv, det_ctx, p, s);
1581
1582
73.4k
                const uint8_t alert_flags = (PACKET_ALERT_FLAG_STATE_MATCH | PACKET_ALERT_FLAG_TX);
1583
73.4k
                SCLogDebug("%p/%"PRIu64" sig %u (%u) matched", tx.tx_ptr, tx.tx_id, s->id, s->num);
1584
73.4k
                AlertQueueAppend(det_ctx, s, p, tx.tx_id, alert_flags);
1585
73.4k
            }
1586
288k
            DetectVarProcessList(det_ctx, p->flow, p);
1587
288k
            RULE_PROFILING_END(det_ctx, s, r, p);
1588
288k
        }
1589
1590
681k
        det_ctx->tx_id = 0;
1591
681k
        det_ctx->tx_id_set = false;
1592
681k
        det_ctx->p = NULL;
1593
1594
        /* see if we have any updated state to store in the tx */
1595
1596
681k
        uint64_t new_detect_flags = 0;
1597
        /* this side of the tx is done */
1598
681k
        if (tx.tx_progress >= tx.tx_end_state) {
1599
341k
            new_detect_flags |= APP_LAYER_TX_INSPECTED_FLAG;
1600
341k
            SCLogDebug("%p/%"PRIu64" tx is done for direction %s. Flag %016"PRIx64,
1601
341k
                    tx.tx_ptr, tx.tx_id,
1602
341k
                    flow_flags & STREAM_TOSERVER ? "toserver" : "toclient",
1603
341k
                    new_detect_flags);
1604
341k
        }
1605
681k
        if (tx.prefilter_flags != tx.prefilter_flags_orig) {
1606
50.9k
            new_detect_flags |= tx.prefilter_flags;
1607
50.9k
            DEBUG_VALIDATE_BUG_ON(new_detect_flags & APP_LAYER_TX_RESERVED_FLAGS);
1608
50.9k
            SCLogDebug("%p/%"PRIu64" updated prefilter flags %016"PRIx64" "
1609
50.9k
                    "(was: %016"PRIx64") for direction %s. Flag %016"PRIx64,
1610
50.9k
                    tx.tx_ptr, tx.tx_id, tx.prefilter_flags, tx.prefilter_flags_orig,
1611
50.9k
                    flow_flags & STREAM_TOSERVER ? "toserver" : "toclient",
1612
50.9k
                    new_detect_flags);
1613
50.9k
        }
1614
681k
        if (new_detect_flags != 0 &&
1615
344k
                (new_detect_flags | tx.detect_flags) != tx.detect_flags)
1616
344k
        {
1617
344k
            new_detect_flags |= tx.detect_flags;
1618
344k
            DEBUG_VALIDATE_BUG_ON(new_detect_flags & APP_LAYER_TX_RESERVED_FLAGS);
1619
344k
            SCLogDebug("%p/%"PRIu64" Storing new flags %016"PRIx64" (was %016"PRIx64")",
1620
344k
                    tx.tx_ptr, tx.tx_id, new_detect_flags, tx.detect_flags);
1621
1622
344k
            StoreDetectFlags(&tx, flow_flags, ipproto, alproto, new_detect_flags);
1623
344k
        }
1624
28.7M
next:
1625
28.7M
        InspectionBufferClean(det_ctx);
1626
1627
28.7M
        if (!ires.has_next)
1628
716k
            break;
1629
28.7M
    }
1630
813k
}
1631
1632
static void DetectRunFrames(ThreadVars *tv, DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx,
1633
        Packet *p, Flow *f, DetectRunScratchpad *scratch)
1634
1.55M
{
1635
1.55M
    const SigGroupHead *const sgh = scratch->sgh;
1636
1.55M
    const AppProto alproto = f->alproto;
1637
1638
1.55M
    FramesContainer *frames_container = AppLayerFramesGetContainer(f);
1639
1.55M
    if (frames_container == NULL) {
1640
363k
        return;
1641
363k
    }
1642
1.18M
    Frames *frames;
1643
1.18M
    if (PKT_IS_TOSERVER(p)) {
1644
696k
        frames = &frames_container->toserver;
1645
696k
    } else {
1646
490k
        frames = &frames_container->toclient;
1647
490k
    }
1648
1649
4.95M
    for (uint32_t idx = 0; idx < frames->cnt; idx++) {
1650
3.77M
        SCLogDebug("frame %u", idx);
1651
3.77M
        Frame *frame = FrameGetByIndex(frames, idx);
1652
3.77M
        if (frame == NULL) {
1653
0
            continue;
1654
0
        }
1655
1656
3.77M
        det_ctx->frame_inspect_progress = 0;
1657
3.77M
        uint32_t array_idx = 0;
1658
3.77M
        uint32_t total_rules = det_ctx->match_array_cnt;
1659
1660
        /* run prefilter engines and merge results into a candidates array */
1661
3.77M
        if (sgh->frame_engines) {
1662
            //            PACKET_PROFILING_DETECT_START(p, PROF_DETECT_PF_TX);
1663
267k
            DetectRunPrefilterFrame(det_ctx, sgh, p, frames, frame, alproto);
1664
            //            PACKET_PROFILING_DETECT_END(p, PROF_DETECT_PF_TX);
1665
267k
            SCLogDebug("%p/%" PRIi64 " rules added from prefilter: %u candidates", frame, frame->id,
1666
267k
                    det_ctx->pmq.rule_id_array_cnt);
1667
1668
267k
            total_rules += det_ctx->pmq.rule_id_array_cnt;
1669
1670
267k
            if (!(RuleMatchCandidateTxArrayHasSpace(
1671
267k
                        det_ctx, total_rules))) { // TODO is it safe to overload?
1672
10
                RuleMatchCandidateTxArrayExpand(det_ctx, total_rules);
1673
10
            }
1674
1675
268k
            for (uint32_t i = 0; i < det_ctx->pmq.rule_id_array_cnt; i++) {
1676
595
                const Signature *s = de_ctx->sig_array[det_ctx->pmq.rule_id_array[i]];
1677
595
                const SigIntId id = s->num;
1678
595
                det_ctx->tx_candidates[array_idx].s = s;
1679
595
                det_ctx->tx_candidates[array_idx].id = id;
1680
595
                det_ctx->tx_candidates[array_idx].flags = NULL;
1681
595
                det_ctx->tx_candidates[array_idx].stream_reset = 0;
1682
595
                array_idx++;
1683
595
            }
1684
267k
            PMQ_RESET(&det_ctx->pmq);
1685
267k
        }
1686
        /* merge 'state' rules from the regular prefilter */
1687
3.77M
        uint32_t x = array_idx;
1688
6.97M
        for (uint32_t i = 0; i < det_ctx->match_array_cnt; i++) {
1689
3.20M
            const Signature *s = det_ctx->match_array[i];
1690
3.20M
            if (s->frame_inspect != NULL) {
1691
110k
                const SigIntId id = s->num;
1692
110k
                det_ctx->tx_candidates[array_idx].s = s;
1693
110k
                det_ctx->tx_candidates[array_idx].id = id;
1694
110k
                det_ctx->tx_candidates[array_idx].flags = NULL;
1695
110k
                det_ctx->tx_candidates[array_idx].stream_reset = 0;
1696
110k
                array_idx++;
1697
1698
110k
                SCLogDebug("%p/%" PRIi64 " rule %u (%u) added from 'match' list", frame, frame->id,
1699
110k
                        s->id, id);
1700
110k
            }
1701
3.20M
        }
1702
3.77M
        SCLogDebug("%p/%" PRIi64 " rules added from 'match' list: %u", frame, frame->id,
1703
3.77M
                array_idx - x);
1704
3.77M
        (void)x;
1705
1706
        /* run rules: inspect the match candidates */
1707
3.88M
        for (uint32_t i = 0; i < array_idx; i++) {
1708
111k
            const Signature *s = det_ctx->tx_candidates[i].s;
1709
1710
            /* deduplicate: rules_array is sorted, but not deduplicated.
1711
             * As they are back to back in that case we can check for it
1712
             * here. We select the stored state one as that comes first
1713
             * in the array. */
1714
111k
            while ((i + 1) < array_idx &&
1715
212
                    det_ctx->tx_candidates[i].s == det_ctx->tx_candidates[i + 1].s) {
1716
48
                i++;
1717
48
            }
1718
111k
            SCLogDebug("%p/%" PRIi64 " inspecting: sid %u (%u)", frame, frame->id, s->id, s->num);
1719
1720
            /* start new inspection */
1721
111k
            SCLogDebug("%p/%" PRIi64 " Start sid %u", frame, frame->id, s->id);
1722
1723
            /* call individual rule inspection */
1724
111k
            RULE_PROFILING_START(p);
1725
111k
            int r = DetectRunInspectRuleHeader(p, f, s, s->flags, s->proto.flags);
1726
111k
            if (r == 1) {
1727
111k
                r = DetectRunFrameInspectRule(tv, det_ctx, s, f, p, frames, frame);
1728
111k
                if (r == 1) {
1729
                    /* match */
1730
12.3k
                    DetectRunPostMatch(tv, det_ctx, p, s);
1731
1732
12.3k
                    uint8_t alert_flags = (PACKET_ALERT_FLAG_STATE_MATCH | PACKET_ALERT_FLAG_FRAME);
1733
12.3k
                    det_ctx->flags |= DETECT_ENGINE_THREAD_CTX_FRAME_ID_SET;
1734
12.3k
                    det_ctx->frame_id = frame->id;
1735
12.3k
                    SCLogDebug(
1736
12.3k
                            "%p/%" PRIi64 " sig %u (%u) matched", frame, frame->id, s->id, s->num);
1737
12.3k
                    if (frame->flags & FRAME_FLAG_TX_ID_SET) {
1738
11.6k
                        alert_flags |= PACKET_ALERT_FLAG_TX;
1739
11.6k
                    }
1740
12.3k
                    AlertQueueAppend(det_ctx, s, p, frame->tx_id, alert_flags);
1741
12.3k
                }
1742
111k
            }
1743
111k
            DetectVarProcessList(det_ctx, p->flow, p);
1744
111k
            RULE_PROFILING_END(det_ctx, s, r, p);
1745
111k
        }
1746
1747
        /* update Frame::inspect_progress here instead of in the code above. The reason is that a
1748
         * frame might be used more than once in buffers with transforms. */
1749
3.77M
        if (frame->inspect_progress < det_ctx->frame_inspect_progress) {
1750
17.0k
            frame->inspect_progress = det_ctx->frame_inspect_progress;
1751
17.0k
            SCLogDebug("frame->inspect_progress: %" PRIu64 " -> updated", frame->inspect_progress);
1752
3.75M
        } else {
1753
3.75M
            SCLogDebug(
1754
3.75M
                    "frame->inspect_progress: %" PRIu64 " -> not updated", frame->inspect_progress);
1755
3.75M
        }
1756
1757
3.77M
        SCLogDebug("%p/%" PRIi64 " rules inspected, running cleanup", frame, frame->id);
1758
3.77M
        InspectionBufferClean(det_ctx);
1759
3.77M
    }
1760
1.18M
}
1761
1762
static DetectEngineThreadCtx *GetTenantById(HashTable *h, uint32_t id)
1763
0
{
1764
    /* technically we need to pass a DetectEngineThreadCtx struct with the
1765
     * tenant_id member. But as that member is the first in the struct, we
1766
     * can use the id directly. */
1767
0
    return HashTableLookup(h, &id, 0);
1768
0
}
1769
1770
static void DetectFlow(ThreadVars *tv,
1771
                       DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx,
1772
                       Packet *p)
1773
7.15M
{
1774
7.15M
    Flow *const f = p->flow;
1775
1776
7.15M
    if (p->flags & PKT_NOPACKET_INSPECTION) {
1777
        /* hack: if we are in pass the entire flow mode, we need to still
1778
         * update the inspect_id forward. So test for the condition here,
1779
         * and call the update code if necessary. */
1780
35.7k
        const int pass = ((f->flags & FLOW_NOPACKET_INSPECTION));
1781
35.7k
        if (pass) {
1782
35.7k
            uint8_t flags = STREAM_FLAGS_FOR_PACKET(p);
1783
35.7k
            flags = FlowGetDisruptionFlags(f, flags);
1784
35.7k
            if (f->alstate) {
1785
31.2k
                AppLayerParserSetTransactionInspectId(f, f->alparser, f->alstate, flags, true);
1786
31.2k
            }
1787
35.7k
        }
1788
35.7k
        SCLogDebug("p->pcap %"PRIu64": no detection on packet, "
1789
35.7k
                "PKT_NOPACKET_INSPECTION is set", p->pcap_cnt);
1790
35.7k
        return;
1791
35.7k
    }
1792
1793
    /* we check the flow drop here, and not the packet drop. This is
1794
     * to allow stream engine "invalid" drop packets to still be
1795
     * evaluated by the stream event rules. */
1796
7.12M
    if (f->flags & FLOW_ACTION_DROP) {
1797
2.68k
        DEBUG_VALIDATE_BUG_ON(!(PKT_IS_PSEUDOPKT(p)) && !PacketCheckAction(p, ACTION_DROP));
1798
2.68k
        SCReturn;
1799
2.68k
    }
1800
1801
    /* see if the packet matches one or more of the sigs */
1802
7.11M
    (void)DetectRun(tv, de_ctx, det_ctx, p);
1803
7.11M
}
1804
1805
1806
static void DetectNoFlow(ThreadVars *tv,
1807
                         DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx,
1808
                         Packet *p)
1809
1.56M
{
1810
    /* No need to perform any detection on this packet, if the given flag is set.*/
1811
1.56M
    if ((p->flags & PKT_NOPACKET_INSPECTION) || (PacketCheckAction(p, ACTION_DROP))) {
1812
0
        return;
1813
0
    }
1814
1815
    /* see if the packet matches one or more of the sigs */
1816
1.56M
    DetectRun(tv, de_ctx, det_ctx, p);
1817
1.56M
    return;
1818
1.56M
}
1819
1820
/** \brief Detection engine thread wrapper.
1821
 *  \param tv thread vars
1822
 *  \param p packet to inspect
1823
 *  \param data thread specific data
1824
 *  \param pq packet queue
1825
 *  \retval TM_ECODE_FAILED error
1826
 *  \retval TM_ECODE_OK ok
1827
 */
1828
TmEcode Detect(ThreadVars *tv, Packet *p, void *data)
1829
8.72M
{
1830
8.72M
    DEBUG_VALIDATE_PACKET(p);
1831
1832
8.72M
    DetectEngineCtx *de_ctx = NULL;
1833
8.72M
    DetectEngineThreadCtx *det_ctx = (DetectEngineThreadCtx *)data;
1834
8.72M
    if (det_ctx == NULL) {
1835
0
        printf("ERROR: Detect has no thread ctx\n");
1836
0
        goto error;
1837
0
    }
1838
1839
8.72M
    if (unlikely(SC_ATOMIC_GET(det_ctx->so_far_used_by_detect) == 0)) {
1840
44.5k
        (void)SC_ATOMIC_SET(det_ctx->so_far_used_by_detect, 1);
1841
44.5k
        SCLogDebug("Detect Engine using new det_ctx - %p",
1842
44.5k
                  det_ctx);
1843
44.5k
    }
1844
1845
    /* if in MT mode _and_ we have tenants registered, use
1846
     * MT logic. */
1847
8.72M
    if (det_ctx->mt_det_ctxs_cnt > 0 && det_ctx->TenantGetId != NULL)
1848
0
    {
1849
0
        uint32_t tenant_id = p->tenant_id;
1850
0
        if (tenant_id == 0)
1851
0
            tenant_id = det_ctx->TenantGetId(det_ctx, p);
1852
0
        if (tenant_id > 0 && tenant_id < det_ctx->mt_det_ctxs_cnt) {
1853
0
            p->tenant_id = tenant_id;
1854
0
            det_ctx = GetTenantById(det_ctx->mt_det_ctxs_hash, tenant_id);
1855
0
            if (det_ctx == NULL)
1856
0
                return TM_ECODE_OK;
1857
0
            de_ctx = det_ctx->de_ctx;
1858
0
            if (de_ctx == NULL)
1859
0
                return TM_ECODE_OK;
1860
1861
0
            if (unlikely(SC_ATOMIC_GET(det_ctx->so_far_used_by_detect) == 0)) {
1862
0
                (void)SC_ATOMIC_SET(det_ctx->so_far_used_by_detect, 1);
1863
0
                SCLogDebug("MT de_ctx %p det_ctx %p (tenant %u)", de_ctx, det_ctx, tenant_id);
1864
0
            }
1865
0
        } else {
1866
            /* use default if no tenants are registered for this packet */
1867
0
            de_ctx = det_ctx->de_ctx;
1868
0
        }
1869
8.72M
    } else {
1870
8.72M
        de_ctx = det_ctx->de_ctx;
1871
8.72M
    }
1872
1873
8.72M
    if (p->flow) {
1874
7.15M
        DetectFlow(tv, de_ctx, det_ctx, p);
1875
7.15M
    } else {
1876
1.56M
        DetectNoFlow(tv, de_ctx, det_ctx, p);
1877
1.56M
    }
1878
1879
#ifdef PROFILE_RULES
1880
    /* aggregate statistics */
1881
    struct timeval ts;
1882
    gettimeofday(&ts, NULL);
1883
    if (ts.tv_sec != det_ctx->rule_perf_last_sync) {
1884
        SCProfilingRuleThreatAggregate(det_ctx);
1885
        det_ctx->rule_perf_last_sync = ts.tv_sec;
1886
    }
1887
#endif
1888
1889
8.72M
    return TM_ECODE_OK;
1890
0
error:
1891
0
    return TM_ECODE_FAILED;
1892
8.72M
}
1893
1894
/** \brief disable file features we don't need
1895
 *  Called if we have no detection engine.
1896
 */
1897
void DisableDetectFlowFileFlags(Flow *f)
1898
0
{
1899
0
    DetectPostInspectFileFlagsUpdate(f, NULL /* no sgh */, STREAM_TOSERVER);
1900
0
    DetectPostInspectFileFlagsUpdate(f, NULL /* no sgh */, STREAM_TOCLIENT);
1901
0
}
1902
1903
#ifdef UNITTESTS
1904
/**
1905
 *  \brief wrapper for old tests
1906
 */
1907
void SigMatchSignatures(
1908
        ThreadVars *tv, DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx, Packet *p)
1909
{
1910
    if (p->flow) {
1911
        DetectFlow(tv, de_ctx, det_ctx, p);
1912
    } else {
1913
        DetectNoFlow(tv, de_ctx, det_ctx, p);
1914
    }
1915
}
1916
#endif
1917
1918
/*
1919
 * TESTS
1920
 */
1921
1922
#ifdef UNITTESTS
1923
#include "tests/detect.c"
1924
#endif
1925