• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

jasonish / suricata / 23262806180

18 Mar 2026 07:20PM UTC coverage: 79.32% (+0.02%) from 79.297%
23262806180

push

github

jasonish
github-ci/builds: update ubuntu builds to rust 1.89

266068 of 335437 relevant lines covered (79.32%)

4677245.13 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

96.38
/src/detect.c
1
/* Copyright (C) 2007-2025 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

29
#include "decode.h"
30
#include "packet.h"
31
#include "flow.h"
32
#include "stream-tcp.h"
33
#include "app-layer.h"
34
#include "app-layer-parser.h"
35
#include "app-layer-frames.h"
36

37
#include "detect.h"
38
#include "detect-dsize.h"
39
#include "detect-engine.h"
40
#include "detect-engine-build.h"
41
#include "detect-engine-frame.h"
42
#include "detect-engine-profile.h"
43

44
#include "detect-engine-alert.h"
45
#include "detect-engine-siggroup.h"
46
#include "detect-engine-address.h"
47
#include "detect-engine-proto.h"
48
#include "detect-engine-port.h"
49
#include "detect-engine-mpm.h"
50
#include "detect-engine-iponly.h"
51
#include "detect-engine-threshold.h"
52
#include "detect-engine-prefilter.h"
53
#include "detect-engine-state.h"
54
#include "detect-engine-analyzer.h"
55

56
#include "detect-engine-payload.h"
57
#include "detect-engine-event.h"
58

59
#include "detect-filestore.h"
60
#include "detect-flowvar.h"
61
#include "detect-replace.h"
62

63
#include "util-validate.h"
64
#include "util-detect.h"
65
#include "util-profiling.h"
66

67
#include "action-globals.h"
68

69
typedef struct DetectRunScratchpad {
70
    const AppProto alproto;
71
    const uint8_t flow_flags; /* flow/state flags: STREAM_* */
72
    const bool app_decoder_events;
73
    /**
74
     *  Either ACTION_DROP (drop:packet) or ACTION_ACCEPT (accept:hook)
75
     *
76
     *  ACTION_DROP means the default policy of drop:packet is applied
77
     *  ACTION_ACCEPT means the default policy of accept:hook is applied
78
     */
79
    const uint8_t default_action;
80
    const SigGroupHead *sgh;
81
} DetectRunScratchpad;
82

83
/* prototypes */
84
static DetectRunScratchpad DetectRunSetup(const DetectEngineCtx *de_ctx,
85
        DetectEngineThreadCtx *det_ctx, Packet *const p, Flow *const pflow,
86
        const uint8_t default_action);
87
static void DetectRunInspectIPOnly(ThreadVars *tv, const DetectEngineCtx *de_ctx,
88
        DetectEngineThreadCtx *det_ctx, Flow * const pflow, Packet * const p);
89
static inline void DetectRunGetRuleGroup(const DetectEngineCtx *de_ctx,
90
        Packet * const p, Flow * const pflow, DetectRunScratchpad *scratch);
91
static inline void DetectRunPrefilterPkt(ThreadVars *tv, const DetectEngineCtx *de_ctx,
92
        DetectEngineThreadCtx *det_ctx, Packet *p, DetectRunScratchpad *scratch);
93
static inline uint8_t DetectRulePacketRules(ThreadVars *const tv,
94
        const DetectEngineCtx *const de_ctx, DetectEngineThreadCtx *const det_ctx, Packet *const p,
95
        Flow *const pflow, const DetectRunScratchpad *scratch);
96
static void DetectRunTx(ThreadVars *tv, DetectEngineCtx *de_ctx,
97
        DetectEngineThreadCtx *det_ctx, Packet *p,
98
        Flow *f, DetectRunScratchpad *scratch);
99
static void DetectRunFrames(ThreadVars *tv, DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx,
100
        Packet *p, Flow *f, DetectRunScratchpad *scratch);
101
static inline void DetectRunPostRules(ThreadVars *tv, const DetectEngineCtx *de_ctx,
102
        DetectEngineThreadCtx *det_ctx, Packet *const p, Flow *const pflow,
103
        DetectRunScratchpad *scratch);
104
static void DetectRunCleanup(DetectEngineThreadCtx *det_ctx,
105
        Packet *p, Flow * const pflow);
106
static inline void DetectRunAppendDefaultAccept(DetectEngineThreadCtx *det_ctx, Packet *p);
107

108
/** \internal
109
 */
110
static void DetectRun(ThreadVars *th_v,
111
        DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx,
112
        Packet *p)
113
{
124,266,320✔
114
    SCEnter();
124,266,320✔
115
    SCLogDebug("pcap_cnt %" PRIu64 " direction %s pkt_src %s", PcapPacketCntGet(p),
124,266,320✔
116
            p->flow ? (FlowGetPacketDirection(p->flow, p) == TOSERVER ? "toserver" : "toclient")
124,266,320✔
117
                    : "noflow",
124,266,320✔
118
            PktSrcToString(p->pkt_src));
124,266,320✔
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
    Flow * const pflow = p->flow;
124,266,320✔
123

124
    DetectRunScratchpad scratch = DetectRunSetup(de_ctx, det_ctx, p, pflow, ACTION_DROP);
124,266,320✔
125

126
    /* run the IPonly engine */
127
    DetectRunInspectIPOnly(th_v, de_ctx, det_ctx, pflow, p);
124,266,320✔
128

129
    /* get our rule group */
130
    DetectRunGetRuleGroup(de_ctx, p, pflow, &scratch);
124,266,320✔
131
    /* if we didn't get a sig group head, we
132
     * have nothing to do.... */
133
    if (scratch.sgh == NULL) {
124,804,869✔
134
        SCLogDebug("no sgh for this packet, nothing to match against");
121,474,062✔
135
        goto end;
121,474,062✔
136
    }
121,474,062✔
137

138
    /* run the prefilters for packets */
139
    DetectRunPrefilterPkt(th_v, de_ctx, det_ctx, p, &scratch);
2,150,814,454✔
140

141
    PACKET_PROFILING_DETECT_START(p, PROF_DETECT_RULES);
2,150,814,454✔
142
    /* inspect the rules against the packet */
143
    const uint8_t pkt_policy = DetectRulePacketRules(th_v, de_ctx, det_ctx, p, pflow, &scratch);
2,150,814,454✔
144
    PACKET_PROFILING_DETECT_END(p, PROF_DETECT_RULES);
2,150,814,454✔
145

146
    /* Only FW rules will already have set the action, IDS rules go through PacketAlertFinalize
147
     *
148
     * If rules told us to drop or accept:packet/accept:flow, we skip app_filter and app_td.
149
     *
150
     * accept:hook won't have set the pkt_policy, so we simply continue.
151
     *
152
     * TODO what about app state progression, cleanup and such? */
153
    if (pkt_policy & (ACTION_DROP | ACTION_ACCEPT)) {
2,150,814,454✔
154
        goto end;
1,049✔
155
    }
1,049✔
156

157
    /* run tx/state inspection. Don't call for ICMP error msgs. */
158
    if (pflow && pflow->alstate && likely(pflow->proto == p->proto)) {
2,150,813,405✔
159
        if (p->proto == IPPROTO_TCP) {
2,087,367✔
160
            if ((p->flags & PKT_STREAM_EST) == 0) {
2,029,281✔
161
                SCLogDebug("packet %" PRIu64 ": skip tcp non-established", PcapPacketCntGet(p));
8,170✔
162
                DetectRunAppendDefaultAccept(det_ctx, p);
8,170✔
163
                goto end;
8,170✔
164
            }
8,170✔
165
            const TcpSession *ssn = p->flow->protoctx;
2,021,111✔
166
            bool setting_nopayload = p->flow->alparser &&
2,021,111✔
167
                                     SCAppLayerParserStateIssetFlag(
2,021,141✔
168
                                             p->flow->alparser, APP_LAYER_PARSER_NO_INSPECTION) &&
2,021,129✔
169
                                     !(p->flags & PKT_NOPAYLOAD_INSPECTION);
2,021,111✔
170
            // we may be right after disabling app-layer (ssh)
171
            if (ssn &&
2,021,111✔
172
                    ((ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED) == 0 || setting_nopayload)) {
2,021,150✔
173
                // PACKET_PROFILING_DETECT_START(p, PROF_DETECT_TX);
174
                DetectRunFrames(th_v, de_ctx, det_ctx, p, pflow, &scratch);
1,853,413✔
175
                // PACKET_PROFILING_DETECT_END(p, PROF_DETECT_TX);
176
            }
1,853,413✔
177
            // no update to transactions
178
            if (!PKT_IS_PSEUDOPKT(p) && p->app_update_direction == 0 &&
2,021,111✔
179
                    ((PKT_IS_TOSERVER(p) && (p->flow->flags & FLOW_TS_APP_UPDATED) == 0) ||
2,021,111✔
180
                            (PKT_IS_TOCLIENT(p) && (p->flow->flags & FLOW_TC_APP_UPDATED) == 0))) {
1,578,615✔
181
                SCLogDebug("packet %" PRIu64 ": no app-layer update", PcapPacketCntGet(p));
1,369,643✔
182
                DetectRunAppendDefaultAccept(det_ctx, p);
1,369,643✔
183
                goto end;
1,369,643✔
184
            }
1,369,643✔
185
        } else if (p->proto == IPPROTO_UDP) {
2,021,111✔
186
            DetectRunFrames(th_v, de_ctx, det_ctx, p, pflow, &scratch);
58,056✔
187
        }
58,056✔
188

189
        PACKET_PROFILING_DETECT_START(p, PROF_DETECT_TX);
709,554✔
190
        DetectRunTx(th_v, de_ctx, det_ctx, p, pflow, &scratch);
709,554✔
191
        PACKET_PROFILING_DETECT_END(p, PROF_DETECT_TX);
709,554✔
192
        /* see if we need to increment the inspect_id and reset the de_state */
193
        PACKET_PROFILING_DETECT_START(p, PROF_DETECT_TX_UPDATE);
709,554✔
194
        AppLayerParserSetTransactionInspectId(
709,554✔
195
                pflow, pflow->alparser, pflow->alstate, scratch.flow_flags, (scratch.sgh == NULL));
709,554✔
196
        PACKET_PROFILING_DETECT_END(p, PROF_DETECT_TX_UPDATE);
709,554✔
197
    } else {
2,149,017,281✔
198
        SCLogDebug("packet %" PRIu64 ": no flow / app-layer", PcapPacketCntGet(p));
2,148,726,038✔
199
        DetectRunAppendDefaultAccept(det_ctx, p);
2,148,726,038✔
200
    }
2,148,726,038✔
201

202
end:
2,151,482,856✔
203
    DetectRunPostRules(th_v, de_ctx, det_ctx, p, pflow, &scratch);
123,532,130✔
204

205
    DetectRunCleanup(det_ctx, p, pflow);
123,532,130✔
206
    SCReturn;
123,532,130✔
207
}
2,150,813,405✔
208

209
/** \internal
210
 */
211
static void DetectRunPacketHook(ThreadVars *th_v, const DetectEngineCtx *de_ctx,
212
        DetectEngineThreadCtx *det_ctx, const SigGroupHead *sgh, Packet *p)
213
{
52✔
214
    SCEnter();
52✔
215
    SCLogDebug("pcap_cnt %" PRIu64 " direction %s pkt_src %s", PcapPacketCntGet(p),
52✔
216
            p->flow ? (FlowGetPacketDirection(p->flow, p) == TOSERVER ? "toserver" : "toclient")
52✔
217
                    : "noflow",
52✔
218
            PktSrcToString(p->pkt_src));
52✔
219

220
    /* Load the Packet's flow early, even though it might not be needed.
221
     * Mark as a constant pointer, although the flow itself can change. */
222
    Flow *const pflow = p->flow;
52✔
223

224
    DetectRunScratchpad scratch = DetectRunSetup(de_ctx, det_ctx, p, pflow, ACTION_ACCEPT);
52✔
225
    scratch.sgh = sgh;
52✔
226

227
    /* if we didn't get a sig group head, we
228
     * have nothing to do.... */
229
    if (scratch.sgh == NULL) {
52✔
230
        SCLogDebug("no sgh for this packet, nothing to match against");
12✔
231
        goto end;
12✔
232
    }
12✔
233

234
    /* run the prefilters for packets */
235
    DetectRunPrefilterPkt(th_v, de_ctx, det_ctx, p, &scratch);
40✔
236

237
    //    PACKET_PROFILING_DETECT_START(p, PROF_DETECT_RULES); // TODO
238
    /* inspect the rules against the packet */
239
    const uint8_t pkt_policy = DetectRulePacketRules(th_v, de_ctx, det_ctx, p, pflow, &scratch);
40✔
240
    //    PACKET_PROFILING_DETECT_END(p, PROF_DETECT_RULES);
241
    if (pkt_policy & (ACTION_DROP | ACTION_ACCEPT)) {
40✔
242
        goto end;
15✔
243
    }
15✔
244

245
end:
52✔
246
    DetectRunPostRules(th_v, de_ctx, det_ctx, p, pflow, &scratch);
52✔
247

248
    DetectRunCleanup(det_ctx, p, pflow);
52✔
249
    SCReturn;
52✔
250
}
40✔
251

252
static void DetectRunPostMatch(ThreadVars *tv,
253
                               DetectEngineThreadCtx *det_ctx, Packet *p,
254
                               const Signature *s)
255
{
1,822,345✔
256
    /* run the packet match functions */
257
    const SigMatchData *smd = s->sm_arrays[DETECT_SM_LIST_POSTMATCH];
1,822,345✔
258
    if (smd != NULL) {
1,822,345✔
259
        KEYWORD_PROFILING_SET_LIST(det_ctx, DETECT_SM_LIST_POSTMATCH);
11,070✔
260

261
        SCLogDebug("running match functions, sm %p", smd);
11,070✔
262

263
        while (1) {
12,312✔
264
            KEYWORD_PROFILING_START;
12,312✔
265
            (void)sigmatch_table[smd->type].Match(det_ctx, p, s, smd->ctx);
12,312✔
266
            KEYWORD_PROFILING_END(det_ctx, smd->type, 1);
12,312✔
267
            if (smd->is_last)
12,312✔
268
                break;
11,070✔
269
            smd++;
1,242✔
270
        }
1,242✔
271
    }
11,070✔
272
}
1,822,345✔
273

274
/**
275
 *  \brief Get the SigGroupHead for a packet.
276
 *
277
 *  \param de_ctx detection engine context
278
 *  \param p packet
279
 *
280
 *  \retval sgh the SigGroupHead or NULL if non applies to the packet
281
 */
282
const SigGroupHead *SigMatchSignaturesGetSgh(const DetectEngineCtx *de_ctx,
283
        const Packet *p)
284
{
118,881,142✔
285
    SCEnter();
118,881,142✔
286
    SigGroupHead *sgh = NULL;
118,881,142✔
287

288
    /* if the packet proto is 0 (not set), we're inspecting it against
289
     * the decoder events sgh we have. */
290
    if (p->proto == 0 && p->events.cnt > 0) {
120,456,111✔
291
        SCReturnPtr(de_ctx->decoder_event_sgh, "SigGroupHead");
120,161,583✔
292
    } else if (p->proto == 0) {
2,147,796,334✔
293
        if (!(PacketIsIPv4(p) || PacketIsIPv6(p))) {
16,798✔
294
            /* not IP, so nothing to do */
295
            SCReturnPtr(NULL, "SigGroupHead");
12,046✔
296
        }
12,046✔
297
    }
16,798✔
298

299
    /* select the flow_gh */
300
    const int dir = (p->flowflags & FLOW_PKT_TOCLIENT) == 0;
2,147,766,190✔
301

302
    int proto = PacketGetIPProto(p);
2,147,766,190✔
303
    if (proto == IPPROTO_TCP) {
2,147,766,190✔
304
        DetectPort *list = de_ctx->flow_gh[dir].tcp;
93,555✔
305
        SCLogDebug("tcp toserver %p, tcp toclient %p: going to use %p", de_ctx->flow_gh[1].tcp,
93,555✔
306
                de_ctx->flow_gh[0].tcp, de_ctx->flow_gh[dir].tcp);
93,555✔
307
        const uint16_t port = dir ? p->dp : p->sp;
93,555✔
308
        SCLogDebug("tcp port %u -> %u:%u", port, p->sp, p->dp);
93,555✔
309
        DetectPort *sghport = DetectPortLookupGroup(list, port);
93,555✔
310
        if (sghport != NULL)
93,555✔
311
            sgh = sghport->sh;
57,878✔
312
        SCLogDebug("TCP list %p, port %u, direction %s, sghport %p, sgh %p", list, port,
93,555✔
313
                dir ? "toserver" : "toclient", sghport, sgh);
93,555✔
314
    } else if (proto == IPPROTO_UDP) {
2,147,708,879✔
315
        DetectPort *list = de_ctx->flow_gh[dir].udp;
32,667✔
316
        uint16_t port = dir ? p->dp : p->sp;
32,667✔
317
        DetectPort *sghport = DetectPortLookupGroup(list, port);
32,667✔
318
        if (sghport != NULL)
32,667✔
319
            sgh = sghport->sh;
18,409✔
320
        SCLogDebug("UDP list %p, port %u, direction %s, sghport %p, sgh %p", list, port,
32,667✔
321
                dir ? "toserver" : "toclient", sghport, sgh);
32,667✔
322
    } else {
2,147,647,128✔
323
        sgh = de_ctx->flow_gh[dir].sgh[proto];
2,147,639,968✔
324
    }
2,147,639,968✔
325

326
    SCReturnPtr(sgh, "SigGroupHead");
2,147,766,190✔
327
}
118,881,142✔
328

329
static inline void DetectPrefilterCopyDeDup(
330
        const DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx)
