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

OISF / suricata / 22627683315

03 Mar 2026 02:32PM UTC coverage: 73.705% (+31.4%) from 42.258%
22627683315

Pull #14935

github

web-flow
Merge 3144d6d12 into 8df4e1001
Pull Request #14935: output/flush: Correct EVE flushing logic

38356 of 77494 branches covered (49.5%)

Branch coverage included in aggregate %.

27 of 53 new or added lines in 3 files covered. (50.94%)

22 existing lines in 11 files now uncovered.

265676 of 335005 relevant lines covered (79.31%)

4948465.01 hits per line

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

90.05
/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
{
131,485,383✔
114
    SCEnter();
131,485,383✔
115
    SCLogDebug("PcapPacketCntGet(p) %" PRIu64 " direction %s pkt_src %s", PcapPacketCntGet(p),
131,485,383!
116
            p->flow ? (FlowGetPacketDirection(p->flow, p) == TOSERVER ? "toserver" : "toclient")
131,485,383✔
117
                    : "noflow",
131,485,383✔
118
            PktSrcToString(p->pkt_src));
131,485,383✔
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;
131,485,383✔
123

124
    DetectRunScratchpad scratch = DetectRunSetup(de_ctx, det_ctx, p, pflow, ACTION_DROP);
131,485,383✔
125

126
    /* run the IPonly engine */
127
    DetectRunInspectIPOnly(th_v, de_ctx, det_ctx, pflow, p);
131,485,383✔
128

129
    /* get our rule group */
130
    DetectRunGetRuleGroup(de_ctx, p, pflow, &scratch);
131,485,383✔
131
    /* if we didn't get a sig group head, we
132
     * have nothing to do.... */
133
    if (scratch.sgh == NULL) {
131,485,383✔
134
        SCLogDebug("no sgh for this packet, nothing to match against");
126,805,115!
135
        goto end;
126,805,115✔
136
    }
126,805,115✔
137

138
    /* run the prefilters for packets */
139
    DetectRunPrefilterPkt(th_v, de_ctx, det_ctx, p, &scratch);
4,680,268✔
140

141
    PACKET_PROFILING_DETECT_START(p, PROF_DETECT_RULES);
4,680,268✔
142
    /* inspect the rules against the packet */
143
    const uint8_t pkt_policy = DetectRulePacketRules(th_v, de_ctx, det_ctx, p, pflow, &scratch);
4,680,268✔
144
    PACKET_PROFILING_DETECT_END(p, PROF_DETECT_RULES);
4,680,268✔
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)) {
4,680,268✔
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)) {
4,679,219✔
159
        if (p->proto == IPPROTO_TCP) {
2,087,277✔
160
            if ((p->flags & PKT_STREAM_EST) == 0) {
2,029,185✔
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,015✔
166
            bool setting_nopayload = p->flow->alparser &&
2,021,015✔
167
                                     SCAppLayerParserStateIssetFlag(
2,021,035✔
168
                                             p->flow->alparser, APP_LAYER_PARSER_NO_INSPECTION) &&
2,021,011✔
169
                                     !(p->flags & PKT_NOPAYLOAD_INSPECTION);
2,021,015✔
170
            // we may be right after disabling app-layer (ssh)
171
            if (ssn &&
2,021,015✔
172
                    ((ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED) == 0 || setting_nopayload)) {
2,021,054✔
173
                // PACKET_PROFILING_DETECT_START(p, PROF_DETECT_TX);
174
                DetectRunFrames(th_v, de_ctx, det_ctx, p, pflow, &scratch);
1,853,219✔
175
                // PACKET_PROFILING_DETECT_END(p, PROF_DETECT_TX);
176
            }
1,853,219✔
177
            // no update to transactions
178
            if (!PKT_IS_PSEUDOPKT(p) && p->app_update_direction == 0 &&
2,021,015✔
179
                    ((PKT_IS_TOSERVER(p) && (p->flow->flags & FLOW_TS_APP_UPDATED) == 0) ||
2,021,015✔
180
                            (PKT_IS_TOCLIENT(p) && (p->flow->flags & FLOW_TC_APP_UPDATED) == 0))) {
1,579,240✔
181
                SCLogDebug("packet %" PRIu64 ": no app-layer update", PcapPacketCntGet(p));
1,370,168!
182
                DetectRunAppendDefaultAccept(det_ctx, p);
1,370,168✔
183
                goto end;
1,370,168✔
184
            }
1,370,168✔
185
        } else if (p->proto == IPPROTO_UDP) {
2,021,015✔
186
            DetectRunFrames(th_v, de_ctx, det_ctx, p, pflow, &scratch);
58,046✔
187
        }
58,046✔
188

189
        PACKET_PROFILING_DETECT_START(p, PROF_DETECT_TX);
708,939✔
190
        DetectRunTx(th_v, de_ctx, det_ctx, p, pflow, &scratch);
708,939✔
191
        PACKET_PROFILING_DETECT_END(p, PROF_DETECT_TX);
708,939✔
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);
708,939✔
194
        AppLayerParserSetTransactionInspectId(
708,939✔
195
                pflow, pflow->alparser, pflow->alstate, scratch.flow_flags, (scratch.sgh == NULL));
708,939✔
196
        PACKET_PROFILING_DETECT_END(p, PROF_DETECT_TX_UPDATE);
708,939✔
197
    } else {
2,883,566✔
198
        SCLogDebug("packet %" PRIu64 ": no flow / app-layer", PcapPacketCntGet(p));
2,591,942!
199
        DetectRunAppendDefaultAccept(det_ctx, p);
2,591,942✔
200
    }
2,591,942✔
201

202
end:
129,834,343✔
203
    DetectRunPostRules(th_v, de_ctx, det_ctx, p, pflow, &scratch);
129,834,343✔
204

205
    DetectRunCleanup(det_ctx, p, pflow);
129,834,343✔
206
    SCReturn;
129,834,343✔
207
}
4,679,219✔
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("PcapPacketCntGet(p) %" 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,014✔
256
    /* run the packet match functions */
257
    const SigMatchData *smd = s->sm_arrays[DETECT_SM_LIST_POSTMATCH];
1,822,014✔
258
    if (smd != NULL) {
1,822,014✔
259
        KEYWORD_PROFILING_SET_LIST(det_ctx, DETECT_SM_LIST_POSTMATCH);
11,064✔
260

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

263
        while (1) {
12,304!
264
            KEYWORD_PROFILING_START;
12,303✔
265
            (void)sigmatch_table[smd->type].Match(det_ctx, p, s, smd->ctx);
12,303✔
266
            KEYWORD_PROFILING_END(det_ctx, smd->type, 1);
12,303✔
267
            if (smd->is_last)
12,303✔
268
                break;
11,063✔
269
            smd++;
1,240✔
270
        }
1,240✔
271
    }
11,064✔
272
}
1,822,014✔
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
{
123,434,733✔
285
    SCEnter();
123,434,733✔
286
    SigGroupHead *sgh = NULL;
123,434,733✔
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) {
127,258,596✔
291
        SCReturnPtr(de_ctx->decoder_event_sgh, "SigGroupHead");
126,964,205✔
292
    } else if (p->proto == 0) {
2,147,796,189✔
293
        if (!(PacketIsIPv4(p) || PacketIsIPv6(p))) {
16,809!
294
            /* not IP, so nothing to do */
295
            SCReturnPtr(NULL, "SigGroupHead");
12,056✔
296
        }
12,056✔
297
    }
16,809✔
298

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

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

326
    SCReturnPtr(sgh, "SigGroupHead");
