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

OISF / suricata / 22618661228

02 Mar 2026 09:33PM UTC coverage: 42.258% (-34.4%) from 76.611%
22618661228

push

github

victorjulien
github-actions: bump actions/download-artifact from 7.0.0 to 8.0.0

Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 7.0.0 to 8.0.0.
- [Release notes](https://github.com/actions/download-artifact/releases)
- [Commits](https://github.com/actions/download-artifact/compare/37930b1c2...70fc10c6e)

---
updated-dependencies:
- dependency-name: actions/download-artifact
  dependency-version: 8.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

91511 of 216553 relevant lines covered (42.26%)

3416852.41 hits per line

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

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

124
    DetectRunScratchpad scratch = DetectRunSetup(de_ctx, det_ctx, p, pflow, ACTION_DROP);
1,182,972✔
125

126
    /* run the IPonly engine */
127
    DetectRunInspectIPOnly(th_v, de_ctx, det_ctx, pflow, p);
1,182,972✔
128

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

138
    /* run the prefilters for packets */
139
    DetectRunPrefilterPkt(th_v, de_ctx, det_ctx, p, &scratch);
1,181,375✔
140

141
    PACKET_PROFILING_DETECT_START(p, PROF_DETECT_RULES);
1,181,375✔
142
    /* inspect the rules against the packet */
143
    const uint8_t pkt_policy = DetectRulePacketRules(th_v, de_ctx, det_ctx, p, pflow, &scratch);
1,181,375✔
144
    PACKET_PROFILING_DETECT_END(p, PROF_DETECT_RULES);
1,181,375✔
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)) {
1,181,375✔
154
        goto end;
×
155
    }
×
156

157
    /* run tx/state inspection. Don't call for ICMP error msgs. */
158
    if (pflow && pflow->alstate && likely(pflow->proto == p->proto)) {
1,181,375✔
159
        if (p->proto == IPPROTO_TCP) {
647,238✔
160
            if ((p->flags & PKT_STREAM_EST) == 0) {
633,029✔
161
                SCLogDebug("packet %" PRIu64 ": skip tcp non-established", PcapPacketCntGet(p));
77✔
162
                DetectRunAppendDefaultAccept(det_ctx, p);
77✔
163
                goto end;
77✔
164
            }
77✔
165
            const TcpSession *ssn = p->flow->protoctx;
632,952✔
166
            bool setting_nopayload = p->flow->alparser &&
632,952✔
167
                                     SCAppLayerParserStateIssetFlag(
632,958✔
168
                                             p->flow->alparser, APP_LAYER_PARSER_NO_INSPECTION) &&
632,958✔
169
                                     !(p->flags & PKT_NOPAYLOAD_INSPECTION);
632,952✔
170
            // we may be right after disabling app-layer (ssh)
171
            if (ssn &&
632,952✔
172
                    ((ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED) == 0 || setting_nopayload)) {
632,952✔
173
                // PACKET_PROFILING_DETECT_START(p, PROF_DETECT_TX);
174
                DetectRunFrames(th_v, de_ctx, det_ctx, p, pflow, &scratch);
628,566✔
175
                // PACKET_PROFILING_DETECT_END(p, PROF_DETECT_TX);
176
            }
628,566✔
177
            // no update to transactions
178
            if (!PKT_IS_PSEUDOPKT(p) && p->app_update_direction == 0 &&
632,952✔
179
                    ((PKT_IS_TOSERVER(p) && (p->flow->flags & FLOW_TS_APP_UPDATED) == 0) ||
632,952✔
180
                            (PKT_IS_TOCLIENT(p) && (p->flow->flags & FLOW_TC_APP_UPDATED) == 0))) {
586,576✔
181
                SCLogDebug("packet %" PRIu64 ": no app-layer update", PcapPacketCntGet(p));
561,147✔
182
                DetectRunAppendDefaultAccept(det_ctx, p);
561,147✔
183
                goto end;
561,147✔
184
            }
561,147✔
185
        } else if (p->proto == IPPROTO_UDP) {
632,952✔
186
            DetectRunFrames(th_v, de_ctx, det_ctx, p, pflow, &scratch);
14,191✔
187
        }
14,191✔
188

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

202
end:
1,183,055✔
203
    DetectRunPostRules(th_v, de_ctx, det_ctx, p, pflow, &scratch);
1,183,055✔
204

205
    DetectRunCleanup(det_ctx, p, pflow);
1,183,055✔
206
    SCReturn;
1,183,055✔
207
}
1,181,375✔
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
{
×
214
    SCEnter();
×
215
    SCLogDebug("PcapPacketCntGet(p) %" PRIu64 " direction %s pkt_src %s", PcapPacketCntGet(p),
×
216
            p->flow ? (FlowGetPacketDirection(p->flow, p) == TOSERVER ? "toserver" : "toclient")
×
217
                    : "noflow",
×
218
            PktSrcToString(p->pkt_src));
×
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;
×
223

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

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

234
    /* run the prefilters for packets */
235
    DetectRunPrefilterPkt(th_v, de_ctx, det_ctx, p, &scratch);
×
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);
×
240
    //    PACKET_PROFILING_DETECT_END(p, PROF_DETECT_RULES);
241
    if (pkt_policy & (ACTION_DROP | ACTION_ACCEPT)) {
×
242
        goto end;
×
243
    }
×
244

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

248
    DetectRunCleanup(det_ctx, p, pflow);
×
249
    SCReturn;
×
250
}
×
251

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

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

263
        while (1) {
912✔
264
            KEYWORD_PROFILING_START;
911✔
265
            (void)sigmatch_table[smd->type].Match(det_ctx, p, s, smd->ctx);
911✔
266
            KEYWORD_PROFILING_END(det_ctx, smd->type, 1);
911✔
267
            if (smd->is_last)
911✔
268
                break;
910✔
269
            smd++;
1✔
270
        }
1✔
271
    }
911✔
272
}
1,549✔
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
{
88,460✔
285
    SCEnter();
88,460✔
286
    SigGroupHead *sgh = NULL;
88,460✔
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) {
88,460✔
291
        SCReturnPtr(de_ctx->decoder_event_sgh, "SigGroupHead");
97✔
292
    } else if (p->proto == 0) {
88,363✔
293
        if (!(PacketIsIPv4(p) || PacketIsIPv6(p))) {
1,232✔
294
            /* not IP, so nothing to do */
295
            SCReturnPtr(NULL, "SigGroupHead");
1,232✔
296
        }
1,232✔
297
    }
1,231✔
298

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

302
    int proto = PacketGetIPProto(p);
87,131✔
303
    if (proto == IPPROTO_TCP) {
87,131✔
304
        DetectPort *list = de_ctx->flow_gh[dir].tcp;
5,851✔
305
        SCLogDebug("tcp toserver %p, tcp toclient %p: going to use %p", de_ctx->flow_gh[1].tcp,
5,851✔
306
                de_ctx->flow_gh[0].tcp, de_ctx->flow_gh[dir].tcp);
5,851✔
307
        const uint16_t port = dir ? p->dp : p->sp;
5,851✔
308
        SCLogDebug("tcp port %u -> %u:%u", port, p->sp, p->dp);
5,851✔
309
        DetectPort *sghport = DetectPortLookupGroup(list, port);
5,851✔
310
        if (sghport != NULL)
5,851✔
311
            sgh = sghport->sh;
5,827✔
312
        SCLogDebug("TCP list %p, port %u, direction %s, sghport %p, sgh %p", list, port,
5,851✔
313
                dir ? "toserver" : "toclient", sghport, sgh);
5,851✔
314
    } else if (proto == IPPROTO_UDP) {
81,280✔
315
        DetectPort *list = de_ctx->flow_gh[dir].udp;
2,711✔
316
        uint16_t port = dir ? p->dp : p->sp;
2,711✔
317
        DetectPort *sghport = DetectPortLookupGroup(list, port);
2,711✔
318
        if (sghport != NULL)
2,711✔
319
            sgh = sghport->sh;
2,710✔
320
        SCLogDebug("UDP list %p, port %u, direction %s, sghport %p, sgh %p", list, port,
2,711✔
321
                dir ? "toserver" : "toclient", sghport, sgh);
2,711✔
322
    } else {
78,569✔
323
        sgh = de_ctx->flow_gh[dir].sgh[proto];
78,569✔
324
    }
78,569✔
325

326
    SCReturnPtr(sgh, "SigGroupHead");
87,131✔
327
}
88,460✔
328

329
static inline void DetectPrefilterCopyDeDup(
330
        const DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx)