331
{
1,314,408✔
332
    SigIntId *pf_ptr = det_ctx->pmq.rule_id_array;
1,314,408✔
333
    uint32_t final_cnt = det_ctx->pmq.rule_id_array_cnt;
1,314,408✔
334
    Signature **sig_array = de_ctx->sig_array;
1,314,408✔
335
    Signature **match_array = det_ctx->match_array;
1,314,408✔
336
    SigIntId previous_id = (SigIntId)-1;
1,314,408✔
337
    while (final_cnt-- > 0) {
4,287,850✔
338
        SigIntId id = *pf_ptr++;
2,973,442✔
339
        Signature *s = sig_array[id];
2,973,442✔
340

341
        /* As the prefilter list can contain duplicates, check for that here. */
342
        if (likely(id != previous_id)) {
2,973,442✔
343
            *match_array++ = s;
2,962,920✔
344
            previous_id = id;
2,962,920✔
345
        }
2,962,920✔
346
    }
2,973,442✔
347

348
    det_ctx->match_array_cnt = (uint32_t)(match_array - det_ctx->match_array);
1,314,408✔
349
    DEBUG_VALIDATE_BUG_ON(det_ctx->pmq.rule_id_array_cnt < det_ctx->match_array_cnt);
1,314,408✔
350
    PMQ_RESET(&det_ctx->pmq);
1,314,408✔
351
}
1,314,408✔
352

353
/** \internal
354
 *  \brief update flow's file tracking flags based on the detection engine
355
 *         A set of flags is prepared that is sent to the File API. The
356
           File API may reject one or more based on the global force settings.
357
 */
358
static inline void
359
DetectPostInspectFileFlagsUpdate(Flow *f, const SigGroupHead *sgh, uint8_t direction)
360
{
131,915✔
361
    uint16_t flow_file_flags = FLOWFILE_INIT;
131,915✔
362

363
    if (sgh == NULL) {
131,915✔
364
        SCLogDebug("requesting disabling all file features for flow");
59,221✔
365
        flow_file_flags = FLOWFILE_NONE;
59,221✔
366
    } else {
77,489✔
367
        if (sgh->filestore_cnt == 0) {
72,695✔
368
            SCLogDebug("requesting disabling filestore for flow");
66,144✔
369
            flow_file_flags |= (FLOWFILE_NO_STORE_TS|FLOWFILE_NO_STORE_TC);
66,144✔
370
        }
66,144✔
371
#ifdef HAVE_MAGIC
72,694✔
372
        if (!(sgh->flags & SIG_GROUP_HEAD_HAVEFILEMAGIC)) {
72,727✔
373
            SCLogDebug("requesting disabling magic for flow");
69,128✔
374
            flow_file_flags |= (FLOWFILE_NO_MAGIC_TS|FLOWFILE_NO_MAGIC_TC);
69,128✔
375
        }
69,128✔
376
#endif
72,694✔
377
        if (!(sgh->flags & SIG_GROUP_HEAD_HAVEFILEMD5)) {
72,727✔
378
            SCLogDebug("requesting disabling md5 for flow");
72,725✔
379
            flow_file_flags |= (FLOWFILE_NO_MD5_TS|FLOWFILE_NO_MD5_TC);
72,725✔
380
        }
72,725✔
381
        if (!(sgh->flags & SIG_GROUP_HEAD_HAVEFILESHA1)) {
72,742✔
382
            SCLogDebug("requesting disabling sha1 for flow");
72,737✔
383
            flow_file_flags |= (FLOWFILE_NO_SHA1_TS|FLOWFILE_NO_SHA1_TC);
72,737✔
384
        }
72,737✔
385
        if (!(sgh->flags & SIG_GROUP_HEAD_HAVEFILESHA256)) {
72,729✔
386
            SCLogDebug("requesting disabling sha256 for flow");
72,723✔
387
            flow_file_flags |= (FLOWFILE_NO_SHA256_TS|FLOWFILE_NO_SHA256_TC);
72,723✔
388
        }
72,723✔
389
    }
72,694✔
390
    if (flow_file_flags != 0) {
131,963✔
391
        FileUpdateFlowFileFlags(f, flow_file_flags, direction);
131,959✔
392
    }
131,959✔
393
}
131,915✔
394

395
static inline void
396
DetectRunPostGetFirstRuleGroup(const Packet *p, Flow *pflow, const SigGroupHead *sgh)
397
{
122,931✔
398
    if ((p->flowflags & FLOW_PKT_TOSERVER) && !(pflow->flags & FLOW_SGH_TOSERVER)) {
122,931✔
399
        /* first time we see this toserver sgh, store it */
400
        pflow->sgh_toserver = sgh;
76,455✔
401
        pflow->flags |= FLOW_SGH_TOSERVER;
76,455✔
402

403
        if (p->proto == IPPROTO_TCP && (sgh == NULL || !(sgh->flags & SIG_GROUP_HEAD_HAVERAWSTREAM))) {
76,455✔
404
            if (pflow->protoctx != NULL) {
41,156✔
405
                TcpSession *ssn = pflow->protoctx;
39,085✔
406
                SCLogDebug("STREAMTCP_STREAM_FLAG_DISABLE_RAW ssn.client");
39,085✔
407
                ssn->client.flags |= STREAMTCP_STREAM_FLAG_DISABLE_RAW;
39,085✔
408
            }
39,085✔
409
        }
41,156✔
410

411
        DetectPostInspectFileFlagsUpdate(pflow,
76,455✔
412
                pflow->sgh_toserver, STREAM_TOSERVER);
76,455✔
413

414
    } else if ((p->flowflags & FLOW_PKT_TOCLIENT) && !(pflow->flags & FLOW_SGH_TOCLIENT)) {
76,455✔
415
        pflow->sgh_toclient = sgh;
46,423✔
416
        pflow->flags |= FLOW_SGH_TOCLIENT;
46,423✔
417

418
        if (p->proto == IPPROTO_TCP && (sgh == NULL || !(sgh->flags & SIG_GROUP_HEAD_HAVERAWSTREAM))) {
46,423✔
419
            if (pflow->protoctx != NULL) {
31,130✔
420
                TcpSession *ssn = pflow->protoctx;
30,766✔
421
                SCLogDebug("STREAMTCP_STREAM_FLAG_DISABLE_RAW ssn.server");
30,766✔
422
                ssn->server.flags |= STREAMTCP_STREAM_FLAG_DISABLE_RAW;
30,766✔
423
            }
30,766✔
424
        }
31,130✔
425

426
        DetectPostInspectFileFlagsUpdate(pflow,
46,423✔
427
                pflow->sgh_toclient, STREAM_TOCLIENT);
46,423✔
428
    }
46,423✔
429
}
122,931✔
430

431
static inline void DetectRunGetRuleGroup(
432
    const DetectEngineCtx *de_ctx,
433
    Packet * const p, Flow * const pflow,
434
    DetectRunScratchpad *scratch)
435
{
124,539,216✔
436
    const SigGroupHead *sgh = NULL;
124,539,216✔
437

438
    if (pflow) {
124,539,216✔
439
        bool use_flow_sgh = false;
3,681,055✔
440
        /* Get the stored sgh from the flow (if any). Make sure we're not using
441
         * the sgh for icmp error packets part of the same stream. */
442
        if (PacketGetIPProto(p) == pflow->proto) { /* filter out icmp */
3,681,056✔
443
            PACKET_PROFILING_DETECT_START(p, PROF_DETECT_GETSGH);
3,680,734✔
444
            if ((p->flowflags & FLOW_PKT_TOSERVER) && (pflow->flags & FLOW_SGH_TOSERVER)) {
3,680,734✔
445
                sgh = pflow->sgh_toserver;
2,266,598✔
446
                SCLogDebug("sgh = pflow->sgh_toserver; => %p", sgh);
2,266,598✔
447
                use_flow_sgh = true;
2,266,598✔
448
            } else if ((p->flowflags & FLOW_PKT_TOCLIENT) && (pflow->flags & FLOW_SGH_TOCLIENT)) {
2,267,119✔
449
                sgh = pflow->sgh_toclient;
1,291,391✔
450
                SCLogDebug("sgh = pflow->sgh_toclient; => %p", sgh);
1,291,391✔
451
                use_flow_sgh = true;
1,291,391✔
452
            }
1,291,391✔
453
            PACKET_PROFILING_DETECT_END(p, PROF_DETECT_GETSGH);
3,680,734✔
454
        }
3,680,734✔
455

456
        if (!(use_flow_sgh)) {
3,681,055✔
457
            PACKET_PROFILING_DETECT_START(p, PROF_DETECT_GETSGH);
123,107✔
458
            sgh = SigMatchSignaturesGetSgh(de_ctx, p);
123,107✔
459
            PACKET_PROFILING_DETECT_END(p, PROF_DETECT_GETSGH);
123,107✔
460

461
            /* HACK: prevent the wrong sgh (or NULL) from being stored in the
462
             * flow's sgh pointers */
463
            if (PacketIsICMPv4(p) && ICMPV4_DEST_UNREACH_IS_VALID(p)) {
123,107✔
464
                ; /* no-op */
166✔
465
            } else {
122,941✔
466
                /* store the found sgh (or NULL) in the flow to save us
467
                 * from looking it up again for the next packet.
468
                 * Also run other tasks */
469
                DetectRunPostGetFirstRuleGroup(p, pflow, sgh);
122,941✔
470
            }
122,941✔
471
        }
123,107✔
472
    } else { /* p->flags & PKT_HAS_FLOW */
124,237,694✔
473
        /* no flow */
474

475
        PACKET_PROFILING_DETECT_START(p, PROF_DETECT_GETSGH);
120,858,161✔
476
        sgh = SigMatchSignaturesGetSgh(de_ctx, p);
120,858,161✔
477
        PACKET_PROFILING_DETECT_END(p, PROF_DETECT_GETSGH);
120,858,161✔
478
    }
120,858,161✔
479

480
    scratch->sgh = sgh;
124,539,216✔
481
}
124,539,216✔
482

483
static void DetectRunInspectIPOnly(ThreadVars *tv, const DetectEngineCtx *de_ctx,
484
        DetectEngineThreadCtx *det_ctx,
485
        Flow * const pflow, Packet * const p)
486
{
128,203,791✔
487
    if (pflow) {
128,203,791✔
488
        if (p->flowflags & (FLOW_PKT_TOSERVER_FIRST | FLOW_PKT_TOCLIENT_FIRST)) {
3,681,050✔
489
            SCLogDebug("testing against \"ip-only\" signatures");
119,925✔
490

491
            PACKET_PROFILING_DETECT_START(p, PROF_DETECT_IPONLY);
119,925✔
492
            IPOnlyMatchPacket(tv, de_ctx, det_ctx, &de_ctx->io_ctx, p);
119,925✔
493
            PACKET_PROFILING_DETECT_END(p, PROF_DETECT_IPONLY);
119,925✔
494
        }
119,925✔
495
    } else { /* p->flags & PKT_HAS_FLOW */
127,902,277✔
496
        /* no flow */
497

498
        /* Even without flow we should match the packet src/dst */
499
        PACKET_PROFILING_DETECT_START(p, PROF_DETECT_IPONLY);
124,522,741✔
500
        IPOnlyMatchPacket(tv, de_ctx, det_ctx, &de_ctx->io_ctx, p);
124,522,741✔
501
        PACKET_PROFILING_DETECT_END(p, PROF_DETECT_IPONLY);
124,522,741✔
502
    }
124,522,741✔
503
}
128,203,791✔
504

505
/** \internal
506
 *  \brief inspect the rule header: protocol, ports, etc
507
 *  \retval bool false if no match, true if match */
508
static inline bool DetectRunInspectRuleHeader(const Packet *p, const Flow *f, const Signature *s,
509
        const uint32_t sflags, const uint8_t s_proto_flags)
510
{
3,007,036✔
511
    /* check if this signature has a requirement for flowvars of some type
512
     * and if so, if we actually have any in the flow. If not, the sig
513
     * can't match and we skip it. */
514
    if ((p->flags & PKT_HAS_FLOW) && (sflags & SIG_FLAG_REQUIRE_FLOWVAR)) {
3,007,036✔
515
        DEBUG_VALIDATE_BUG_ON(f == NULL);
134,372✔
516

517
        /* no flowvars? skip this sig */
518
        const bool fv = f->flowvar != NULL;
134,372✔
519
        if (!fv) {
134,372✔
520
            SCLogDebug("skipping sig as the flow has no flowvars and sig "
17,587✔
521
                    "has SIG_FLAG_REQUIRE_FLOWVAR flag set.");
17,587✔
522
            return false;
17,587✔
523
        }
17,587✔
524
    }
134,372✔
525

526
    if ((s_proto_flags & DETECT_PROTO_IPV4) && !PacketIsIPv4(p)) {
2,989,449✔
527
        SCLogDebug("ip version didn't match");
90✔
528
        return false;
90✔
529
    }
90✔
530
    if ((s_proto_flags & DETECT_PROTO_IPV6) && !PacketIsIPv6(p)) {
2,989,359✔
531
        SCLogDebug("ip version didn't match");
2,759✔
532
        return false;
2,759✔
533
    }
2,759✔
534

535
    if (DetectProtoContainsProto(&s->proto, PacketGetIPProto(p)) == 0) {
2,986,600✔
536
        SCLogDebug("proto didn't match");
50✔
537
        return false;
50✔
538
    }
50✔
539

540
    /* check the source & dst port in the sig */
541
    if (p->proto == IPPROTO_TCP || p->proto == IPPROTO_UDP || p->proto == IPPROTO_SCTP) {
2,986,550✔
542
        if (!(sflags & SIG_FLAG_DP_ANY)) {
1,943,908✔
543
            if (p->flags & PKT_IS_FRAGMENT)
190,990✔
544
                return false;
4✔
545
            const DetectPort *dport = DetectPortLookupGroup(s->dp, p->dp);
190,986✔
546
            if (dport == NULL) {
190,986✔
547
                SCLogDebug("dport didn't match.");
109,034✔
548
                return false;
109,034✔
549
            }
109,034✔
550
        }
190,986✔
551
        if (!(sflags & SIG_FLAG_SP_ANY)) {
1,834,870✔
552
            if (p->flags & PKT_IS_FRAGMENT)
27,173✔
553
                return false;
22✔
554
            const DetectPort *sport = DetectPortLookupGroup(s->sp, p->sp);
27,151✔
555
            if (sport == NULL) {
27,151✔
556
                SCLogDebug("sport didn't match.");
16,165✔
557
                return false;
16,165✔
558
            }
16,165✔
559
        }
27,151✔
560
    } else if ((sflags & (SIG_FLAG_DP_ANY|SIG_FLAG_SP_ANY)) != (SIG_FLAG_DP_ANY|SIG_FLAG_SP_ANY)) {
2,173,886✔
561
        SCLogDebug("port-less protocol and sig needs ports");
287✔
562
        return false;
287✔
563
    }
287✔
564

565
    /* check the destination address */
566
    if (!(sflags & SIG_FLAG_DST_ANY)) {
2,861,038✔
567
        if (PacketIsIPv4(p)) {
1,196,280✔
568
            if (DetectAddressMatchIPv4(s->addr_dst_match4, s->addr_dst_match4_cnt, &p->dst) == 0)
761,339✔
569
                return false;
18,051✔
570
        } else if (PacketIsIPv6(p)) {
808,121✔
571
            if (DetectAddressMatchIPv6(s->addr_dst_match6, s->addr_dst_match6_cnt, &p->dst) == 0)
434,941✔
572
                return false;
150,454✔
573
        }
434,941✔
574
    }
1,196,280✔
575
    /* check the source address */
576
    if (!(sflags & SIG_FLAG_SRC_ANY)) {
2,692,533✔
577
        if (PacketIsIPv4(p)) {
363,282✔
578
            if (DetectAddressMatchIPv4(s->addr_src_match4, s->addr_src_match4_cnt, &p->src) == 0)
79,640✔
579
                return false;
58,900✔
580
        } else if (PacketIsIPv6(p)) {
289,517✔
581
            if (DetectAddressMatchIPv6(s->addr_src_match6, s->addr_src_match6_cnt, &p->src) == 0)
283,635✔
582
                return false;
283,515✔
583
        }
283,635✔
584
    }
363,282✔
585

586
    return true;
2,350,118✔
587
}
2,692,533✔
588

589
/** \internal
590
 *  \brief run packet/stream prefilter engines
591
 */
592
static inline void DetectRunPrefilterPkt(ThreadVars *tv, const DetectEngineCtx *de_ctx,
593
        DetectEngineThreadCtx *det_ctx, Packet *p, DetectRunScratchpad *scratch)
594
{
3,331,584✔
595
    /* create our prefilter mask */
596
    PacketCreateMask(p, &p->sig_mask, scratch->alproto, scratch->app_decoder_events);
3,331,584✔
597
    /* run the prefilter engines */
598
    Prefilter(det_ctx, scratch->sgh, p, scratch->flow_flags, p->sig_mask);
3,331,584✔
599
    /* create match list if we have non-pf and/or pf */
600
    if (det_ctx->pmq.rule_id_array_cnt) {
3,331,584✔
601
#ifdef PROFILING
602
        if (tv) {
603
            StatsCounterAvgAddI64(
604
                    &tv->stats, det_ctx->counter_mpm_list, (int64_t)det_ctx->pmq.rule_id_array_cnt);
605
        }
606
#endif
607
        PACKET_PROFILING_DETECT_START(p, PROF_DETECT_PF_SORT2);
1,314,406✔
608
        DetectPrefilterCopyDeDup(de_ctx, det_ctx);
1,314,406✔
609
        PACKET_PROFILING_DETECT_END(p, PROF_DETECT_PF_SORT2);
1,314,406✔
610
    }
1,314,406✔
611
}
3,331,584✔
612

613
/** \internal
614
 *  \brief check if the tx whose id is given is the only one
615
 *  live transaction for the flow in the given direction
616
 *
617
 *  \param f flow
618
 *  \param txid transaction id
619
 *  \param dir direction
620
 *
621
 *  \retval bool true if we are sure this tx is the only one live in said direction
622
 */