2,147,766,049✔
327
}
123,434,733✔
328

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

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

348
    det_ctx->match_array_cnt = (uint32_t)(match_array - det_ctx->match_array);
1,313,794✔
349
    DEBUG_VALIDATE_BUG_ON(det_ctx->pmq.rule_id_array_cnt < det_ctx->match_array_cnt);
1,313,794✔
350
    PMQ_RESET(&det_ctx->pmq);
1,313,794✔
351
}
1,313,794✔
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,871✔
361
    uint16_t flow_file_flags = FLOWFILE_INIT;
131,871✔
362

363
    if (sgh == NULL) {
131,871✔
364
        SCLogDebug("requesting disabling all file features for flow");
59,166!
365
        flow_file_flags = FLOWFILE_NONE;
59,166✔
366
    } else {
77,403✔
367
        if (sgh->filestore_cnt == 0) {
72,705✔
368
            SCLogDebug("requesting disabling filestore for flow");
66,101!
369
            flow_file_flags |= (FLOWFILE_NO_STORE_TS|FLOWFILE_NO_STORE_TC);
66,101✔
370
        }
66,101✔
371
#ifdef HAVE_MAGIC
72,705✔
372
        if (!(sgh->flags & SIG_GROUP_HEAD_HAVEFILEMAGIC)) {
72,707✔
373
            SCLogDebug("requesting disabling magic for flow");
69,092!
374
            flow_file_flags |= (FLOWFILE_NO_MAGIC_TS|FLOWFILE_NO_MAGIC_TC);
69,092✔
375
        }
69,092✔
376
#endif
72,705✔
377
        if (!(sgh->flags & SIG_GROUP_HEAD_HAVEFILEMD5)) {
72,706✔
378
            SCLogDebug("requesting disabling md5 for flow");
72,687!
379
            flow_file_flags |= (FLOWFILE_NO_MD5_TS|FLOWFILE_NO_MD5_TC);
72,687✔
380
        }
72,687✔
381
        if (!(sgh->flags & SIG_GROUP_HEAD_HAVEFILESHA1)) {
72,706!
382
            SCLogDebug("requesting disabling sha1 for flow");
72,704!
383
            flow_file_flags |= (FLOWFILE_NO_SHA1_TS|FLOWFILE_NO_SHA1_TC);
72,704✔
384
        }
72,704✔
385
        if (!(sgh->flags & SIG_GROUP_HEAD_HAVEFILESHA256)) {
72,706✔
386
            SCLogDebug("requesting disabling sha256 for flow");
72,691!
387
            flow_file_flags |= (FLOWFILE_NO_SHA256_TS|FLOWFILE_NO_SHA256_TC);
72,691✔
388
        }
72,691✔
389
    }
72,705✔
390
    if (flow_file_flags != 0) {
131,874✔
391
        FileUpdateFlowFileFlags(f, flow_file_flags, direction);
131,874✔
392
    }
131,874✔
393
}
131,871✔
394

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

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

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

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

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

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

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

438
    if (pflow) {
130,439,062✔
439
        bool use_flow_sgh = false;
3,682,667✔
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,682,667✔
443
            PACKET_PROFILING_DETECT_START(p, PROF_DETECT_GETSGH);
3,682,381✔
444
            if ((p->flowflags & FLOW_PKT_TOSERVER) && (pflow->flags & FLOW_SGH_TOSERVER)) {
3,682,381✔
445
                sgh = pflow->sgh_toserver;
2,266,851✔
446
                SCLogDebug("sgh = pflow->sgh_toserver; => %p", sgh);
2,266,851!
447
                use_flow_sgh = true;
2,266,851✔
448
            } else if ((p->flowflags & FLOW_PKT_TOCLIENT) && (pflow->flags & FLOW_SGH_TOCLIENT)) {
2,267,372✔
449
                sgh = pflow->sgh_toclient;
1,292,886✔
450
                SCLogDebug("sgh = pflow->sgh_toclient; => %p", sgh);
1,292,886!
451
                use_flow_sgh = true;
1,292,886✔
452
            }
1,292,886✔
453
            PACKET_PROFILING_DETECT_END(p, PROF_DETECT_GETSGH);
3,682,381✔
454
        }
3,682,381✔
455

456
        if (!(use_flow_sgh)) {
3,682,667✔
457
            PACKET_PROFILING_DETECT_START(p, PROF_DETECT_GETSGH);
123,059✔
458
            sgh = SigMatchSignaturesGetSgh(de_ctx, p);
123,059✔
459
            PACKET_PROFILING_DETECT_END(p, PROF_DETECT_GETSGH);
123,059✔
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,059✔
464
                ; /* no-op */
166✔
465
            } else {
122,893✔
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,893✔
470
            }
122,893✔
471
        }
123,059✔
472
    } else { /* p->flags & PKT_HAS_FLOW */
130,137,625✔
473
        /* no flow */
474

475
        PACKET_PROFILING_DETECT_START(p, PROF_DETECT_GETSGH);
126,756,395✔
476
        sgh = SigMatchSignaturesGetSgh(de_ctx, p);
126,756,395✔
477
        PACKET_PROFILING_DETECT_END(p, PROF_DETECT_GETSGH);
126,756,395✔
478
    }
126,756,395✔
479

480
    scratch->sgh = sgh;
130,439,062✔
481
}
130,439,062✔
482

483
static void DetectRunInspectIPOnly(ThreadVars *tv, const DetectEngineCtx *de_ctx,
484
        DetectEngineThreadCtx *det_ctx,
485
        Flow * const pflow, Packet * const p)