331
{
493,583✔
332
    SigIntId *pf_ptr = det_ctx->pmq.rule_id_array;
493,583✔
333
    uint32_t final_cnt = det_ctx->pmq.rule_id_array_cnt;
493,583✔
334
    Signature **sig_array = de_ctx->sig_array;
493,583✔
335
    Signature **match_array = det_ctx->match_array;
493,583✔
336
    SigIntId previous_id = (SigIntId)-1;
493,583✔
337
    while (final_cnt-- > 0) {
1,199,054✔
338
        SigIntId id = *pf_ptr++;
705,471✔
339
        Signature *s = sig_array[id];
705,471✔
340

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

348
    det_ctx->match_array_cnt = (uint32_t)(match_array - det_ctx->match_array);
493,583✔
349
    DEBUG_VALIDATE_BUG_ON(det_ctx->pmq.rule_id_array_cnt < det_ctx->match_array_cnt);
493,583✔
350
    PMQ_RESET(&det_ctx->pmq);
493,583✔
351
}
493,583✔
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
{
7,994✔
361
    uint16_t flow_file_flags = FLOWFILE_INIT;
7,994✔
362

363
    if (sgh == NULL) {
7,994✔
364
        SCLogDebug("requesting disabling all file features for flow");
26✔
365
        flow_file_flags = FLOWFILE_NONE;
26✔
366
    } else {
7,968✔
367
        if (sgh->filestore_cnt == 0) {
7,970✔
368
            SCLogDebug("requesting disabling filestore for flow");
7,962✔
369
            flow_file_flags |= (FLOWFILE_NO_STORE_TS|FLOWFILE_NO_STORE_TC);
7,962✔
370
        }
7,962✔
371
#ifdef HAVE_MAGIC
7,968✔
372
        if (!(sgh->flags & SIG_GROUP_HEAD_HAVEFILEMAGIC)) {
7,971✔
373
            SCLogDebug("requesting disabling magic for flow");
7,971✔
374
            flow_file_flags |= (FLOWFILE_NO_MAGIC_TS|FLOWFILE_NO_MAGIC_TC);
7,971✔
375
        }
7,971✔
376
#endif
7,968✔
377
        if (!(sgh->flags & SIG_GROUP_HEAD_HAVEFILEMD5)) {
7,970✔
378
            SCLogDebug("requesting disabling md5 for flow");
7,969✔
379
            flow_file_flags |= (FLOWFILE_NO_MD5_TS|FLOWFILE_NO_MD5_TC);
7,969✔
380
        }
7,969✔
381
        if (!(sgh->flags & SIG_GROUP_HEAD_HAVEFILESHA1)) {
7,970✔
382
            SCLogDebug("requesting disabling sha1 for flow");
7,964✔
383
            flow_file_flags |= (FLOWFILE_NO_SHA1_TS|FLOWFILE_NO_SHA1_TC);
7,964✔
384
        }
7,964✔
385
        if (!(sgh->flags & SIG_GROUP_HEAD_HAVEFILESHA256)) {
7,970✔
386
            SCLogDebug("requesting disabling sha256 for flow");
7,965✔
387
            flow_file_flags |= (FLOWFILE_NO_SHA256_TS|FLOWFILE_NO_SHA256_TC);
7,965✔
388
        }
7,965✔
389
    }
7,968✔
390
    if (flow_file_flags != 0) {
7,996✔
391
        FileUpdateFlowFileFlags(f, flow_file_flags, direction);
7,996✔
392
    }
7,996✔
393
}
7,994✔
394

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

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

411
        DetectPostInspectFileFlagsUpdate(pflow,
4,546✔
412
                pflow->sgh_toserver, STREAM_TOSERVER);
4,546✔
413

414
    } else if ((p->flowflags & FLOW_PKT_TOCLIENT) && !(pflow->flags & FLOW_SGH_TOCLIENT)) {
4,546✔
415
        pflow->sgh_toclient = sgh;
3,452✔
416
        pflow->flags |= FLOW_SGH_TOCLIENT;
3,452✔
417

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

426
        DetectPostInspectFileFlagsUpdate(pflow,
3,452✔
427
                pflow->sgh_toclient, STREAM_TOCLIENT);
3,452✔
428
    }
3,452✔
429
}
7,994✔
430

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

438
    if (pflow) {
1,183,030✔
439
        bool use_flow_sgh = false;
1,102,576✔
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 */
1,102,576✔
443
            PACKET_PROFILING_DETECT_START(p, PROF_DETECT_GETSGH);
1,102,544✔
444
            if ((p->flowflags & FLOW_PKT_TOSERVER) && (pflow->flags & FLOW_SGH_TOSERVER)) {
1,102,544✔
445
                sgh = pflow->sgh_toserver;
759,744✔
446
                SCLogDebug("sgh = pflow->sgh_toserver; => %p", sgh);
759,744✔
447
                use_flow_sgh = true;
759,744✔
448
            } else if ((p->flowflags & FLOW_PKT_TOCLIENT) && (pflow->flags & FLOW_SGH_TOCLIENT)) {
759,744✔
449
                sgh = pflow->sgh_toclient;
334,875✔
450
                SCLogDebug("sgh = pflow->sgh_toclient; => %p", sgh);
334,875✔
451
                use_flow_sgh = true;
334,875✔
452
            }
334,875✔
453
            PACKET_PROFILING_DETECT_END(p, PROF_DETECT_GETSGH);
1,102,544✔
454
        }
1,102,544✔
455

456
        if (!(use_flow_sgh)) {
1,102,576✔
457
            PACKET_PROFILING_DETECT_START(p, PROF_DETECT_GETSGH);
8,008✔
458
            sgh = SigMatchSignaturesGetSgh(de_ctx, p);
8,008✔
459
            PACKET_PROFILING_DETECT_END(p, PROF_DETECT_GETSGH);
8,008✔
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)) {
8,008✔
464
                ; /* no-op */
20✔
465
            } else {
7,988✔
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);
7,988✔
470
            }
7,988✔
471
        }
8,008✔
472
    } else { /* p->flags & PKT_HAS_FLOW */
1,120,854✔
473
        /* no flow */
474

475
        PACKET_PROFILING_DETECT_START(p, PROF_DETECT_GETSGH);
80,454✔
476
        sgh = SigMatchSignaturesGetSgh(de_ctx, p);
80,454✔
477
        PACKET_PROFILING_DETECT_END(p, PROF_DETECT_GETSGH);
80,454✔
478
    }
80,454✔
479

480
    scratch->sgh = sgh;
1,183,030✔
481
}
1,183,030✔
482

483
static void DetectRunInspectIPOnly(ThreadVars *tv, const DetectEngineCtx *de_ctx,
484
        DetectEngineThreadCtx *det_ctx,
485
        Flow * const pflow, Packet * const p)
486
{
1,183,061✔
487
    if (pflow) {
1,183,061✔
488
        if (p->flowflags & (FLOW_PKT_TOSERVER_FIRST | FLOW_PKT_TOCLIENT_FIRST)) {
1,102,520✔
489
            SCLogDebug("testing against \"ip-only\" signatures");
7,995✔
490

491
            PACKET_PROFILING_DETECT_START(p, PROF_DETECT_IPONLY);
7,995✔
492
            IPOnlyMatchPacket(tv, de_ctx, det_ctx, &de_ctx->io_ctx, p);
7,995✔
493
            PACKET_PROFILING_DETECT_END(p, PROF_DETECT_IPONLY);
7,995✔
494
        }
7,995✔
495
    } else { /* p->flags & PKT_HAS_FLOW */
1,120,799✔
496
        /* no flow */
497

498
        /* Even without flow we should match the packet src/dst */
499
        PACKET_PROFILING_DETECT_START(p, PROF_DETECT_IPONLY);
80,541✔
500
        IPOnlyMatchPacket(tv, de_ctx, det_ctx, &de_ctx->io_ctx, p);
80,541✔
501
        PACKET_PROFILING_DETECT_END(p, PROF_DETECT_IPONLY);
80,541✔
502
    }
80,541✔
503
}
1,183,061✔
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
{
683,689✔
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)) {
683,710✔
515
        DEBUG_VALIDATE_BUG_ON(f == NULL);
118,270✔
516

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

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

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

540
    /* check the source & dst port in the sig */
541
    if (p->proto == IPPROTO_TCP || p->proto == IPPROTO_UDP || p->proto == IPPROTO_SCTP) {
673,531✔
542
        if (!(sflags & SIG_FLAG_DP_ANY)) {
220,755✔
543
            if (p->flags & PKT_IS_FRAGMENT)
177,460✔
544
                return false;
×
545
            const DetectPort *dport = DetectPortLookupGroup(s->dp, p->dp);
177,460✔
546
            if (dport == NULL) {
177,460✔
547
                SCLogDebug("dport didn't match.");
105,550✔
548
                return false;
105,550✔
549
            }
105,550✔
550
        }
177,460✔
551
        if (!(sflags & SIG_FLAG_SP_ANY)) {
115,205✔
552
            if (p->flags & PKT_IS_FRAGMENT)
16,389✔
553
                return false;
×
554
            const DetectPort *sport = DetectPortLookupGroup(s->sp, p->sp);
16,389✔
555
            if (sport == NULL) {
16,389✔
556
                SCLogDebug("sport didn't match.");
7,731✔
557
                return false;
7,731✔
558
            }
7,731✔
559
        }
16,389✔
560
    } else if ((sflags & (SIG_FLAG_DP_ANY|SIG_FLAG_SP_ANY)) != (SIG_FLAG_DP_ANY|SIG_FLAG_SP_ANY)) {
452,772✔
561
        SCLogDebug("port-less protocol and sig needs ports");
×
562
        return false;
×
563
    }
×
564

565
    /* check the destination address */
566
    if (!(sflags & SIG_FLAG_DST_ANY)) {
560,246✔
567
        if (PacketIsIPv4(p)) {
254,480✔
568
            if (DetectAddressMatchIPv4(s->addr_dst_match4, s->addr_dst_match4_cnt, &p->dst) == 0)
104,105✔
569
                return false;
17,403✔
570
        } else if (PacketIsIPv6(p)) {
150,375✔
571
            if (DetectAddressMatchIPv6(s->addr_dst_match6, s->addr_dst_match6_cnt, &p->dst) == 0)
150,374✔
572
                return false;
150,374✔
573
        }
150,374✔
574
    }
254,480✔
575
    /* check the source address */
576
    if (!(sflags & SIG_FLAG_SRC_ANY)) {
392,469✔
577
        if (PacketIsIPv4(p)) {
357,593✔
578
            if (DetectAddressMatchIPv4(s->addr_src_match4, s->addr_src_match4_cnt, &p->src) == 0)
74,093✔
579
                return false;
58,981✔
580
        } else if (PacketIsIPv6(p)) {
283,502✔
581
            if (DetectAddressMatchIPv6(s->addr_src_match6, s->addr_src_match6_cnt, &p->src) == 0)
283,502✔
582
                return false;
283,502✔
583
        }
283,502✔
584
    }
357,593✔
585

586
    return true;
49,986✔
587
}
392,469✔
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
{
1,181,463✔
595
    /* create our prefilter mask */
596
    PacketCreateMask(p, &p->sig_mask, scratch->alproto, scratch->app_decoder_events);
1,181,463✔
597
    /* run the prefilter engines */
598
    Prefilter(det_ctx, scratch->sgh, p, scratch->flow_flags, p->sig_mask);
1,181,463✔
599
    /* create match list if we have non-pf and/or pf */
600
    if (det_ctx->pmq.rule_id_array_cnt) {
1,181,463✔
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);
493,591✔
608
        DetectPrefilterCopyDeDup(de_ctx, det_ctx);
493,591✔
609
        PACKET_PROFILING_DETECT_END(p, PROF_DETECT_PF_SORT2);
493,591✔
610
    }