623
static bool IsOnlyTxInDirection(Flow *f, uint64_t txid, uint8_t dir)
624
{
50✔
625
    uint64_t tx_cnt = AppLayerParserGetTxCnt(f, f->alstate);
50✔
626
    if (tx_cnt == txid + 1) {
50✔
627
        // only live tx
628
        return true;
44✔
629
    }
44✔
630
    if (tx_cnt == txid + 2) {
6✔
631
        // 2 live txs, one after us
632
        void *tx = AppLayerParserGetTx(f->proto, f->alproto, f->alstate, txid + 1);
3✔
633
        if (tx) {
3✔
634
            AppLayerTxData *txd = AppLayerParserGetTxData(f->proto, f->alproto, tx);
3✔
635
            // test if the other tx is unidirectional in the other way
636
            if ((dir == STREAM_TOSERVER && (txd->flags & APP_LAYER_TX_SKIP_INSPECT_TS)) ||
3✔
637
                    (dir == STREAM_TOCLIENT && (txd->flags & APP_LAYER_TX_SKIP_INSPECT_TC))) {
3✔
638
                return true;
2✔
639
            }
2✔
640
        }
3✔
641
    }
3✔
642
    return false;
4✔
643
}
6✔
644

645
static int SortHelper(const void *a, const void *b)
646
{
207✔
647
    const Signature *sa = *(const Signature **)a;
207✔
648
    const Signature *sb = *(const Signature **)b;
207✔
649
    if (sa->iid == sb->iid)
207✔
650
        return 0;
×
651
    return sa->iid > sb->iid ? 1 : -1;
207✔
652
}
207✔
653

654
static inline uint8_t DetectRulePacketRules(ThreadVars *const tv,
655
        const DetectEngineCtx *const de_ctx, DetectEngineThreadCtx *const det_ctx, Packet *const p,
656
        Flow *const pflow, const DetectRunScratchpad *scratch)
657
{
3,331,671✔
658
    uint8_t action = 0;
3,331,671✔
659
    bool fw_verdict = false;
3,331,671✔
660
    const bool have_fw_rules = EngineModeIsFirewall();
3,331,671✔
661
    const Signature *next_s = NULL;
3,331,671✔
662

663
    /* inspect the sigs against the packet */
664
    /* Prefetch the next signature. */
665
    SigIntId match_cnt = det_ctx->match_array_cnt;
3,331,671✔
666
#ifdef PROFILING
667
    if (tv) {
668
        StatsCounterAvgAddI64(&tv->stats, det_ctx->counter_match_list, (int64_t)match_cnt);
669
    }
670
#endif
671
    Signature **match_array = det_ctx->match_array;
3,331,671✔
672

673
    SGH_PROFILING_RECORD(det_ctx, scratch->sgh);
3,331,671✔
674
#ifdef PROFILING
675
    if (match_cnt >= de_ctx->profile_match_logging_threshold)
676
        RulesDumpMatchArray(det_ctx, scratch->sgh, p);
677
#endif
678

679
    bool skip_fw = false;
3,331,671✔
680
    uint32_t sflags, next_sflags = 0;
3,331,671✔
681
    if (match_cnt) {
3,331,672✔
682
        next_s = *match_array++;
1,314,395✔
683
        next_sflags = next_s->flags;
1,314,395✔
684
    }
1,314,395✔
685
    while (match_cnt--) {
6,293,107✔
686
        RULE_PROFILING_START(p);
2,962,308✔
687
        bool break_out_of_packet_filter = false;
2,962,308✔
688
        uint8_t alert_flags = 0;
2,962,308✔
689
#ifdef PROFILE_RULES
690
        bool smatch = false; /* signature match */
691
#endif
692
        const Signature *s = next_s;
2,962,308✔
693
        sflags = next_sflags;
2,962,308✔
694
        if (match_cnt) {
2,962,308✔
695
            next_s = *match_array++;
1,648,255✔
696
            next_sflags = next_s->flags;
1,648,255✔
697
        }
1,648,255✔
698
        const uint8_t s_proto_flags = s->proto.flags;
2,962,308✔
699

700
        SCLogDebug("packet %" PRIu64 ": inspecting signature id %" PRIu32 "", PcapPacketCntGet(p),
2,962,308✔
701
                s->id);
2,962,308✔
702

703
        /* if we accept:hook'd the `packet_filter` hook, we skip the rest of the firewall rules. */
704
        if (s->flags & SIG_FLAG_FIREWALL) {
2,962,308✔
705
            if (skip_fw) {
4,539✔
706
                SCLogDebug("skipping firewall rule %u", s->id);
628✔
707
                goto next;
628✔
708
            }
628✔
709
        } else if (have_fw_rules) {
2,957,769✔
710
            /* fw mode, we skip anything after the fw rules if:
711
             * - flow pass is set
712
             * - packet pass (e.g. exception policy) */
713
            if (p->flags & PKT_NOPACKET_INSPECTION ||
4✔
714
                    (pflow != NULL && pflow->flags & (FLOW_ACTION_PASS))) {
4✔
715
                SCLogDebug("skipping firewall rule %u", s->id);
1✔
716
                break_out_of_packet_filter = true;
1✔
717
                goto next;
1✔
718
            }
1✔
719
        }
4✔
720

721
        if (s->app_inspect != NULL) {
2,961,679✔
722
            goto next; // handle sig in DetectRunTx
130,565✔
723
        }
130,565✔
724
        if (s->frame_inspect != NULL) {
2,831,114✔
725
            goto next; // handle sig in DetectRunFrame
×
726
        }
×
727

728
        /* skip pkt sigs for flow end packets */
729
        if ((p->flags & PKT_PSEUDO_STREAM_END) != 0 && s->type == SIG_TYPE_PKT)
2,831,114✔
730
            goto next;
1,143✔
731

732
        /* don't run mask check for stateful rules.
733
         * There we depend on prefilter */
734
        if ((s->mask & p->sig_mask) != s->mask) {
2,829,971✔
735
            SCLogDebug("mask mismatch %x & %x != %x", s->mask, p->sig_mask, s->mask);
679✔
736
            goto next;
679✔
737
        }
679✔
738

739
        if (SigDsizePrefilter(p, s, sflags))
2,829,292✔
740
            goto next;
34,315✔
741

742
        /* if the sig has alproto and the session as well they should match */
743
        if (likely(sflags & SIG_FLAG_APPLAYER)) {
2,794,977✔
744
            if (s->alproto != ALPROTO_UNKNOWN && !AppProtoEquals(s->alproto, scratch->alproto)) {
29,134✔
745
                SCLogDebug("alproto mismatch");
5,346✔
746
                goto next;
5,346✔
747
            }
5,346✔
748
        }
29,133✔
749

750
        if (!DetectRunInspectRuleHeader(p, pflow, s, sflags, s_proto_flags)) {
2,789,631✔
751
            goto next;
644,281✔
752
        }
644,281✔
753

754
        if (!DetectEnginePktInspectionRun(tv, det_ctx, s, pflow, p, &alert_flags)) {
2,145,350✔
755
            goto next;
363,407✔
756
        }
363,407✔
757

758
#ifdef PROFILE_RULES
759
        smatch = true;
760
#endif
761
        DetectRunPostMatch(tv, det_ctx, p, s);
1,781,943✔
762

763
        uint64_t txid = PACKET_ALERT_NOTX;
1,781,943✔
764
        if (pflow && pflow->alstate) {
1,781,954✔
765
            uint8_t dir = (p->flowflags & FLOW_PKT_TOCLIENT) ? STREAM_TOCLIENT : STREAM_TOSERVER;
1,001,441✔
766
            txid = AppLayerParserGetTransactionInspectId(pflow->alparser, dir);
1,001,441✔
767
            if ((s->alproto != ALPROTO_UNKNOWN && pflow->proto == IPPROTO_UDP) ||
1,001,441✔
768
                    (de_ctx->guess_applayer && IsOnlyTxInDirection(pflow, txid, dir))) {
1,001,441✔
769
                // if there is a UDP specific app-layer signature,
770
                // or only one live transaction
771
                // try to use the good tx for the packet direction
772
                void *tx_ptr =
890✔
773
                        AppLayerParserGetTx(pflow->proto, pflow->alproto, pflow->alstate, txid);
890✔
774
                AppLayerTxData *txd =
890✔
775
                        tx_ptr ? AppLayerParserGetTxData(pflow->proto, pflow->alproto, tx_ptr)
890✔
776
                               : NULL;
890✔
777
                if (txd && txd->guessed_applayer_logged < de_ctx->guess_applayer_log_limit) {
890✔
778
                    alert_flags |= PACKET_ALERT_FLAG_TX;
747✔
779
                    if (pflow->proto != IPPROTO_UDP) {
747✔
780
                        alert_flags |= PACKET_ALERT_FLAG_TX_GUESSED;
26✔
781
                    }
26✔
782
                    txd->guessed_applayer_logged++;
747✔
783
                }
747✔
784
            }
890✔
785
        }
1,001,441✔
786
        AlertQueueAppend(det_ctx, s, p, txid, alert_flags);
1,781,943✔
787

788
        if (det_ctx->post_rule_work_queue.len > 0) {
1,781,943✔
789
            /* run post match prefilter engines on work queue */
790
            PrefilterPostRuleMatch(det_ctx, scratch->sgh, p, pflow);
43✔
791

792
            if (det_ctx->pmq.rule_id_array_cnt > 0) {
43✔
793
                /* undo "prefetch" */
794
                if (next_s)
37✔
795
                    match_array--;
37✔
796
                SCLogDebug("sig_array_len %u det_ctx->pmq.rule_id_array_cnt %u",
37✔
797
                        de_ctx->sig_array_len, det_ctx->pmq.rule_id_array_cnt);
37✔
798
                const Signature **r = det_ctx->replace;
37✔
799
                for (uint32_t x = 0; x < match_cnt; x++) {
90✔
800
                    *r++ = match_array[x];
53✔
801
                    SCLogDebug("appended %u", match_array[x]->id);
53✔
802
                }
53✔
803
                /* append the prefilter results, then sort it */
804
                for (uint32_t x = 0; x < det_ctx->pmq.rule_id_array_cnt; x++) {
101✔
805
                    SCLogDebug("adding iid %u", det_ctx->pmq.rule_id_array[x]);
64✔
806
                    Signature *ts = de_ctx->sig_array[det_ctx->pmq.rule_id_array[x]];
64✔
807
                    SCLogDebug("adding id %u", ts->id);
64✔
808
                    if (ts->app_inspect == NULL) {
64✔
809
                        *r++ = ts;
59✔
810
                        match_cnt++;
59✔
811
                    }
59✔
812
                }
64✔
813
                if (match_cnt > 1) {
37✔
814
                    qsort(det_ctx->replace, match_cnt, sizeof(Signature *), SortHelper);
27✔
815
                }
27✔
816
                /* rewrite match_array to include the new additions, and deduplicate
817
                 * while at it. */
818
                Signature **m = match_array;
37✔
819
                Signature *last_sig = NULL;
37✔
820
                uint32_t skipped = 0;
37✔
821
                for (uint32_t x = 0; x < match_cnt; x++) {
149✔
822
                    /* de-duplicate */
823
                    if (last_sig == *m) {
112✔
824
                        skipped++;
9✔
825
                        continue;
9✔
826
                    }
9✔
827
                    last_sig = *m;
103✔
828
                    *m++ = (Signature *)det_ctx->replace[x];
103✔
829
                }
103✔
830
                match_cnt -= skipped;
37✔
831
                /* prefetch next */
832
                next_s = *match_array++;
37✔
833
                next_sflags = next_s->flags;
37✔
834
                SCLogDebug("%u rules added", det_ctx->pmq.rule_id_array_cnt);
37✔
835
                det_ctx->post_rule_work_queue.len = 0;
37✔
836
                PMQ_RESET(&det_ctx->pmq);
37✔
837
            }
37✔
838
        }
43✔
839

840
        /* firewall logic in the packet:filter table:
841
         * 1. firewall rules preceed the packet:td rules in the list
842
         * 2. if no rule issues an accept, we drop
843
         * 3. drop is immediate
844
         * 4. accept:
845
         *    - hook: skip rest of fw rules, inspect packet:td rules
846
         *    - packet: immediate accept, no packet:td or app:* inspect
847
         *    - flow: as packet, but applied to all future packets in the
848
         *            flow as well
849
         */
850
        if (s->flags & SIG_FLAG_FIREWALL) {
1,781,943✔
851
            if (s->action & (ACTION_ACCEPT)) {
1,821✔
852
                fw_verdict = true;
1,778✔
853

854
                enum ActionScope as = s->action_scope;
1,778✔
855
                if (as == ACTION_SCOPE_HOOK) {
1,778✔
856
                    /* accept:hook: jump to first TD. Implemented as:
857
                     * skip until the first TD rule.
858
                     * Don't update action as we're just continuing to the next hook. */
859
                    skip_fw = true;
1,239✔
860

861
                } else if (as == ACTION_SCOPE_PACKET) {
1,239✔
862
                    /* accept:packet: break loop, return accept */
863
                    action |= s->action;
535✔
864
                    break_out_of_packet_filter = true;
535✔
865

866
                } else if (as == ACTION_SCOPE_FLOW) {
535✔
867
                    /* accept:flow: break loop, return accept */
868
                    action |= s->action;
4✔
869
                    break_out_of_packet_filter = true;
4✔
870

871
                    /* set immediately, as we're in hook "packet_filter" */
872
                    if (pflow) {
4✔
873
                        pflow->flags |= FLOW_ACTION_ACCEPT;
4✔
874
                    }
4✔
875
                }
4✔
876
            } else if (s->action & ACTION_DROP) {
1,778✔
877
                /* apply a drop immediately here */
878
                fw_verdict = true;
30✔
879
                action |= s->action;
30✔
880
                break_out_of_packet_filter = true;
30✔
881
            }
30✔
882
        }
1,821✔
883
next:
2,962,006✔
884
        DetectVarProcessList(det_ctx, pflow, p);
2,962,006✔
885
        DetectReplaceFree(det_ctx);
2,962,006✔
886
        RULE_PROFILING_END(det_ctx, s, smatch, p);
2,962,006✔
887

888
        /* fw accept:packet or accept:flow means we're done here */
889
        if (break_out_of_packet_filter)
2,962,006✔
890
            break;
570✔
891

892
        continue;
2,961,436✔
893
    }
2,962,006✔
894

895
    /* if no rule told us to accept, and no rule explicitly dropped, we invoke the default drop
896
     * policy
897
     */
898
    if (have_fw_rules && scratch->default_action == ACTION_DROP) {
3,331,369✔
899
        if (!fw_verdict) {
2,288✔
900
            DEBUG_VALIDATE_BUG_ON(action & ACTION_DROP);
495✔
901
            PacketDrop(p, ACTION_DROP, PKT_DROP_REASON_DEFAULT_PACKET_POLICY);
495✔
902
            action |= ACTION_DROP;
495✔
903
        } else {
1,793✔
904
            /* apply fw action */
905
            p->action |= action;
1,793✔
906
        }
1,793✔
907
    }
2,288✔
908
    return action;
3,331,369✔
909
}
3,331,671✔
910

911
/** \internal
912
 *  \param default_action either ACTION_DROP (drop:packet) or ACTION_ACCEPT (accept:hook)
913
 *
914
 *  ACTION_DROP means the default policy of drop:packet is applied
915
 *  ACTION_ACCEPT means the default policy of accept:hook is applied
916
 */
917
static DetectRunScratchpad DetectRunSetup(const DetectEngineCtx *de_ctx,
918
        DetectEngineThreadCtx *det_ctx, Packet *const p, Flow *const pflow,
919
        const uint8_t default_action)