486
{
132,621,506✔
487
    if (pflow) {
132,621,506✔
488
        if (p->flowflags & (FLOW_PKT_TOSERVER_FIRST | FLOW_PKT_TOCLIENT_FIRST)) {
3,682,685✔
489
            SCLogDebug("testing against \"ip-only\" signatures");
119,880!
490

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

498
        /* Even without flow we should match the packet src/dst */
499
        PACKET_PROFILING_DETECT_START(p, PROF_DETECT_IPONLY);
128,938,821✔
500
        IPOnlyMatchPacket(tv, de_ctx, det_ctx, &de_ctx->io_ctx, p);
128,938,821✔
501
        PACKET_PROFILING_DETECT_END(p, PROF_DETECT_IPONLY);
128,938,821✔
502
    }
128,938,821✔
503
}
132,621,506✔
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,006,053✔
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,006,061✔
515
        DEBUG_VALIDATE_BUG_ON(f == NULL);
134,483✔
516

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

526
    if ((s_proto_flags & DETECT_PROTO_IPV4) && !PacketIsIPv4(p)) {
2,988,595!
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,988,505!
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,985,746✔
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,985,699✔
542
        if (!(sflags & SIG_FLAG_DP_ANY)) {
1,943,125✔
543
            if (p->flags & PKT_IS_FRAGMENT)
191,130!
544
                return false;
4✔
545
            const DetectPort *dport = DetectPortLookupGroup(s->dp, p->dp);
191,126✔
546
            if (dport == NULL) {
191,126✔
547
                SCLogDebug("dport didn't match.");
109,192!
548
                return false;
109,192✔
549
            }
109,192✔
550
        }
191,126✔
551
        if (!(sflags & SIG_FLAG_SP_ANY)) {
1,833,929✔
552
            if (p->flags & PKT_IS_FRAGMENT)
27,131!
553
                return false;
22✔
554
            const DetectPort *sport = DetectPortLookupGroup(s->sp, p->sp);
27,109✔
555
            if (sport == NULL) {
27,109✔
556
                SCLogDebug("sport didn't match.");
16,150!
557
                return false;
16,150✔
558
            }
16,150✔
559
        }
27,109✔
560
    } else if ((sflags & (SIG_FLAG_DP_ANY|SIG_FLAG_SP_ANY)) != (SIG_FLAG_DP_ANY|SIG_FLAG_SP_ANY)) {
2,172,917!
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,860,041✔
567
        if (PacketIsIPv4(p)) {
1,196,293✔
568
            if (DetectAddressMatchIPv4(s->addr_dst_match4, s->addr_dst_match4_cnt, &p->dst) == 0)
761,414✔
569
                return false;
18,049✔
570
        } else if (PacketIsIPv6(p)) {
808,054!
571
            if (DetectAddressMatchIPv6(s->addr_dst_match6, s->addr_dst_match6_cnt, &p->dst) == 0)
434,880!
572
                return false;
150,393✔
573
        }
434,880✔
574
    }
1,196,293✔
575
    /* check the source address */
576
    if (!(sflags & SIG_FLAG_SRC_ANY)) {
2,691,599✔
577
        if (PacketIsIPv4(p)) {
363,248!
578
            if (DetectAddressMatchIPv4(s->addr_src_match4, s->addr_src_match4_cnt, &p->src) == 0)
79,613✔
579
                return false;
59,036✔
580
        } else if (PacketIsIPv6(p)) {
289,354!
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,248✔
585

586
    return true;
2,349,048✔
587
}
2,691,599✔
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,330,923✔
595
    /* create our prefilter mask */
596
    PacketCreateMask(p, &p->sig_mask, scratch->alproto, scratch->app_decoder_events);
3,330,923✔
597
    /* run the prefilter engines */
598
    Prefilter(det_ctx, scratch->sgh, p, scratch->flow_flags, p->sig_mask);
3,330,923✔
599
    /* create match list if we have non-pf and/or pf */
600
    if (det_ctx->pmq.rule_id_array_cnt) {
3,330,923✔
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,313,799✔
608
        DetectPrefilterCopyDeDup(de_ctx, det_ctx);
1,313,799✔
609
        PACKET_PROFILING_DETECT_END(p, PROF_DETECT_PF_SORT2);
1,313,799✔
610
    }
1,313,799✔
611
}
3,330,923✔
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,330,737✔
658
    uint8_t action = 0;
3,330,737✔
659
    bool fw_verdict = false;
3,330,737✔
660
    const bool have_fw_rules = EngineModeIsFirewall();
3,330,737✔
661
    const Signature *next_s = NULL;
3,330,737✔
662

663
    /* inspect the sigs against the packet */
664
    /* Prefetch the next signature. */
665
    SigIntId match_cnt = det_ctx->match_array_cnt;
3,330,737✔
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,330,737✔
672

673
    SGH_PROFILING_RECORD(det_ctx, scratch->sgh);
3,330,737✔
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,330,737✔
680
    uint32_t sflags, next_sflags = 0;
3,330,737✔
681
    if (match_cnt) {
3,330,737✔
682
        next_s = *match_array++;
1,313,803✔
683
        next_sflags = next_s->flags;
1,313,803✔
684
    }
1,313,803✔
685
    while (match_cnt--) {
6,291,905✔
686
        RULE_PROFILING_START(p);
2,961,841✔
687
        bool break_out_of_packet_filter = false;
2,961,841✔
688
        uint8_t alert_flags = 0;
2,961,841✔
689
#ifdef PROFILE_RULES
690
        bool smatch = false; /* signature match */
691
#endif
692
        const Signature *s = next_s;
2,961,841✔
693
        sflags = next_sflags;
2,961,841✔
694
        if (match_cnt) {
2,961,841✔
695
            next_s = *match_array++;
1,648,357✔
696
            next_sflags = next_s->flags;
1,648,357✔
697
        }
1,648,357✔
698
        const uint8_t s_proto_flags = s->proto.flags;
2,961,841✔
699

700
        SCLogDebug("packet %" PRIu64 ": inspecting signature id %" PRIu32 "", PcapPacketCntGet(p),
2,961,841!
701
                s->id);
2,961,841✔
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,961,841✔
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,302✔
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,212✔
722
            goto next; // handle sig in DetectRunTx
130,535✔
723
        }
130,535✔
724
        if (s->frame_inspect != NULL) {
2,830,677!
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,830,677✔
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,534✔
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,828,855✔
740
            goto next;
34,306✔
741

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

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

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

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

763
        uint64_t txid = PACKET_ALERT_NOTX;
1,781,413✔
764
        if (pflow && pflow->alstate) {
1,781,413✔
765
            uint8_t dir = (p->flowflags & FLOW_PKT_TOCLIENT) ? STREAM_TOCLIENT : STREAM_TOSERVER;
1,000,876✔
766
            txid = AppLayerParserGetTransactionInspectId(pflow->alparser, dir);
1,000,876✔
767
            if ((s->alproto != ALPROTO_UNKNOWN && pflow->proto == IPPROTO_UDP) ||
1,000,876✔
768
                    (de_ctx->guess_applayer && IsOnlyTxInDirection(pflow, txid, dir))) {
1,000,876✔
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 =
892✔
773
                        AppLayerParserGetTx(pflow->proto, pflow->alproto, pflow->alstate, txid);
892✔
774
                AppLayerTxData *txd =
892✔
775
                        tx_ptr ? AppLayerParserGetTxData(pflow->proto, pflow->alproto, tx_ptr)
892!
776
                               : NULL;
892✔
777
                if (txd && txd->guessed_applayer_logged < de_ctx->guess_applayer_log_limit) {
892!
778
                    alert_flags |= PACKET_ALERT_FLAG_TX;
749✔
779
                    if (pflow->proto != IPPROTO_UDP) {
749✔
780
                        alert_flags |= PACKET_ALERT_FLAG_TX_GUESSED;
26✔
781
                    }
26✔
782
                    txd->guessed_applayer_logged++;
749✔
783
                }
749✔
784
            }
892✔
785
        }
1,000,876✔
786
        AlertQueueAppend(det_ctx, s, p, txid, alert_flags);
1,781,413✔
787

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

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

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

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

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

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

891
        /* fw accept:packet or accept:flow means we're done here */
892
        if (break_out_of_packet_filter)
2,961,738✔
893
            break;
570✔
894

895
        continue;
2,961,168✔
896
    }
2,961,738✔
897

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

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

928
    PACKET_PROFILING_DETECT_START(p, PROF_DETECT_SETUP);
128,186,444✔
929

930
#ifdef UNITTESTS
1,338✔
931
    if (RunmodeIsUnittests()) {
1,338✔
932
        p->alerts.cnt = 0;
1,313✔
933
        p->alerts.discarded = 0;
1,313✔
934
        p->alerts.suppressed = 0;
1,313✔
935
    }
1,313✔
936
#endif
1,338✔
937
    det_ctx->filestore_cnt = 0;
128,186,444✔
938
    det_ctx->base64_decoded_len = 0;
128,186,444✔
939
    det_ctx->raw_stream_progress = 0;
128,186,444✔
940
    det_ctx->match_array_cnt = 0;
128,186,444✔
941
    det_ctx->json_content_len = 0;
128,186,444✔
942

943
    det_ctx->alert_queue_size = 0;
128,186,444✔
944
    p->alerts.drop.action = 0;
128,186,444✔
945

946
#ifdef DEBUG
947
    if (p->flags & PKT_STREAM_ADD) {
948
        det_ctx->pkt_stream_add_cnt++;
949
    }
950
#endif
951

952
    /* grab the protocol state we will detect on */
953
    if (p->flags & PKT_HAS_FLOW) {
128,186,444✔
954
        DEBUG_VALIDATE_BUG_ON(pflow == NULL);
3,682,555✔
955

956
        if (p->flowflags & FLOW_PKT_TOSERVER) {
3,682,555✔
957
            flow_flags = STREAM_TOSERVER;
2,343,313✔
958
            SCLogDebug("flag STREAM_TOSERVER set");
2,343,313!
959
        } else if (p->flowflags & FLOW_PKT_TOCLIENT) {
2,343,313✔
960
            flow_flags = STREAM_TOCLIENT;
1,339,437✔
961
            SCLogDebug("flag STREAM_TOCLIENT set");
1,339,437!
962
        }
1,339,437✔
963
        SCLogDebug("p->flowflags 0x%02x", p->flowflags);
3,682,555!
964

965
        if (p->flags & PKT_PSEUDO_STREAM_END) {
3,682,555✔
966
            flow_flags |= STREAM_EOF;
16,317✔
967
            SCLogDebug("STREAM_EOF set");
16,317!
968
        }
16,317✔
969

970
        /* store tenant_id in the flow so that we can use it
971
         * for creating pseudo packets */
972
        if (p->tenant_id > 0 && pflow->tenant_id == 0) {
3,682,555!
973
            pflow->tenant_id = p->tenant_id;
×
974
        }
×
975

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

987
            pflow->de_ctx_version = de_ctx->version;
1,486✔
988
            SCGenericVarFree(pflow->flowvar);
1,486✔
989
            pflow->flowvar = NULL;
1,486✔
990

991
            DetectEngineStateResetTxs(pflow);
1,486✔
992
        }
1,486✔
993

994
        /* Retrieve the app layer state and protocol and the tcp reassembled
995
         * stream chunks. */
996
        if ((p->proto == IPPROTO_TCP && (p->flags & PKT_STREAM_EST)) ||
3,682,555✔
997
                (p->proto == IPPROTO_UDP) ||
3,682,555✔
998
                (p->proto == IPPROTO_SCTP && (p->flowflags & FLOW_PKT_ESTABLISHED)))
3,682,555!
999
        {
2,659,550✔
1000
            /* update flow flags with knowledge on disruptions */
1001
            flow_flags = FlowGetDisruptionFlags(pflow, flow_flags);
2,659,550✔
1002
            alproto = FlowGetAppProtocol(pflow);
2,659,550✔
1003
            if (p->proto == IPPROTO_TCP && pflow->protoctx &&
2,659,550✔
1004
                    StreamReassembleRawHasDataReady(pflow->protoctx, p)) {
2,659,550✔
1005
                p->flags |= PKT_DETECT_HAS_STREAMDATA;
244,998✔
1006
            }
244,998✔
1007
            SCLogDebug("alproto %u", alproto);
2,659,550!
1008
        } else {
2,660,183✔
1009
            SCLogDebug("packet doesn't have established flag set (proto %d)", p->proto);
1,023,005!
1010
        }
1,023,005✔
1011

1012
        app_decoder_events = AppLayerParserHasDecoderEvents(pflow->alparser);
3,682,555✔
1013
    }