493,591✔
611
}
1,181,463✔
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
{
×
625
    uint64_t tx_cnt = AppLayerParserGetTxCnt(f, f->alstate);
×
626
    if (tx_cnt == txid + 1) {
×
627
        // only live tx
628
        return true;
×
629
    }
×
630
    if (tx_cnt == txid + 2) {
×
631
        // 2 live txs, one after us
632
        void *tx = AppLayerParserGetTx(f->proto, f->alproto, f->alstate, txid + 1);
×
633
        if (tx) {
×
634
            AppLayerTxData *txd = AppLayerParserGetTxData(f->proto, f->alproto, tx);
×
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)) ||
×
637
                    (dir == STREAM_TOCLIENT && (txd->flags & APP_LAYER_TX_SKIP_INSPECT_TC))) {
×
638
                return true;
×
639
            }
×
640
        }
×
641
    }
×
642
    return false;
×
643
}
×
644

645
static int SortHelper(const void *a, const void *b)
646
{
×
647
    const Signature *sa = *(const Signature **)a;
×
648
    const Signature *sb = *(const Signature **)b;
×
649
    if (sa->iid == sb->iid)
×
650
        return 0;
×
651
    return sa->iid > sb->iid ? 1 : -1;
×
652
}
×
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
{
1,181,498✔
658
    uint8_t action = 0;
1,181,498✔
659
    bool fw_verdict = false;
1,181,498✔
660
    const bool have_fw_rules = EngineModeIsFirewall();
1,181,498✔
661
    const Signature *next_s = NULL;
1,181,498✔
662

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

673
    SGH_PROFILING_RECORD(det_ctx, scratch->sgh);
1,181,498✔
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;
1,181,498✔
680
    uint32_t sflags, next_sflags = 0;
1,181,498✔
681
    if (match_cnt) {
1,181,498✔
682
        next_s = *match_array++;
493,593✔
683
        next_sflags = next_s->flags;
493,593✔
684
    }
493,593✔
685
    while (match_cnt--) {
1,877,815✔
686
        RULE_PROFILING_START(p);
696,338✔
687
        bool break_out_of_packet_filter = false;
696,338✔
688
        uint8_t alert_flags = 0;
696,338✔
689
#ifdef PROFILE_RULES
690
        bool smatch = false; /* signature match */
691
#endif
692
        const Signature *s = next_s;
696,338✔
693
        sflags = next_sflags;
696,338✔
694
        if (match_cnt) {
696,338✔
695
            next_s = *match_array++;
202,759✔
696
            next_sflags = next_s->flags;
202,759✔
697
        }
202,759✔
698
        const uint8_t s_proto_flags = s->proto.flags;
696,338✔
699

700
        SCLogDebug("packet %" PRIu64 ": inspecting signature id %" PRIu32 "", PcapPacketCntGet(p),
696,338✔
701
                s->id);
696,338✔
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) {
696,338✔
705
            if (skip_fw) {
×
706
                SCLogDebug("skipping firewall rule %u", s->id);
×
707
                goto next;
×
708
            }
×
709
        } else if (have_fw_rules) {
696,338✔
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 ||
×
714
                    (pflow != NULL && pflow->flags & (FLOW_ACTION_PASS))) {
×
715
                SCLogDebug("skipping firewall rule %u", s->id);
×
716
                break_out_of_packet_filter = true;
×
717
                goto next;
×
718
            }
×
719
        }
×
720

721
        if (s->app_inspect != NULL) {
696,338✔
722
            goto next; // handle sig in DetectRunTx
882✔
723
        }
882✔
724
        if (s->frame_inspect != NULL) {
695,456✔
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)
695,456✔
730
            goto next;
×
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) {
695,456✔
735
            SCLogDebug("mask mismatch %x & %x != %x", s->mask, p->sig_mask, s->mask);
×
736
            goto next;
×
737
        }
×
738

739
        if (SigDsizePrefilter(p, s, sflags))
695,456✔
740
            goto next;
30,493✔
741

742
        /* if the sig has alproto and the session as well they should match */
743
        if (likely(sflags & SIG_FLAG_APPLAYER)) {
664,963✔
744
            if (s->alproto != ALPROTO_UNKNOWN && !AppProtoEquals(s->alproto, scratch->alproto)) {
9,012✔
745
                SCLogDebug("alproto mismatch");
4,544✔
746
                goto next;
4,544✔
747
            }
4,544✔
748
        }
9,012✔
749

750
        if (!DetectRunInspectRuleHeader(p, pflow, s, sflags, s_proto_flags)) {
660,419✔
751
            goto next;
622,894✔
752
        }
622,894✔
753

754
        if (!DetectEnginePktInspectionRun(tv, det_ctx, s, pflow, p, &alert_flags)) {
37,525✔
755
            goto next;
36,254✔
756
        }
36,254✔
757

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

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

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

792
            if (det_ctx->pmq.rule_id_array_cnt > 0) {
×
793
                /* undo "prefetch" */
794
                if (next_s)
×
795
                    match_array--;
×
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?
×
799
                SCLogDebug("sig_array_len %u det_ctx->pmq.rule_id_array_cnt %u",
×
800
                        de_ctx->sig_array_len, det_ctx->pmq.rule_id_array_cnt);
×
801
                const Signature **r = replace;
×
802
                for (uint32_t x = 0; x < match_cnt; x++) {
×
803
                    *r++ = match_array[x];
×
804
                    SCLogDebug("appended %u", match_array[x]->id);
×
805
                }
×
806
                /* append the prefilter results, then sort it */
807
                for (uint32_t x = 0; x < det_ctx->pmq.rule_id_array_cnt; x++) {
×
808
                    SCLogDebug("adding iid %u", det_ctx->pmq.rule_id_array[x]);
×
809
                    Signature *ts = de_ctx->sig_array[det_ctx->pmq.rule_id_array[x]];
×
810
                    SCLogDebug("adding id %u", ts->id);
×
811
                    if (ts->app_inspect == NULL) {
×
812
                        *r++ = ts;
×
813
                        match_cnt++;
×
814
                    }
×
815
                }
×
816
                if (match_cnt > 1) {
×
817
                    qsort(replace, match_cnt, sizeof(Signature *), SortHelper);
×
818
                }
×
819
                /* rewrite match_array to include the new additions, and deduplicate
820
                 * while at it. */
821
                Signature **m = match_array;
×
822
                Signature *last_sig = NULL;
×
823
                uint32_t skipped = 0;
×
824
                for (uint32_t x = 0; x < match_cnt; x++) {
×
825
                    /* de-duplicate */
826
                    if (last_sig == *m) {
×
827
                        skipped++;
×
828
                        continue;
×
829
                    }
×
830
                    last_sig = *m;
×
831
                    *m++ = (Signature *)replace[x];
×
832
                }
×
833
                match_cnt -= skipped;
×
834
                /* prefetch next */
835
                next_s = *match_array++;
×
836
                next_sflags = next_s->flags;
×
837
                SCLogDebug("%u rules added", det_ctx->pmq.rule_id_array_cnt);
×
838
                det_ctx->post_rule_work_queue.len = 0;
×
839
                PMQ_RESET(&det_ctx->pmq);
×
840
            }
×
841
        }
×
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,271✔
854
            if (s->action & (ACTION_ACCEPT)) {
×
855
                fw_verdict = true;
×
856

857
                enum ActionScope as = s->action_scope;
×
858
                if (as == ACTION_SCOPE_HOOK) {
×
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;
×
863

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

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

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

891
        /* fw accept:packet or accept:flow means we're done here */
892
        if (break_out_of_packet_filter)
696,317✔
893
            break;
×
894

895
        continue;
696,317✔
896
    }
696,317✔
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) {
1,181,477✔
902
        if (!fw_verdict) {
×
903
            DEBUG_VALIDATE_BUG_ON(action & ACTION_DROP);
×
904
            PacketDrop(p, ACTION_DROP, PKT_DROP_REASON_DEFAULT_PACKET_POLICY);
×
905
            action |= ACTION_DROP;
×
906
        } else {
×
907
            /* apply fw action */
908
            p->action |= action;
×
909
        }
×
910
    }
×
911
    return action;
1,181,477✔
912
}
1,181,498✔
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
{
1,182,979✔
924
    AppProto alproto = ALPROTO_UNKNOWN;
1,182,979✔
925
    uint8_t flow_flags = 0; /* flow/state flags */
1,182,979✔
926
    bool app_decoder_events = false;
1,182,979✔
927

928
    PACKET_PROFILING_DETECT_START(p, PROF_DETECT_SETUP);
1,182,979✔
929

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

943
    det_ctx->alert_queue_size = 0;
1,182,979✔
944
    p->alerts.drop.action = 0;
1,182,979✔
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) {
1,182,979✔
954
        DEBUG_VALIDATE_BUG_ON(pflow == NULL);
1,102,466✔
955

956
        if (p->flowflags & FLOW_PKT_TOSERVER) {
1,102,466✔
957
            flow_flags = STREAM_TOSERVER;
764,235✔
958
            SCLogDebug("flag STREAM_TOSERVER set");
764,235✔
959
        } else if (p->flowflags & FLOW_PKT_TOCLIENT) {
764,235✔
960
            flow_flags = STREAM_TOCLIENT;
338,316✔
961
            SCLogDebug("flag STREAM_TOCLIENT set");
338,316✔
962
        }
338,316✔
963
        SCLogDebug("p->flowflags 0x%02x", p->flowflags);
1,102,466✔
964

965
        if (p->flags & PKT_PSEUDO_STREAM_END) {
1,102,466✔
966
            flow_flags |= STREAM_EOF;
2,295✔
967
            SCLogDebug("STREAM_EOF set");
2,295✔
968
        }
2,295✔
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) {
1,102,466✔
973
            pflow->tenant_id = p->tenant_id;
×
974
        }
×
975

976
        /* live ruleswap check for flow updates */
977
        if (pflow->de_ctx_version == 0) {
1,102,466✔
978
            /* first time this flow is inspected, set id */
979
            pflow->de_ctx_version = de_ctx->version;
4,535✔
980
        } else if (pflow->de_ctx_version != de_ctx->version) {
1,097,931✔
981
            /* first time we inspect flow with this de_ctx, reset */
982
            pflow->flags &= ~FLOW_SGH_TOSERVER;
×
983
            pflow->flags &= ~FLOW_SGH_TOCLIENT;
×
984
            pflow->sgh_toserver = NULL;
×
985
            pflow->sgh_toclient = NULL;
×
986

987
            pflow->de_ctx_version = de_ctx->version;
×
988
            SCGenericVarFree(pflow->flowvar);
×
989
            pflow->flowvar = NULL;
×
990

991
            DetectEngineStateResetTxs(pflow);
×
992
        }