920
{
124,115,279✔
921
    AppProto alproto = ALPROTO_UNKNOWN;
124,115,279✔
922
    uint8_t flow_flags = 0; /* flow/state flags */
124,115,279✔
923
    bool app_decoder_events = false;
124,115,279✔
924

925
    PACKET_PROFILING_DETECT_START(p, PROF_DETECT_SETUP);
124,115,279✔
926

927
#ifdef UNITTESTS
1,338✔
928
    if (RunmodeIsUnittests()) {
1,338✔
929
        p->alerts.cnt = 0;
1,313✔
930
        p->alerts.discarded = 0;
1,313✔
931
        p->alerts.suppressed = 0;
1,313✔
932
    }
1,313✔
933
#endif
1,338✔
934
    det_ctx->filestore_cnt = 0;
124,115,279✔
935
    det_ctx->base64_decoded_len = 0;
124,115,279✔
936
    det_ctx->raw_stream_progress = 0;
124,115,279✔
937
    det_ctx->match_array_cnt = 0;
124,115,279✔
938
    det_ctx->json_content_len = 0;
124,115,279✔
939

940
    det_ctx->alert_queue_size = 0;
124,115,279✔
941
    p->alerts.drop.action = 0;
124,115,279✔
942

943
#ifdef DEBUG
944
    if (p->flags & PKT_STREAM_ADD) {
945
        det_ctx->pkt_stream_add_cnt++;
946
    }
947
#endif
948

949
    /* grab the protocol state we will detect on */
950
    if (p->flags & PKT_HAS_FLOW) {
124,115,279✔
951
        DEBUG_VALIDATE_BUG_ON(pflow == NULL);
3,680,900✔
952

953
        if (p->flowflags & FLOW_PKT_TOSERVER) {
3,680,900✔
954
            flow_flags = STREAM_TOSERVER;
2,343,084✔
955
            SCLogDebug("flag STREAM_TOSERVER set");
2,343,084✔
956
        } else if (p->flowflags & FLOW_PKT_TOCLIENT) {
2,343,084✔
957
            flow_flags = STREAM_TOCLIENT;
1,338,002✔
958
            SCLogDebug("flag STREAM_TOCLIENT set");
1,338,002✔
959
        }
1,338,002✔
960
        SCLogDebug("p->flowflags 0x%02x", p->flowflags);
3,680,900✔
961

962
        if (p->flags & PKT_PSEUDO_STREAM_END) {
3,680,900✔
963
            flow_flags |= STREAM_EOF;
16,341✔
964
            SCLogDebug("STREAM_EOF set");
16,341✔
965
        }
16,341✔
966

967
        /* store tenant_id in the flow so that we can use it
968
         * for creating pseudo packets */
969
        if (p->tenant_id > 0 && pflow->tenant_id == 0) {
3,680,900✔
970
            pflow->tenant_id = p->tenant_id;
×
971
        }
×
972

973
        /* live ruleswap check for flow updates */
974
        if (pflow->de_ctx_version == 0) {
3,680,900✔
975
            /* first time this flow is inspected, set id */
976
            pflow->de_ctx_version = de_ctx->version;
75,919✔
977
        } else if (pflow->de_ctx_version != de_ctx->version) {
3,605,081✔
978
            /* first time we inspect flow with this de_ctx, reset */
979
            pflow->flags &= ~FLOW_SGH_TOSERVER;
1,486✔
980
            pflow->flags &= ~FLOW_SGH_TOCLIENT;
1,486✔
981
            pflow->sgh_toserver = NULL;
1,486✔
982
            pflow->sgh_toclient = NULL;
1,486✔
983

984
            pflow->de_ctx_version = de_ctx->version;
1,486✔
985
            SCGenericVarFree(pflow->flowvar);
1,486✔
986
            pflow->flowvar = NULL;
1,486✔
987

988
            DetectEngineStateResetTxs(pflow);
1,486✔
989
        }
1,486✔
990

991
        /* Retrieve the app layer state and protocol and the tcp reassembled
992
         * stream chunks. */
993
        if ((p->proto == IPPROTO_TCP && (p->flags & PKT_STREAM_EST)) ||
3,680,900✔
994
                (p->proto == IPPROTO_UDP) ||
3,680,900✔
995
                (p->proto == IPPROTO_SCTP && (p->flowflags & FLOW_PKT_ESTABLISHED)))
3,680,900✔
996
        {
2,657,767✔
997
            /* update flow flags with knowledge on disruptions */
998
            flow_flags = FlowGetDisruptionFlags(pflow, flow_flags);
2,657,767✔
999
            alproto = FlowGetAppProtocol(pflow);
2,657,767✔
1000
            if (p->proto == IPPROTO_TCP && pflow->protoctx &&
2,657,767✔
1001
                    StreamReassembleRawHasDataReady(pflow->protoctx, p)) {
2,657,767✔
1002
                p->flags |= PKT_DETECT_HAS_STREAMDATA;
244,946✔
1003
            }
244,946✔
1004
            SCLogDebug("alproto %u", alproto);
2,657,767✔
1005
        } else {
2,658,402✔
1006
            SCLogDebug("packet doesn't have established flag set (proto %d)", p->proto);
1,023,133✔
1007
        }
1,023,133✔
1008

1009
        app_decoder_events = AppLayerParserHasDecoderEvents(pflow->alparser);
3,680,900✔
1010
    }
3,680,900✔
1011

1012
    DetectRunScratchpad pad = { alproto, flow_flags, app_decoder_events, default_action, NULL };
124,115,279✔
1013
    PACKET_PROFILING_DETECT_END(p, PROF_DETECT_SETUP);
124,115,279✔
1014
    return pad;
124,115,279✔
1015
}
124,115,279✔
1016

1017
static inline void DetectRunPostRules(ThreadVars *tv, const DetectEngineCtx *de_ctx,
1018
        DetectEngineThreadCtx *det_ctx, Packet *const p, Flow *const pflow,
1019
        DetectRunScratchpad *scratch)
1020
{
122,309,272✔
1021
    /* so now let's iterate the alerts and remove the ones after a pass rule
1022
     * matched (if any). This is done inside PacketAlertFinalize() */
1023
    /* PR: installed "tag" keywords are handled after the threshold inspection */
1024

1025
    PACKET_PROFILING_DETECT_START(p, PROF_DETECT_ALERT);
122,309,272✔
1026
    PacketAlertFinalize(de_ctx, det_ctx, p);
122,309,272✔
1027
    if (p->alerts.cnt > 0) {
122,309,272✔
1028
        StatsCounterAddI64(&tv->stats, det_ctx->counter_alerts, (uint64_t)p->alerts.cnt);
676,769✔
1029
    }
676,769✔
1030
    if (p->alerts.discarded > 0) {
122,309,272✔
1031
        StatsCounterAddI64(
32✔
1032
                &tv->stats, det_ctx->counter_alerts_overflow, (uint64_t)p->alerts.discarded);
32✔
1033
    }
32✔
1034
    if (p->alerts.suppressed > 0) {
122,309,272✔
1035
        StatsCounterAddI64(
14,432✔
1036
                &tv->stats, det_ctx->counter_alerts_suppressed, (uint64_t)p->alerts.suppressed);
14,432✔
1037
    }
14,432✔
1038
    PACKET_PROFILING_DETECT_END(p, PROF_DETECT_ALERT);
122,309,272✔
1039

1040
    /* firewall: "fail" closed if we don't have an ACCEPT. This can happen
1041
     * if there was no rule group. */
1042
    // TODO review packet src types here
1043
    if (EngineModeIsFirewall() && !(p->action & ACTION_ACCEPT) && p->pkt_src == PKT_SRC_WIRE &&
122,309,272✔
1044
            scratch->default_action == ACTION_DROP) {
122,309,272✔
1045
        SCLogDebug("packet %" PRIu64 ": droppit as no ACCEPT set %02x (pkt %s)",
509✔
1046
                PcapPacketCntGet(p), p->action, PktSrcToString(p->pkt_src));
509✔
1047
        PacketDrop(p, ACTION_DROP, PKT_DROP_REASON_DEFAULT_PACKET_POLICY);
509✔
1048
    }
509✔
1049
}
122,309,272✔
1050

1051
static void DetectRunCleanup(DetectEngineThreadCtx *det_ctx,
1052
        Packet *p, Flow * const pflow)
1053
{
121,735,909✔
1054
    PACKET_PROFILING_DETECT_START(p, PROF_DETECT_CLEANUP);
121,735,909✔
1055
    InspectionBufferClean(det_ctx);
121,735,909✔
1056

1057
    if (pflow != NULL) {
121,735,909✔
1058
        /* update inspected tracker for raw reassembly */
1059
        if (p->proto == IPPROTO_TCP && pflow->protoctx != NULL &&
3,681,033✔
1060
                (p->flags & PKT_DETECT_HAS_STREAMDATA)) {
3,681,033✔
1061
            StreamReassembleRawUpdateProgress(pflow->protoctx, p,
244,946✔
1062
                    det_ctx->raw_stream_progress);
244,946✔
1063
        }
244,946✔
1064
    }
3,681,033✔
1065
    PACKET_PROFILING_DETECT_END(p, PROF_DETECT_CLEANUP);
121,735,909✔
1066
    SCReturn;
121,735,909✔
1067
}
121,735,909✔
1068

1069
void RuleMatchCandidateTxArrayInit(DetectEngineThreadCtx *det_ctx, uint32_t size)
1070
{
33,391✔
1071
    DEBUG_VALIDATE_BUG_ON(det_ctx->tx_candidates);
33,391✔
1072
    det_ctx->tx_candidates = SCCalloc(size, sizeof(RuleMatchCandidateTx));
33,391✔
1073
    if (det_ctx->tx_candidates == NULL) {
33,391✔
1074
        FatalError("failed to allocate %" PRIu64 " bytes",
×
1075
                (uint64_t)(size * sizeof(RuleMatchCandidateTx)));
×
1076
    }
×
1077
    det_ctx->tx_candidates_size = size;
33,391✔
1078
    SCLogDebug("array initialized to %u elements (%"PRIu64" bytes)",
33,391✔
1079
            size, (uint64_t)(size * sizeof(RuleMatchCandidateTx)));
33,391✔
1080
}
33,391✔
1081

1082
void RuleMatchCandidateTxArrayFree(DetectEngineThreadCtx *det_ctx)
1083
{
48,506✔
1084
    SCFree(det_ctx->tx_candidates);
48,506✔
1085
    det_ctx->tx_candidates_size = 0;
48,506✔
1086
}
48,506✔
1087

1088
/* if size >= cur_space */
1089
static inline bool RuleMatchCandidateTxArrayHasSpace(const DetectEngineThreadCtx *det_ctx,
1090
        const uint32_t need)
1091
{
1,314,428✔
1092
    if (det_ctx->tx_candidates_size >= need)
1,314,428✔
1093
        return 1;
1,313,923✔
1094
    return 0;
2,147,484,160✔
1095
}
1,314,428✔
1096

1097
/* realloc */
1098
static int RuleMatchCandidateTxArrayExpand(DetectEngineThreadCtx *det_ctx, const uint32_t needed)
1099
{
522✔
1100
    const uint32_t old_size = det_ctx->tx_candidates_size;
522✔
1101
    uint32_t new_size = needed;
522✔
1102
    void *ptmp = SCRealloc(det_ctx->tx_candidates, (new_size * sizeof(RuleMatchCandidateTx)));
522✔
1103
    if (ptmp == NULL) {
522✔
1104
        FatalError("failed to expand to %" PRIu64 " bytes",
×
1105
                (uint64_t)(new_size * sizeof(RuleMatchCandidateTx)));
×
1106
        // TODO can this be handled more gracefully?
1107
    }
×
1108
    det_ctx->tx_candidates = ptmp;
522✔
1109
    det_ctx->tx_candidates_size = new_size;
522✔
1110
    SCLogDebug("array expanded from %u to %u elements (%"PRIu64" bytes -> %"PRIu64" bytes)",
522✔
1111
            old_size, new_size, (uint64_t)(old_size * sizeof(RuleMatchCandidateTx)),
522✔
1112
            (uint64_t)(new_size * sizeof(RuleMatchCandidateTx))); (void)old_size;
522✔
1113
    return 1;
522✔
1114
}
522✔
1115

1116
/** \internal
1117
 *  \brief sort helper for sorting match candidates by id: ascending
1118
 *
1119
 *  The id field is set from Signature::num, so we sort the candidates to match the signature
1120
 *  sort order (ascending), where candidates that have flags go first.
1121
 */
1122
static int
1123
DetectRunTxSortHelper(const void *a, const void *b)
1124
{
26,866✔
1125
    const RuleMatchCandidateTx *s0 = a;
26,866✔
1126
    const RuleMatchCandidateTx *s1 = b;
26,866✔
1127
    if (s1->id == s0->id) {
26,866✔
1128
        if (s1->flags && !s0->flags)
15,849✔
1129
            return 1;
15,820✔
1130
        else if (!s1->flags && s0->flags)
29✔
1131
            return -1;
×
1132
        return 0;
29✔
1133
    } else
15,849✔
1134
        return s0->id > s1->id ? 1 : -1;
11,017✔
1135
}
26,866✔
1136

1137
#if 0
1138
#define TRACE_SID_TXS(sid,txs,...)          \
1139
    do {                                    \
1140
        char _trace_buf[2048];              \
1141
        snprintf(_trace_buf, sizeof(_trace_buf), __VA_ARGS__);  \
1142
        SCLogNotice("%p/%"PRIu64"/%u: %s", txs->tx_ptr, txs->tx_id, sid, _trace_buf);   \
1143
    } while(0)
1144
#else
1145
#define TRACE_SID_TXS(sid,txs,...)
1146
#endif
1147

1148
// Get inner transaction for engine
1149
void *DetectGetInnerTx(void *tx_ptr, AppProto alproto, AppProto engine_alproto, uint8_t flow_flags)
1150
{
3,511,328✔
1151
    if (unlikely(alproto == ALPROTO_DOH2)) {
3,511,328✔
1152
        if (engine_alproto == ALPROTO_DNS) {
69,816✔
1153
            // need to get the dns tx pointer
1154
            tx_ptr = SCDoH2GetDnsTx(tx_ptr, flow_flags);
8,178✔
1155
        } else if (engine_alproto != ALPROTO_HTTP2 && engine_alproto != ALPROTO_UNKNOWN) {
61,638✔
1156
            // incompatible engine->alproto with flow alproto
1157
            tx_ptr = NULL;
45,157✔
1158
        }
45,157✔
1159
    } else if (engine_alproto != alproto && engine_alproto != ALPROTO_UNKNOWN) {
3,441,512✔
1160
        // incompatible engine->alproto with flow alproto
1161
        tx_ptr = NULL;
2,716,040✔
1162
    }
2,716,040✔
1163
    return tx_ptr;
3,511,328✔
1164
}
3,511,328✔
1165

1166
/** \internal
1167
 *  \brief inspect a rule against a transaction
1168
 *
1169
 *  Inspect a rule. New detection or continued stateful
1170
 *  detection.
1171
 *
1172
 *  \param stored_flags pointer to stored flags or NULL.
1173
 *         If stored_flags is set it means we're continuing
1174
 *         inspection from an earlier run.
1175
 *
1176
 *  \retval bool true sig matched, false didn't match
1177
 */
1178
static bool DetectRunTxInspectRule(ThreadVars *tv,
1179
        DetectEngineCtx *de_ctx,
1180
        DetectEngineThreadCtx *det_ctx,
1181
        Packet *p,
1182
        Flow *f,
1183
        const uint8_t in_flow_flags,   // direction, EOF, etc
1184
        void *alstate,
1185
        DetectTransaction *tx,
1186
        const Signature *s,
1187
        uint32_t *stored_flags,
1188
        RuleMatchCandidateTx *can,
1189
        DetectRunScratchpad *scratch)