3,682,555✔
1014

1015
    DetectRunScratchpad pad = { alproto, flow_flags, app_decoder_events, default_action, NULL };
128,186,444✔
1016
    PACKET_PROFILING_DETECT_END(p, PROF_DETECT_SETUP);
128,186,444✔
1017
    return pad;
128,186,444✔
1018
}
128,186,444✔
1019

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

1028
    PACKET_PROFILING_DETECT_START(p, PROF_DETECT_ALERT);
128,756,617✔
1029
    PacketAlertFinalize(de_ctx, det_ctx, p);
128,756,617✔
1030
    if (p->alerts.cnt > 0) {
128,756,617✔
1031
        StatsCounterAddI64(&tv->stats, det_ctx->counter_alerts, (uint64_t)p->alerts.cnt);
676,654✔
1032
    }
676,654✔
1033
    if (p->alerts.discarded > 0) {
128,756,617✔
1034
        StatsCounterAddI64(
32✔
1035
                &tv->stats, det_ctx->counter_alerts_overflow, (uint64_t)p->alerts.discarded);
32✔
1036
    }
32✔
1037
    if (p->alerts.suppressed > 0) {
128,756,617✔
1038
        StatsCounterAddI64(
14,434✔
1039
                &tv->stats, det_ctx->counter_alerts_suppressed, (uint64_t)p->alerts.suppressed);
14,434✔
1040
    }
14,434✔
1041
    PACKET_PROFILING_DETECT_END(p, PROF_DETECT_ALERT);
128,756,617✔
1042

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

1054
static void DetectRunCleanup(DetectEngineThreadCtx *det_ctx,
1055
        Packet *p, Flow * const pflow)
1056
{
127,786,975✔
1057
    PACKET_PROFILING_DETECT_START(p, PROF_DETECT_CLEANUP);
127,786,975✔
1058
    InspectionBufferClean(det_ctx);
127,786,975✔
1059

1060
    if (pflow != NULL) {
127,786,975✔
1061
        /* update inspected tracker for raw reassembly */
1062
        if (p->proto == IPPROTO_TCP && pflow->protoctx != NULL &&
3,682,770✔
1063
                (p->flags & PKT_DETECT_HAS_STREAMDATA)) {
3,682,770✔
1064
            StreamReassembleRawUpdateProgress(pflow->protoctx, p,
244,999✔
1065
                    det_ctx->raw_stream_progress);
244,999✔
1066
        }
244,999✔
1067
    }
3,682,770✔
1068
    PACKET_PROFILING_DETECT_END(p, PROF_DETECT_CLEANUP);
127,786,975✔
1069
    SCReturn;
127,786,975✔
1070
}
127,786,975✔
1071

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

1085
void RuleMatchCandidateTxArrayFree(DetectEngineThreadCtx *det_ctx)
1086
{
48,504✔
1087
    SCFree(det_ctx->tx_candidates);
48,504✔
1088
    det_ctx->tx_candidates_size = 0;
48,504✔
1089
}
48,504✔
1090

1091
/* if size >= cur_space */
1092
static inline bool RuleMatchCandidateTxArrayHasSpace(const DetectEngineThreadCtx *det_ctx,
1093
        const uint32_t need)
1094
{
1,315,230✔
1095
    if (det_ctx->tx_candidates_size >= need)
1,315,230✔
1096
        return 1;
1,314,720✔
1097
    return 0;
2,147,484,164✔
1098
}
1,315,230✔
1099

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

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

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

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

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

1203
    TRACE_SID_TXS(s->id, tx, "starting %s", direction ? "toclient" : "toserver");
187,466✔
1204

1205
    /* for a new inspection we inspect pkt header and packet matches */
1206
    if (likely(stored_flags == NULL)) {
187,466✔
1207
        TRACE_SID_TXS(s->id, tx, "first inspect, run packet matches");
187,265✔
1208
        if (!DetectRunInspectRuleHeader(p, f, s, s->flags, s->proto.flags)) {
187,265✔
1209
            TRACE_SID_TXS(s->id, tx, "DetectRunInspectRuleHeader() no match");
12,537✔
1210
            return false;
12,537✔
1211
        }
12,537✔
1212
        if (!DetectEnginePktInspectionRun(tv, det_ctx, s, f, p, NULL)) {
174,728✔
1213
            TRACE_SID_TXS(s->id, tx, "DetectEnginePktInspectionRun no match");
262✔
1214
            return false;
262✔
1215
        }
262✔
1216
        /* stream mpm and negated mpm sigs can end up here with wrong proto */
1217
        if (!(AppProtoEquals(s->alproto, f->alproto) || s->alproto == ALPROTO_UNKNOWN)) {
174,466✔
1218
            TRACE_SID_TXS(s->id, tx, "alproto mismatch");
414✔
1219
            return false;
414✔
1220
        }
414✔
1221
    }