×
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)) ||
1,102,466✔
997
                (p->proto == IPPROTO_UDP) ||
1,102,466✔
998
                (p->proto == IPPROTO_SCTP && (p->flowflags & FLOW_PKT_ESTABLISHED)))
1,102,466✔
999
        {
659,597✔
1000
            /* update flow flags with knowledge on disruptions */
1001
            flow_flags = FlowGetDisruptionFlags(pflow, flow_flags);
659,597✔
1002
            alproto = FlowGetAppProtocol(pflow);
659,597✔
1003
            if (p->proto == IPPROTO_TCP && pflow->protoctx &&
659,597✔
1004
                    StreamReassembleRawHasDataReady(pflow->protoctx, p)) {
659,597✔
1005
                p->flags |= PKT_DETECT_HAS_STREAMDATA;
19,566✔
1006
            }
19,566✔
1007
            SCLogDebug("alproto %u", alproto);
659,597✔
1008
        } else {
659,602✔
1009
            SCLogDebug("packet doesn't have established flag set (proto %d)", p->proto);
442,869✔
1010
        }
442,869✔
1011

1012
        app_decoder_events = AppLayerParserHasDecoderEvents(pflow->alparser);
1,102,466✔
1013
    }
1,102,466✔
1014

1015
    DetectRunScratchpad pad = { alproto, flow_flags, app_decoder_events, default_action, NULL };
1,182,979✔
1016
    PACKET_PROFILING_DETECT_END(p, PROF_DETECT_SETUP);
1,182,979✔
1017
    return pad;
1,182,979✔
1018
}
1,182,979✔
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
{
1,183,004✔
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);
1,183,004✔
1029
    PacketAlertFinalize(de_ctx, det_ctx, p);
1,183,004✔
1030
    if (p->alerts.cnt > 0) {
1,183,004✔
1031
        StatsCounterAddI64(&tv->stats, det_ctx->counter_alerts, (uint64_t)p->alerts.cnt);
308✔
1032
    }
308✔
1033
    if (p->alerts.discarded > 0) {
1,183,004✔
1034
        StatsCounterAddI64(
1✔
1035
                &tv->stats, det_ctx->counter_alerts_overflow, (uint64_t)p->alerts.discarded);
1✔
1036
    }
1✔
1037
    if (p->alerts.suppressed > 0) {
1,183,004✔
1038
        StatsCounterAddI64(
1,121✔
1039
                &tv->stats, det_ctx->counter_alerts_suppressed, (uint64_t)p->alerts.suppressed);
1,121✔
1040
    }
1,121✔
1041
    PACKET_PROFILING_DETECT_END(p, PROF_DETECT_ALERT);
1,183,004✔
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 &&
1,183,004✔
1047
            scratch->default_action == ACTION_DROP) {
1,183,004✔
1048
        SCLogDebug("packet %" PRIu64 ": droppit as no ACCEPT set %02x (pkt %s)",
×
1049
                PcapPacketCntGet(p), p->action, PktSrcToString(p->pkt_src));
×
1050
        PacketDrop(p, ACTION_DROP, PKT_DROP_REASON_DEFAULT_PACKET_POLICY);
×
1051
    }
×
1052
}
1,183,004✔
1053

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

1060
    if (pflow != NULL) {
1,182,963✔
1061
        /* update inspected tracker for raw reassembly */
1062
        if (p->proto == IPPROTO_TCP && pflow->protoctx != NULL &&
1,102,450✔
1063
                (p->flags & PKT_DETECT_HAS_STREAMDATA)) {
1,102,450✔
1064
            StreamReassembleRawUpdateProgress(pflow->protoctx, p,
19,571✔
1065
                    det_ctx->raw_stream_progress);
19,571✔
1066
        }
19,571✔
1067
    }
1,102,450✔
1068
    PACKET_PROFILING_DETECT_END(p, PROF_DETECT_CLEANUP);
1,182,963✔
1069
    SCReturn;
1,182,963✔
1070
}
1,182,963✔
1071

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

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

1091
/* if size >= cur_space */
1092
static inline bool RuleMatchCandidateTxArrayHasSpace(const DetectEngineThreadCtx *det_ctx,
1093
        const uint32_t need)
1094
{
67,012✔
1095
    if (det_ctx->tx_candidates_size >= need)
67,012✔
1096
        return 1;
67,047✔
1097
    return 0;
2,147,483,647✔
1098
}
67,012✔
1099

1100
/* realloc */
1101
static int RuleMatchCandidateTxArrayExpand(DetectEngineThreadCtx *det_ctx, const uint32_t needed)
1102
{
×
1103
    const uint32_t old_size = det_ctx->tx_candidates_size;
×
1104
    uint32_t new_size = needed;
×
1105
    void *ptmp = SCRealloc(det_ctx->tx_candidates, (new_size * sizeof(RuleMatchCandidateTx)));
×
1106
    if (ptmp == NULL) {
×
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;
×
1112
    det_ctx->tx_candidates_size = new_size;
×
1113
    SCLogDebug("array expanded from %u to %u elements (%"PRIu64" bytes -> %"PRIu64" bytes)",
×
1114
            old_size, new_size, (uint64_t)(old_size * sizeof(RuleMatchCandidateTx)),
×
1115
            (uint64_t)(new_size * sizeof(RuleMatchCandidateTx))); (void)old_size;
×
1116
    return 1;
×
1117
}
×
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
{
120✔
1128
    const RuleMatchCandidateTx *s0 = a;
120✔
1129
    const RuleMatchCandidateTx *s1 = b;
120✔
1130
    if (s1->id == s0->id) {
120✔
1131
        if (s1->flags && !s0->flags)
5✔
1132
            return 1;
5✔
1133
        else if (!s1->flags && s0->flags)
×
1134
            return -1;
×
1135
        return 0;
×
1136
    } else
5✔
1137
        return s0->id > s1->id ? 1 : -1;
115✔
1138
}
120✔
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
{
2,189,641✔
1154
    if (unlikely(alproto == ALPROTO_DOH2)) {
2,189,641✔
1155
        if (engine_alproto == ALPROTO_DNS) {
6,394✔
1156
            // need to get the dns tx pointer
1157
            tx_ptr = SCDoH2GetDnsTx(tx_ptr, flow_flags);
272✔
1158
        } else if (engine_alproto != ALPROTO_HTTP2 && engine_alproto != ALPROTO_UNKNOWN) {
6,122✔
1159
            // incompatible engine->alproto with flow alproto
1160
            tx_ptr = NULL;
4,442✔
1161
        }
4,442✔
1162
    } else if (engine_alproto != alproto && engine_alproto != ALPROTO_UNKNOWN) {
2,183,247✔
1163
        // incompatible engine->alproto with flow alproto
1164
        tx_ptr = NULL;
1,929,261✔
1165
    }
1,929,261✔
1166
    return tx_ptr;
2,189,641✔
1167
}
2,189,641✔
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
{
23,309✔
1194
    const uint8_t flow_flags = in_flow_flags;
23,309✔
1195
    const int direction = (flow_flags & STREAM_TOSERVER) ? 0 : 1;
23,309✔
1196
    uint32_t inspect_flags = stored_flags ? *stored_flags : 0;
23,309✔
1197
    int total_matches = 0;
23,309✔
1198
    uint16_t file_no_match = 0;
23,309✔
1199
    bool retval = false;
23,309✔
1200
    bool mpm_before_progress = false;   // is mpm engine before progress?
23,309✔
1201
    bool mpm_in_progress = false;       // is mpm engine in a buffer we will revisit?
23,309✔
1202

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

1205
    /* for a new inspection we inspect pkt header and packet matches */
1206
    if (likely(stored_flags == NULL)) {
23,320✔
1207
        TRACE_SID_TXS(s->id, tx, "first inspect, run packet matches");
23,320✔
1208
        if (!DetectRunInspectRuleHeader(p, f, s, s->flags, s->proto.flags)) {
23,320✔
1209
            TRACE_SID_TXS(s->id, tx, "DetectRunInspectRuleHeader() no match");
10,786✔
1210
            return false;
10,786✔
1211
        }
10,786✔
1212
        if (!DetectEnginePktInspectionRun(tv, det_ctx, s, f, p, NULL)) {
12,534✔
1213
            TRACE_SID_TXS(s->id, tx, "DetectEnginePktInspectionRun no match");
117✔
1214
            return false;
117✔
1215
        }
117✔
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)) {
12,417✔
1218
            TRACE_SID_TXS(s->id, tx, "alproto mismatch");
225✔
1219
            return false;
225✔
1220
        }
225✔
1221
    }
12,417✔
1222

1223
    const DetectEngineAppInspectionEngine *engine = s->app_inspect;
12,181✔
1224
    do {
30,296✔
1225
        TRACE_SID_TXS(s->id, tx, "engine %p inspect_flags %x", engine, inspect_flags);
30,296✔
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)) &&
30,296✔
1229
                (direction == engine->dir || ((s->flags & SIG_FLAG_TXBOTHDIR) && direction == 1))) {
30,296✔
1230

1231
            void *tx_ptr = DetectGetInnerTx(tx->tx_ptr, f->alproto, engine->alproto, flow_flags);
30,282✔
1232
            if (tx_ptr == NULL) {
30,282✔
1233
                if (engine->alproto != ALPROTO_UNKNOWN) {
11,932✔
1234
                    /* special case: file_data on 'alert tcp' will have engines
1235
                     * in the list that are not for us. */
1236
                    engine = engine->next;
11,932✔
1237
                    continue;
11,932✔
1238
                } else {
2,147,483,647✔
1239
                    tx_ptr = tx->tx_ptr;
2,147,483,647✔
1240
                }
2,147,483,647✔
1241
            }
11,929✔
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) {
18,350✔
1246
                SCLogDebug("tx progress %d < engine progress %d",
×
1247
                        tx->tx_progress, engine->progress);
×
1248
                break;
×
1249
            }