1190
{
188,215✔
1191
    const uint8_t flow_flags = in_flow_flags;
188,215✔
1192
    const int direction = (flow_flags & STREAM_TOSERVER) ? 0 : 1;
188,215✔
1193
    uint32_t inspect_flags = stored_flags ? *stored_flags : 0;
188,215✔
1194
    int total_matches = 0;
188,215✔
1195
    uint16_t file_no_match = 0;
188,215✔
1196
    bool retval = false;
188,215✔
1197
    bool mpm_before_progress = false;   // is mpm engine before progress?
188,215✔
1198
    bool mpm_in_progress = false;       // is mpm engine in a buffer we will revisit?
188,215✔
1199

1200
    TRACE_SID_TXS(s->id, tx, "starting %s", direction ? "toclient" : "toserver");
188,215✔
1201

1202
    /* for a new inspection we inspect pkt header and packet matches */
1203
    if (likely(stored_flags == NULL)) {
188,215✔
1204
        TRACE_SID_TXS(s->id, tx, "first inspect, run packet matches");
188,012✔
1205
        if (!DetectRunInspectRuleHeader(p, f, s, s->flags, s->proto.flags)) {
188,012✔
1206
            TRACE_SID_TXS(s->id, tx, "DetectRunInspectRuleHeader() no match");
12,599✔
1207
            return false;
12,599✔
1208
        }
12,599✔
1209
        if (!DetectEnginePktInspectionRun(tv, det_ctx, s, f, p, NULL)) {
175,413✔
1210
            TRACE_SID_TXS(s->id, tx, "DetectEnginePktInspectionRun no match");
269✔
1211
            return false;
269✔
1212
        }
269✔
1213
        /* stream mpm and negated mpm sigs can end up here with wrong proto */
1214
        if (!(AppProtoEquals(s->alproto, f->alproto) || s->alproto == ALPROTO_UNKNOWN)) {
175,144✔
1215
            TRACE_SID_TXS(s->id, tx, "alproto mismatch");
412✔
1216
            return false;
412✔
1217
        }
412✔
1218
    }
175,144✔
1219

1220
    const DetectEngineAppInspectionEngine *engine = s->app_inspect;
174,935✔
1221
    do {
370,373✔
1222
        TRACE_SID_TXS(s->id, tx, "engine %p inspect_flags %x", engine, inspect_flags);
370,373✔
1223
        // also if it is not the same direction, but
1224
        // this is a transactional signature, and we are toclient
1225
        if (!(inspect_flags & BIT_U32(engine->id)) &&
370,373✔
1226
                (direction == engine->dir || ((s->flags & SIG_FLAG_TXBOTHDIR) && direction == 1))) {
370,373✔
1227

1228
            void *tx_ptr = DetectGetInnerTx(tx->tx_ptr, f->alproto, engine->alproto, flow_flags);
269,772✔
1229
            if (tx_ptr == NULL) {
269,772✔
1230
                if (engine->alproto != ALPROTO_UNKNOWN) {
86,604✔
1231
                    /* special case: file_data on 'alert tcp' will have engines
1232
                     * in the list that are not for us. */
1233
                    engine = engine->next;
86,604✔
1234
                    continue;
86,604✔
1235
                } else {
86,604✔
1236
                    tx_ptr = tx->tx_ptr;
×
1237
                }
×
1238
            }
86,604✔
1239

1240
            /* engines are sorted per progress, except that the one with
1241
             * mpm/prefilter enabled is first */
1242
            if (tx->tx_progress < engine->progress) {
183,168✔
1243
                SCLogDebug("tx progress %d < engine progress %d",
32✔
1244
                        tx->tx_progress, engine->progress);
32✔
1245
                break;
32✔
1246
            }
32✔
1247
            if (engine->mpm) {
183,136✔
1248
                if (tx->tx_progress > engine->progress) {
28,732✔
1249
                    TRACE_SID_TXS(s->id, tx,
24,176✔
1250
                            "engine->mpm: t->tx_progress %u > engine->progress %u, so set "
24,176✔
1251
                            "mpm_before_progress",
24,176✔
1252
                            tx->tx_progress, engine->progress);
24,176✔
1253
                    mpm_before_progress = true;
24,176✔
1254
                } else if (tx->tx_progress == engine->progress) {
24,176✔
1255
                    TRACE_SID_TXS(s->id, tx,
4,557✔
1256
                            "engine->mpm: t->tx_progress %u == engine->progress %u, so set "
4,557✔
1257
                            "mpm_in_progress",
4,557✔
1258
                            tx->tx_progress, engine->progress);
4,557✔
1259
                    mpm_in_progress = true;
4,557✔
1260
                }
4,557✔
1261
            }
28,732✔
1262

1263
            uint8_t engine_flags = flow_flags;
183,136✔
1264
            if (direction != engine->dir) {
183,136✔
1265
                engine_flags = flow_flags ^ (STREAM_TOCLIENT | STREAM_TOSERVER);
8✔
1266
            }
8✔
1267
            /* run callback: but bypass stream callback if we can */
1268
            uint8_t match;
183,136✔
1269
            if (unlikely(engine->stream && can->stream_stored)) {
183,136✔
1270
                match = can->stream_result;
×
1271
                TRACE_SID_TXS(s->id, tx, "stream skipped, stored result %d used instead", match);
×
1272
            } else if (engine->v2.Callback == NULL) {
183,136✔
1273
                /* TODO is this the cleanest way to support a non-app sig on a app hook? */
1274

1275
                if (tx->tx_progress > engine->progress) {
575✔
1276
                    mpm_before_progress = true; // TODO needs a new name now
87✔
1277
                }
87✔
1278

1279
                /* we don't have to store a "hook" match, also don't want to keep any state to make
1280
                 * sure the hook gets invoked again until tx progress progresses. */
1281
                if (tx->tx_progress <= engine->progress)
575✔
1282
                    return DETECT_ENGINE_INSPECT_SIG_MATCH;
488✔
1283

1284
                /* if progress > engine progress, track state to avoid additional matches */
1285
                match = DETECT_ENGINE_INSPECT_SIG_MATCH;
87✔
1286
            } else {
182,561✔
1287
                KEYWORD_PROFILING_SET_LIST(det_ctx, engine->sm_list);
182,561✔
1288
                DEBUG_VALIDATE_BUG_ON(engine->v2.Callback == NULL);
182,561✔
1289
                match = engine->v2.Callback(
182,561✔
1290
                        de_ctx, det_ctx, engine, s, f, engine_flags, alstate, tx_ptr, tx->tx_id);
182,561✔
1291
                TRACE_SID_TXS(s->id, tx, "engine %p match %d", engine, match);
182,561✔
1292
                if (engine->stream) {
182,561✔
1293
                    can->stream_stored = true;
290✔
1294
                    can->stream_result = match;
290✔
1295
                    TRACE_SID_TXS(s->id, tx, "stream ran, store result %d for next tx (if any)", match);
290✔
1296
                }
290✔
1297
            }
182,561✔
1298
            if (match == DETECT_ENGINE_INSPECT_SIG_MATCH) {
182,648✔
1299
                inspect_flags |= BIT_U32(engine->id);
45,594✔
1300
                engine = engine->next;
45,594✔
1301
                total_matches++;
45,594✔
1302
                continue;
45,594✔
1303
            } else if (match == DETECT_ENGINE_INSPECT_SIG_MATCH_MORE_FILES) {
137,325✔
1304
                /* if the file engine matched, but indicated more
1305
                 * files are still in progress, we don't set inspect
1306
                 * flags as these would end inspection for this tx */
1307
                engine = engine->next;
×
1308
                total_matches++;
×
1309
                continue;
×
1310
            } else if (match == DETECT_ENGINE_INSPECT_SIG_CANT_MATCH) {
137,054✔
1311
                inspect_flags |= DE_STATE_FLAG_SIG_CANT_MATCH;
44,423✔
1312
                inspect_flags |= BIT_U32(engine->id);
44,423✔
1313
            } else if (match == DETECT_ENGINE_INSPECT_SIG_CANT_MATCH_FILES) {
114,066✔
1314
                inspect_flags |= DE_STATE_FLAG_SIG_CANT_MATCH;
12,476✔
1315
                inspect_flags |= BIT_U32(engine->id);
12,476✔
1316
                file_no_match = 1;
12,476✔
1317
            }
12,476✔
1318
            /* implied DETECT_ENGINE_INSPECT_SIG_NO_MATCH */
1319
            if (engine->mpm && mpm_before_progress) {
137,054✔
1320
                inspect_flags |= DE_STATE_FLAG_SIG_CANT_MATCH;
10,388✔
1321
                inspect_flags |= BIT_U32(engine->id);
10,388✔
1322
            }
10,388✔
1323
            break;
137,054✔
1324
        } else if (!(inspect_flags & BIT_U32(engine->id)) && s->flags & SIG_FLAG_TXBOTHDIR &&
182,648✔
1325
                   direction != engine->dir) {
100,601✔
1326
            // for transactional rules, the engines on the opposite direction
1327
            // are ordered by progress on the different side
1328
            // so we have a two mixed-up lists, and we skip the elements
1329
            if (direction == 0 && engine->next == NULL) {
48✔
1330
                // do not match yet on request only
1331
                break;
23✔
1332
            }
23✔
1333
            engine = engine->next;
25✔
1334
            continue;
25✔
1335
        }
48✔
1336

1337
        engine = engine->next;
100,553✔
1338
    } while (engine != NULL);
232,776✔
1339
    TRACE_SID_TXS(s->id, tx, "inspect_flags %x, total_matches %u, engine %p",
174,447✔
1340
            inspect_flags, total_matches, engine);
174,447✔
1341

1342
    if (engine == NULL && total_matches) {
174,447✔
1343
        inspect_flags |= DE_STATE_FLAG_FULL_INSPECT;
37,330✔
1344
        TRACE_SID_TXS(s->id, tx, "MATCH");
37,330✔
1345
        retval = true;
37,330✔
1346
    }
37,330✔
1347

1348
    if (stored_flags) {
174,447✔
1349
        *stored_flags = inspect_flags;
207✔
1350
        TRACE_SID_TXS(s->id, tx, "continue inspect flags %08x", inspect_flags);
207✔
1351
    } else {
174,240✔
1352
        // store... or? If tx is done we might not want to come back to this tx
1353

1354
        // also... if mpmid tracking is enabled, we won't do a sig again for this tx...
1355
        TRACE_SID_TXS(s->id, tx, "start inspect flags %08x", inspect_flags);
174,240✔
1356
        if (inspect_flags & DE_STATE_FLAG_SIG_CANT_MATCH) {
174,240✔
1357
            if (file_no_match) {
58,571✔
1358
                /* if we have a mismatch on a file sig, we need to keep state.
1359
                 * We may get another file on the same tx (for http and smtp
1360
                 * at least), so for a new file we need to re-eval the sig.
1361
                 * Thoughts / TODO:
1362
                 *  - not for some protos that have 1 file per tx (e.g. nfs)
1363
                 *  - maybe we only need this for file sigs that mix with
1364
                 *    other matches? E.g. 'POST + filename', is different than
1365
                 *    just 'filename'.
1366
                 */
1367
                DetectRunStoreStateTx(scratch->sgh, f, tx->tx_ptr, tx->tx_id, s,
12,475✔
1368
                        inspect_flags, flow_flags, file_no_match);
12,475✔
1369
            }
12,475✔
1370
        } else if ((inspect_flags & DE_STATE_FLAG_FULL_INSPECT) && mpm_before_progress) {
133,132✔
1371
            TRACE_SID_TXS(s->id, tx, "no need to store match sig, "
9,227✔
1372
                    "mpm won't trigger for it anymore");
9,227✔
1373

1374
            if (inspect_flags & DE_STATE_FLAG_FILE_INSPECT) {
9,227✔
1375
                TRACE_SID_TXS(s->id, tx, "except that for new files, "
101✔
1376
                        "we may have to revisit anyway");
101✔
1377
                DetectRunStoreStateTx(scratch->sgh, f, tx->tx_ptr, tx->tx_id, s,
101✔
1378
                        inspect_flags, flow_flags, file_no_match);
101✔
1379
            }
101✔
1380
        } else if ((inspect_flags & DE_STATE_FLAG_FULL_INSPECT) == 0 && mpm_in_progress) {
106,620✔
1381
            TRACE_SID_TXS(s->id, tx, "no need to store no-match sig, "
1,025✔
1382
                    "mpm will revisit it");
1,025✔
1383
        } else if (inspect_flags != 0 || file_no_match != 0) {
106,120✔
1384
            TRACE_SID_TXS(s->id, tx, "storing state: flags %08x", inspect_flags);
28,446✔
1385
            DetectRunStoreStateTx(scratch->sgh, f, tx->tx_ptr, tx->tx_id, s,
28,446✔
1386
                    inspect_flags, flow_flags, file_no_match);
28,446✔
1387
        }
28,446✔
1388
    }
174,240✔
1389

1390
    return retval;
174,447✔
1391
}
174,935✔
1392

1393
#define NO_TX                                                                                      \
1394
    {                                                                                              \
17,328,846✔
1395
        NULL, 0, NULL, NULL, 0, 0, 0, 0,                                                           \
17,328,846✔
1396
    }
17,328,846✔
1397

1398
/** \internal
1399
 *  \brief get a DetectTransaction object
1400
 *  \retval struct filled with relevant info or all nulls/0s
1401
 */
1402
static DetectTransaction GetDetectTx(const uint8_t ipproto, const AppProto alproto,
1403
        const uint64_t tx_id, void *tx_ptr, const int tx_end_state, const uint8_t flow_flags)
1404
{
17,814,859✔
1405
    AppLayerTxData *txd = AppLayerParserGetTxData(ipproto, alproto, tx_ptr);
17,814,859✔
1406
    const int tx_progress = AppLayerParserGetStateProgress(ipproto, alproto, tx_ptr, flow_flags);
17,814,859✔
1407
    bool updated = (flow_flags & STREAM_TOSERVER) ? txd->updated_ts : txd->updated_tc;
17,814,859✔
1408
    if (!updated && tx_progress < tx_end_state && ((flow_flags & STREAM_EOF) == 0)) {
17,814,859✔
1409
        DetectTransaction no_tx = NO_TX;
10,433,934✔
1410
        return no_tx;
10,433,934✔
1411
    }
10,433,934✔
1412
    const uint8_t inspected_flag =
7,380,925✔
1413
            (flow_flags & STREAM_TOSERVER) ? APP_LAYER_TX_INSPECTED_TS : APP_LAYER_TX_INSPECTED_TC;
7,380,925✔
1414
    if (unlikely(txd->flags & inspected_flag)) {
7,380,925✔
1415
        SCLogDebug("%" PRIu64 " tx already fully inspected for %s. Flags %02x", tx_id,
6,892,454✔
1416
                flow_flags & STREAM_TOSERVER ? "toserver" : "toclient", txd->flags);
6,892,454✔
1417
        DetectTransaction no_tx = NO_TX;
6,892,454✔
1418
        return no_tx;
6,892,454✔
1419
    }
6,892,454✔
1420
    const uint8_t skip_flag = (flow_flags & STREAM_TOSERVER) ? APP_LAYER_TX_SKIP_INSPECT_TS
488,471✔
1421
                                                             : APP_LAYER_TX_SKIP_INSPECT_TC;
488,471✔
1422
    if (unlikely(txd->flags & skip_flag)) {
488,471✔
1423
        SCLogDebug("%" PRIu64 " tx should not be inspected in direction %s. Flags %02x", tx_id,
2,458✔
1424
                flow_flags & STREAM_TOSERVER ? "toserver" : "toclient", txd->flags);
2,458✔
1425
        DetectTransaction no_tx = NO_TX;
2,458✔
1426
        return no_tx;
2,458✔
1427
    }
2,458✔
1428

1429
    const uint8_t detect_progress =
486,013✔
1430
            (flow_flags & STREAM_TOSERVER) ? txd->detect_progress_ts : txd->detect_progress_tc;
486,013✔
1431

1432
    const int dir_int = (flow_flags & STREAM_TOSERVER) ? 0 : 1;
486,013✔
1433
    DetectEngineState *tx_de_state = txd->de_state;
486,013✔
1434
    DetectEngineStateDirection *tx_dir_state =
486,013✔
1435
            tx_de_state ? &tx_de_state->dir_state[dir_int] : NULL;
486,013✔
1436
    DetectTransaction tx = {
486,013✔
1437
        .tx_ptr = tx_ptr,
486,013✔
1438
        .tx_id = tx_id,
486,013✔
1439
        .tx_data_ptr = (struct AppLayerTxData *)txd,
486,013✔
1440
        .de_state = tx_dir_state,
486,013✔
1441
        .detect_progress = detect_progress,
486,013✔
1442
        .detect_progress_orig = detect_progress,
486,013✔
1443
        .tx_progress = tx_progress,
486,013✔
1444
        .tx_end_state = tx_end_state,
486,013✔
1445
    };
486,013✔
1446
    return tx;
486,013✔
1447
}
488,471✔
1448

1449
static inline void StoreDetectProgress(
1450
        DetectTransaction *tx, const uint8_t flow_flags, const uint8_t progress)
1451
{
175,022✔
1452
    AppLayerTxData *txd = (AppLayerTxData *)tx->tx_data_ptr;
175,022✔
1453
    if (flow_flags & STREAM_TOSERVER) {
175,022✔
1454
        txd->detect_progress_ts = progress;
90,118✔
1455
    } else {
96,463✔
1456
        txd->detect_progress_tc = progress;
84,904✔
1457
    }
84,904✔
1458
}
175,022✔
1459

1460
// Merge 'state' rules from the regular prefilter
1461
// updates array_idx on the way
1462
static inline void RuleMatchCandidateMergeStateRules(
1463
        DetectEngineThreadCtx *det_ctx, uint32_t *array_idx)
1464
{
475,431✔
1465
    // Now, we will merge 2 sorted lists :
1466
    // the one in det_ctx->tx_candidates
1467
    // and the one in det_ctx->match_array
1468
    // For match_array, we take only the relevant elements where s->app_inspect != NULL
1469

1470
    // Basically, we iterate at the same time over the 2 lists
1471
    // comparing and taking an element from either.
1472

1473
    // Trick is to do so in place in det_ctx->tx_candidates,
1474
    // so as to minimize the number of moves in det_ctx->tx_candidates.
1475
    // For this, the algorithm traverses the lists in reverse order.
1476
    // Otherwise, if the first element of match_array was to be put before
1477
    // all tx_candidates, we would need to shift all tx_candidates
1478

1479
    // Retain the number of elements sorted in tx_candidates before merge
1480
    uint32_t j = *array_idx;
475,431✔
1481
    // First loop only counting the number of elements to add
1482
    for (uint32_t i = 0; i < det_ctx->match_array_cnt; i++) {
953,094✔
1483
        const Signature *s = det_ctx->match_array[i];
477,663✔
1484
        if (s->app_inspect != NULL) {
477,663✔
1485
            (*array_idx)++;
36,634✔
1486
        }
36,634✔
1487
    }
477,663✔
1488
    // Future number of elements in tx_candidates after merge
1489
    uint32_t k = *array_idx;
475,431✔
1490

1491
    if (k == j) {
475,431✔
1492
        // no new element from match_array to merge in tx_candidates
1493
        return;
457,708✔
1494
    }
457,708✔
1495

1496
    // variable i is for all elements of match_array (even not relevant ones)
1497
    // variable j is for elements of tx_candidates before merge
1498
    // variable k is for elements of tx_candidates after merge
1499
    for (uint32_t i = det_ctx->match_array_cnt; i > 0;) {
59,206✔
1500
        const Signature *s = det_ctx->match_array[i - 1];
41,483✔
1501
        if (s->app_inspect == NULL) {
41,483✔
1502
            // no relevant element, get the next one from match_array
1503
            i--;
4,684✔
1504
            continue;
4,684✔
1505
        }
4,684✔
1506
        // we have one element from match_array to merge in tx_candidates
1507
        k--;
36,799✔
1508
        if (j > 0) {
36,799✔
1509
            // j > 0 means there is still at least one element in tx_candidates to merge
1510
            const RuleMatchCandidateTx *s0 = &det_ctx->tx_candidates[j - 1];
1,023✔
1511
            if (s->iid <= s0->id) {
1,023✔
1512
                // get next element from previous tx_candidates
1513
                j--;
165✔
1514
                // take the element from tx_candidates before merge
1515
                det_ctx->tx_candidates[k].s = det_ctx->tx_candidates[j].s;
165✔
1516
                det_ctx->tx_candidates[k].id = det_ctx->tx_candidates[j].id;
165✔
1517
                det_ctx->tx_candidates[k].flags = det_ctx->tx_candidates[j].flags;
165✔
1518
                det_ctx->tx_candidates[k].stream_reset = det_ctx->tx_candidates[j].stream_reset;
165✔
1519
                continue;
165✔
1520
            }
165✔
1521
        } // otherwise
1,023✔
1522
        // get next element from match_array
1523
        i--;
36,634✔
1524
        // take the element from match_array
1525
        det_ctx->tx_candidates[k].s = s;
36,634✔
1526
        det_ctx->tx_candidates[k].id = s->iid;
36,634✔
1527
        det_ctx->tx_candidates[k].flags = NULL;
36,634✔
1528
        det_ctx->tx_candidates[k].stream_reset = 0;
36,634✔
1529
    }
36,634✔
1530
    // Even if k > 0 or j > 0, the loop is over. (Note that j == k now)
1531
    // The remaining elements in tx_candidates up to k were already sorted
1532
    // and come before any other element later in the list
1533
}
17,723✔
1534

1535
/**
1536
 * \internal
1537
 * \brief Check and update firewall rules state.
1538
 *
1539
 * \param skip_fw_hook bool to indicate firewall rules skips
1540
 * For state `skip_before_progress` should be skipped.
1541
 *
1542
 * \param skip_before_progress progress value to skip rules before.
1543
 * Only used if `skip_fw_hook` is set.
1544
 *
1545
 * \param last_for_progress[out] set to true if this is the last rule for a progress value
1546
 *
1547
 * \param fw_next_progress_missing[out] set to true if the next fw rule does not target the next
1548
 * progress value, or there is no fw rule for that value.
1549
 *
1550
 * \retval 0 no action needed
1551
 * \retval 1 rest of rules shouldn't inspected
1552
 * \retval -1 skip this rule
1553
 */