174,466✔
1222

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

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

1243
            /* engines are sorted per progress, except that the one with
1244
             * mpm/prefilter enabled is first */
1245
            if (tx->tx_progress < engine->progress) {
182,469✔
1246
                SCLogDebug("tx progress %d < engine progress %d",
32!
1247
                        tx->tx_progress, engine->progress);
32✔
1248
                break;
32✔
1249
            }
32✔
1250
            if (engine->mpm) {
182,437✔
1251
                if (tx->tx_progress > engine->progress) {
28,684✔
1252
                    TRACE_SID_TXS(s->id, tx,
24,140✔
1253
                            "engine->mpm: t->tx_progress %u > engine->progress %u, so set "
24,140✔
1254
                            "mpm_before_progress",
24,140✔
1255
                            tx->tx_progress, engine->progress);
24,140✔
1256
                    mpm_before_progress = true;
24,140✔
1257
                } else if (tx->tx_progress == engine->progress) {
24,140!
1258
                    TRACE_SID_TXS(s->id, tx,
4,546✔
1259
                            "engine->mpm: t->tx_progress %u == engine->progress %u, so set "
4,546✔
1260
                            "mpm_in_progress",
4,546✔
1261
                            tx->tx_progress, engine->progress);
4,546✔
1262
                    mpm_in_progress = true;
4,546✔
1263
                }
4,546✔
1264
            }
28,684✔
1265

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

1278
                if (tx->tx_progress > engine->progress) {
1,270✔
1279
                    mpm_before_progress = true; // TODO needs a new name now
92✔
1280
                }
92✔
1281

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

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

1340
        engine = engine->next;
99,734✔
1341
    } while (engine != NULL);
231,310✔
1342
    TRACE_SID_TXS(s->id, tx, "inspect_flags %x, total_matches %u, engine %p",
173,075✔
1343
            inspect_flags, total_matches, engine);
173,075✔
1344

1345
    if (engine == NULL && total_matches) {
173,075!
1346
        inspect_flags |= DE_STATE_FLAG_FULL_INSPECT;
36,834✔
1347
        TRACE_SID_TXS(s->id, tx, "MATCH");
36,834✔
1348
        retval = true;
36,834✔
1349
    }
36,834✔
1350

1351
    if (stored_flags) {
173,075✔
1352
        *stored_flags = inspect_flags;
205✔
1353
        TRACE_SID_TXS(s->id, tx, "continue inspect flags %08x", inspect_flags);
205✔
1354
    } else {
172,870✔
1355
        // store... or? If tx is done we might not want to come back to this tx
1356

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

1377
            if (inspect_flags & DE_STATE_FLAG_FILE_INSPECT) {
9,206✔
1378
                TRACE_SID_TXS(s->id, tx, "except that for new files, "
102✔
1379
                        "we may have to revisit anyway");
102✔
1380
                DetectRunStoreStateTx(scratch->sgh, f, tx->tx_ptr, tx->tx_id, s,
102✔
1381
                        inspect_flags, flow_flags, file_no_match);
102✔
1382
            }
102✔
1383
        } else if ((inspect_flags & DE_STATE_FLAG_FULL_INSPECT) == 0 && mpm_in_progress) {
105,341✔
1384
            TRACE_SID_TXS(s->id, tx, "no need to store no-match sig, "
1,016✔
1385
                    "mpm will revisit it");
1,016✔
1386
        } else if (inspect_flags != 0 || file_no_match != 0) {
104,832!
1387
            TRACE_SID_TXS(s->id, tx, "storing state: flags %08x", inspect_flags);
27,971✔
1388
            DetectRunStoreStateTx(scratch->sgh, f, tx->tx_ptr, tx->tx_id, s,
27,971✔
1389
                    inspect_flags, flow_flags, file_no_match);
27,971✔
1390
        }
27,971✔
1391
    }
172,870✔
1392

1393
    return retval;
173,075✔
1394
}
174,253✔
1395

1396
#define NO_TX                                                                                      \
1397
    {                                                                                              \
17,348,757✔
1398
        NULL, 0, NULL, NULL, 0, 0, 0, 0,                                                           \
17,348,757✔
1399
    }
17,348,757✔
1400

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

1432
    const uint8_t detect_progress =
475,224✔
1433
            (flow_flags & STREAM_TOSERVER) ? txd->detect_progress_ts : txd->detect_progress_tc;
475,224✔
1434

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

1452
static inline void StoreDetectProgress(
1453
        DetectTransaction *tx, const uint8_t flow_flags, const uint8_t progress)
1454
{
174,535✔
1455
    AppLayerTxData *txd = (AppLayerTxData *)tx->tx_data_ptr;
174,535✔
1456
    if (flow_flags & STREAM_TOSERVER) {
174,535✔
1457
        txd->detect_progress_ts = progress;
89,858✔
1458
    } else {
96,196✔
1459
        txd->detect_progress_tc = progress;
84,677✔
1460
    }
84,677✔
1461
}
174,535✔
1462

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

1473
    // Basically, we iterate at the same time over the 2 lists
1474
    // comparing and taking an element from either.
1475

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

1482
    // Retain the number of elements sorted in tx_candidates before merge
1483
    uint32_t j = *array_idx;
475,402✔
1484
    // First loop only counting the number of elements to add
1485
    for (uint32_t i = 0; i < det_ctx->match_array_cnt; i++) {
952,998✔
1486
        const Signature *s = det_ctx->match_array[i];
477,596✔
1487
        if (s->app_inspect != NULL) {
477,596✔
1488
            (*array_idx)++;
36,616✔
1489
        }
36,616✔
1490
    }
477,596✔
1491
    // Future number of elements in tx_candidates after merge
1492
    uint32_t k = *array_idx;
475,402✔
1493

1494
    if (k == j) {
475,402✔
1495
        // no new element from match_array to merge in tx_candidates
1496
        return;
457,719✔
1497
    }
457,719✔
1498

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

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

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

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

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

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

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

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

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

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

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

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

1718
    const uint64_t total_txs = AppLayerParserGetTxCnt(f, alstate);
708,915✔
1719
    uint64_t tx_id_min = AppLayerParserGetTransactionInspectId(f->alparser, flow_flags);
708,915✔
1720
    const int tx_end_state = AppLayerParserGetStateProgressCompletionStatus(alproto, flow_flags);
708,915✔
1721

1722
    AppLayerGetTxIteratorFunc IterFunc = AppLayerGetTxIterator(ipproto, alproto);
708,915✔
1723
    AppLayerGetTxIterState state = { 0 };
708,915✔
1724

1725
    uint32_t fw_verdicted = 0;
708,915✔
1726
    uint32_t tx_inspected = 0;
708,915✔
1727
    const bool have_fw_rules = EngineModeIsFirewall();
708,915✔
1728

1729
    SCLogDebug("packet %" PRIu64, PcapPacketCntGet(p));
708,915!
1730