×
1250
            if (engine->mpm) {
18,350✔
1251
                if (tx->tx_progress > engine->progress) {
12,191✔
1252
                    TRACE_SID_TXS(s->id, tx,
11,409✔
1253
                            "engine->mpm: t->tx_progress %u > engine->progress %u, so set "
11,409✔
1254
                            "mpm_before_progress",
11,409✔
1255
                            tx->tx_progress, engine->progress);
11,409✔
1256
                    mpm_before_progress = true;
11,409✔
1257
                } else if (tx->tx_progress == engine->progress) {
11,409✔
1258
                    TRACE_SID_TXS(s->id, tx,
782✔
1259
                            "engine->mpm: t->tx_progress %u == engine->progress %u, so set "
782✔
1260
                            "mpm_in_progress",
782✔
1261
                            tx->tx_progress, engine->progress);
782✔
1262
                    mpm_in_progress = true;
782✔
1263
                }
782✔
1264
            }
12,191✔
1265

1266
            uint8_t engine_flags = flow_flags;
18,350✔
1267
            if (direction != engine->dir) {
18,350✔
1268
                engine_flags = flow_flags ^ (STREAM_TOCLIENT | STREAM_TOSERVER);
×
1269
            }
×
1270
            /* run callback: but bypass stream callback if we can */
1271
            uint8_t match;
18,350✔
1272
            if (unlikely(engine->stream && can->stream_stored)) {
18,350✔
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) {
18,350✔
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) {
×
1279
                    mpm_before_progress = true; // TODO needs a new name now
×
1280
                }
×
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)
×
1285
                    return DETECT_ENGINE_INSPECT_SIG_MATCH;
×
1286

1287
                /* if progress > engine progress, track state to avoid additional matches */
1288
                match = DETECT_ENGINE_INSPECT_SIG_MATCH;
×
1289
            } else {
18,350✔
1290
                KEYWORD_PROFILING_SET_LIST(det_ctx, engine->sm_list);
18,350✔
1291
                DEBUG_VALIDATE_BUG_ON(engine->v2.Callback == NULL);
18,350✔
1292
                match = engine->v2.Callback(
18,350✔
1293
                        de_ctx, det_ctx, engine, s, f, engine_flags, alstate, tx_ptr, tx->tx_id);
18,350✔
1294
                TRACE_SID_TXS(s->id, tx, "engine %p match %d", engine, match);
18,350✔
1295
                if (engine->stream) {
18,350✔
1296
                    can->stream_stored = true;
3✔
1297
                    can->stream_result = match;
3✔
1298
                    TRACE_SID_TXS(s->id, tx, "stream ran, store result %d for next tx (if any)", match);
3✔
1299
                }
3✔
1300
            }
18,350✔
1301
            if (match == DETECT_ENGINE_INSPECT_SIG_MATCH) {
18,350✔
1302
                inspect_flags |= BIT_U32(engine->id);
6,455✔
1303
                engine = engine->next;
6,455✔
1304
                total_matches++;
6,455✔
1305
                continue;
6,455✔
1306
            } else if (match == DETECT_ENGINE_INSPECT_SIG_MATCH_MORE_FILES) {
11,895✔
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) {
11,895✔
1314
                inspect_flags |= DE_STATE_FLAG_SIG_CANT_MATCH;
10,538✔
1315
                inspect_flags |= BIT_U32(engine->id);
10,538✔
1316
            } else if (match == DETECT_ENGINE_INSPECT_SIG_CANT_MATCH_FILES) {
10,538✔
1317
                inspect_flags |= DE_STATE_FLAG_SIG_CANT_MATCH;
×
1318
                inspect_flags |= BIT_U32(engine->id);
×
1319
                file_no_match = 1;
×
1320
            }
×
1321
            /* implied DETECT_ENGINE_INSPECT_SIG_NO_MATCH */
1322
            if (engine->mpm && mpm_before_progress) {
11,895✔
1323
                inspect_flags |= DE_STATE_FLAG_SIG_CANT_MATCH;
6,756✔
1324
                inspect_flags |= BIT_U32(engine->id);
6,756✔
1325
            }
6,756✔
1326
            break;
11,895✔
1327
        } else if (!(inspect_flags & BIT_U32(engine->id)) && s->flags & SIG_FLAG_TXBOTHDIR &&
18,350✔
1328
                   direction != engine->dir) {
14✔
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) {
×
1333
                // do not match yet on request only
1334
                break;
×
1335
            }
×
1336
            engine = engine->next;
×
1337
            continue;
×
1338
        }
×
1339

1340
        engine = engine->next;
14✔
1341
    } while (engine != NULL);
18,401✔
1342
    TRACE_SID_TXS(s->id, tx, "inspect_flags %x, total_matches %u, engine %p",
12,181✔
1343
            inspect_flags, total_matches, engine);
12,181✔
1344

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

1351
    if (stored_flags) {
12,181✔
1352
        *stored_flags = inspect_flags;
8✔
1353
        TRACE_SID_TXS(s->id, tx, "continue inspect flags %08x", inspect_flags);
8✔
1354
    } else {
12,173✔
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);
12,173✔
1359
        if (inspect_flags & DE_STATE_FLAG_SIG_CANT_MATCH) {
12,173✔
1360
            if (file_no_match) {
11,128✔
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,
×
1371
                        inspect_flags, flow_flags, file_no_match);
×
1372
            }
×
1373
        } else if ((inspect_flags & DE_STATE_FLAG_FULL_INSPECT) && mpm_before_progress) {
11,128✔
1374
            TRACE_SID_TXS(s->id, tx, "no need to store match sig, "
267✔
1375
                    "mpm won't trigger for it anymore");
267✔
1376

1377
            if (inspect_flags & DE_STATE_FLAG_FILE_INSPECT) {
267✔
1378
                TRACE_SID_TXS(s->id, tx, "except that for new files, "
×
1379
                        "we may have to revisit anyway");
×
1380
                DetectRunStoreStateTx(scratch->sgh, f, tx->tx_ptr, tx->tx_id, s,
×
1381
                        inspect_flags, flow_flags, file_no_match);
×
1382
            }
×
1383
        } else if ((inspect_flags & DE_STATE_FLAG_FULL_INSPECT) == 0 && mpm_in_progress) {
787✔
1384
            TRACE_SID_TXS(s->id, tx, "no need to store no-match sig, "
750✔
1385
                    "mpm will revisit it");
750✔
1386
        } else if (inspect_flags != 0 || file_no_match != 0) {
750✔
1387
            TRACE_SID_TXS(s->id, tx, "storing state: flags %08x", inspect_flags);
48✔
1388
            DetectRunStoreStateTx(scratch->sgh, f, tx->tx_ptr, tx->tx_id, s,
48✔
1389
                    inspect_flags, flow_flags, file_no_match);
48✔
1390
        }
48✔
1391
    }
12,173✔
1392

1393
    return retval;
12,181✔
1394
}
12,181✔
1395

1396
#define NO_TX                                                                                      \
1397
    {                                                                                              \
3,785,543✔
1398
        NULL, 0, NULL, NULL, 0, 0, 0, 0,                                                           \
3,785,543✔
1399
    }
3,785,543✔
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
{
3,850,603✔
1408
    AppLayerTxData *txd = AppLayerParserGetTxData(ipproto, alproto, tx_ptr);
3,850,603✔
1409
    const int tx_progress = AppLayerParserGetStateProgress(ipproto, alproto, tx_ptr, flow_flags);
3,850,603✔
1410
    bool updated = (flow_flags & STREAM_TOSERVER) ? txd->updated_ts : txd->updated_tc;
3,850,603✔
1411
    if (!updated && tx_progress < tx_end_state && ((flow_flags & STREAM_EOF) == 0)) {
3,850,603✔
1412
        DetectTransaction no_tx = NO_TX;
2,248,531✔
1413
        return no_tx;
2,248,531✔
1414
    }
2,248,531✔
1415
    const uint8_t inspected_flag =
1,602,072✔
1416
            (flow_flags & STREAM_TOSERVER) ? APP_LAYER_TX_INSPECTED_TS : APP_LAYER_TX_INSPECTED_TC;
1,602,072✔
1417
    if (unlikely(txd->flags & inspected_flag)) {
1,602,072✔
1418
        SCLogDebug("%" PRIu64 " tx already fully inspected for %s. Flags %02x", tx_id,
1,536,766✔
1419
                flow_flags & STREAM_TOSERVER ? "toserver" : "toclient", txd->flags);
1,536,766✔
1420
        DetectTransaction no_tx = NO_TX;
1,536,766✔
1421
        return no_tx;
1,536,766✔
1422
    }
1,536,766✔
1423
    const uint8_t skip_flag = (flow_flags & STREAM_TOSERVER) ? APP_LAYER_TX_SKIP_INSPECT_TS
65,306✔
1424
                                                             : APP_LAYER_TX_SKIP_INSPECT_TC;
65,306✔
1425
    if (unlikely(txd->flags & skip_flag)) {
65,306✔
1426
        SCLogDebug("%" PRIu64 " tx should not be inspected in direction %s. Flags %02x", tx_id,
246✔
1427
                flow_flags & STREAM_TOSERVER ? "toserver" : "toclient", txd->flags);
246✔
1428
        DetectTransaction no_tx = NO_TX;
246✔
1429
        return no_tx;
246✔
1430
    }
246✔
1431

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

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

1452
static inline void StoreDetectProgress(
1453
        DetectTransaction *tx, const uint8_t flow_flags, const uint8_t progress)
1454
{
12,352✔
1455
    AppLayerTxData *txd = (AppLayerTxData *)tx->tx_data_ptr;
12,352✔
1456
    if (flow_flags & STREAM_TOSERVER) {
12,352✔
1457
        txd->detect_progress_ts = progress;
3,006✔
1458
    } else {
9,346✔
1459
        txd->detect_progress_tc = progress;
9,346✔
1460
    }
9,346✔
1461
}
12,352✔
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
{
67,039✔
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;
67,039✔
1484
    // First loop only counting the number of elements to add
1485
    for (uint32_t i = 0; i < det_ctx->match_array_cnt; i++) {
253,104✔
1486
        const Signature *s = det_ctx->match_array[i];
186,065✔
1487
        if (s->app_inspect != NULL) {
186,065✔
1488
            (*array_idx)++;
617✔
1489
        }
617✔
1490
    }
186,065✔
1491
    // Future number of elements in tx_candidates after merge
1492
    uint32_t k = *array_idx;
67,039✔
1493

1494
    if (k == j) {
67,039✔
1495
        // no new element from match_array to merge in tx_candidates
1496
        return;
66,493✔
1497
    }
66,493✔
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;) {
2,307✔
1503
        const Signature *s = det_ctx->match_array[i - 1];
1,761✔
1504
        if (s->app_inspect == NULL) {
1,761✔
1505
            // no relevant element, get the next one from match_array
1506
            i--;
1,055✔
1507
            continue;
1,055✔
1508
        }
1,055✔
1509
        // we have one element from match_array to merge in tx_candidates
1510
        k--;
706✔
1511
        if (j > 0) {
706✔
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];
503✔
1514
            if (s->iid <= s0->id) {
503✔
1515
                // get next element from previous tx_candidates
1516
                j--;
89✔
1517
                // take the element from tx_candidates before merge
1518
                det_ctx->tx_candidates[k].s = det_ctx->tx_candidates[j].s;
89✔
1519
                det_ctx->tx_candidates[k].id = det_ctx->tx_candidates[j].id;
89✔
1520
                det_ctx->tx_candidates[k].flags = det_ctx->tx_candidates[j].flags;
89✔
1521
                det_ctx->tx_candidates[k].stream_reset = det_ctx->tx_candidates[j].stream_reset;
89✔
1522
                continue;
89✔
1523
            }
89✔
1524
        } // otherwise
503✔
1525
        // get next element from match_array
1526
        i--;
617✔
1527
        // take the element from match_array
1528
        det_ctx->tx_candidates[k].s = s;
617✔
1529
        det_ctx->tx_candidates[k].id = s->iid;
617✔
1530
        det_ctx->tx_candidates[k].flags = NULL;
617✔
1531
        det_ctx->tx_candidates[k].stream_reset = 0;
617✔
1532
    }