1554
static int DetectRunTxCheckFirewallPolicy(DetectEngineThreadCtx *det_ctx, Packet *p, Flow *f,
1555
        DetectTransaction *tx, const Signature *s, const uint32_t can_idx, const uint32_t can_size,
1556
        bool *skip_fw_hook, const uint8_t skip_before_progress, bool *last_for_progress,
1557
        bool *fw_next_progress_missing)
1558
{
276✔
1559
    if (s->flags & SIG_FLAG_FIREWALL) {
276✔
1560
        /* check if the next sig is on the same progress hook. If not, we need to apply our
1561
         * default policy in case the current sig doesn't apply one. If the next sig has a
1562
         * progress beyond our progress + 1, it means the next progress has no rules and needs
1563
         * the default policy applied. But only after we evaluate the current rule first, as
1564
         * that may override it.
1565
         * TODO should we do this after dedup below? */
1566

1567
        if (can_idx + 1 < can_size) {
259✔
1568
            const Signature *next_s = det_ctx->tx_candidates[can_idx + 1].s;
169✔
1569
            SCLogDebug(
169✔
1570
                    "peek: peeking at sid %u / progress %u", next_s->id, next_s->app_progress_hook);
169✔
1571
            if (next_s->flags & SIG_FLAG_FIREWALL) {
169✔
1572
                if (s->app_progress_hook != next_s->app_progress_hook) {
158✔
1573
                    SCLogDebug("peek: next sid progress %u != current progress %u, so current "
145✔
1574
                               "is last for progress",
145✔
1575
                            next_s->app_progress_hook, s->app_progress_hook);
145✔
1576
                    *last_for_progress = true;
145✔
1577

1578
                    if (next_s->app_progress_hook - s->app_progress_hook > 1) {
145✔
1579
                        SCLogDebug("peek: missing progress, so we'll drop that unless we get a "
3✔
1580
                                   "sweeping accept first");
3✔
1581
                        *fw_next_progress_missing = true;
3✔
1582
                    }
3✔
1583
                }
145✔
1584
            } else {
158✔
1585
                SCLogDebug("peek: next sid not a fw rule, so current is last for progress");
11✔
1586
                *last_for_progress = true;
11✔
1587
            }
11✔
1588
        } else {
169✔
1589
            SCLogDebug("peek: no peek beyond last rule");
90✔
1590
            if (s->app_progress_hook < tx->tx_progress) {
90✔
1591
                SCLogDebug("peek: there are no rules to allow the state after this rule");
17✔
1592
                *fw_next_progress_missing = true;
17✔
1593
            }
17✔
1594
        }
90✔
1595

1596
        if ((*skip_fw_hook) == true) {
259✔
1597
            if (s->app_progress_hook <= skip_before_progress) {
149✔
1598
                return -1;
8✔
1599
            }
8✔
1600
            *skip_fw_hook = false;
141✔
1601
        }
141✔
1602
    } else {
259✔
1603
        /* fw mode, we skip anything after the fw rules if:
1604
         * - flow pass is set
1605
         * - packet pass (e.g. exception policy) */
1606
        if (p->flags & PKT_NOPACKET_INSPECTION || (f->flags & (FLOW_ACTION_PASS))) {
17✔
1607
            SCLogDebug("skipping firewall rule %u", s->id);
×
1608
            return 1;
×
1609
        }
×
1610
    }
17✔
1611
    return 0;
268✔
1612
}
276✔
1613

1614
// TODO move into det_ctx?
1615
thread_local Signature default_accept;
1616
static inline void DetectRunAppendDefaultAccept(DetectEngineThreadCtx *det_ctx, Packet *p)
1617
{
2,621,005✔
1618
    if (EngineModeIsFirewall()) {
2,621,005✔
1619
        memset(&default_accept, 0, sizeof(default_accept));
1,132✔
1620
        default_accept.action = ACTION_ACCEPT;
1,132✔
1621
        default_accept.action_scope = ACTION_SCOPE_PACKET;
1,132✔
1622
        default_accept.iid = UINT32_MAX;
1,132✔
1623
        default_accept.type = SIG_TYPE_PKT;
1,132✔
1624
        default_accept.flags = SIG_FLAG_FIREWALL;
1,132✔
1625
        AlertQueueAppend(det_ctx, &default_accept, p, 0, PACKET_ALERT_FLAG_APPLY_ACTION_TO_PACKET);
1,132✔
1626
    }
1,132✔
1627
}
2,621,005✔
1628

1629
/** \internal
1630
 * \brief see if the accept rule needs to apply to the packet
1631
 */
1632
static inline bool ApplyAcceptToPacket(
1633
        const uint64_t total_txs, const DetectTransaction *tx, const Signature *s)
1634
{
37,822✔
1635
    if ((s->flags & SIG_FLAG_FIREWALL) == 0) {
37,822✔
1636
        return false;
37,571✔
1637
    }
37,571✔
1638
    if ((s->action & ACTION_ACCEPT) == 0) {
251✔
1639
        return false;
3✔
1640
    }
3✔
1641

1642
    /* for accept:tx we need:
1643
     * - packet will only be accepted if this is set on the last tx
1644
     */
1645
    if (s->action_scope == ACTION_SCOPE_TX) {
248✔
1646
        if (total_txs == tx->tx_id + 1) {
18✔
1647
            return true;
18✔
1648
        }
18✔
1649
    }
18✔
1650
    /* for accept:hook we need a bit more checking:
1651
     * - packet will only be accepted if this is set on the last tx
1652
     * - the hook accepted should be the last progress available. */
1653
    if (s->action_scope == ACTION_SCOPE_HOOK) {
230✔
1654
        if ((total_txs == tx->tx_id + 1) && /* last tx */
217✔
1655
                (s->app_progress_hook == tx->tx_progress)) {
217✔
1656
            return true;
71✔
1657
        }
71✔
1658
    }
217✔
1659
    return false;
159✔
1660
}
230✔
1661

1662
/** \internal
1663
 * \retval bool true: break_out_of_app_filter, false: don't break out */
1664
static bool ApplyAccept(Packet *p, const uint8_t flow_flags, const Signature *s,
1665
        DetectTransaction *tx, const int tx_end_state, const bool fw_next_progress_missing,
1666
        bool *tx_fw_verdict, bool *skip_fw_hook, uint8_t *skip_before_progress)
1667
{
248✔
1668
    *tx_fw_verdict = true;
248✔
1669

1670
    const enum ActionScope as = s->action_scope;
248✔
1671
    /* accept:hook: jump to first rule of next state.
1672
     * Implemented as skip until the first rule of next state. */
1673
    if (as == ACTION_SCOPE_HOOK) {
248✔
1674
        *skip_fw_hook = true;
217✔
1675
        *skip_before_progress = s->app_progress_hook;
217✔
1676

1677
        /* if there is no fw rule for the next progress value,
1678
         * we invoke the default drop policy. */
1679
        if (fw_next_progress_missing) {
217✔
1680
            SCLogDebug("%" PRIu64 ": %s default drop for progress", PcapPacketCntGet(p),
5✔
1681
                    flow_flags & STREAM_TOSERVER ? "toserver" : "toclient");
5✔
1682
            PacketDrop(p, ACTION_DROP, PKT_DROP_REASON_DEFAULT_APP_POLICY);
5✔
1683
            p->flow->flags |= FLOW_ACTION_DROP;
5✔
1684
            return true;
5✔
1685
        }
5✔
1686
        return false;
212✔
1687
    } else if (as == ACTION_SCOPE_TX) {
217✔
1688
        tx->tx_data_ptr->flags |= APP_LAYER_TX_ACCEPT;
18✔
1689
        *skip_fw_hook = true;
18✔
1690
        *skip_before_progress = (uint8_t)tx_end_state + 1; // skip all hooks
18✔
1691
        SCLogDebug(
18✔
1692
                "accept:tx applied, skip_fw_hook, skip_before_progress %u", *skip_before_progress);
18✔
1693
        return false;
18✔
1694
    } else if (as == ACTION_SCOPE_PACKET) {
18✔
1695
        return true;
4✔
1696
    } else if (as == ACTION_SCOPE_FLOW) {
9✔
1697
        return true;
9✔
1698
    }
9✔
1699
    return false;
×
1700
}
248✔
1701

1702
static void DetectRunTx(ThreadVars *tv,
1703
                    DetectEngineCtx *de_ctx,
1704
                    DetectEngineThreadCtx *det_ctx,
1705
                    Packet *p,
1706
                    Flow *f,
1707
                    DetectRunScratchpad *scratch)