1731
    while (1) {
18,127,884!
1732
        AppLayerGetTxIterTuple ires = IterFunc(ipproto, alproto, alstate, tx_id_min, total_txs, &state);
18,127,867✔
1733
        if (ires.tx_ptr == NULL)
18,127,867✔
1734
            break;
303,838✔
1735

1736
        DetectTransaction tx =
17,824,029✔
1737
                GetDetectTx(ipproto, alproto, ires.tx_id, ires.tx_ptr, tx_end_state, flow_flags);
17,824,029✔
1738
        if (tx.tx_ptr == NULL) {
17,824,029✔
1739
            SCLogDebug("%p/%"PRIu64" no transaction to inspect",
17,348,754!
1740
                    tx.tx_ptr, tx_id_min);
17,348,754✔
1741

1742
            tx_id_min++; // next (if any) run look for +1
17,348,754✔
1743
            goto next;
17,348,754✔
1744
        }
17,348,754✔
1745
        tx_id_min = tx.tx_id + 1; // next look for cur + 1
475,275✔
1746
        tx_inspected++;
475,275✔
1747

1748
        SCLogDebug("%p/%" PRIu64 " txd flags %02x", tx.tx_ptr, tx_id_min, tx.tx_data_ptr->flags);
475,275!
1749

1750
        det_ctx->tx_id = tx.tx_id;
475,275✔
1751
        det_ctx->tx_id_set = true;
475,275✔
1752
        det_ctx->p = p;
475,275✔
1753

1754
        bool do_sort = false; // do we need to sort the tx candidate list?
475,275✔
1755
        uint32_t array_idx = 0;
475,275✔
1756
        uint32_t total_rules = det_ctx->match_array_cnt;
475,275✔
1757
        total_rules += (tx.de_state ? tx.de_state->cnt : 0);
475,275✔
1758

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

1768
            total_rules += det_ctx->pmq.rule_id_array_cnt;
382,419✔
1769
            if (!(RuleMatchCandidateTxArrayHasSpace(det_ctx, total_rules))) {
382,419✔
1770
                RuleMatchCandidateTxArrayExpand(det_ctx, total_rules);
503✔
1771
            }
503✔
1772

1773
            for (uint32_t i = 0; i < det_ctx->pmq.rule_id_array_cnt; i++) {
543,186✔
1774
                const Signature *s = de_ctx->sig_array[det_ctx->pmq.rule_id_array[i]];
160,767✔
1775
                const SigIntId id = s->iid;
160,767✔
1776
                det_ctx->tx_candidates[array_idx].s = s;
160,767✔
1777
                det_ctx->tx_candidates[array_idx].id = id;
160,767✔
1778
                det_ctx->tx_candidates[array_idx].flags = NULL;
160,767✔
1779
                det_ctx->tx_candidates[array_idx].stream_reset = 0;
160,767✔
1780
                array_idx++;
160,767✔
1781
            }
160,767✔
1782
            PMQ_RESET(&det_ctx->pmq);
382,419✔
1783
        } else {
382,419✔
1784
            if (!(RuleMatchCandidateTxArrayHasSpace(det_ctx, total_rules))) {
92,856✔
1785
                RuleMatchCandidateTxArrayExpand(det_ctx, total_rules);
8✔
1786
            }
8✔
1787
        }
92,856✔
1788

1789
        /* merge 'state' rules from the regular prefilter */
1790
#ifdef PROFILING
1791
        uint32_t x = array_idx;
1792
#endif
1793
        RuleMatchCandidateMergeStateRules(det_ctx, &array_idx);
475,275✔
1794

1795
        /* merge stored state into results */