617✔
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
}
546✔
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
{
×
1562
    if (s->flags & SIG_FLAG_FIREWALL) {
×
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) {
×
1571
            const Signature *next_s = det_ctx->tx_candidates[can_idx + 1].s;
×
1572
            SCLogDebug(
×
1573
                    "peek: peeking at sid %u / progress %u", next_s->id, next_s->app_progress_hook);
×
1574
            if (next_s->flags & SIG_FLAG_FIREWALL) {
×
1575
                if (s->app_progress_hook != next_s->app_progress_hook) {
×
1576
                    SCLogDebug("peek: next sid progress %u != current progress %u, so current "
×
1577
                               "is last for progress",
×
1578
                            next_s->app_progress_hook, s->app_progress_hook);
×
1579
                    *last_for_progress = true;
×
1580

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

1599
        if ((*skip_fw_hook) == true) {
×
1600
            if (s->app_progress_hook <= skip_before_progress) {
×
1601
                return -1;
×
1602
            }
×
1603
            *skip_fw_hook = false;
×
1604
        }
×
1605
    } else {
×
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))) {
×
1610
            SCLogDebug("skipping firewall rule %u", s->id);
×
1611
            return 1;
×
1612
        }
×
1613
    }
×
1614
    return 0;
×
1615
}
×
1616

1617
// TODO move into det_ctx?
1618
thread_local Signature default_accept;
1619
static inline void DetectRunAppendDefaultAccept(DetectEngineThreadCtx *det_ctx, Packet *p)
1620
{
1,095,519✔
1621
    if (EngineModeIsFirewall()) {
1,095,519✔
1622
        memset(&default_accept, 0, sizeof(default_accept));
×
1623
        default_accept.action = ACTION_ACCEPT;
×
1624
        default_accept.action_scope = ACTION_SCOPE_PACKET;
×
1625
        default_accept.iid = UINT32_MAX;
×
1626
        default_accept.type = SIG_TYPE_PKT;
×
1627
        default_accept.flags = SIG_FLAG_FIREWALL;
×
1628
        AlertQueueAppend(det_ctx, &default_accept, p, 0, PACKET_ALERT_FLAG_APPLY_ACTION_TO_PACKET);
×
1629
    }
×
1630
}
1,095,519✔
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
{
278✔
1638
    if ((s->flags & SIG_FLAG_FIREWALL) == 0) {
278✔
1639
        return false;
278✔
1640
    }
278✔
1641
    if ((s->action & ACTION_ACCEPT) == 0) {
×
1642
        return false;
×
1643
    }
×
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) {
×
1649
        if (total_txs == tx->tx_id + 1) {
×
1650
            return true;
×
1651
        }
×
1652
    }
×
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) {
×
1657
        if ((total_txs == tx->tx_id + 1) && /* last tx */
×
1658
                (s->app_progress_hook == tx->tx_progress)) {
×
1659
            return true;
×
1660
        }
×
1661
    }
×
1662
    return false;
×
1663
}
×
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
{
×
1671
    *tx_fw_verdict = true;
×
1672

1673
    const enum ActionScope as = s->action_scope;
×
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) {
×
1677
        *skip_fw_hook = true;
×
1678
        *skip_before_progress = s->app_progress_hook;
×
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) {
×
1683
            SCLogDebug("%" PRIu64 ": %s default drop for progress", PcapPacketCntGet(p),
×
1684
                    flow_flags & STREAM_TOSERVER ? "toserver" : "toclient");
×
1685
            PacketDrop(p, ACTION_DROP, PKT_DROP_REASON_DEFAULT_APP_POLICY);
×
1686
            p->flow->flags |= FLOW_ACTION_DROP;
×
1687
            return true;
×
1688
        }
×
1689
        return false;
×
1690
    } else if (as == ACTION_SCOPE_TX) {
×
1691
        tx->tx_data_ptr->flags |= APP_LAYER_TX_ACCEPT;
×
1692
        *skip_fw_hook = true;
×
1693
        *skip_before_progress = (uint8_t)tx_end_state + 1; // skip all hooks
×
1694
        SCLogDebug(
×
1695
                "accept:tx applied, skip_fw_hook, skip_before_progress %u", *skip_before_progress);
×
1696
        return false;
×
1697
    } else if (as == ACTION_SCOPE_PACKET) {
×
1698
        return true;
×
1699
    } else if (as == ACTION_SCOPE_FLOW) {
×
1700
        return true;
×
1701
    }
×
1702
    return false;
×
1703
}
×
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
{
85,972✔
1712
    const uint8_t flow_flags = scratch->flow_flags;
85,972✔
1713
    const SigGroupHead * const sgh = scratch->sgh;
85,972✔
1714
    void * const alstate = f->alstate;
85,972✔
1715
    const uint8_t ipproto = f->proto;
85,972✔
1716
    const AppProto alproto = f->alproto;
85,972✔
1717

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

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

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

1729
    SCLogDebug("packet %" PRIu64, PcapPacketCntGet(p));
85,972✔
1730

1731
    while (1) {
3,876,733✔
1732
        AppLayerGetTxIterTuple ires = IterFunc(ipproto, alproto, alstate, tx_id_min, total_txs, &state);
3,876,565✔
1733
        if (ires.tx_ptr == NULL)
3,876,565✔
1734
            break;
29,042✔
1735

1736
        DetectTransaction tx =
3,847,523✔
1737
                GetDetectTx(ipproto, alproto, ires.tx_id, ires.tx_ptr, tx_end_state, flow_flags);
3,847,523✔
1738
        if (tx.tx_ptr == NULL) {
3,847,523✔
1739
            SCLogDebug("%p/%"PRIu64" no transaction to inspect",
3,783,360✔
1740
                    tx.tx_ptr, tx_id_min);
3,783,360✔
1741

1742
            tx_id_min++; // next (if any) run look for +1
3,783,360✔
1743
            goto next;
3,783,360✔
1744
        }
3,783,360✔
1745
        tx_id_min = tx.tx_id + 1; // next look for cur + 1
64,163✔
1746
        tx_inspected++;
64,163✔
1747

1748
        SCLogDebug("%p/%" PRIu64 " txd flags %02x", tx.tx_ptr, tx_id_min, tx.tx_data_ptr->flags);
64,163✔
1749

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

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

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

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

1773
            for (uint32_t i = 0; i < det_ctx->pmq.rule_id_array_cnt; i++) {
89,077✔
1774
                const Signature *s = de_ctx->sig_array[det_ctx->pmq.rule_id_array[i]];
22,870✔
1775
                const SigIntId id = s->iid;
22,870✔
1776
                det_ctx->tx_candidates[array_idx].s = s;
22,870✔
1777
                det_ctx->tx_candidates[array_idx].id = id;
22,870✔
1778
                det_ctx->tx_candidates[array_idx].flags = NULL;
22,870✔
1779
                det_ctx->tx_candidates[array_idx].stream_reset = 0;
22,870✔
1780
                array_idx++;
22,870✔
1781
            }
22,870✔
1782
            PMQ_RESET(&det_ctx->pmq);
66,207✔
1783
        } else {
2,147,483,647✔
1784
            if (!(RuleMatchCandidateTxArrayHasSpace(det_ctx, total_rules))) {
2,147,483,647✔
1785
                RuleMatchCandidateTxArrayExpand(det_ctx, total_rules);
×
1786
            }
×
1787
        }
2,147,483,647✔
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);
64,163✔
1794

1795
        /* merge stored state into results */
1796
        if (tx.de_state != NULL) {
64,163✔
1797
            const uint32_t old = array_idx;
102✔
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);
102✔
1802
            if (have_new_file) {
102✔
1803
                SCLogDebug("%p/%"PRIu64" destate: need to consider new file",
8✔
1804
                        tx.tx_ptr, tx.tx_id);
8✔
1805
                tx.de_state->flags &= ~DETECT_ENGINE_STATE_FLAG_FILE_NEW;
8✔
1806
            }
8✔
1807

1808
            SigIntId state_cnt = 0;
102✔
1809
            DeStateStore *tx_store = tx.de_state->head;