1708
{
709,553✔
1709
    const uint8_t flow_flags = scratch->flow_flags;
709,553✔
1710
    const SigGroupHead * const sgh = scratch->sgh;
709,553✔
1711
    void * const alstate = f->alstate;
709,553✔
1712
    const uint8_t ipproto = f->proto;
709,553✔
1713
    const AppProto alproto = f->alproto;
709,553✔
1714

1715
    const uint64_t total_txs = AppLayerParserGetTxCnt(f, alstate);
709,553✔
1716
    uint64_t tx_id_min = AppLayerParserGetTransactionInspectId(f->alparser, flow_flags);
709,553✔
1717
    const int tx_end_state = AppLayerParserGetStateProgressCompletionStatus(alproto, flow_flags);
709,553✔
1718

1719
    AppLayerGetTxIteratorFunc IterFunc = AppLayerGetTxIterator(ipproto, alproto);
709,553✔
1720
    AppLayerGetTxIterState state = { 0 };
709,553✔
1721

1722
    uint32_t fw_verdicted = 0;
709,553✔
1723
    uint32_t tx_inspected = 0;
709,553✔
1724
    const bool have_fw_rules = EngineModeIsFirewall();
709,553✔
1725

1726
    SCLogDebug("packet %" PRIu64, PcapPacketCntGet(p));
709,553✔
1727

1728
    while (1) {
18,103,586✔
1729
        AppLayerGetTxIterTuple ires = IterFunc(ipproto, alproto, alstate, tx_id_min, total_txs, &state);
18,103,549✔
1730
        if (ires.tx_ptr == NULL)
18,103,549✔
1731
            break;
303,722✔
1732

1733
        DetectTransaction tx =
17,799,827✔
1734
                GetDetectTx(ipproto, alproto, ires.tx_id, ires.tx_ptr, tx_end_state, flow_flags);
17,799,827✔
1735
        if (tx.tx_ptr == NULL) {
17,799,827✔
1736
            SCLogDebug("%p/%"PRIu64" no transaction to inspect",
17,326,181✔
1737
                    tx.tx_ptr, tx_id_min);
17,326,181✔
1738

1739
            tx_id_min++; // next (if any) run look for +1
17,326,181✔
1740
            goto next;
17,326,181✔
1741
        }
17,326,181✔
1742
        tx_id_min = tx.tx_id + 1; // next look for cur + 1
473,646✔
1743
        tx_inspected++;
473,646✔
1744

1745
        SCLogDebug("%p/%" PRIu64 " txd flags %02x", tx.tx_ptr, tx_id_min, tx.tx_data_ptr->flags);
473,646✔
1746

1747
        det_ctx->tx_id = tx.tx_id;
473,646✔
1748
        det_ctx->tx_id_set = true;
473,646✔
1749
        det_ctx->p = p;
473,646✔
1750

1751
        bool do_sort = false; // do we need to sort the tx candidate list?
473,646✔
1752
        uint32_t array_idx = 0;
473,646✔
1753
        uint32_t total_rules = det_ctx->match_array_cnt;
473,646✔
1754
        total_rules += (tx.de_state ? tx.de_state->cnt : 0);
473,646✔
1755

1756
        /* run prefilter engines and merge results into a candidates array */
1757
        if (sgh->tx_engines) {
474,464✔
1758
            PACKET_PROFILING_DETECT_START(p, PROF_DETECT_PF_TX);
382,422✔
1759
            DetectRunPrefilterTx(det_ctx, sgh, p, ipproto, flow_flags, alproto,
382,422✔
1760
                    alstate, &tx);
382,422✔
1761
            PACKET_PROFILING_DETECT_END(p, PROF_DETECT_PF_TX);
382,422✔
1762
            SCLogDebug("%p/%"PRIu64" rules added from prefilter: %u candidates",
382,422✔
1763
                    tx.tx_ptr, tx.tx_id, det_ctx->pmq.rule_id_array_cnt);
382,422✔
1764

1765
            total_rules += det_ctx->pmq.rule_id_array_cnt;
382,422✔
1766
            if (!(RuleMatchCandidateTxArrayHasSpace(det_ctx, total_rules))) {
382,422✔
1767
                RuleMatchCandidateTxArrayExpand(det_ctx, total_rules);
505✔
1768
            }
505✔
1769

1770
            for (uint32_t i = 0; i < det_ctx->pmq.rule_id_array_cnt; i++) {
543,915✔
1771
                const Signature *s = de_ctx->sig_array[det_ctx->pmq.rule_id_array[i]];
161,493✔
1772
                const SigIntId id = s->iid;
161,493✔
1773
                det_ctx->tx_candidates[array_idx].s = s;
161,493✔
1774
                det_ctx->tx_candidates[array_idx].id = id;
161,493✔
1775
                det_ctx->tx_candidates[array_idx].flags = NULL;
161,493✔
1776
                det_ctx->tx_candidates[array_idx].stream_reset = 0;
161,493✔
1777
                array_idx++;
161,493✔
1778
            }
161,493✔
1779
            PMQ_RESET(&det_ctx->pmq);
382,422✔
1780
        } else {
2,147,799,881✔
1781
            if (!(RuleMatchCandidateTxArrayHasSpace(det_ctx, total_rules))) {
2,147,575,689✔
1782
                RuleMatchCandidateTxArrayExpand(det_ctx, total_rules);
8✔
1783
            }
8✔
1784
        }
2,147,575,689✔
1785

1786
        /* merge 'state' rules from the regular prefilter */
1787
#ifdef PROFILING
1788
        uint32_t x = array_idx;
1789
#endif
1790
        RuleMatchCandidateMergeStateRules(det_ctx, &array_idx);
473,646✔
1791

1792
        /* merge stored state into results */
1793
        if (tx.de_state != NULL) {
473,646✔
1794
            const uint32_t old = array_idx;
24,545✔
1795

1796
            /* if tx.de_state->flags has 'new file' set and sig below has
1797
             * 'file inspected' flag, reset the file part of the state */
1798
            const bool have_new_file = (tx.de_state->flags & DETECT_ENGINE_STATE_FLAG_FILE_NEW);
24,545✔
1799
            if (have_new_file) {
24,545✔
1800
                SCLogDebug("%p/%"PRIu64" destate: need to consider new file",
152✔
1801
                        tx.tx_ptr, tx.tx_id);
152✔
1802
                tx.de_state->flags &= ~DETECT_ENGINE_STATE_FLAG_FILE_NEW;
152✔
1803
            }
152✔
1804

1805
            SigIntId state_cnt = 0;
24,545✔
1806
            DeStateStore *tx_store = tx.de_state->head;
24,545✔
1807
            for (; tx_store != NULL; tx_store = tx_store->next) {
35,067✔
1808
                SCLogDebug("tx_store %p", tx_store);
10,522✔
1809

1810
                SigIntId store_cnt = 0;
10,522✔
1811
                for (store_cnt = 0;
10,522✔
1812
                        store_cnt < DE_STATE_CHUNK_SIZE && state_cnt < tx.de_state->cnt;
22,764✔
1813
                        store_cnt++, state_cnt++)
12,242✔
1814
                {
12,242✔
1815
                    DeStateStoreItem *item = &tx_store->store[store_cnt];
12,242✔
1816
                    SCLogDebug("rule id %u, inspect_flags %u", item->sid, item->flags);
12,242✔
1817
                    if (have_new_file && (item->flags & DE_STATE_FLAG_FILE_INSPECT)) {
12,242✔
1818
                        /* remove part of the state. File inspect engine will now
1819
                         * be able to run again */
1820
                        item->flags &= ~(DE_STATE_FLAG_SIG_CANT_MATCH|DE_STATE_FLAG_FULL_INSPECT|DE_STATE_FLAG_FILE_INSPECT);
7✔
1821
                        SCLogDebug("rule id %u, post file reset inspect_flags %u", item->sid, item->flags);
7✔
1822
                    }
7✔
1823
                    det_ctx->tx_candidates[array_idx].s = de_ctx->sig_array[item->sid];
12,242✔
1824
                    det_ctx->tx_candidates[array_idx].id = item->sid;
12,242✔
1825
                    det_ctx->tx_candidates[array_idx].flags = &item->flags;
12,242✔
1826
                    det_ctx->tx_candidates[array_idx].stream_reset = 0;
12,242✔
1827
                    array_idx++;
12,242✔
1828
                }
12,242✔
1829
            }
10,522✔
1830
            do_sort |= (old && old != array_idx); // sort if continue list adds sids
24,545✔
1831
            SCLogDebug("%p/%" PRIu64 " rules added from 'continue' list: %u", tx.tx_ptr, tx.tx_id,
24,545✔
1832
                    array_idx - old);
24,545✔
1833
        }
24,545✔
1834
        if (do_sort) {
473,646✔
1835
            qsort(det_ctx->tx_candidates, array_idx, sizeof(RuleMatchCandidateTx),
8,516✔
1836
                    DetectRunTxSortHelper);
8,516✔
1837
        }
8,516✔
1838

1839
#ifdef PROFILING
1840
        if (array_idx >= de_ctx->profile_match_logging_threshold)
1841
            RulesDumpTxMatchArray(det_ctx, scratch->sgh, p, tx.tx_id, array_idx, x);
1842
#endif
1843

1844
#ifdef DEBUG
1845
        for (uint32_t i = 0; i < array_idx; i++) {
1846
            RuleMatchCandidateTx *can = &det_ctx->tx_candidates[i];
1847
            const Signature *s = det_ctx->tx_candidates[i].s;
1848
            SCLogDebug("%u: sid %u flags %p", i, s->id, can->flags);
1849
        }
1850
#endif
1851
        bool skip_fw_hook = false;
473,646✔
1852
        uint8_t skip_before_progress = 0;
473,646✔
1853
        bool fw_next_progress_missing = false;
473,646✔
1854

1855
        /* if there are no rules / rule candidates, make sure we don't
1856
         * invoke the default drop */
1857
        if (have_fw_rules && array_idx == 0 && (tx.tx_data_ptr->flags & APP_LAYER_TX_ACCEPT)) {
473,646✔
1858
            fw_verdicted++;
16✔
1859

1860
            /* current tx is the last we have, append a blank accept:packet */
1861
            if (total_txs == tx.tx_id + 1) {
16✔
1862
                DetectRunAppendDefaultAccept(det_ctx, p);
16✔
1863
                return;
16✔
1864
            }
16✔
1865
            goto next;
×
1866
        }
16✔
1867

1868
        bool tx_fw_verdict = false;
473,646✔
1869
        /* run rules: inspect the match candidates */
1870
        for (uint32_t i = 0; i < array_idx; i++) {
673,868✔
1871
            RuleMatchCandidateTx *can = &det_ctx->tx_candidates[i];
200,257✔
1872
            const Signature *s = det_ctx->tx_candidates[i].s;
200,257✔
1873
            uint32_t *inspect_flags = det_ctx->tx_candidates[i].flags;
200,257✔
1874
            bool break_out_of_app_filter = false;
200,257✔
1875

1876
            SCLogDebug("%" PRIu64 ": sid:%u: %s tx %u/%u/%u sig %u", PcapPacketCntGet(p), s->id,
200,257✔
1877
                    flow_flags & STREAM_TOSERVER ? "toserver" : "toclient", tx.tx_progress,
200,257✔
1878
                    tx.detect_progress, tx.detect_progress_orig, s->app_progress_hook);
200,257✔
1879

1880
            /* deduplicate: rules_array is sorted, but not deduplicated:
1881
             * both mpm and stored state could give us the same sid.
1882
             * As they are back to back in that case we can check for it
1883
             * here. We select the stored state one as that comes first
1884
             * in the array. */
1885
            while ((i + 1) < array_idx &&
210,376✔
1886
                    det_ctx->tx_candidates[i].s == det_ctx->tx_candidates[i + 1].s) {
210,376✔
1887
                SCLogDebug("%p/%" PRIu64 " inspecting SKIP NEXT: sid %u (%u), flags %08x",
10,119✔
1888
                        tx.tx_ptr, tx.tx_id, s->id, s->iid, inspect_flags ? *inspect_flags : 0);
10,119✔
1889
                i++;
10,119✔
1890
            }
10,119✔
1891

1892
            /* skip fw rules if we're in accept:tx mode */
1893
            if (have_fw_rules && (tx.tx_data_ptr->flags & APP_LAYER_TX_ACCEPT)) {
200,257✔
1894
                /* append a blank accept:packet action for the APP_LAYER_TX_ACCEPT,
1895
                 * if this is the last tx */
1896
                if (!tx_fw_verdict) {
22✔
1897
                    const bool accept_tx_applies_to_packet = total_txs == tx.tx_id + 1;
14✔
1898
                    if (accept_tx_applies_to_packet) {
14✔
1899
                        SCLogDebug("accept:(tx|hook): should be applied to the packet");
14✔
1900
                        DetectRunAppendDefaultAccept(det_ctx, p);
14✔
1901
                    }
14✔
1902
                }
14✔
1903
                tx_fw_verdict = true;
22✔
1904

1905
                if (s->flags & SIG_FLAG_FIREWALL) {
22✔
1906
                    SCLogDebug("APP_LAYER_TX_ACCEPT, so skip rule");
6✔
1907
                    continue;
6✔
1908
                }
6✔
1909

1910
                /* threat detect rules will be inspected */
1911
            }
22✔
1912

1913
            SCLogDebug("%p/%" PRIu64 " inspecting: sid %u (%u), flags %08x", tx.tx_ptr, tx.tx_id,
200,251✔
1914
                    s->id, s->iid, inspect_flags ? *inspect_flags : 0);
200,251✔
1915

1916
            if (inspect_flags) {
200,251✔
1917
                if (*inspect_flags & DE_STATE_FLAG_FULL_INSPECT) {
12,238✔
1918
                    SCLogDebug("%p/%" PRIu64
6,909✔
1919
                               " inspecting: sid %u (%u), flags %08x DE_STATE_FLAG_FULL_INSPECT",
6,909✔
1920
                            tx.tx_ptr, tx.tx_id, s->id, s->iid, *inspect_flags);
6,909✔
1921

1922
                    /* if we're still in the same progress state as an earlier full
1923
                     * match, we need to apply the same accept */
1924
                    if (have_fw_rules && (s->flags & SIG_FLAG_FIREWALL) &&
6,909✔
1925
                            (s->action & ACTION_ACCEPT) && s->app_progress_hook == tx.tx_progress) {
6,909✔
1926
                        const bool fw_accept_to_packet = ApplyAcceptToPacket(total_txs, &tx, s);
4✔
1927
                        break_out_of_app_filter = ApplyAccept(p, flow_flags, s, &tx, tx_end_state,
4✔
1928
                                fw_next_progress_missing, &tx_fw_verdict, &skip_fw_hook,
4✔
1929
                                &skip_before_progress);
4✔
1930
                        if (fw_accept_to_packet)
4✔
1931
                            DetectRunAppendDefaultAccept(det_ctx, p);
4✔
1932
                        if (break_out_of_app_filter)
4✔
1933
                            break;
×
1934
                    }
4✔
1935
                    continue;
6,909✔
1936
                }
6,909✔
1937
                if (*inspect_flags & DE_STATE_FLAG_SIG_CANT_MATCH) {
5,329✔
1938
                    SCLogDebug("%p/%" PRIu64
5,122✔
1939
                               " inspecting: sid %u (%u), flags %08x DE_STATE_FLAG_SIG_CANT_MATCH",
5,122✔
1940
                            tx.tx_ptr, tx.tx_id, s->id, s->iid, *inspect_flags);
5,122✔
1941
                    continue;
5,122✔
1942
                }
5,122✔
1943
            }
5,329✔
1944

1945
            if (inspect_flags) {
188,220✔
1946
                /* continue previous inspection */
1947
                SCLogDebug("%p/%" PRIu64 " Continuing sid %u", tx.tx_ptr, tx.tx_id, s->id);
207✔
1948
            } else {
188,013✔
1949
                /* start new inspection */
1950
                SCLogDebug("%p/%"PRIu64" Start sid %u", tx.tx_ptr, tx.tx_id, s->id);
188,013✔
1951
            }
188,013✔
1952

1953
            bool last_for_progress = false;
188,220✔
1954
            if (have_fw_rules) {
188,220✔
1955
                int fw_r = DetectRunTxCheckFirewallPolicy(det_ctx, p, f, &tx, s, i, array_idx,
276✔
1956
                        &skip_fw_hook, skip_before_progress, &last_for_progress,
276✔
1957
                        &fw_next_progress_missing);
276✔
1958
                if (fw_r == -1)
276✔
1959
                    continue;
8✔
1960
                if (fw_r == 1)
268✔
1961
                    break;
×
1962
            }
268✔
1963

1964
            /* call individual rule inspection */
1965
            RULE_PROFILING_START(p);
188,212✔
1966
            const int r = DetectRunTxInspectRule(tv, de_ctx, det_ctx, p, f, flow_flags,
188,212✔
1967
                    alstate, &tx, s, inspect_flags, can, scratch);
188,212✔
1968
            if (r == 1) {
188,212✔
1969
                /* match */
1970
                DetectRunPostMatch(tv, det_ctx, p, s);
37,818✔
1971

1972
                /* see if we need to apply tx/hook accept to the packet. This can be needed when
1973
                 * we've completed the inspection so far for an incomplete tx, and an accept:tx or
1974
                 * accept:hook is the last match.*/
1975
                const bool fw_accept_to_packet = ApplyAcceptToPacket(total_txs, &tx, s);
37,818✔
1976

1977
                uint8_t alert_flags = (PACKET_ALERT_FLAG_STATE_MATCH | PACKET_ALERT_FLAG_TX);
37,818✔
1978
                if (fw_accept_to_packet) {
37,818✔
1979
                    SCLogDebug("accept:(tx|hook): should be applied to the packet");
85✔
1980
                    alert_flags |= PACKET_ALERT_FLAG_APPLY_ACTION_TO_PACKET;
85✔
1981
                }
85✔
1982

1983
                SCLogDebug(
37,818✔
1984
                        "%p/%" PRIu64 " sig %u (%u) matched", tx.tx_ptr, tx.tx_id, s->id, s->iid);
37,818✔
1985
                AlertQueueAppend(det_ctx, s, p, tx.tx_id, alert_flags);
37,818✔
1986

1987
                if ((s->flags & SIG_FLAG_FIREWALL) && (s->action & ACTION_ACCEPT)) {
37,818✔
1988
                    break_out_of_app_filter = ApplyAccept(p, flow_flags, s, &tx, tx_end_state,
244✔
1989
                            fw_next_progress_missing, &tx_fw_verdict, &skip_fw_hook,
244✔
1990
                            &skip_before_progress);
244✔
1991
                }
244✔
1992
            } else if (last_for_progress) {
150,608✔
1993
                SCLogDebug("sid %u: not a match: %s rule, last_for_progress %s", s->id,
1✔
1994
                        (s->flags & SIG_FLAG_FIREWALL) ? "firewall" : "regular",
1✔
1995
                        BOOL2STR(last_for_progress));
1✔
1996
                if (s->flags & SIG_FLAG_FIREWALL) {
1✔
1997
                    SCLogDebug("%" PRIu64 ": %s default drop for progress", PcapPacketCntGet(p),
1✔
1998
                            flow_flags & STREAM_TOSERVER ? "toserver" : "toclient");
1✔
1999
                    /* if this rule was the last for our progress state, and it didn't match,
2000
                     * we have to invoke the default drop policy. */
2001
                    PacketDrop(p, ACTION_DROP, PKT_DROP_REASON_DEFAULT_APP_POLICY);
1✔
2002
                    p->flow->flags |= FLOW_ACTION_DROP;
1✔
2003
                    break_out_of_app_filter = true;
1✔
2004
                    tx_fw_verdict = true;
1✔
2005
                }
1✔
2006
            }
1✔
2007
            DetectVarProcessList(det_ctx, p->flow, p);
188,212✔
2008
            RULE_PROFILING_END(det_ctx, s, r, p);
188,212✔
2009

2010
            if (det_ctx->post_rule_work_queue.len > 0) {
188,212✔
2011
                SCLogDebug("%p/%" PRIu64 " post_rule_work_queue len %u", tx.tx_ptr, tx.tx_id,
19✔
2012
                        det_ctx->post_rule_work_queue.len);
19✔
2013
                /* run post match prefilter engines on work queue */
2014
                PrefilterPostRuleMatch(det_ctx, scratch->sgh, p, f);
19✔
2015

2016
                uint32_t prev_array_idx = array_idx;
19✔
2017
                for (uint32_t j = 0; j < det_ctx->pmq.rule_id_array_cnt; j++) {
43✔
2018
                    const Signature *ts = de_ctx->sig_array[det_ctx->pmq.rule_id_array[j]];
24✔
2019
                    if (ts->app_inspect != NULL) {
24✔
2020
                        const SigIntId id = ts->iid;
24✔
2021
                        det_ctx->tx_candidates[array_idx].s = ts;
24✔
2022
                        det_ctx->tx_candidates[array_idx].id = id;
24✔
2023
                        det_ctx->tx_candidates[array_idx].flags = NULL;
24✔
2024
                        det_ctx->tx_candidates[array_idx].stream_reset = 0;
24✔
2025
                        array_idx++;
24✔
2026

2027
                        SCLogDebug("%p/%" PRIu64 " rule %u (%u) added from 'post match' prefilter",
24✔
2028
                                tx.tx_ptr, tx.tx_id, ts->id, id);
24✔
2029
                    }
24✔
2030
                }
24✔
2031
                SCLogDebug("%p/%" PRIu64 " rules added from 'post match' prefilter: %u", tx.tx_ptr,
19✔
2032
                        tx.tx_id, array_idx - prev_array_idx);
19✔
2033
                if (prev_array_idx != array_idx) {
19✔
2034
                    /* sort, but only part of array we're still going to process */
2035
                    qsort(det_ctx->tx_candidates + i, array_idx - i, sizeof(RuleMatchCandidateTx),
19✔
2036
                            DetectRunTxSortHelper);
19✔
2037
                }
19✔
2038
                det_ctx->post_rule_work_queue.len = 0;
19✔
2039
                PMQ_RESET(&det_ctx->pmq);
19✔
2040
            }
19✔
2041

2042
            if (break_out_of_app_filter)
188,212✔
2043
                break;
19✔
2044
        }
188,212✔
2045
        if (tx_fw_verdict)
473,630✔
2046
            fw_verdicted++;
122✔
2047

2048
        det_ctx->tx_id = 0;
473,630✔
2049
        det_ctx->tx_id_set = false;
473,630✔
2050
        det_ctx->p = NULL;
473,630✔
2051

2052
        /* see if we have any updated state to store in the tx */
2053

2054
        /* this side of the tx is done */
2055
        if (tx.tx_progress >= tx.tx_end_state) {
473,630✔
2056
            SCLogDebug("%" PRIu64 ": %s tx done", PcapPacketCntGet(p),
281,134✔
2057
                    flow_flags & STREAM_TOSERVER ? "toserver" : "toclient");
281,134✔
2058
            const uint8_t inspected_flag = (flow_flags & STREAM_TOSERVER)
281,134✔
2059
                                                   ? APP_LAYER_TX_INSPECTED_TS
281,134✔
2060
                                                   : APP_LAYER_TX_INSPECTED_TC;
281,134✔
2061
            tx.tx_data_ptr->flags |= inspected_flag;
281,134✔
2062
            SCLogDebug("%p/%" PRIu64 " tx is done for direction %s. Progress %02x", tx.tx_ptr,
281,134✔
2063
                    tx.tx_id, flow_flags & STREAM_TOSERVER ? "toserver" : "toclient",
281,134✔
2064
                    tx.detect_progress);
281,134✔
2065
        }
281,134✔
2066

2067
        if (tx.detect_progress != tx.detect_progress_orig) {
473,630✔
2068
            SCLogDebug("%" PRIu64 ": %s tx state change %u -> %u", PcapPacketCntGet(p),
175,021✔
2069
                    flow_flags & STREAM_TOSERVER ? "toserver" : "toclient", tx.detect_progress_orig,
175,021✔
2070
                    tx.detect_progress);
175,021✔
2071
            SCLogDebug("%p/%" PRIu64 " Storing new progress %02x (was %02x)", tx.tx_ptr, tx.tx_id,
175,021✔
2072
                    tx.detect_progress, tx.detect_progress_orig);
175,021✔
2073

2074
            StoreDetectProgress(&tx, flow_flags, tx.detect_progress);
175,021✔
2075
        }
175,021✔
2076

2077
        InspectionBufferClean(det_ctx);
473,630✔
2078

2079
    next:
17,800,018✔
2080
        if (!ires.has_next)
17,800,018✔
2081
            break;
405,995✔
2082
    }
17,800,018✔
2083

2084
    /* apply default policy if there were txs to inspect, we have fw rules and non of the rules
2085
     * applied a policy. */
2086
    SCLogDebug("packet %" PRIu64 ": tx_inspected %u fw_verdicted %u", PcapPacketCntGet(p),
709,744✔
2087
            tx_inspected, fw_verdicted);
709,744✔
2088
    if (tx_inspected && have_fw_rules && tx_inspected != fw_verdicted) {
709,744✔
2089
        SCLogDebug("%" PRIu64 ": %s default drop", PcapPacketCntGet(p),
3✔
2090
                flow_flags & STREAM_TOSERVER ? "toserver" : "toclient");
3✔
2091
        PacketDrop(p, ACTION_DROP, PKT_DROP_REASON_DEFAULT_APP_POLICY);
3✔
2092
        p->flow->flags |= FLOW_ACTION_DROP;
3✔
2093
        return;
3✔
2094
    }
3✔
2095
    /* if all tables have been bypassed, we accept:packet */
2096
    if (tx_inspected == 0 && fw_verdicted == 0 && have_fw_rules) {
709,741✔
2097
        DetectRunAppendDefaultAccept(det_ctx, p);
5✔
2098
    }
5✔
2099
}
709,741✔
2100

2101
static void DetectRunFrames(ThreadVars *tv, DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx,
2102
        Packet *p, Flow *f, DetectRunScratchpad *scratch)