1796
        if (tx.de_state != NULL) {
475,275✔
1797
            const uint32_t old = array_idx;
24,355✔
1798

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

1808
            SigIntId state_cnt = 0;
24,355✔
1809
            DeStateStore *tx_store = tx.de_state->head;
24,355✔
1810
            for (; tx_store != NULL; tx_store = tx_store->next) {
34,838✔
1811
                SCLogDebug("tx_store %p", tx_store);
10,483!
1812

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

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

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

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

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

1871
        bool tx_fw_verdict = false;
475,275✔
1872
        /* run rules: inspect the match candidates */
1873
        for (uint32_t i = 0; i < array_idx; i++) {
674,609✔
1874
            RuleMatchCandidateTx *can = &det_ctx->tx_candidates[i];
199,369✔
1875
            const Signature *s = det_ctx->tx_candidates[i].s;
199,369✔
1876
            uint32_t *inspect_flags = det_ctx->tx_candidates[i].flags;
199,369✔
1877
            bool break_out_of_app_filter = false;
199,369✔
1878

1879
            SCLogDebug("%" PRIu64 ": sid:%u: %s tx %u/%u/%u sig %u", PcapPacketCntGet(p), s->id,
199,369!
1880
                    flow_flags & STREAM_TOSERVER ? "toserver" : "toclient", tx.tx_progress,
199,369✔
1881
                    tx.detect_progress, tx.detect_progress_orig, s->app_progress_hook);
199,369✔
1882

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

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

1908
                if (s->flags & SIG_FLAG_FIREWALL) {
22✔
1909
                    SCLogDebug("APP_LAYER_TX_ACCEPT, so skip rule");
6!
1910
                    continue;
6✔
1911
                }
6✔
1912

1913
                /* threat detect rules will be inspected */
1914
            }
22✔
1915

1916
            SCLogDebug("%p/%" PRIu64 " inspecting: sid %u (%u), flags %08x", tx.tx_ptr, tx.tx_id,
199,363!
1917
                    s->id, s->iid, inspect_flags ? *inspect_flags : 0);
199,363✔
1918

1919
            if (inspect_flags) {
199,363✔
1920
                if (*inspect_flags & DE_STATE_FLAG_FULL_INSPECT) {
12,101✔
1921
                    SCLogDebug("%p/%" PRIu64
6,774!
1922
                               " inspecting: sid %u (%u), flags %08x DE_STATE_FLAG_FULL_INSPECT",
6,774✔
1923
                            tx.tx_ptr, tx.tx_id, s->id, s->iid, *inspect_flags);
6,774✔
1924

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

1948
            if (inspect_flags) {
187,467✔
1949
                /* continue previous inspection */
1950
                SCLogDebug("%p/%" PRIu64 " Continuing sid %u", tx.tx_ptr, tx.tx_id, s->id);
205!
1951
            } else {
187,262✔
1952
                /* start new inspection */
1953
                SCLogDebug("%p/%"PRIu64" Start sid %u", tx.tx_ptr, tx.tx_id, s->id);
187,262!
1954
            }
187,262✔
1955

1956
            bool last_for_progress = false;
187,467✔
1957
            if (have_fw_rules) {
187,467✔
1958
                int fw_r = DetectRunTxCheckFirewallPolicy(det_ctx, p, f, &tx, s, i, array_idx,
276✔
1959
                        &skip_fw_hook, skip_before_progress, &last_for_progress,
276✔
1960
                        &fw_next_progress_missing);
276✔
1961
                if (fw_r == -1)
276✔
1962
                    continue;
8✔
1963
                if (fw_r == 1)
268!
1964
                    break;
×
1965
            }
268✔
1966

1967
            /* call individual rule inspection */
1968
            RULE_PROFILING_START(p);
187,459✔
1969
            const int r = DetectRunTxInspectRule(tv, de_ctx, det_ctx, p, f, flow_flags,
187,459✔
1970
                    alstate, &tx, s, inspect_flags, can, scratch);
187,459✔
1971
            if (r == 1) {
187,459✔
1972
                /* match */
1973
                DetectRunPostMatch(tv, det_ctx, p, s);
38,013✔
1974

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

1980
                uint8_t alert_flags = (PACKET_ALERT_FLAG_STATE_MATCH | PACKET_ALERT_FLAG_TX);
38,013✔
1981
                if (fw_accept_to_packet) {
38,013✔
1982
                    SCLogDebug("accept:(tx|hook): should be applied to the packet");
85!
1983
                    alert_flags |= PACKET_ALERT_FLAG_APPLY_ACTION_TO_PACKET;
85✔
1984
                }
85✔
1985

1986
                SCLogDebug(
38,013!
1987
                        "%p/%" PRIu64 " sig %u (%u) matched", tx.tx_ptr, tx.tx_id, s->id, s->iid);
38,013✔
1988
                AlertQueueAppend(det_ctx, s, p, tx.tx_id, alert_flags);
38,013✔
1989

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

2013
            if (det_ctx->post_rule_work_queue.len > 0) {
187,459✔
2014
                SCLogDebug("%p/%" PRIu64 " post_rule_work_queue len %u", tx.tx_ptr, tx.tx_id,
19!
2015
                        det_ctx->post_rule_work_queue.len);
19✔
2016
                /* run post match prefilter engines on work queue */
2017
                PrefilterPostRuleMatch(det_ctx, scratch->sgh, p, f);
19✔
2018

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

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

2045
            if (break_out_of_app_filter)
187,459✔
2046
                break;
19✔
2047
        }
187,459✔
2048
        if (tx_fw_verdict)
475,259✔
2049
            fw_verdicted++;
122✔
2050

2051
        det_ctx->tx_id = 0;
475,259✔
2052
        det_ctx->tx_id_set = false;
475,259✔
2053
        det_ctx->p = NULL;
475,259✔
2054

2055
        /* see if we have any updated state to store in the tx */
2056

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

2070
        if (tx.detect_progress != tx.detect_progress_orig) {
475,259✔
2071
            SCLogDebug("%" PRIu64 ": %s tx state change %u -> %u", PcapPacketCntGet(p),
174,527!
2072
                    flow_flags & STREAM_TOSERVER ? "toserver" : "toclient", tx.detect_progress_orig,
174,527✔
2073
                    tx.detect_progress);
174,527✔
2074
            SCLogDebug("%p/%" PRIu64 " Storing new progress %02x (was %02x)", tx.tx_ptr, tx.tx_id,
174,527!
2075
                    tx.detect_progress, tx.detect_progress_orig);
174,527✔
2076

2077
            StoreDetectProgress(&tx, flow_flags, tx.detect_progress);
174,527✔
2078
        }
174,527✔
2079

2080
        InspectionBufferClean(det_ctx);
475,259✔
2081

2082
    next:
17,824,154✔
2083
        if (!ires.has_next)
17,824,154✔
2084
            break;
405,197✔
2085
    }
17,824,154✔
2086

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

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

2110
    /* for TCP, limit inspection to pseudo packets or real packet that did
2111
     * an app-layer update. */
2112
    if (p->proto == IPPROTO_TCP && !PKT_IS_PSEUDOPKT(p) &&
1,911,246✔
2113
            ((PKT_IS_TOSERVER(p) && (f->flags & FLOW_TS_APP_UPDATED) == 0) ||
1,911,246✔
2114
                    (PKT_IS_TOCLIENT(p) && (f->flags & FLOW_TC_APP_UPDATED) == 0))) {
1,839,543✔
2115
        SCLogDebug("pcap_cnt %" PRIu64 ": %s: skip frame inspection for TCP w/o APP UPDATE",
1,233,357!
2116
                PcapPacketCntGet(p), PKT_IS_TOSERVER(p) ? "toserver" : "toclient");
1,233,357✔
2117
        return;
1,233,357✔
2118
    }
1,233,357✔
2119
    FramesContainer *frames_container = AppLayerFramesGetContainer(f);
677,889✔
2120
    if (frames_container == NULL) {
677,928✔
2121
        return;
245,814✔
2122
    }
245,814✔
2123
    Frames *frames;
2,147,915,761✔
2124
    if (PKT_IS_TOSERVER(p)) {
2,147,915,761✔
2125
        frames = &frames_container->toserver;
221,457✔
2126
    } else {
2,147,705,104✔
2127
        frames = &frames_container->toclient;
2,147,694,304✔
2128
    }
2,147,694,304✔
2129

2130
    for (uint32_t idx = 0; idx < frames->cnt; idx++) {
2,159,086,574✔
2131
        SCLogDebug("frame %u", idx);
11,170,813!
2132
        Frame *frame = FrameGetByIndex(frames, idx);
11,170,813✔
2133
        if (frame == NULL) {
11,170,813!
2134
            continue;
×
2135
        }
×
2136

2137
        det_ctx->frame_inspect_progress = 0;
11,170,813✔
2138
        uint32_t array_idx = 0;
11,170,813✔
2139
        uint32_t total_rules = det_ctx->match_array_cnt;
11,170,813✔
2140

2141
        /* run prefilter engines and merge results into a candidates array */
2142
        if (sgh->frame_engines) {
11,170,813✔
2143
            //            PACKET_PROFILING_DETECT_START(p, PROF_DETECT_PF_TX);
2144
            DetectRunPrefilterFrame(det_ctx, sgh, p, frames, frame, alproto);
839,824✔
2145
            //            PACKET_PROFILING_DETECT_END(p, PROF_DETECT_PF_TX);
2146
            SCLogDebug("%p/%" PRIi64 " rules added from prefilter: %u candidates", frame, frame->id,
839,824!
2147
                    det_ctx->pmq.rule_id_array_cnt);
839,824✔
2148

2149
            total_rules += det_ctx->pmq.rule_id_array_cnt;
839,824✔
2150

2151
            if (!(RuleMatchCandidateTxArrayHasSpace(
839,824!
2152
                        det_ctx, total_rules))) { // TODO is it safe to overload?
839,824✔
2153
                RuleMatchCandidateTxArrayExpand(det_ctx, total_rules);
9✔
2154
            }
9✔
2155

2156
            for (uint32_t i = 0; i < det_ctx->pmq.rule_id_array_cnt; i++) {
869,541✔
2157
                const Signature *s = de_ctx->sig_array[det_ctx->pmq.rule_id_array[i]];
29,717✔
2158
                const SigIntId id = s->iid;
29,717✔
2159
                det_ctx->tx_candidates[array_idx].s = s;
29,717✔
2160
                det_ctx->tx_candidates[array_idx].id = id;
29,717✔
2161
                det_ctx->tx_candidates[array_idx].flags = NULL;
29,717✔
2162
                det_ctx->tx_candidates[array_idx].stream_reset = 0;
29,717✔
2163
                array_idx++;
29,717✔
2164
            }
29,717✔
2165
            PMQ_RESET(&det_ctx->pmq);
839,824✔
2166
        }
839,824✔
2167
        /* merge 'state' rules from the regular prefilter */
2168
        uint32_t x = array_idx;
11,170,813✔
2169
        for (uint32_t i = 0; i < det_ctx->match_array_cnt; i++) {
12,661,123✔
2170
            const Signature *s = det_ctx->match_array[i];
1,490,310✔
2171
            if (s->frame_inspect != NULL) {
1,490,310!
2172
                const SigIntId id = s->iid;
×
2173
                det_ctx->tx_candidates[array_idx].s = s;
×
2174
                det_ctx->tx_candidates[array_idx].id = id;
×
2175
                det_ctx->tx_candidates[array_idx].flags = NULL;
×
2176
                det_ctx->tx_candidates[array_idx].stream_reset = 0;
×
2177
                array_idx++;
×
2178

2179
                SCLogDebug("%p/%" PRIi64 " rule %u (%u) added from 'match' list", frame, frame->id,
×
2180
                        s->id, id);
×
2181
            }
×
2182
        }
1,490,310✔
2183
        SCLogDebug("%p/%" PRIi64 " rules added from 'match' list: %u", frame, frame->id,
11,170,813!
2184
                array_idx - x);
11,170,813✔
2185
        (void)x;
11,170,813✔
2186

2187
        /* run rules: inspect the match candidates */
2188
        for (uint32_t i = 0; i < array_idx; i++) {
11,200,444✔
2189
            const Signature *s = det_ctx->tx_candidates[i].s;
29,631✔
2190

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

2201
            /* start new inspection */
2202
            SCLogDebug("%p/%" PRIi64 " Start sid %u", frame, frame->id, s->id);
29,631!
2203

2204
            /* call individual rule inspection */
2205
            RULE_PROFILING_START(p);
29,631✔
2206
            bool r = DetectRunInspectRuleHeader(p, f, s, s->flags, s->proto.flags);
29,631✔
2207
            if (r) {
29,631✔
2208
                r = DetectRunFrameInspectRule(tv, det_ctx, s, f, p, frames, frame);
29,625✔
2209
                if (r) {
29,625✔
2210
                    /* match */
2211
                    DetectRunPostMatch(tv, det_ctx, p, s);
2,629✔
2212

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

2227
        /* update Frame::inspect_progress here instead of in the code above. The reason is that a
2228
         * frame might be used more than once in buffers with transforms. */
2229
        if (frame->inspect_progress < det_ctx->frame_inspect_progress) {
11,170,813✔
2230
            frame->inspect_progress = det_ctx->frame_inspect_progress;
13,111✔
2231
            SCLogDebug("frame->inspect_progress: %" PRIu64 " -> updated", frame->inspect_progress);
13,111!
2232
        } else {
11,160,634✔
2233
            SCLogDebug(
11,157,702!
2234
                    "frame->inspect_progress: %" PRIu64 " -> not updated", frame->inspect_progress);
11,157,702✔
2235
        }
11,157,702✔
2236

2237
        SCLogDebug("%p/%" PRIi64 " rules inspected, running cleanup", frame, frame->id);
11,170,813!
2238
        InspectionBufferClean(det_ctx);
11,170,813✔
2239
    }
11,170,813✔
2240
}
2,147,915,761✔
2241

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

2250
static void DetectFlow(ThreadVars *tv,
2251
                       DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx,
2252
                       Packet *p)
2253
{
3,702,134✔
2254
    Flow *const f = p->flow;
3,702,134✔
2255

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

2264
    /* in firewall mode, we still need to run the fw rulesets even for exception policy pass */
2265
    bool skip = false;
3,702,134✔
2266
    if (EngineModeIsFirewall()) {
3,696,585✔
2267
        skip = (f->flags & (FLOW_ACTION_ACCEPT));
3,274✔
2268

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

2294
    /* see if the packet matches one or more of the sigs */
2295
    DetectRun(tv, de_ctx, det_ctx, p);
3,682,653✔
2296
}
3,682,653✔
2297

2298

2299
static void DetectNoFlow(ThreadVars *tv,
2300
                         DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx,
2301
                         Packet *p)
2302
{
126,126,432✔
2303
    /* No need to perform any detection on this packet, if the given flag is set.*/
2304
    if ((p->flags & PKT_NOPACKET_INSPECTION) || (PacketCheckAction(p, ACTION_DROP))) {
126,703,008✔
2305
        return;
21✔
2306
    }
21✔
2307

2308
    /* see if the packet matches one or more of the sigs */
2309
    DetectRun(tv, de_ctx, det_ctx, p);
126,126,411✔
2310
}
126,126,411✔
2311

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

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

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

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

2333
/** \brief Detection engine thread wrapper.
2334
 *  \param tv thread vars
2335
 *  \param p packet to inspect
2336
 *  \param data thread specific data
2337
 *  \param pq packet queue
2338
 *  \retval TM_ECODE_FAILED error
2339
 *  \retval TM_ECODE_OK ok
2340
 */
2341
TmEcode Detect(ThreadVars *tv, Packet *p, void *data)
2342
{
130,469,003✔
2343
    DEBUG_VALIDATE_PACKET(p);
130,469,003✔
2344

2345
    DetectEngineCtx *de_ctx = NULL;
130,469,003✔
2346
    DetectEngineThreadCtx *det_ctx = (DetectEngineThreadCtx *)data;
130,469,003✔
2347
    if (det_ctx == NULL) {
130,469,003!
2348
        printf("ERROR: Detect has no thread ctx\n");
×
2349
        goto error;
×
2350
    }
×
2351

2352
    if (unlikely(SC_ATOMIC_GET(det_ctx->so_far_used_by_detect) == 0)) {
130,469,003✔
2353
        (void)SC_ATOMIC_SET(det_ctx->so_far_used_by_detect, 1);
22,054✔
2354
        SCLogDebug("Detect Engine using new det_ctx - %p",
22,054!
2355
                  det_ctx);
22,054✔
2356
    }
22,054✔
2357

2358
    /* if in MT mode _and_ we have tenants registered, use
2359
     * MT logic. */
2360
    if (det_ctx->mt_det_ctxs_cnt > 0 && det_ctx->TenantGetId != NULL)
130,469,003!
2361
    {
156✔
2362
        uint32_t tenant_id = p->tenant_id;
156✔
2363
        if (tenant_id == 0)
156!
2364
            tenant_id = det_ctx->TenantGetId(det_ctx, p);
156✔
2365
        if (tenant_id > 0 && tenant_id < det_ctx->mt_det_ctxs_cnt) {
156!
2366
            p->tenant_id = tenant_id;
×
2367
            det_ctx = GetTenantById(det_ctx->mt_det_ctxs_hash, tenant_id);
×
2368
            if (det_ctx == NULL)
×
2369
                return TM_ECODE_OK;
×
2370
            de_ctx = det_ctx->de_ctx;
×
2371
            if (de_ctx == NULL)
×
2372
                return TM_ECODE_OK;
×
2373

2374
            if (unlikely(SC_ATOMIC_GET(det_ctx->so_far_used_by_detect) == 0)) {
×
2375
                (void)SC_ATOMIC_SET(det_ctx->so_far_used_by_detect, 1);
×
2376
                SCLogDebug("MT de_ctx %p det_ctx %p (tenant %u)", de_ctx, det_ctx, tenant_id);
×
2377
            }
×
2378
        } else {
156✔
2379
            /* use default if no tenants are registered for this packet */
2380
            de_ctx = det_ctx->de_ctx;
156✔
2381
        }
156✔
2382
    } else {
130,468,847✔
2383
        de_ctx = det_ctx->de_ctx;
130,468,847✔
2384
    }
130,468,847✔
2385

2386
    if (p->flow) {
130,469,003✔
2387
        DetectFlow(tv, de_ctx, det_ctx, p);
3,701,349✔
2388
    } else {
130,167,992✔
2389
        DetectNoFlow(tv, de_ctx, det_ctx, p);
126,767,654✔
2390
    }
126,767,654✔
2391

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

2402
    return TM_ECODE_OK;
130,469,003✔
2403
error:
×
2404
    return TM_ECODE_FAILED;
×
2405
}
130,469,003✔
2406

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

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

2431
/*
2432
 * TESTS
2433
 */
2434

2435
#ifdef UNITTESTS
2436
#include "tests/detect.c"
2437
#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