102✔
1810
            for (; tx_store != NULL; tx_store = tx_store->next) {
155✔
1811
                SCLogDebug("tx_store %p", tx_store);
53✔
1812

1813
                SigIntId store_cnt = 0;
53✔
1814
                for (store_cnt = 0;
53✔
1815
                        store_cnt < DE_STATE_CHUNK_SIZE && state_cnt < tx.de_state->cnt;
108✔
1816
                        store_cnt++, state_cnt++)
55✔
1817
                {
55✔
1818
                    DeStateStoreItem *item = &tx_store->store[store_cnt];
55✔
1819
                    SCLogDebug("rule id %u, inspect_flags %u", item->sid, item->flags);
55✔
1820
                    if (have_new_file && (item->flags & DE_STATE_FLAG_FILE_INSPECT)) {
55✔
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);
×
1824
                        SCLogDebug("rule id %u, post file reset inspect_flags %u", item->sid, item->flags);
×
1825
                    }
×
1826
                    det_ctx->tx_candidates[array_idx].s = de_ctx->sig_array[item->sid];
55✔
1827
                    det_ctx->tx_candidates[array_idx].id = item->sid;
55✔
1828
                    det_ctx->tx_candidates[array_idx].flags = &item->flags;
55✔
1829
                    det_ctx->tx_candidates[array_idx].stream_reset = 0;
55✔
1830
                    array_idx++;
55✔
1831
                }
55✔
1832
            }
53✔
1833
            do_sort |= (old && old != array_idx); // sort if continue list adds sids
102✔
1834
            SCLogDebug("%p/%" PRIu64 " rules added from 'continue' list: %u", tx.tx_ptr, tx.tx_id,
102✔
1835
                    array_idx - old);
102✔
1836
        }
102✔
1837
        if (do_sort) {
64,163✔
1838
            qsort(det_ctx->tx_candidates, array_idx, sizeof(RuleMatchCandidateTx),
12✔
1839
                    DetectRunTxSortHelper);
12✔
1840
        }
12✔
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;
64,163✔
1855
        uint8_t skip_before_progress = 0;
64,163✔
1856
        bool fw_next_progress_missing = false;
64,163✔
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)) {
64,163✔
1861
            fw_verdicted++;
×
1862

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

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

1879
            SCLogDebug("%" PRIu64 ": sid:%u: %s tx %u/%u/%u sig %u", PcapPacketCntGet(p), s->id,
23,354✔
1880
                    flow_flags & STREAM_TOSERVER ? "toserver" : "toclient", tx.tx_progress,
23,354✔
1881
                    tx.detect_progress, tx.detect_progress_orig, s->app_progress_hook);
23,354✔
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 &&
23,556✔
1889
                    det_ctx->tx_candidates[i].s == det_ctx->tx_candidates[i + 1].s) {
23,556✔
1890
                SCLogDebug("%p/%" PRIu64 " inspecting SKIP NEXT: sid %u (%u), flags %08x",
202✔
1891
                        tx.tx_ptr, tx.tx_id, s->id, s->iid, inspect_flags ? *inspect_flags : 0);
202✔
1892
                i++;
202✔
1893
            }
202✔
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)) {
23,354✔
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) {
×
1900
                    const bool accept_tx_applies_to_packet = total_txs == tx.tx_id + 1;
×
1901
                    if (accept_tx_applies_to_packet) {
×
1902
                        SCLogDebug("accept:(tx|hook): should be applied to the packet");
×
1903
                        DetectRunAppendDefaultAccept(det_ctx, p);
×
1904
                    }
×
1905
                }
×
1906
                tx_fw_verdict = true;
×
1907

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

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

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

1919
            if (inspect_flags) {
23,354✔
1920
                if (*inspect_flags & DE_STATE_FLAG_FULL_INSPECT) {
55✔
1921
                    SCLogDebug("%p/%" PRIu64
47✔
1922
                               " inspecting: sid %u (%u), flags %08x DE_STATE_FLAG_FULL_INSPECT",
47✔
1923
                            tx.tx_ptr, tx.tx_id, s->id, s->iid, *inspect_flags);
47✔
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) &&
47✔
1928
                            (s->action & ACTION_ACCEPT) && s->app_progress_hook == tx.tx_progress) {
47✔
1929
                        const bool fw_accept_to_packet = ApplyAcceptToPacket(total_txs, &tx, s);
×
1930
                        break_out_of_app_filter = ApplyAccept(p, flow_flags, s, &tx, tx_end_state,
×
1931
                                fw_next_progress_missing, &tx_fw_verdict, &skip_fw_hook,
×
1932
                                &skip_before_progress);
×
1933
                        if (fw_accept_to_packet)
×
1934
                            DetectRunAppendDefaultAccept(det_ctx, p);
×
1935
                        if (break_out_of_app_filter)
×
1936
                            break;
×
1937
                    }
×
1938
                    continue;
47✔
1939
                }
47✔
1940
                if (*inspect_flags & DE_STATE_FLAG_SIG_CANT_MATCH) {
8✔
1941
                    SCLogDebug("%p/%" PRIu64
×
1942
                               " inspecting: sid %u (%u), flags %08x DE_STATE_FLAG_SIG_CANT_MATCH",
×
1943
                            tx.tx_ptr, tx.tx_id, s->id, s->iid, *inspect_flags);
×
1944
                    continue;
×
1945
                }
×
1946
            }
8✔
1947

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

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

1967
            /* call individual rule inspection */
1968
            RULE_PROFILING_START(p);
23,307✔
1969
            const int r = DetectRunTxInspectRule(tv, de_ctx, det_ctx, p, f, flow_flags,
23,307✔
1970
                    alstate, &tx, s, inspect_flags, can, scratch);
23,307✔
1971
            if (r == 1) {
23,307✔
1972
                /* match */
1973
                DetectRunPostMatch(tv, det_ctx, p, s);
279✔
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);
279✔
1979

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

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

1990
                if ((s->flags & SIG_FLAG_FIREWALL) && (s->action & ACTION_ACCEPT)) {
279✔
1991
                    break_out_of_app_filter = ApplyAccept(p, flow_flags, s, &tx, tx_end_state,
×
1992
                            fw_next_progress_missing, &tx_fw_verdict, &skip_fw_hook,
×
1993
                            &skip_before_progress);
×
1994
                }
×
1995
            } else if (last_for_progress) {
23,028✔
1996
                SCLogDebug("sid %u: not a match: %s rule, last_for_progress %s", s->id,
×
1997
                        (s->flags & SIG_FLAG_FIREWALL) ? "firewall" : "regular",
×
1998
                        BOOL2STR(last_for_progress));
×
1999
                if (s->flags & SIG_FLAG_FIREWALL) {
×
2000
                    SCLogDebug("%" PRIu64 ": %s default drop for progress", PcapPacketCntGet(p),
×
2001
                            flow_flags & STREAM_TOSERVER ? "toserver" : "toclient");
×
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);
×
2005
                    p->flow->flags |= FLOW_ACTION_DROP;
×
2006
                    break_out_of_app_filter = true;
×
2007
                    tx_fw_verdict = true;
×
2008
                }
×
2009
            }
×
2010
            DetectVarProcessList(det_ctx, p->flow, p);
23,307✔
2011
            RULE_PROFILING_END(det_ctx, s, r, p);
23,307✔
2012

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

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

2030
                        SCLogDebug("%p/%" PRIu64 " rule %u (%u) added from 'post match' prefilter",
×
2031
                                tx.tx_ptr, tx.tx_id, ts->id, id);
×
2032
                    }
×
2033
                }
×
2034
                SCLogDebug("%p/%" PRIu64 " rules added from 'post match' prefilter: %u", tx.tx_ptr,
×
2035
                        tx.tx_id, array_idx - prev_array_idx);
×
2036
                if (prev_array_idx != array_idx) {
×
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),
×
2039
                            DetectRunTxSortHelper);
×
2040
                }
×
2041
                det_ctx->post_rule_work_queue.len = 0;
×
2042
                PMQ_RESET(&det_ctx->pmq);
×
2043
            }
×
2044

2045
            if (break_out_of_app_filter)
23,307✔
2046
                break;
×
2047
        }
23,307✔
2048
        if (tx_fw_verdict)
64,163✔
2049
            fw_verdicted++;
×
2050

2051
        det_ctx->tx_id = 0;
64,163✔
2052
        det_ctx->tx_id_set = false;
64,163✔
2053
        det_ctx->p = NULL;
64,163✔
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) {
64,163✔
2059
            SCLogDebug("%" PRIu64 ": %s tx done", PcapPacketCntGet(p),
34,722✔
2060
                    flow_flags & STREAM_TOSERVER ? "toserver" : "toclient");
34,722✔
2061
            const uint8_t inspected_flag = (flow_flags & STREAM_TOSERVER)
34,722✔
2062
                                                   ? APP_LAYER_TX_INSPECTED_TS
34,722✔
2063
                                                   : APP_LAYER_TX_INSPECTED_TC;
34,722✔
2064
            tx.tx_data_ptr->flags |= inspected_flag;
34,722✔
2065
            SCLogDebug("%p/%" PRIu64 " tx is done for direction %s. Progress %02x", tx.tx_ptr,
34,722✔
2066
                    tx.tx_id, flow_flags & STREAM_TOSERVER ? "toserver" : "toclient",
34,722✔
2067
                    tx.detect_progress);
34,722✔
2068
        }
34,722✔
2069

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

2077
            StoreDetectProgress(&tx, flow_flags, tx.detect_progress);
12,355✔
2078
        }
12,355✔
2079

2080
        InspectionBufferClean(det_ctx);
64,163✔
2081

2082
    next:
3,847,717✔
2083
        if (!ires.has_next)
3,847,717✔
2084
            break;
56,956✔
2085
    }
3,847,717✔
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),
86,166✔
2090
            tx_inspected, fw_verdicted);
86,166✔
2091
    if (tx_inspected && have_fw_rules && tx_inspected != fw_verdicted) {
86,166✔
2092
        SCLogDebug("%" PRIu64 ": %s default drop", PcapPacketCntGet(p),
×
2093
                flow_flags & STREAM_TOSERVER ? "toserver" : "toclient");
×
2094
        PacketDrop(p, ACTION_DROP, PKT_DROP_REASON_DEFAULT_APP_POLICY);
×
2095
        p->flow->flags |= FLOW_ACTION_DROP;
×
2096
        return;
×
2097
    }
×
2098
    /* if all tables have been bypassed, we accept:packet */
2099
    if (tx_inspected == 0 && fw_verdicted == 0 && have_fw_rules) {
86,166✔
2100
        DetectRunAppendDefaultAccept(det_ctx, p);
×
2101
    }
×
2102
}
86,166✔
2103

2104
static void DetectRunFrames(ThreadVars *tv, DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx,
2105
        Packet *p, Flow *f, DetectRunScratchpad *scratch)
2106
{
642,740✔
2107
    const SigGroupHead *const sgh = scratch->sgh;
642,740✔
2108
    const AppProto alproto = f->alproto;
642,740✔
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) &&
642,740✔
2113
            ((PKT_IS_TOSERVER(p) && (f->flags & FLOW_TS_APP_UPDATED) == 0) ||
642,740✔
2114
                    (PKT_IS_TOCLIENT(p) && (f->flags & FLOW_TC_APP_UPDATED) == 0))) {
626,404✔
2115
        SCLogDebug("pcap_cnt %" PRIu64 ": %s: skip frame inspection for TCP w/o APP UPDATE",
560,352✔
2116
                PcapPacketCntGet(p), PKT_IS_TOSERVER(p) ? "toserver" : "toclient");
560,352✔
2117
        return;
560,352✔
2118
    }
560,352✔
2119
    FramesContainer *frames_container = AppLayerFramesGetContainer(f);
82,388✔
2120
    if (frames_container == NULL) {
82,425✔
2121
        return;
82,425✔
2122
    }
82,425✔
2123
    Frames *frames;
2,147,483,647✔
2124
    if (PKT_IS_TOSERVER(p)) {
2,147,483,647✔
2125
        frames = &frames_container->toserver;
×
2126
    } else {
2,147,483,647✔
2127
        frames = &frames_container->toclient;
2,147,483,647✔
2128
    }
2,147,483,647✔
2129

2130
    for (uint32_t idx = 0; idx < frames->cnt; idx++) {
2,147,483,647✔
2131
        SCLogDebug("frame %u", idx);
×
2132
        Frame *frame = FrameGetByIndex(frames, idx);
×
2133
        if (frame == NULL) {
×
2134
            continue;
×
2135
        }
×
2136

2137
        det_ctx->frame_inspect_progress = 0;
×
2138
        uint32_t array_idx = 0;
×
2139
        uint32_t total_rules = det_ctx->match_array_cnt;
×
2140

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

2149
            total_rules += det_ctx->pmq.rule_id_array_cnt;
×
2150

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

2156
            for (uint32_t i = 0; i < det_ctx->pmq.rule_id_array_cnt; i++) {
×
2157
                const Signature *s = de_ctx->sig_array[det_ctx->pmq.rule_id_array[i]];
×
2158
                const SigIntId id = s->iid;
×
2159
                det_ctx->tx_candidates[array_idx].s = s;
×
2160
                det_ctx->tx_candidates[array_idx].id = id;
×
2161
                det_ctx->tx_candidates[array_idx].flags = NULL;
×
2162
                det_ctx->tx_candidates[array_idx].stream_reset = 0;
×
2163
                array_idx++;
×
2164
            }
×
2165
            PMQ_RESET(&det_ctx->pmq);
×
2166
        }
×
2167
        /* merge 'state' rules from the regular prefilter */
2168
        uint32_t x = array_idx;
×
2169
        for (uint32_t i = 0; i < det_ctx->match_array_cnt; i++) {
×
2170
            const Signature *s = det_ctx->match_array[i];
×
2171
            if (s->frame_inspect != NULL) {
×
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
        }
×
2183
        SCLogDebug("%p/%" PRIi64 " rules added from 'match' list: %u", frame, frame->id,
×
2184
                array_idx - x);
×
2185
        (void)x;
×
2186

2187
        /* run rules: inspect the match candidates */
2188
        for (uint32_t i = 0; i < array_idx; i++) {
×
2189
            const Signature *s = det_ctx->tx_candidates[i].s;
×
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 &&
×
2196
                    det_ctx->tx_candidates[i].s == det_ctx->tx_candidates[i + 1].s) {
×
2197
                i++;
×
2198
            }
×
2199
            SCLogDebug("%p/%" PRIi64 " inspecting: sid %u (%u)", frame, frame->id, s->id, s->iid);
×
2200

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

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

2213
                    uint8_t alert_flags = (PACKET_ALERT_FLAG_STATE_MATCH | PACKET_ALERT_FLAG_FRAME);
×
2214
                    det_ctx->frame_id = frame->id;
×
2215
                    SCLogDebug(
×
2216
                            "%p/%" PRIi64 " sig %u (%u) matched", frame, frame->id, s->id, s->iid);
×
2217
                    if (frame->flags & FRAME_FLAG_TX_ID_SET) {
×
2218
                        alert_flags |= PACKET_ALERT_FLAG_TX;
×
2219
                    }
×
2220
                    AlertQueueAppend(det_ctx, s, p, frame->tx_id, alert_flags);
×
2221
                }
×
2222
            }
×
2223
            DetectVarProcessList(det_ctx, p->flow, p);
×
2224
            RULE_PROFILING_END(det_ctx, s, r, p);
×
2225
        }
×
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) {
×
2230
            frame->inspect_progress = det_ctx->frame_inspect_progress;
×
2231
            SCLogDebug("frame->inspect_progress: %" PRIu64 " -> updated", frame->inspect_progress);
×
2232
        } else {
×
2233
            SCLogDebug(
×
2234
                    "frame->inspect_progress: %" PRIu64 " -> not updated", frame->inspect_progress);
×
2235
        }
×
2236

2237
        SCLogDebug("%p/%" PRIi64 " rules inspected, running cleanup", frame, frame->id);
×
2238
        InspectionBufferClean(det_ctx);
×
2239
    }
×
2240
}
2,147,483,647✔
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
{
1,102,506✔
2254
    Flow *const f = p->flow;
1,102,506✔
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) {
1,102,506✔
2260
        DEBUG_VALIDATE_BUG_ON(!(PKT_IS_PSEUDOPKT(p)) && !PacketCheckAction(p, ACTION_DROP));
×
2261
        SCReturn;
×
2262
    }
×
2263

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

2269
    } else {
1,102,506✔
2270
        skip = (p->flags & PKT_NOPACKET_INSPECTION || f->flags & (FLOW_ACTION_PASS));
1,102,506✔
2271
    }
1,102,506✔
2272
    if (skip) {
1,102,506✔
2273
        /* enforce prior accept:flow */
2274
        if (f->flags & FLOW_ACTION_ACCEPT) {
×
2275
            p->action |= ACTION_ACCEPT;
×
2276
        }
×
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));
×
2281
        if (pass) {
×
2282
            uint8_t flags = STREAM_FLAGS_FOR_PACKET(p);
×
2283
            flags = FlowGetDisruptionFlags(f, flags);
×
2284
            if (f->alstate) {
×
2285
                AppLayerParserSetTransactionInspectId(f, f->alparser, f->alstate, flags, true);
×
2286
            }
×
2287
        }
×
2288
        SCLogDebug("p->pcap %" PRIu64 ": no detection on packet, "
×
2289
                   "PKT_NOPACKET_INSPECTION is set",
×
2290
                PcapPacketCntGet(p));
×
2291
        return;
×
2292
    }
×
2293

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

2298

2299
static void DetectNoFlow(ThreadVars *tv,
2300
                         DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx,
2301
                         Packet *p)
2302
{
80,567✔
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))) {
80,567✔
2305
        return;
20✔
2306
    }
20✔
2307

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

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

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

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

2328
    SCLogDebug("thread id: %u, packet %" PRIu64 ", sgh %p", tv->id, PcapPacketCntGet(p), sgh);
×
2329
    DetectRunPacketHook(tv, de_ctx, det_ctx, sgh, p);
×
2330
    return p->action;
×
2331
}
×
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
{
1,182,990✔
2343
    DEBUG_VALIDATE_PACKET(p);
1,182,990✔
2344

2345
    DetectEngineCtx *de_ctx = NULL;
1,182,990✔
2346
    DetectEngineThreadCtx *det_ctx = (DetectEngineThreadCtx *)data;
1,182,990✔
2347
    if (det_ctx == NULL) {
1,182,990✔
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)) {
1,182,990✔
2353
        (void)SC_ATOMIC_SET(det_ctx->so_far_used_by_detect, 1);
1,860✔
2354
        SCLogDebug("Detect Engine using new det_ctx - %p",
1,860✔
2355
                  det_ctx);
1,860✔
2356
    }
1,860✔
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)
1,182,990✔
2361
    {
×
2362
        uint32_t tenant_id = p->tenant_id;
×
2363
        if (tenant_id == 0)
×
2364
            tenant_id = det_ctx->TenantGetId(det_ctx, p);
×
2365
        if (tenant_id > 0 && tenant_id < det_ctx->mt_det_ctxs_cnt) {
×
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 {
×
2379
            /* use default if no tenants are registered for this packet */
2380
            de_ctx = det_ctx->de_ctx;
×
2381
        }
×
2382
    } else {
1,182,990✔
2383
        de_ctx = det_ctx->de_ctx;
1,182,990✔
2384
    }
1,182,990✔
2385

2386
    if (p->flow) {
1,182,990✔
2387
        DetectFlow(tv, de_ctx, det_ctx, p);
1,102,429✔
2388
    } else {
1,120,728✔
2389
        DetectNoFlow(tv, de_ctx, det_ctx, p);
80,561✔
2390
    }
80,561✔
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;
1,182,990✔
2403
error:
×
2404
    return TM_ECODE_FAILED;
×
2405
}
1,182,990✔
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
{
×
2412
    DetectPostInspectFileFlagsUpdate(f, NULL /* no sgh */, STREAM_TOSERVER);
×
2413
    DetectPostInspectFileFlagsUpdate(f, NULL /* no sgh */, STREAM_TOCLIENT);
×
2414
}
×
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
{
2423
    if (p->flow) {
2424
        DetectFlow(tv, de_ctx, det_ctx, p);
2425
    } else {
2426
        DetectNoFlow(tv, de_ctx, det_ctx, p);
2427
    }
2428
}
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