2103
{
1,911,420✔
2104
    const SigGroupHead *const sgh = scratch->sgh;
1,911,420✔
2105
    const AppProto alproto = f->alproto;
1,911,420✔
2106

2107
    /* for TCP, limit inspection to pseudo packets or real packet that did
2108
     * an app-layer update. */
2109
    if (p->proto == IPPROTO_TCP && !PKT_IS_PSEUDOPKT(p) &&
1,911,420✔
2110
            ((PKT_IS_TOSERVER(p) && (f->flags & FLOW_TS_APP_UPDATED) == 0) ||
1,911,420✔
2111
                    (PKT_IS_TOCLIENT(p) && (f->flags & FLOW_TC_APP_UPDATED) == 0))) {
1,839,703✔
2112
        SCLogDebug("pcap_cnt %" PRIu64 ": %s: skip frame inspection for TCP w/o APP UPDATE",
1,232,949✔
2113
                PcapPacketCntGet(p), PKT_IS_TOSERVER(p) ? "toserver" : "toclient");
1,232,949✔
2114
        return;
1,232,949✔
2115
    }
1,232,949✔
2116
    FramesContainer *frames_container = AppLayerFramesGetContainer(f);
678,471✔
2117
    if (frames_container == NULL) {
678,533✔
2118
        return;
246,688✔
2119
    }
246,688✔
2120
    Frames *frames;
2,147,915,492✔
2121
    if (PKT_IS_TOSERVER(p)) {
2,147,915,492✔
2122
        frames = &frames_container->toserver;
221,701✔
2123
    } else {
2,147,705,348✔
2124
        frames = &frames_container->toclient;
2,147,693,791✔
2125
    }
2,147,693,791✔
2126

2127
    for (uint32_t idx = 0; idx < frames->cnt; idx++) {
2,159,085,177✔
2128
        SCLogDebug("frame %u", idx);
11,169,685✔
2129
        Frame *frame = FrameGetByIndex(frames, idx);
11,169,685✔
2130
        if (frame == NULL) {
11,169,685✔
2131
            continue;
×
2132
        }
×
2133

2134
        det_ctx->frame_inspect_progress = 0;
11,169,685✔
2135
        uint32_t array_idx = 0;
11,169,685✔
2136
        uint32_t total_rules = det_ctx->match_array_cnt;
11,169,685✔
2137

2138
        /* run prefilter engines and merge results into a candidates array */
2139
        if (sgh->frame_engines) {
11,169,685✔
2140
            //            PACKET_PROFILING_DETECT_START(p, PROF_DETECT_PF_TX);
2141
            DetectRunPrefilterFrame(det_ctx, sgh, p, frames, frame, alproto);
838,991✔
2142
            //            PACKET_PROFILING_DETECT_END(p, PROF_DETECT_PF_TX);
2143
            SCLogDebug("%p/%" PRIi64 " rules added from prefilter: %u candidates", frame, frame->id,
838,991✔
2144
                    det_ctx->pmq.rule_id_array_cnt);
838,991✔
2145

2146
            total_rules += det_ctx->pmq.rule_id_array_cnt;
838,991✔
2147

2148
            if (!(RuleMatchCandidateTxArrayHasSpace(
838,991✔
2149
                        det_ctx, total_rules))) { // TODO is it safe to overload?
838,991✔
2150
                RuleMatchCandidateTxArrayExpand(det_ctx, total_rules);
9✔
2151
            }
9✔
2152

2153
            for (uint32_t i = 0; i < det_ctx->pmq.rule_id_array_cnt; i++) {
868,430✔
2154
                const Signature *s = de_ctx->sig_array[det_ctx->pmq.rule_id_array[i]];
29,439✔
2155
                const SigIntId id = s->iid;
29,439✔
2156
                det_ctx->tx_candidates[array_idx].s = s;
29,439✔
2157
                det_ctx->tx_candidates[array_idx].id = id;
29,439✔
2158
                det_ctx->tx_candidates[array_idx].flags = NULL;
29,439✔
2159
                det_ctx->tx_candidates[array_idx].stream_reset = 0;
29,439✔
2160
                array_idx++;
29,439✔
2161
            }
29,439✔
2162
            PMQ_RESET(&det_ctx->pmq);
838,991✔
2163
        }
838,991✔
2164
        /* merge 'state' rules from the regular prefilter */
2165
        uint32_t x = array_idx;
11,169,685✔
2166
        for (uint32_t i = 0; i < det_ctx->match_array_cnt; i++) {
12,660,370✔
2167
            const Signature *s = det_ctx->match_array[i];
1,490,685✔
2168
            if (s->frame_inspect != NULL) {
1,490,685✔
2169
                const SigIntId id = s->iid;
×
2170
                det_ctx->tx_candidates[array_idx].s = s;
×
2171
                det_ctx->tx_candidates[array_idx].id = id;
×
2172
                det_ctx->tx_candidates[array_idx].flags = NULL;
×
2173
                det_ctx->tx_candidates[array_idx].stream_reset = 0;
×
2174
                array_idx++;
×
2175

2176
                SCLogDebug("%p/%" PRIi64 " rule %u (%u) added from 'match' list", frame, frame->id,
×
2177
                        s->id, id);
×
2178
            }
×
2179
        }
1,490,685✔
2180
        SCLogDebug("%p/%" PRIi64 " rules added from 'match' list: %u", frame, frame->id,
11,169,685✔
2181
                array_idx - x);
11,169,685✔
2182
        (void)x;
11,169,685✔
2183

2184
        /* run rules: inspect the match candidates */
2185
        for (uint32_t i = 0; i < array_idx; i++) {
11,199,038✔
2186
            const Signature *s = det_ctx->tx_candidates[i].s;
29,353✔
2187

2188
            /* deduplicate: rules_array is sorted, but not deduplicated.
2189
             * As they are back to back in that case we can check for it
2190
             * here. We select the stored state one as that comes first
2191
             * in the array. */
2192
            while ((i + 1) < array_idx &&
29,439✔
2193
                    det_ctx->tx_candidates[i].s == det_ctx->tx_candidates[i + 1].s) {
29,439✔
2194
                i++;
86✔
2195
            }
86✔
2196
            SCLogDebug("%p/%" PRIi64 " inspecting: sid %u (%u)", frame, frame->id, s->id, s->iid);
29,353✔
2197

2198
            /* start new inspection */
2199
            SCLogDebug("%p/%" PRIi64 " Start sid %u", frame, frame->id, s->id);
29,353✔
2200

2201
            /* call individual rule inspection */
2202
            RULE_PROFILING_START(p);
29,353✔
2203
            bool r = DetectRunInspectRuleHeader(p, f, s, s->flags, s->proto.flags);
29,353✔
2204
            if (r) {
29,353✔
2205
                r = DetectRunFrameInspectRule(tv, det_ctx, s, f, p, frames, frame);
29,347✔
2206
                if (r) {
29,347✔
2207
                    /* match */
2208
                    DetectRunPostMatch(tv, det_ctx, p, s);
2,584✔
2209

2210
                    uint8_t alert_flags = (PACKET_ALERT_FLAG_STATE_MATCH | PACKET_ALERT_FLAG_FRAME);
2,584✔
2211
                    det_ctx->frame_id = frame->id;
2,584✔
2212
                    SCLogDebug(
2,584✔
2213
                            "%p/%" PRIi64 " sig %u (%u) matched", frame, frame->id, s->id, s->iid);
2,584✔
2214
                    if (frame->flags & FRAME_FLAG_TX_ID_SET) {
2,584✔
2215
                        alert_flags |= PACKET_ALERT_FLAG_TX;
698✔
2216
                    }
698✔
2217
                    AlertQueueAppend(det_ctx, s, p, frame->tx_id, alert_flags);
2,584✔
2218
                }
2,584✔
2219
            }
29,347✔
2220
            DetectVarProcessList(det_ctx, p->flow, p);
29,353✔
2221
            RULE_PROFILING_END(det_ctx, s, r, p);
29,353✔
2222
        }
29,353✔
2223

2224
        /* update Frame::inspect_progress here instead of in the code above. The reason is that a
2225
         * frame might be used more than once in buffers with transforms. */
2226
        if (frame->inspect_progress < det_ctx->frame_inspect_progress) {
11,169,685✔
2227
            frame->inspect_progress = det_ctx->frame_inspect_progress;
13,065✔
2228
            SCLogDebug("frame->inspect_progress: %" PRIu64 " -> updated", frame->inspect_progress);
13,065✔
2229
        } else {
11,159,552✔
2230
            SCLogDebug(
11,156,620✔
2231
                    "frame->inspect_progress: %" PRIu64 " -> not updated", frame->inspect_progress);
11,156,620✔
2232
        }
11,156,620✔
2233

2234
        SCLogDebug("%p/%" PRIi64 " rules inspected, running cleanup", frame, frame->id);
11,169,685✔
2235
        InspectionBufferClean(det_ctx);
11,169,685✔
2236
    }
11,169,685✔
2237
}
2,147,915,492✔
2238

2239
static DetectEngineThreadCtx *GetTenantById(HashTable *h, uint32_t id)
2240
{
×
2241
    /* technically we need to pass a DetectEngineThreadCtx struct with the
2242
     * tenant_id member. But as that member is the first in the struct, we
2243
     * can use the id directly. */
2244
    return HashTableLookup(h, &id, 0);
×
2245
}
×
2246

2247
static void DetectFlow(ThreadVars *tv,
2248
                       DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx,
2249
                       Packet *p)
2250
{
3,700,221✔
2251
    Flow *const f = p->flow;
3,700,221✔
2252

2253
    /* we check the flow drop here, and not the packet drop. This is
2254
     * to allow stream engine "invalid" drop packets to still be
2255
     * evaluated by the stream event rules. */
2256
    if (f->flags & FLOW_ACTION_DROP) {
3,700,221✔
2257
        DEBUG_VALIDATE_BUG_ON(!(PKT_IS_PSEUDOPKT(p)) && !PacketCheckAction(p, ACTION_DROP));
5,549✔
2258
        SCReturn;
5,549✔
2259
    }
5,549✔
2260

2261
    /* in firewall mode, we still need to run the fw rulesets even for exception policy pass */
2262
    bool skip = false;
3,700,221✔
2263
    if (EngineModeIsFirewall()) {
3,694,672✔
2264
        skip = (f->flags & (FLOW_ACTION_ACCEPT));
3,274✔
2265

2266
    } else {
3,691,398✔
2267
        skip = (p->flags & PKT_NOPACKET_INSPECTION || f->flags & (FLOW_ACTION_PASS));
3,691,583✔
2268
    }
3,691,398✔
2269
    if (skip) {
3,694,672✔
2270
        /* enforce prior accept:flow */
2271
        if (f->flags & FLOW_ACTION_ACCEPT) {
13,932✔
2272
            p->action |= ACTION_ACCEPT;
989✔
2273
        }
989✔
2274
        /* hack: if we are in pass the entire flow mode, we need to still
2275
         * update the inspect_id forward. So test for the condition here,
2276
         * and call the update code if necessary. */
2277
        const int pass = (f->flags & (FLOW_ACTION_PASS | FLOW_ACTION_ACCEPT));
13,932✔
2278
        if (pass) {
13,932✔
2279
            uint8_t flags = STREAM_FLAGS_FOR_PACKET(p);
13,929✔
2280
            flags = FlowGetDisruptionFlags(f, flags);
13,929✔
2281
            if (f->alstate) {
13,929✔
2282
                AppLayerParserSetTransactionInspectId(f, f->alparser, f->alstate, flags, true);
11,090✔
2283
            }
11,090✔
2284
        }
13,929✔
2285
        SCLogDebug("p->pcap %" PRIu64 ": no detection on packet, "
13,932✔
2286
                   "PKT_NOPACKET_INSPECTION is set",
13,932✔
2287
                PcapPacketCntGet(p));
13,932✔
2288
        return;
13,932✔
2289
    }
13,932✔
2290

2291
    /* see if the packet matches one or more of the sigs */
2292
    DetectRun(tv, de_ctx, det_ctx, p);
3,680,740✔
2293
}
3,680,740✔
2294

2295

2296
static void DetectNoFlow(ThreadVars *tv,
2297
                         DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx,
2298
                         Packet *p)
2299
{
120,302,710✔
2300
    /* No need to perform any detection on this packet, if the given flag is set.*/
2301
    if ((p->flags & PKT_NOPACKET_INSPECTION) || (PacketCheckAction(p, ACTION_DROP))) {
120,302,712✔
2302
        return;
21✔
2303
    }
21✔
2304

2305
    /* see if the packet matches one or more of the sigs */
2306
    DetectRun(tv, de_ctx, det_ctx, p);
120,302,689✔
2307
}
120,302,689✔
2308

2309
uint8_t DetectPreFlow(ThreadVars *tv, DetectEngineThreadCtx *det_ctx, Packet *p)
2310
{
26✔
2311
    const DetectEngineCtx *de_ctx = det_ctx->de_ctx;
26✔
2312
    const SigGroupHead *sgh = de_ctx->pre_flow_sgh;
26✔
2313

2314
    SCLogDebug("thread id: %u, packet %" PRIu64 ", sgh %p", tv->id, PcapPacketCntGet(p), sgh);
26✔
2315
    DetectRunPacketHook(tv, de_ctx, det_ctx, sgh, p);
26✔
2316
    return p->action;
26✔
2317
}
26✔
2318

2319
uint8_t DetectPreStream(ThreadVars *tv, DetectEngineThreadCtx *det_ctx, Packet *p)
2320
{
26✔
2321
    const DetectEngineCtx *de_ctx = det_ctx->de_ctx;
26✔
2322
    const int direction = (PKT_IS_TOCLIENT(p) != 0);
26✔
2323
    const SigGroupHead *sgh = de_ctx->pre_stream_sgh[direction];
26✔
2324

2325
    SCLogDebug("thread id: %u, packet %" PRIu64 ", sgh %p", tv->id, PcapPacketCntGet(p), sgh);
26✔
2326
    DetectRunPacketHook(tv, de_ctx, det_ctx, sgh, p);
26✔
2327
    return p->action;
26✔
2328
}
26✔
2329

2330
/** \brief Detection engine thread wrapper.
2331
 *  \param tv thread vars
2332
 *  \param p packet to inspect
2333
 *  \param data thread specific data
2334
 *  \param pq packet queue
2335
 *  \retval TM_ECODE_FAILED error
2336
 *  \retval TM_ECODE_OK ok
2337
 */
2338
TmEcode Detect(ThreadVars *tv, Packet *p, void *data)
2339
{
125,736,613✔
2340
    DEBUG_VALIDATE_PACKET(p);
125,736,613✔
2341

2342
    DetectEngineCtx *de_ctx = NULL;
125,736,613✔
2343
    DetectEngineThreadCtx *det_ctx = (DetectEngineThreadCtx *)data;
125,736,613✔
2344
    if (det_ctx == NULL) {
125,736,613✔
2345
        printf("ERROR: Detect has no thread ctx\n");
×
2346
        goto error;
×
2347
    }
×
2348

2349
    if (unlikely(SC_ATOMIC_GET(det_ctx->so_far_used_by_detect) == 0)) {
125,736,613✔
2350
        (void)SC_ATOMIC_SET(det_ctx->so_far_used_by_detect, 1);
22,076✔
2351
        SCLogDebug("Detect Engine using new det_ctx - %p",
22,076✔
2352
                  det_ctx);
22,076✔
2353
    }
22,076✔
2354

2355
    /* if in MT mode _and_ we have tenants registered, use
2356
     * MT logic. */
2357
    if (det_ctx->mt_det_ctxs_cnt > 0 && det_ctx->TenantGetId != NULL)
125,736,613✔
2358
    {
150✔
2359
        uint32_t tenant_id = p->tenant_id;
150✔
2360
        if (tenant_id == 0)
150✔
2361
            tenant_id = det_ctx->TenantGetId(det_ctx, p);
150✔
2362
        if (tenant_id > 0 && tenant_id < det_ctx->mt_det_ctxs_cnt) {
150✔
2363
            p->tenant_id = tenant_id;
×
2364
            det_ctx = GetTenantById(det_ctx->mt_det_ctxs_hash, tenant_id);
×
2365
            if (det_ctx == NULL)
×
2366
                return TM_ECODE_OK;
×
2367
            de_ctx = det_ctx->de_ctx;
×
2368
            if (de_ctx == NULL)
×
2369
                return TM_ECODE_OK;
×
2370

2371
            if (unlikely(SC_ATOMIC_GET(det_ctx->so_far_used_by_detect) == 0)) {
×
2372
                (void)SC_ATOMIC_SET(det_ctx->so_far_used_by_detect, 1);
×
2373
                SCLogDebug("MT de_ctx %p det_ctx %p (tenant %u)", de_ctx, det_ctx, tenant_id);
×
2374
            }
×
2375
        } else {
150✔
2376
            /* use default if no tenants are registered for this packet */
2377
            de_ctx = det_ctx->de_ctx;
150✔
2378
        }
150✔
2379
    } else {
125,736,463✔
2380
        de_ctx = det_ctx->de_ctx;
125,736,463✔
2381
    }
125,736,463✔
2382

2383
    if (p->flow) {
125,736,613✔
2384
        DetectFlow(tv, de_ctx, det_ctx, p);
3,699,681✔
2385
    } else {
125,435,623✔
2386
        DetectNoFlow(tv, de_ctx, det_ctx, p);
122,036,932✔
2387
    }
122,036,932✔
2388

2389
#ifdef PROFILE_RULES
2390
    /* aggregate statistics */
2391
    struct timeval ts;
2392
    gettimeofday(&ts, NULL);
2393
    if (ts.tv_sec != det_ctx->rule_perf_last_sync) {
2394
        SCProfilingRuleThreatAggregate(det_ctx);
2395
        det_ctx->rule_perf_last_sync = ts.tv_sec;
2396
    }
2397
#endif
2398

2399
    return TM_ECODE_OK;
125,736,613✔
2400
error:
×
2401
    return TM_ECODE_FAILED;
×
2402
}
125,736,613✔
2403

2404
/** \brief disable file features we don't need
2405
 *  Called if we have no detection engine.
2406
 */
2407
void DisableDetectFlowFileFlags(Flow *f)
2408
{
4,542✔
2409
    DetectPostInspectFileFlagsUpdate(f, NULL /* no sgh */, STREAM_TOSERVER);
4,542✔
2410
    DetectPostInspectFileFlagsUpdate(f, NULL /* no sgh */, STREAM_TOCLIENT);
4,542✔
2411
}
4,542✔
2412

2413
#ifdef UNITTESTS
2414
/**
2415
 *  \brief wrapper for old tests
2416
 */
2417
void SigMatchSignatures(
2418
        ThreadVars *tv, DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx, Packet *p)
2419
{
1,335✔
2420
    if (p->flow) {
1,335✔
2421
        DetectFlow(tv, de_ctx, det_ctx, p);
841✔
2422
    } else {
841✔
2423
        DetectNoFlow(tv, de_ctx, det_ctx, p);
494✔
2424
    }
494✔
2425
}
1,335✔
2426
#endif
2427

2428
/*
2429
 * TESTS
2430
 */
2431

2432
#ifdef UNITTESTS
2433
#include "tests/detect.c"
2434
#endif
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc