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

OISF / suricata / 23350122333

20 Mar 2026 03:33PM UTC coverage: 76.492% (-2.8%) from 79.315%
23350122333

Pull #15053

github

web-flow
Merge f5bf69f97 into 6587e363a
Pull Request #15053: Flow queue/v3

113 of 129 new or added lines in 9 files covered. (87.6%)

9534 existing lines in 453 files now uncovered.

256601 of 335461 relevant lines covered (76.49%)

4680806.66 hits per line

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

88.37
/src/detect-engine-prefilter.c
1
/* Copyright (C) 2016-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
 * Prefilter engine
24
 *
25
 * Prefilter engines have as purpose to check for a critical common part of
26
 * a set of rules. If the condition is present in the traffic, the rules
27
 * will have to be inspected individually. Otherwise, the rules can be
28
 * skipped.
29
 *
30
 * The best example of this is the MPM. From each rule take a pattern and
31
 * add it to the MPM state machine. Inspect that in one step and only
32
 * individually inspect the rules that had a match in MPM.
33
 *
34
 * This prefilter API is designed to abstract this logic so that it becomes
35
 * easier to add other types of prefilters.
36
 *
37
 * The prefilter engines are structured as a simple list of engines. Each
38
 * engine checks for a condition using it's callback function and private
39
 * data. It then adds the rule match candidates to the PrefilterRuleStore
40
 * structure.
41
 *
42
 * After the engines have run the resulting list of match candidates is
43
 * sorted by the rule id's so that the individual inspection happens in
44
 * the correct order.
45
 */
46

47
#include "suricata-common.h"
48
#include "suricata.h"
49

50
#include "detect-engine.h"
51
#include "detect-engine-prefilter.h"
52
#include "detect-engine-mpm.h"
53
#include "detect-engine-frame.h"
54
#include "detect-engine-uint.h"
55

56
#include "app-layer-parser.h"
57
#include "app-layer-htp.h"
58

59
#include "util-profiling.h"
60
#include "util-validate.h"
61
#include "util-hash-string.h"
62

63
static int PrefilterStoreGetId(DetectEngineCtx *de_ctx,
64
        const char *name, void (*FreeFunc)(void *));
65
static const PrefilterStore *PrefilterStoreGetStore(const DetectEngineCtx *de_ctx,
66
        const uint32_t id);
67

68
static inline void QuickSortSigIntId(SigIntId *sids, uint32_t n)
69
{
1,955,571✔
70
    if (n < 2)
1,955,571✔
71
        return;
1,091,499✔
72
    SigIntId p = sids[n / 2];
864,072✔
73
    SigIntId *l = sids;
864,072✔
74
    SigIntId *r = sids + n - 1;
864,072✔
75
    while (l <= r) {
12,291,233✔
76
        if (*l < p)
11,427,161✔
77
            l++;
5,440,144✔
78
        else if (*r > p)
5,987,017✔
79
            r--;
4,996,350✔
80
        else {
990,667✔
81
            SigIntId t = *l;
990,667✔
82
            *l = *r;
990,667✔
83
            *r = t;
990,667✔
84
            l++;
990,667✔
85
            r--;
990,667✔
86
        }
990,667✔
87
    }
11,427,161✔
88
    QuickSortSigIntId(sids, (uint32_t)(r - sids) + 1);
864,072✔
89
    QuickSortSigIntId(l, (uint32_t)(sids + n - l));
864,072✔
90
}
864,072✔
91

92
/**
93
 * \brief run prefilter engines on a transaction
94
 */
95
void DetectRunPrefilterTx(DetectEngineThreadCtx *det_ctx,
96
        const SigGroupHead *sgh,
97
        Packet *p,
98
        const uint8_t ipproto,
99
        const uint8_t flow_flags,
100
        const AppProto alproto,
101
        void *alstate,
102
        DetectTransaction *tx)
103
{
116,467✔
104
    /* reset rule store */
105
    det_ctx->pmq.rule_id_array_cnt = 0;
116,467✔
106

107
    SCLogDebug("packet %" PRIu64 " tx %p progress %d tx->detect_progress %02x", PcapPacketCntGet(p),
116,467✔
108
            tx->tx_ptr, tx->tx_progress, tx->detect_progress);
116,467✔
109

110
    PrefilterEngine *engine = sgh->tx_engines;
116,467✔
111
    do {
2,242,996✔
112
        // based on flow alproto, and engine, we get right tx_ptr
113
        void *tx_ptr = DetectGetInnerTx(tx->tx_ptr, alproto, engine->alproto, flow_flags);
2,242,996✔
114
        if (tx_ptr == NULL) {
2,242,996✔
115
            // incompatible engine->alproto with flow alproto
116
            goto next;
1,976,097✔
117
        }
1,976,097✔
118

119
        if (engine->ctx.tx_min_progress != -1) {
268,157✔
120
#ifdef DEBUG
121
            const char *pname = AppLayerParserGetStateNameById(ipproto, engine->alproto,
122
                    engine->ctx.tx_min_progress, flow_flags & (STREAM_TOSERVER | STREAM_TOCLIENT));
123
            SCLogDebug("engine %p min_progress %d %s:%s", engine, engine->ctx.tx_min_progress,
124
                    AppProtoToString(engine->alproto), pname);
125
#endif
126
            /* if engine needs tx state to be higher, break out. */
127
            if (engine->ctx.tx_min_progress > tx->tx_progress)
268,152✔
128
                break;
8,195✔
129
            if (tx->tx_progress > engine->ctx.tx_min_progress) {
259,957✔
130
                SCLogDebug("tx->tx_progress %u > engine->ctx.tx_min_progress %d", tx->tx_progress,
225,990✔
131
                        engine->ctx.tx_min_progress);
225,990✔
132

133
                /* if state value is at or beyond engine state, we can skip it. It means we ran at
134
                 * least once already. */
135
                if (tx->detect_progress > engine->ctx.tx_min_progress) {
225,990✔
136
                    SCLogDebug("tx already marked progress as beyond engine: %u > %u",
118,504✔
137
                            tx->detect_progress, engine->ctx.tx_min_progress);
118,504✔
138
                    goto next;
118,504✔
139
                } else {
127,524✔
140
                    SCLogDebug("tx->tx_progress %u > engine->ctx.tx_min_progress %d: "
107,486✔
141
                               "tx->detect_progress %u",
107,486✔
142
                            tx->tx_progress, engine->ctx.tx_min_progress, tx->detect_progress);
107,486✔
143
                }
107,486✔
144
            }
225,990✔
145
#ifdef DEBUG
146
            uint32_t old = det_ctx->pmq.rule_id_array_cnt;
147
#endif
148
            PREFILTER_PROFILING_START(det_ctx);
141,453✔
149
            engine->cb.PrefilterTx(det_ctx, engine->pectx, p, p->flow, tx_ptr, tx->tx_id,
141,453✔
150
                    tx->tx_data_ptr, flow_flags);
141,453✔
151
            PREFILTER_PROFILING_END(det_ctx, engine->gid);
141,453✔
152
            SCLogDebug("engine %p min_progress %d %s:%s: results %u", engine,
141,453✔
153
                    engine->ctx.tx_min_progress, AppProtoToString(engine->alproto), pname,
141,453✔
154
                    det_ctx->pmq.rule_id_array_cnt - old);
141,453✔
155

156
            if (tx->tx_progress > engine->ctx.tx_min_progress && engine->is_last_for_progress) {
141,453✔
157
                /* track with an offset of one, so that tx->progress 0 complete is tracked
158
                 * as 1, progress 1 as 2, etc. This is to allow 0 to mean: nothing tracked, even
159
                 * though a parser may use 0 as a valid value. */
160
                tx->detect_progress = engine->ctx.tx_min_progress + 1;
39,577✔
161
                SCLogDebug("tx->tx_progress %d engine->ctx.tx_min_progress %d "
39,577✔
162
                           "engine->is_last_for_progress %d => tx->detect_progress updated to %02x",
39,577✔
163
                        tx->tx_progress, engine->ctx.tx_min_progress, engine->is_last_for_progress,
39,577✔
164
                        tx->detect_progress);
39,577✔
165
            }
39,577✔
166
        } else {
2,147,506,530✔
167
            PREFILTER_PROFILING_START(det_ctx);
2,147,483,652✔
168
            engine->cb.PrefilterTx(det_ctx, engine->pectx, p, p->flow, tx_ptr, tx->tx_id,
2,147,483,652✔
169
                    tx->tx_data_ptr, flow_flags);
2,147,483,652✔
170
            PREFILTER_PROFILING_END(det_ctx, engine->gid);
2,147,483,652✔
171
        }
2,147,483,652✔
172
    next:
2,234,201✔
173
        if (engine->is_last)
2,234,201✔
174
            break;
108,267✔
175
        engine++;
2,125,934✔
176
    } while (1);
2,125,934✔
177

178
    /* Sort the rule list to lets look at pmq.
179
     * NOTE due to merging of 'stream' pmqs we *MAY* have duplicate entries */
180
    if (likely(det_ctx->pmq.rule_id_array_cnt > 1)) {
115,867✔
181
        PACKET_PROFILING_DETECT_START(p, PROF_DETECT_PF_SORT1);
6,454✔
182
        QuickSortSigIntId(det_ctx->pmq.rule_id_array, det_ctx->pmq.rule_id_array_cnt);
6,454✔
183
        PACKET_PROFILING_DETECT_END(p, PROF_DETECT_PF_SORT1);
6,454✔
184
    }
6,454✔
185
}
115,867✔
186

187
/** \brief invoke post-rule match "prefilter" engines
188
 *
189
 * Invoke prefilter engines that depend on a rule match to run.
190
 * e.g. the flowbits:set prefilter that adds sids that depend on
191
 * a flowbit "set" to the match array.
192
 */
193
void PrefilterPostRuleMatch(
194
        DetectEngineThreadCtx *det_ctx, const SigGroupHead *sgh, Packet *p, Flow *f)
195
{
5✔
196
    SCLogDebug("post-rule-match engines %p", sgh->post_rule_match_engines);
5✔
197
    if (sgh->post_rule_match_engines) {
5✔
198
        PrefilterEngine *engine = sgh->post_rule_match_engines;
5✔
199
        do {
5✔
200
            SCLogDebug("running post-rule-match engine");
5✔
201
            PREFILTER_PROFILING_START(det_ctx);
5✔
202
            engine->cb.PrefilterPostRule(det_ctx, engine->pectx, p, f);
5✔
203
            PREFILTER_PROFILING_END(det_ctx, engine->gid);
5✔
204

205
            if (engine->is_last)
5✔
206
                break;
5✔
207
            engine++;
×
208
        } while (1);
×
209

210
        if (det_ctx->pmq.rule_id_array_cnt > 1) {
5✔
211
            QuickSortSigIntId(det_ctx->pmq.rule_id_array, det_ctx->pmq.rule_id_array_cnt);
4✔
212
        }
4✔
213
    }
5✔
214
}
5✔
215

216
void Prefilter(DetectEngineThreadCtx *det_ctx, const SigGroupHead *sgh, Packet *p,
217
        const uint8_t flags, const SignatureMask mask)
218
{
2,501,220✔
219
    SCEnter();
2,501,220✔
220
#if 0
221
    /* TODO review this check */
222
    SCLogDebug("sgh %p frame_engines %p", sgh, sgh->frame_engines);
223
    if (p->proto == IPPROTO_TCP && sgh->frame_engines && p->flow &&
224
            p->flow->alproto != ALPROTO_UNKNOWN && p->flow->alparser != NULL) {
225
        PACKET_PROFILING_DETECT_START(p, PROF_DETECT_PF_RECORD);
226
        PrefilterFrames(det_ctx, sgh, p, flags, p->flow->alproto);
227
        PACKET_PROFILING_DETECT_END(p, PROF_DETECT_PF_RECORD);
228
    }
229
#endif
230
    if (sgh->pkt_engines) {
2,501,220✔
231
        PACKET_PROFILING_DETECT_START(p, PROF_DETECT_PF_PKT);
1,505,264✔
232
        /* run packet engines */
233
        PrefilterEngine *engine = sgh->pkt_engines;
1,505,264✔
234
        do {
1,637,849✔
235
            /* run engine if:
236
             * mask matches
237
             * no hook is used OR hook matches
238
             */
239
            if (((engine->ctx.pkt.mask & mask) == engine->ctx.pkt.mask) &&
1,637,849✔
240
                    (engine->ctx.pkt.hook == 0 || (p->pkt_hooks & BIT_U16(engine->ctx.pkt.hook)))) {
1,637,849✔
241
                PREFILTER_PROFILING_START(det_ctx);
1,475,624✔
242
                engine->cb.Prefilter(det_ctx, p, engine->pectx);
1,475,624✔
243
                PREFILTER_PROFILING_END(det_ctx, engine->gid);
1,475,624✔
244
            }
1,475,624✔
245

246
            if (engine->is_last)
1,637,849✔
247
                break;
1,505,214✔
248
            engine++;
132,635✔
249
        } while (1);
132,635✔
250
        PACKET_PROFILING_DETECT_END(p, PROF_DETECT_PF_PKT);
816,947✔
251
    }
1,505,264✔
252

253
    /* run payload inspecting engines */
254
    if (sgh->payload_engines &&
2,501,220✔
255
        (p->payload_len || (p->flags & PKT_DETECT_HAS_STREAMDATA)) &&
2,501,220✔
256
        !(p->flags & PKT_NOPAYLOAD_INSPECTION))
2,501,220✔
257
    {
1,052,655✔
258
        PACKET_PROFILING_DETECT_START(p, PROF_DETECT_PF_PAYLOAD);
1,052,655✔
259
        PrefilterEngine *engine = sgh->payload_engines;
1,052,655✔
260
        while (1) {
1,384,853✔
261
            PREFILTER_PROFILING_START(det_ctx);
1,384,853✔
262
            engine->cb.Prefilter(det_ctx, p, engine->pectx);
1,384,853✔
263
            PREFILTER_PROFILING_END(det_ctx, engine->gid);
1,384,853✔
264

265
            if (engine->is_last)
1,384,853✔
266
                break;
1,052,674✔
267
            engine++;
332,179✔
268
        }
332,179✔
269
        PACKET_PROFILING_DETECT_END(p, PROF_DETECT_PF_PAYLOAD);
1,052,655✔
270
    }
1,052,655✔
271

272
    /* Sort the rule list to lets look at pmq.
273
     * NOTE due to merging of 'stream' pmqs we *MAY* have duplicate entries */
274
    if (likely(det_ctx->pmq.rule_id_array_cnt > 1)) {
2,501,220✔
275
        PACKET_PROFILING_DETECT_START(p, PROF_DETECT_PF_SORT1);
220,926✔
276
        QuickSortSigIntId(det_ctx->pmq.rule_id_array, det_ctx->pmq.rule_id_array_cnt);
220,926✔
277
        PACKET_PROFILING_DETECT_END(p, PROF_DETECT_PF_SORT1);
220,926✔
278
    }
220,926✔
279
    SCReturn;
2,501,220✔
280
}
2,501,220✔
281

282
int PrefilterAppendEngine(DetectEngineCtx *de_ctx, SigGroupHead *sgh, PrefilterPktFn PrefilterFunc,
283
        SignatureMask mask, enum SignatureHookPkt hook, void *pectx, void (*FreeFunc)(void *pectx),
284
        const char *name)
285
{
1,906✔
286
    if (sgh == NULL || PrefilterFunc == NULL || pectx == NULL)
1,906✔
287
        return -1;
×
288

289
    PrefilterEngineList *e = SCMallocAligned(sizeof(*e), CLS);
1,906✔
290
    if (e == NULL)
1,906✔
291
        return -1;
×
292
    memset(e, 0x00, sizeof(*e));
1,906✔
293

294
    // TODO right now we represent the hook in a u8 in the prefilter engine for space reasons.
295
    BUG_ON(hook >= 8);
1,906✔
296

297
    e->Prefilter = PrefilterFunc;
1,906✔
298
    e->pectx = pectx;
1,906✔
299
    e->Free = FreeFunc;
1,906✔
300
    e->pkt_mask = mask;
1,906✔
301
    e->pkt_hook = hook;
1,906✔
302

303
    if (sgh->init->pkt_engines == NULL) {
1,906✔
304
        sgh->init->pkt_engines = e;
1,851✔
305
    } else {
1,851✔
306
        PrefilterEngineList *t = sgh->init->pkt_engines;
55✔
307
        while (t->next != NULL) {
64✔
308
            t = t->next;
9✔
309
        }
9✔
310

311
        t->next = e;
55✔
312
        e->id = t->id + 1;
55✔
313
    }
55✔
314

315
    e->name = name;
1,906✔
316
    e->gid = PrefilterStoreGetId(de_ctx, e->name, e->Free);
1,906✔
317
    return 0;
1,906✔
318
}
1,906✔
319

320
int PrefilterAppendPayloadEngine(DetectEngineCtx *de_ctx, SigGroupHead *sgh,
321
        PrefilterPktFn PrefilterFunc, void *pectx, void (*FreeFunc)(void *pectx), const char *name)
322
{
1,795✔
323
    if (sgh == NULL || PrefilterFunc == NULL || pectx == NULL)
1,795✔
324
        return -1;
16✔
325

326
    PrefilterEngineList *e = SCMallocAligned(sizeof(*e), CLS);
1,779✔
327
    if (e == NULL)
1,779✔
328
        return -1;
×
329
    memset(e, 0x00, sizeof(*e));
1,779✔
330

331
    e->Prefilter = PrefilterFunc;
1,779✔
332
    e->pectx = pectx;
1,779✔
333
    e->Free = FreeFunc;
1,779✔
334

335
    if (sgh->init->payload_engines == NULL) {
1,779✔
336
        sgh->init->payload_engines = e;
1,476✔
337
    } else {
1,476✔
338
        PrefilterEngineList *t = sgh->init->payload_engines;
303✔
339
        while (t->next != NULL) {
303✔
340
            t = t->next;
×
341
        }
×
342

343
        t->next = e;
303✔
344
        e->id = t->id + 1;
303✔
345
    }
303✔
346

347
    e->name = name;
1,779✔
348
    e->gid = PrefilterStoreGetId(de_ctx, e->name, e->Free);
1,779✔
349
    return 0;
1,779✔
350
}
1,779✔
351

352
int PrefilterAppendTxEngine(DetectEngineCtx *de_ctx, SigGroupHead *sgh,
353
        PrefilterTxFn PrefilterTxFunc, AppProto alproto, int tx_min_progress, void *pectx,
354
        void (*FreeFunc)(void *pectx), const char *name)
355
{
10,941✔
356
    if (sgh == NULL || PrefilterTxFunc == NULL || pectx == NULL)
10,941✔
357
        return -1;
×
358

359
    PrefilterEngineList *e = SCMallocAligned(sizeof(*e), CLS);
10,941✔
360
    if (e == NULL)
10,941✔
361
        return -1;
×
362
    memset(e, 0x00, sizeof(*e));
10,941✔
363

364
    e->PrefilterTx = PrefilterTxFunc;
10,941✔
365
    e->pectx = pectx;
10,941✔
366
    e->alproto = alproto;
10,941✔
367
    // TODO change function prototype ?
368
    DEBUG_VALIDATE_BUG_ON(tx_min_progress > INT8_MAX);
10,941✔
369
    e->tx_min_progress = (uint8_t)tx_min_progress;
10,941✔
370
    e->Free = FreeFunc;
10,941✔
371

372
    if (sgh->init->tx_engines == NULL) {
10,941✔
373
        sgh->init->tx_engines = e;
2,781✔
374
    } else {
8,427✔
375
        PrefilterEngineList *t = sgh->init->tx_engines;
8,160✔
376
        while (t->next != NULL) {
126,235✔
377
            t = t->next;
118,075✔
378
        }
118,075✔
379

380
        t->next = e;
8,160✔
381
        e->id = t->id + 1;
8,160✔
382
    }
8,160✔
383

384
    e->name = name;
10,941✔
385
    e->gid = PrefilterStoreGetId(de_ctx, e->name, e->Free);
10,941✔
386
    return 0;
10,941✔
387
}
10,941✔
388

389
int PrefilterAppendFrameEngine(DetectEngineCtx *de_ctx, SigGroupHead *sgh,
390
        PrefilterFrameFn PrefilterFrameFunc, AppProto alproto, uint8_t frame_type, void *pectx,
391
        void (*FreeFunc)(void *pectx), const char *name)
392
{
147✔
393
    if (sgh == NULL || PrefilterFrameFunc == NULL || pectx == NULL)
147✔
394
        return -1;
×
395

396
    PrefilterEngineList *e = SCMallocAligned(sizeof(*e), CLS);
147✔
397
    if (e == NULL)
147✔
398
        return -1;
×
399
    memset(e, 0x00, sizeof(*e));
147✔
400

401
    e->frame_type = frame_type;
147✔
402
    e->alproto = alproto;
147✔
403
    e->PrefilterFrame = PrefilterFrameFunc;
147✔
404
    e->pectx = pectx;
147✔
405
    e->Free = FreeFunc;
147✔
406

407
    if (sgh->init->frame_engines == NULL) {
147✔
408
        sgh->init->frame_engines = e;
69✔
409
    } else {
78✔
410
        PrefilterEngineList *t = sgh->init->frame_engines;
78✔
411
        while (t->next != NULL) {
151✔
412
            t = t->next;
73✔
413
        }
73✔
414

415
        t->next = e;
78✔
416
        e->id = t->id + 1;
78✔
417
    }
78✔
418

419
    e->name = name;
147✔
420
    e->gid = PrefilterStoreGetId(de_ctx, e->name, e->Free);
147✔
421
    return 0;
147✔
422
}
147✔
423

424
int PrefilterAppendPostRuleEngine(DetectEngineCtx *de_ctx, SigGroupHead *sgh,
425
        void (*PrefilterPostRuleFunc)(
426
                DetectEngineThreadCtx *det_ctx, const void *pectx, Packet *p, Flow *f),
427
        void *pectx, void (*FreeFunc)(void *pectx), const char *name)
428
{
7✔
429
    if (sgh == NULL || PrefilterPostRuleFunc == NULL || pectx == NULL)
7✔
430
        return -1;
×
431

432
    PrefilterEngineList *e = SCMallocAligned(sizeof(*e), CLS);
7✔
433
    if (e == NULL)
7✔
434
        return -1;
×
435
    memset(e, 0x00, sizeof(*e));
7✔
436
    e->PrefilterPostRule = PrefilterPostRuleFunc;
7✔
437
    e->pectx = pectx;
7✔
438
    e->Free = FreeFunc;
7✔
439

440
    if (sgh->init->post_rule_match_engines == NULL) {
7✔
441
        sgh->init->post_rule_match_engines = e;
7✔
442
    } else {
7✔
443
        PrefilterEngineList *t = sgh->init->post_rule_match_engines;
×
444
        while (t->next != NULL) {
×
445
            t = t->next;
×
446
        }
×
447

448
        t->next = e;
×
449
        e->id = t->id + 1;
×
450
    }
×
451

452
    e->name = name;
7✔
453
    e->gid = PrefilterStoreGetId(de_ctx, e->name, e->Free);
7✔
454
    return 0;
7✔
455
}
7✔
456

457
static void PrefilterFreeEngineList(PrefilterEngineList *e)
458
{
14,780✔
459
    if (e->Free && e->pectx) {
14,780✔
460
        e->Free(e->pectx);
×
461
    }
×
462
    SCFreeAligned(e);
14,780✔
463
}
14,780✔
464

465
void PrefilterFreeEnginesList(PrefilterEngineList *list)
466
{
576,540✔
467
    PrefilterEngineList *t = list;
576,540✔
468

469
    while (t != NULL) {
591,320✔
470
        PrefilterEngineList *next = t->next;
14,780✔
471
        PrefilterFreeEngineList(t);
14,780✔
472
        t = next;
14,780✔
473
    }
14,780✔
474
}
576,540✔
475

476
static void PrefilterFreeEngines(const DetectEngineCtx *de_ctx, PrefilterEngine *list)
477
{
6,184✔
478
    PrefilterEngine *t = list;
6,184✔
479

480
    while (1) {
14,780✔
481
        const PrefilterStore *s = PrefilterStoreGetStore(de_ctx, t->gid);
14,780✔
482
        if (s && s->FreeFunc && t->pectx) {
14,780✔
483
            s->FreeFunc(t->pectx);
13,001✔
484
        }
13,001✔
485

486
        if (t->is_last)
14,780✔
487
            break;
6,184✔
488
        t++;
8,596✔
489
    }
8,596✔
490
    SCFreeAligned(list);
6,184✔
491
}
6,184✔
492

493
void PrefilterCleanupRuleGroup(const DetectEngineCtx *de_ctx, SigGroupHead *sgh)
494
{
115,308✔
495
    if (sgh->pkt_engines) {
115,308✔
496
        PrefilterFreeEngines(de_ctx, sgh->pkt_engines);
1,851✔
497
        sgh->pkt_engines = NULL;
1,851✔
498
    }
1,851✔
499
    if (sgh->payload_engines) {
115,308✔
500
        PrefilterFreeEngines(de_ctx, sgh->payload_engines);
1,476✔
501
        sgh->payload_engines = NULL;
1,476✔
502
    }
1,476✔
503
    if (sgh->tx_engines) {
115,308✔
504
        PrefilterFreeEngines(de_ctx, sgh->tx_engines);
2,781✔
505
        sgh->tx_engines = NULL;
2,781✔
506
    }
2,781✔
507
    if (sgh->frame_engines) {
115,308✔
508
        PrefilterFreeEngines(de_ctx, sgh->frame_engines);
69✔
509
        sgh->frame_engines = NULL;
69✔
510
    }
69✔
511
    if (sgh->post_rule_match_engines) {
115,308✔
512
        PrefilterFreeEngines(de_ctx, sgh->post_rule_match_engines);
7✔
513
        sgh->post_rule_match_engines = NULL;
7✔
514
    }
7✔
515
}
115,308✔
516

517
static int PrefilterSetupRuleGroupSortHelper(const void *a, const void *b)
518
{
26,740✔
519
    const PrefilterEngine *s0 = a;
26,740✔
520
    const PrefilterEngine *s1 = b;
26,740✔
521
    if (s1->ctx.tx_min_progress == s0->ctx.tx_min_progress) {
26,740✔
522
        if (s1->alproto == s0->alproto) {
12,682✔
523
            return s0->local_id > s1->local_id ? 1 : -1;
8,640✔
524
        } else {
8,750✔
525
            return s0->alproto > s1->alproto ? 1 : -1;
4,042✔
526
        }
4,042✔
527
    } else {
14,060✔
528
        return s0->ctx.tx_min_progress > s1->ctx.tx_min_progress ? 1 : -1;
14,058✔
529
    }
14,058✔
530
}
26,740✔
531

532
/** prefilter engine data for the non-prefilter engine for the prefilter API */
533
struct PrefilterNonPFDataSig {
534
    uint32_t sid : 30;
535
    uint32_t type : 2; /**< type for `value` field below: 0:alproto 1:dport 2:dsize */
536
    uint16_t value;
537
    /* since we have 2 more bytes available due to padding, we can add some additional
538
     * filters here. */
539
    union {
540
        struct {
541
            SignatureMask sig_mask;
542
        } pkt;
543
        struct {
544
            /* filter for frame type */
545
            uint8_t type;
546
        } frame;
547
        struct {
548
            uint8_t foo; // TODO unused
549
        } app;
550
    };
551
};
552

553
struct PrefilterNonPFData {
554
    uint32_t size;
555
    struct PrefilterNonPFDataSig array[];
556
};
557

558
struct PrefilterNonPFDataTx {
559
    uint32_t size;
560
    uint32_t array[];
561
};
562

563
/** \internal
564
 *  \brief wrapper for use in APIs */
565
static void PrefilterNonPFDataFree(void *data)
566
{
2,794✔
567
    SCFree(data);
2,794✔
568
}
2,794✔
569

570
static void PrefilterTxNonPF(DetectEngineThreadCtx *det_ctx, const void *pectx, Packet *p, Flow *f,
571
        void *tx, const uint64_t tx_id, const AppLayerTxData *tx_data, const uint8_t flags)
572
{
7,033✔
573
    const struct PrefilterNonPFDataTx *data = (const struct PrefilterNonPFDataTx *)pectx;
7,033✔
574
    SCLogDebug("adding %u sids", data->size);
7,033✔
575
    PrefilterAddSids(&det_ctx->pmq, data->array, data->size);
7,033✔
576
}
7,033✔
577

578
#ifdef NONPF_PKT_STATS
579
static thread_local uint64_t prefilter_pkt_nonpf_called = 0;
580
static thread_local uint64_t prefilter_pkt_nonpf_mask_fail = 0;
581
static thread_local uint64_t prefilter_pkt_nonpf_alproto_fail = 0;
582
static thread_local uint64_t prefilter_pkt_nonpf_dsize_fail = 0;
583
static thread_local uint64_t prefilter_pkt_nonpf_dport_fail = 0;
584
static thread_local uint64_t prefilter_pkt_nonpf_sids = 0;
585
#define NONPF_PKT_STATS_INCR(s) (s)++
586
#else
587
#define NONPF_PKT_STATS_INCR(s)
588
#endif
589

590
void PrefilterPktNonPFStatsDump(void)
591
{
13,063✔
592
#ifdef NONPF_PKT_STATS
593
    SCLogDebug("prefilter non-pf: called:%" PRIu64 ", mask_fail:%" PRIu64 ", alproto fail:%" PRIu64
594
               ", dport fail:%" PRIu64 ", dsize fail:%" PRIu64 ", sids:%" PRIu64
595
               ", avg sids:%" PRIu64,
596
            prefilter_pkt_nonpf_called, prefilter_pkt_nonpf_mask_fail,
597
            prefilter_pkt_nonpf_alproto_fail, prefilter_pkt_nonpf_dport_fail,
598
            prefilter_pkt_nonpf_dsize_fail, prefilter_pkt_nonpf_sids,
599
            prefilter_pkt_nonpf_called ? prefilter_pkt_nonpf_sids / prefilter_pkt_nonpf_called : 0);
600
#endif
601
}
13,063✔
602

603
static void PrefilterPktNonPF(DetectEngineThreadCtx *det_ctx, Packet *p, const void *pectx)
604
{
1,338,999✔
605
    const uint16_t alproto = p->flow ? p->flow->alproto : ALPROTO_UNKNOWN;
1,338,999✔
606
    const SignatureMask mask = p->sig_mask;
1,338,999✔
607
    const struct PrefilterNonPFData *data = (const struct PrefilterNonPFData *)pectx;
1,338,999✔
608
    SCLogDebug("adding %u sids", data->size);
1,338,999✔
609
    NONPF_PKT_STATS_INCR(prefilter_pkt_nonpf_called);
1,338,999✔
610
    for (uint32_t i = 0; i < data->size; i++) {
7,734,413✔
611
        const struct PrefilterNonPFDataSig *ds = &data->array[i];
6,395,478✔
612
        const SignatureMask rule_mask = ds->pkt.sig_mask;
6,395,478✔
613
        if ((rule_mask & mask) == rule_mask) {
6,395,478✔
614
            switch (ds->type) {
2,447,167✔
615
                case 0:
2,341,237✔
616
                    if (ds->value == ALPROTO_UNKNOWN || AppProtoEquals(ds->value, alproto)) {
2,341,237✔
617
                        const uint32_t sid = ds->sid;
1,669,287✔
618
                        PrefilterAddSids(&det_ctx->pmq, &sid, 1);
1,669,287✔
619
                        NONPF_PKT_STATS_INCR(prefilter_pkt_nonpf_sids);
1,669,287✔
620
                    } else {
2,328,423✔
621
                        NONPF_PKT_STATS_INCR(prefilter_pkt_nonpf_alproto_fail);
671,950✔
622
                    }
671,950✔
623
                    break;
2,341,237✔
624
                case 1:
23,233✔
625
                    if (ds->value == p->dp) {
23,233✔
626
                        const uint32_t sid = ds->sid;
6,994✔
627
                        PrefilterAddSids(&det_ctx->pmq, &sid, 1);
6,994✔
628
                        NONPF_PKT_STATS_INCR(prefilter_pkt_nonpf_sids);
6,994✔
629
                    } else {
16,239✔
630
                        NONPF_PKT_STATS_INCR(prefilter_pkt_nonpf_dport_fail);
16,239✔
631
                    }
16,239✔
632
                    break;
23,233✔
633
                case 2:
82,702✔
634
                    if (ds->value == p->payload_len) {
82,702✔
635
                        const uint32_t sid = ds->sid;
1,092✔
636
                        PrefilterAddSids(&det_ctx->pmq, &sid, 1);
1,092✔
637
                        NONPF_PKT_STATS_INCR(prefilter_pkt_nonpf_sids);
1,092✔
638
                    } else {
82,267✔
639
                        NONPF_PKT_STATS_INCR(prefilter_pkt_nonpf_dsize_fail);
81,610✔
640
                    }
81,610✔
641
                    break;
82,702✔
642
            }
2,447,167✔
643
        } else {
5,621,706✔
644
            NONPF_PKT_STATS_INCR(prefilter_pkt_nonpf_mask_fail);
3,948,311✔
645
        }
3,948,311✔
646
    }
6,395,478✔
647
}
1,338,999✔
648

649
static void PrefilterPktNonPFHookFlowStart(
650
        DetectEngineThreadCtx *det_ctx, Packet *p, const void *pectx)
651
{
9✔
652
    if (p->flowflags & (FLOW_PKT_TOSERVER_FIRST | FLOW_PKT_TOCLIENT_FIRST)) {
9✔
653
        PrefilterPktNonPF(det_ctx, p, pectx);
9✔
654
    }
9✔
655
}
9✔
656

657
/** \internal
658
 *  \brief engine to select the non-prefilter rules for frames
659
 *  Checks the alproto and type as well.
660
 *  Direction needs no checking as the rule groups are per direction. */
661
static void PrefilterFrameNonPF(DetectEngineThreadCtx *det_ctx, const void *pectx, Packet *p,
662
        const Frames *frames, const Frame *frame)
663
{
599✔
664
    DEBUG_VALIDATE_BUG_ON(p->flow == NULL);
599✔
665
    const uint16_t alproto = p->flow->alproto;
599✔
666
    const struct PrefilterNonPFData *data = (const struct PrefilterNonPFData *)pectx;
599✔
667
    SCLogDebug("adding %u sids", data->size);
599✔
668
    for (uint32_t i = 0; i < data->size; i++) {
1,198✔
669
        const struct PrefilterNonPFDataSig *ds = &data->array[i];
599✔
670
        if (ds->frame.type == frame->type &&
599✔
671
                (ds->value == ALPROTO_UNKNOWN || AppProtoEquals(ds->value, alproto))) {
599✔
672
            const uint32_t sid = ds->sid;
197✔
673
            PrefilterAddSids(&det_ctx->pmq, &sid, 1);
197✔
674
        }
197✔
675
    }
599✔
676
}
599✔
677

678
/* helper funcs for the non prefilter names hash */
679

680
static uint32_t NonPFNamesHash(HashTable *h, void *data, uint16_t _len)
681
{
1,430✔
682
    const char *str = data;
1,430✔
683
    return StringHashDjb2((const uint8_t *)str, (uint16_t)strlen(str)) % h->array_size;
1,430✔
684
}
1,430✔
685

686
static char NonPFNamesCompare(void *data1, uint16_t _len1, void *data2, uint16_t len2)
687
{
×
688
    const char *s1 = data1;
×
689
    const char *s2 = data2;
×
690
    return StringHashCompareFunc(data1, (uint16_t)strlen(s1), data2, (uint16_t)strlen(s2));
×
691
}
×
692

693
static void NonPFNamesFree(void *data)
694
{
1,430✔
695
    SCFree(data);
1,430✔
696
}
1,430✔
697

698
/* helper funcs for assembling non-prefilter engines */
699

700
struct TxNonPFData {
701
    AppProto alproto;
702
    int dir;      /**< 0: toserver, 1: toclient */
703
    int progress; /**< progress state value to register at */
704
    int sig_list; /**< special handling: normally 0, but for special cases (app-layer-state,
705
                     app-layer-event) use the list id to create separate engines */
706
    uint32_t sigs_cnt;
707
    struct PrefilterNonPFDataSig *sigs;
708
    const char *engine_name; /**< pointer to name owned by DetectEngineCtx::non_pf_engine_names */
709
};
710

711
static uint32_t TxNonPFHash(HashListTable *h, void *data, uint16_t _len)
712
{
4,155✔
713
    struct TxNonPFData *d = data;
4,155✔
714
    return (d->alproto + d->progress + d->dir + d->sig_list) % h->array_size;
4,155✔
715
}
4,155✔
716

717
static char TxNonPFCompare(void *data1, uint16_t _len1, void *data2, uint16_t len2)
718
{
1,443✔
719
    struct TxNonPFData *d1 = data1;
1,443✔
720
    struct TxNonPFData *d2 = data2;
1,443✔
721
    return d1->alproto == d2->alproto && d1->progress == d2->progress && d1->dir == d2->dir &&
1,443✔
722
           d1->sig_list == d2->sig_list;
1,443✔
723
}
1,443✔
724

725
static void TxNonPFFree(void *data)
726
{
1,430✔
727
    struct TxNonPFData *d = data;
1,430✔
728
    SCFree(d->sigs);
1,430✔
729
    SCFree(d);
1,430✔
730
}
1,430✔
731

732
static int TxNonPFAddSig(DetectEngineCtx *de_ctx, HashListTable *tx_engines_hash,
733
        const AppProto alproto, const int dir, const int16_t progress, const int sig_list,
734
        const char *name, const Signature *s)
735
{
2,725✔
736
    const uint32_t max_sids = DetectEngineGetMaxSigId(de_ctx);
2,725✔
737

738
    struct TxNonPFData lookup = {
2,725✔
739
        .alproto = alproto,
2,725✔
740
        .dir = dir,
2,725✔
741
        .progress = progress,
2,725✔
742
        .sig_list = sig_list,
2,725✔
743
        .sigs_cnt = 0,
2,725✔
744
        .sigs = NULL,
2,725✔
745
        .engine_name = NULL,
2,725✔
746
    };
2,725✔
747
    struct TxNonPFData *e = HashListTableLookup(tx_engines_hash, &lookup, 0);
2,725✔
748
    if (e != NULL) {
2,725✔
749
        bool found = false;
1,295✔
750
        // avoid adding same sid multiple times
751
        for (uint32_t y = 0; y < e->sigs_cnt; y++) {
4,766✔
752
            if (e->sigs[y].sid == s->iid) {
3,579✔
753
                found = true;
108✔
754
                break;
108✔
755
            }
108✔
756
        }
3,579✔
757
        if (!found) {
1,295✔
758
            BUG_ON(e->sigs_cnt == max_sids);
1,187✔
759
            e->sigs[e->sigs_cnt].sid = s->iid;
1,187✔
760
            e->sigs[e->sigs_cnt].value = alproto;
1,187✔
761
            e->sigs_cnt++;
1,187✔
762
        }
1,187✔
763
        return 0;
1,295✔
764
    }
1,295✔
765

766
    struct TxNonPFData *add = SCCalloc(1, sizeof(*add));
1,430✔
767
    if (add == NULL) {
1,430✔
768
        return -1;
×
769
    }
×
770
    add->dir = dir;
1,430✔
771
    add->alproto = alproto;
1,430✔
772
    add->progress = progress;
1,430✔
773
    add->sig_list = sig_list;
1,430✔
774
    add->sigs = SCCalloc(max_sids, sizeof(struct PrefilterNonPFDataSig));
1,430✔
775
    if (add->sigs == NULL) {
1,430✔
776
        SCFree(add);
×
777
        return -1;
×
778
    }
×
779
    add->sigs_cnt = 0;
1,430✔
780
    add->sigs[add->sigs_cnt].sid = s->iid;
1,430✔
781
    add->sigs[add->sigs_cnt].value = alproto;
1,430✔
782
    add->sigs_cnt++;
1,430✔
783

784
    char engine_name[128];
1,430✔
785
    snprintf(engine_name, sizeof(engine_name), "%s:%s:non_pf:%s", AppProtoToString(alproto), name,
1,430✔
786
            dir == 0 ? "toserver" : "toclient");
1,430✔
787
    char *engine_name_heap = SCStrdup(engine_name);
1,430✔
788
    if (engine_name_heap == NULL) {
1,430✔
789
        SCFree(add->sigs);
×
790
        SCFree(add);
×
791
        return -1;
×
792
    }
×
793
    int result = HashTableAdd(
1,430✔
794
            de_ctx->non_pf_engine_names, engine_name_heap, (uint16_t)strlen(engine_name_heap));
1,430✔
795
    if (result != 0) {
1,430✔
796
        SCFree(add->sigs);
×
797
        SCFree(add);
×
798
        return -1;
×
799
    }
×
800

801
    add->engine_name = engine_name_heap;
1,430✔
802
    SCLogDebug("engine_name_heap %s", engine_name_heap);
1,430✔
803

804
    int ret = HashListTableAdd(tx_engines_hash, add, 0);
1,430✔
805
    if (ret != 0) {
1,430✔
806
        SCFree(add->sigs);
×
807
        SCFree(add);
×
808
        return -1;
×
809
    }
×
810

811
    return 0;
1,430✔
812
}
1,430✔
813

814
/** \internal
815
 *  \brief setup non-prefilter rules in special "non-prefilter" engines that are registered in the
816
 * prefilter logic.
817
 *
818
 *  \retval 0 ok
819
 *  \retval -1 error
820
 */
821
static int SetupNonPrefilter(DetectEngineCtx *de_ctx, SigGroupHead *sgh)
822
{
5,473✔
823
    const uint32_t max_sids = DetectEngineGetMaxSigId(de_ctx);
5,473✔
824
    SCLogDebug("max_sids %u", max_sids);
5,473✔
825
    struct PrefilterNonPFDataSig *pkt_non_pf_array = SCCalloc(max_sids, sizeof(*pkt_non_pf_array));
5,473✔
826
    if (pkt_non_pf_array == NULL) {
5,473✔
827
        return -1;
×
828
    }
×
829
    uint32_t pkt_non_pf_array_size = 0;
5,473✔
830
    struct PrefilterNonPFDataSig *frame_non_pf_array =
5,473✔
831
            SCCalloc(max_sids, sizeof(*frame_non_pf_array));
5,473✔
832
    if (frame_non_pf_array == NULL) {
5,473✔
833
        SCFree(pkt_non_pf_array);
×
834
        return -1;
×
835
    }
×
836
    uint32_t frame_non_pf_array_size = 0;
5,473✔
837

838
    struct PrefilterNonPFDataSig *pkt_hook_flow_start_non_pf_array =
5,473✔
839
            SCCalloc(max_sids, sizeof(*pkt_hook_flow_start_non_pf_array));
5,473✔
840
    if (pkt_hook_flow_start_non_pf_array == NULL) {
5,473✔
841
        SCFree(pkt_non_pf_array);
×
842
        SCFree(frame_non_pf_array);
×
843
        return -1;
×
844
    }
×
845
    uint32_t pkt_hook_flow_start_non_pf_array_size = 0;
5,473✔
846
    SignatureMask pkt_hook_flow_start_mask = 0;
5,473✔
847
    bool pkt_hook_flow_start_mask_init = false;
5,473✔
848

849
    HashListTable *tx_engines_hash =
5,473✔
850
            HashListTableInit(256, TxNonPFHash, TxNonPFCompare, TxNonPFFree);
5,473✔
851
    if (tx_engines_hash == NULL) {
5,473✔
852
        SCFree(pkt_non_pf_array);
×
853
        SCFree(pkt_hook_flow_start_non_pf_array);
×
854
        SCFree(frame_non_pf_array);
×
855
        return -1;
×
856
    }
×
857

858
    if (de_ctx->non_pf_engine_names == NULL) {
5,473✔
859
        de_ctx->non_pf_engine_names =
2,503✔
860
                HashTableInit(512, NonPFNamesHash, NonPFNamesCompare, NonPFNamesFree);
2,503✔
861
        if (de_ctx->non_pf_engine_names == NULL) {
2,503✔
862
            SCFree(pkt_non_pf_array);
×
863
            SCFree(pkt_hook_flow_start_non_pf_array);
×
864
            SCFree(frame_non_pf_array);
×
865
            HashListTableFree(tx_engines_hash);
×
866
            return -1;
×
867
        }
×
868
    }
2,503✔
869

870
    SignatureMask pkt_mask = 0;
5,473✔
871
    bool pkt_mask_init = false;
5,473✔
872
#ifdef NONPF_PKT_STATS
873
    uint32_t nonpf_pkt_alproto = 0;
874
    uint32_t nonpf_pkt_dsize = 0;
875
    uint32_t nonpf_pkt_dport = 0;
876
#endif
877
    const int app_events_list_id = DetectBufferTypeGetByName("app-layer-events");
5,473✔
878
    SCLogDebug("app_events_list_id %d", app_events_list_id);
5,473✔
879
    const int app_state_list_id = DetectBufferTypeGetByName("app-layer-state");
5,473✔
880
    SCLogDebug("app_state_list_id %d", app_state_list_id);
5,473✔
881
    for (uint32_t sig = 0; sig < sgh->init->sig_cnt; sig++) {
3,361,364✔
882
        Signature *s = sgh->init->match_array[sig];
3,355,891✔
883
        if (s == NULL)
3,355,891✔
884
            continue;
×
885
        SCLogDebug("checking sid %u for non-prefilter", s->id);
3,355,891✔
886
        if (s->init_data->mpm_sm != NULL && (s->flags & SIG_FLAG_MPM_NEG) == 0)
3,355,891✔
887
            continue;
3,010,165✔
888
        if (s->init_data->prefilter_sm != NULL)
345,726✔
889
            continue;
96✔
890
        if ((s->flags & (SIG_FLAG_PREFILTER | SIG_FLAG_MPM_NEG)) == SIG_FLAG_PREFILTER)
345,630✔
891
            continue;
×
892
        SCLogDebug("setting up sid %u for non-prefilter", s->id);
345,630✔
893

894
        uint8_t frame_type = 0; /**< only a single type per rule */
345,630✔
895
        bool tx_non_pf = false;
345,630✔
896
        bool frame_non_pf = false;
345,630✔
897
        bool pkt_non_pf = false;
345,630✔
898

899
        if (s->init_data->hook.type == SIGNATURE_HOOK_TYPE_PKT &&
345,630✔
900
                s->init_data->hook.t.pkt.ph == SIGNATURE_HOOK_PKT_FLOW_START) {
345,630✔
901
            // TODO code duplication with regular pkt case below
902

903
            /* for pkt non prefilter, we have some space in the structure,
904
             * so we can squeeze another filter */
905
            uint8_t type;
38✔
906
            uint16_t value;
38✔
907
            if ((s->flags & SIG_FLAG_DSIZE) && s->dsize_mode == DETECT_UINT_EQ) {
38✔
908
                SCLogDebug("dsize extra match");
2✔
909
                type = 2;
2✔
910
                value = s->dsize_low;
2✔
911
            } else if (s->dp != NULL && s->dp->next == NULL && s->dp->port == s->dp->port2) {
36✔
912
                type = 1;
3✔
913
                value = s->dp->port;
3✔
914
            } else {
33✔
915
                type = 0;
33✔
916
                value = s->alproto;
33✔
917
            }
33✔
918
            pkt_hook_flow_start_non_pf_array[pkt_hook_flow_start_non_pf_array_size].sid = s->iid;
38✔
919
            pkt_hook_flow_start_non_pf_array[pkt_hook_flow_start_non_pf_array_size].value = value;
38✔
920
            pkt_hook_flow_start_non_pf_array[pkt_hook_flow_start_non_pf_array_size].type = type;
38✔
921
            pkt_hook_flow_start_non_pf_array[pkt_hook_flow_start_non_pf_array_size].pkt.sig_mask =
38✔
922
                    s->mask;
38✔
923
            pkt_hook_flow_start_non_pf_array_size++;
38✔
924

925
            if (pkt_hook_flow_start_mask_init) {
38✔
926
                pkt_hook_flow_start_mask &= s->mask;
12✔
927
            } else {
26✔
928
                pkt_hook_flow_start_mask = s->mask;
26✔
929
                pkt_hook_flow_start_mask_init = true;
26✔
930
            }
26✔
931

932
            SCLogDebug("flow_start hook");
38✔
933
            continue; // done for this sig
38✔
934
        }
38✔
935

936
        for (uint32_t x = 0; x < s->init_data->buffer_index; x++) {
350,235✔
937
            const int list_id = s->init_data->buffers[x].id;
4,643✔
938
            const DetectBufferType *buf = DetectEngineBufferTypeGetById(de_ctx, list_id);
4,643✔
939
            if (buf == NULL)
4,643✔
940
                continue;
×
941
            /* for now, exclude app-layer-events, as they are not tied to a specific
942
             * progress value like other keywords. */
943
            SCLogDebug("list_id %d buf %p", list_id, buf);
4,643✔
944
            if (list_id == app_events_list_id)
4,643✔
945
                continue;
3,647✔
946
            if (buf->packet) {
996✔
947
                SCLogDebug("packet buf");
86✔
948
                /* packet is handled below */
949
                pkt_non_pf = true;
86✔
950
            } else if (buf->frame) {
940✔
951
                for (DetectEngineFrameInspectionEngine *f = de_ctx->frame_inspect_engines;
4✔
952
                        f != NULL; f = f->next) {
9✔
953
                    if (!((((s->flags & SIG_FLAG_TOSERVER) != 0 && f->dir == 0) ||
9✔
954
                                  ((s->flags & SIG_FLAG_TOCLIENT) != 0 && f->dir == 1)) &&
9✔
955
                                list_id == (int)f->sm_list &&
9✔
956
                                AppProtoEquals(s->alproto, f->alproto)))
9✔
957
                        continue;
5✔
958

959
                    SCLogDebug("frame '%s' type %u", buf->name, f->type);
4✔
960
                    frame_type = f->type;
4✔
961
                    frame_non_pf = true;
4✔
962

963
                    frame_non_pf_array[frame_non_pf_array_size].sid = s->iid;
4✔
964
                    frame_non_pf_array[frame_non_pf_array_size].value = s->alproto;
4✔
965
                    frame_non_pf_array[frame_non_pf_array_size].frame.type = frame_type;
4✔
966
                    frame_non_pf_array_size++;
4✔
967
                    break;
4✔
968
                }
9✔
969

970
            } else {
906✔
971
                SCLogDebug("x %u list_id %d", x, list_id);
906✔
972
                for (DetectEngineAppInspectionEngine *app = de_ctx->app_inspect_engines;
906✔
973
                        app != NULL; app = app->next) {
622,623✔
974
                    SCLogDebug("app %p proto %s list_d %d sig dir %0x", app,
621,717✔
975
                            AppProtoToString(app->alproto), app->sm_list,
621,717✔
976
                            s->flags & (SIG_FLAG_TOSERVER | SIG_FLAG_TOCLIENT));
621,717✔
977

978
                    /* skip if:
979
                     * - not in our dir
980
                     * - not our list
981
                     * - app proto mismatch. Both sig and app can have proto or unknown */
982
                    if (!((((s->flags & SIG_FLAG_TOSERVER) != 0 && app->dir == 0) ||
621,717✔
983
                                  ((s->flags & SIG_FLAG_TOCLIENT) != 0 && app->dir == 1)) &&
621,717✔
984
                                list_id == (int)app->sm_list &&
621,717✔
985
                                (s->alproto == ALPROTO_UNKNOWN || app->alproto == ALPROTO_UNKNOWN ||
621,717✔
986
                                        AppProtoEquals(s->alproto, app->alproto))))
4,105✔
987
                        continue;
619,156✔
988

989
                    int sig_list = 0;
2,561✔
990
                    if (list_id == app_state_list_id)
2,561✔
991
                        sig_list = app_state_list_id;
1✔
992
                    if (TxNonPFAddSig(de_ctx, tx_engines_hash, app->alproto, app->dir,
2,561✔
993
                                app->progress, sig_list, buf->name, s) != 0) {
2,561✔
994
                        goto error;
×
995
                    }
×
996
                    tx_non_pf = true;
2,561✔
997
                }
2,561✔
998
            }
906✔
999
        }
996✔
1000
        /* handle hook only rules */
1001
        if (!tx_non_pf && s->init_data->hook.type == SIGNATURE_HOOK_TYPE_APP) {
345,592✔
1002
            const int dir = (s->flags & SIG_FLAG_TOSERVER) ? 0 : 1;
164✔
1003
            const char *pname = AppLayerParserGetStateNameById(IPPROTO_TCP, // TODO
164✔
1004
                    s->alproto, s->init_data->hook.t.app.app_progress,
164✔
1005
                    dir == 0 ? STREAM_TOSERVER : STREAM_TOCLIENT);
164✔
1006

1007
            if (TxNonPFAddSig(de_ctx, tx_engines_hash, s->alproto, dir,
164✔
1008
                        (int16_t)s->init_data->hook.t.app.app_progress, s->init_data->hook.sm_list,
164✔
1009
                        pname, s) != 0) {
164✔
1010
                goto error;
×
1011
            }
×
1012
            tx_non_pf = true;
164✔
1013
        }
164✔
1014
        /* mark as prefiltered as the sig is now part of a engine */
1015
        // s->flags |= SIG_FLAG_PREFILTER;
1016
        //  TODO doesn't work for sigs that are in multiple sgh's
1017

1018
        /* default to pkt if there was no tx or frame match */
1019
        if (!(tx_non_pf || frame_non_pf)) {
345,592✔
1020
            if (!pkt_non_pf) {
344,570✔
1021
                SCLogDebug("not frame, not tx, so pkt");
344,497✔
1022
            }
344,497✔
1023
            pkt_non_pf = true;
344,570✔
1024
        }
344,570✔
1025

1026
        SCLogDebug("setting up sid %u for non-prefilter: %s", s->id,
345,592✔
1027
                tx_non_pf ? "tx engine" : (frame_non_pf ? "frame engine" : "pkt engine"));
345,592✔
1028

1029
        if (pkt_non_pf) {
345,592✔
1030
            /* for pkt non prefilter, we have some space in the structure,
1031
             * so we can squeeze another filter */
1032
            uint8_t type;
344,579✔
1033
            uint16_t value;
344,579✔
1034
            if ((s->flags & SIG_FLAG_DSIZE) && s->dsize_mode == DETECT_UINT_EQ) {
344,579✔
1035
                SCLogDebug("dsize extra match");
184✔
1036
                type = 2;
184✔
1037
                value = s->dsize_low;
184✔
1038
#ifdef NONPF_PKT_STATS
1039
                nonpf_pkt_dsize++;
1040
#endif
1041
            } else if (s->dp != NULL && s->dp->next == NULL && s->dp->port == s->dp->port2) {
344,395✔
1042
                type = 1;
365✔
1043
                value = s->dp->port;
365✔
1044
#ifdef NONPF_PKT_STATS
1045
                nonpf_pkt_dport++;
1046
#endif
1047
            } else {
344,030✔
1048
                type = 0;
344,030✔
1049
                value = s->alproto;
344,030✔
1050
#ifdef NONPF_PKT_STATS
1051
                nonpf_pkt_alproto++;
1052
#endif
1053
            }
344,030✔
1054

1055
            pkt_non_pf_array[pkt_non_pf_array_size].sid = s->iid;
344,579✔
1056
            pkt_non_pf_array[pkt_non_pf_array_size].value = value;
344,579✔
1057
            pkt_non_pf_array[pkt_non_pf_array_size].type = type;
344,579✔
1058
            pkt_non_pf_array[pkt_non_pf_array_size].pkt.sig_mask = s->mask;
344,579✔
1059
            pkt_non_pf_array_size++;
344,579✔
1060

1061
            if (pkt_mask_init) {
344,579✔
1062
                pkt_mask &= s->mask;
342,807✔
1063
            } else {
342,894✔
1064
                pkt_mask = s->mask;
1,772✔
1065
                pkt_mask_init = true;
1,772✔
1066
            }
1,772✔
1067
        }
344,579✔
1068
    }
345,592✔
1069

1070
    /* for each unique sig set, add an engine */
1071
    for (HashListTableBucket *b = HashListTableGetListHead(tx_engines_hash); b != NULL;
6,903✔
1072
            b = HashListTableGetListNext(b)) {
5,473✔
1073
        struct TxNonPFData *t = HashListTableGetListData(b);
1,430✔
1074
        SCLogDebug("%s engine for %s hook %d has %u non-pf sigs",
1,430✔
1075
                t->dir == 0 ? "toserver" : "toclient", AppProtoToString(t->alproto), t->progress,
1,430✔
1076
                t->sigs_cnt);
1,430✔
1077

1078
        if (((sgh->init->direction & SIG_FLAG_TOSERVER) && t->dir == 1) ||
1,430✔
1079
                ((sgh->init->direction & SIG_FLAG_TOCLIENT) && t->dir == 0)) {
1,430✔
1080
            SCLogDebug("skipped");
438✔
1081
            continue;
438✔
1082
        }
438✔
1083

1084
        /* register special progress value to indicate we need to run it all the time */
1085
        int engine_progress = t->progress;
992✔
1086
        if (t->sig_list == app_state_list_id) {
992✔
1087
            SCLogDebug("engine %s for state list", t->engine_name);
1✔
1088
            engine_progress = -1;
1✔
1089
        }
1✔
1090

1091
        struct PrefilterNonPFDataTx *data =
992✔
1092
                SCCalloc(1, sizeof(*data) + t->sigs_cnt * sizeof(data->array[0]));
992✔
1093
        if (data == NULL)
992✔
1094
            goto error;
×
1095
        data->size = t->sigs_cnt;
992✔
1096
        for (uint32_t i = 0; i < t->sigs_cnt; i++) {
2,688✔
1097
            data->array[i] = t->sigs[i].sid;
1,696✔
1098
        }
1,696✔
1099
        if (PrefilterAppendTxEngine(de_ctx, sgh, PrefilterTxNonPF, t->alproto, engine_progress,
992✔
1100
                    (void *)data, PrefilterNonPFDataFree, t->engine_name) < 0) {
992✔
1101
            SCFree(data);
×
1102
            goto error;
×
1103
        }
×
1104
    }
992✔
1105
    HashListTableFree(tx_engines_hash);
5,473✔
1106
    tx_engines_hash = NULL;
5,473✔
1107

1108
    if (pkt_non_pf_array_size) {
5,473✔
1109
        struct PrefilterNonPFData *data =
1,772✔
1110
                SCCalloc(1, sizeof(*data) + pkt_non_pf_array_size * sizeof(data->array[0]));
1,772✔
1111
        if (data == NULL)
1,772✔
1112
            goto error;
×
1113
        data->size = pkt_non_pf_array_size;
1,772✔
1114
        memcpy((uint8_t *)&data->array, pkt_non_pf_array,
1,772✔
1115
                pkt_non_pf_array_size * sizeof(data->array[0]));
1,772✔
1116
        enum SignatureHookPkt hook = SIGNATURE_HOOK_PKT_NOT_SET; // TODO review
1,772✔
1117
        if (PrefilterAppendEngine(de_ctx, sgh, PrefilterPktNonPF, pkt_mask, hook, (void *)data,
1,772✔
1118
                    PrefilterNonPFDataFree, "packet:non_pf") < 0) {
1,772✔
1119
            SCFree(data);
×
1120
            goto error;
×
1121
        }
×
1122
    }
1,772✔
1123
    if (pkt_hook_flow_start_non_pf_array_size) {
5,473✔
1124
        struct PrefilterNonPFData *data = SCCalloc(
26✔
1125
                1, sizeof(*data) + pkt_hook_flow_start_non_pf_array_size * sizeof(data->array[0]));
26✔
1126
        if (data == NULL)
26✔
1127
            goto error;
×
1128
        data->size = pkt_hook_flow_start_non_pf_array_size;
26✔
1129
        memcpy((uint8_t *)&data->array, pkt_hook_flow_start_non_pf_array,
26✔
1130
                pkt_hook_flow_start_non_pf_array_size * sizeof(data->array[0]));
26✔
1131
        SCLogDebug("packet:flow_start:non_pf added with %u rules", data->size);
26✔
1132
        enum SignatureHookPkt hook = SIGNATURE_HOOK_PKT_FLOW_START;
26✔
1133
        if (PrefilterAppendEngine(de_ctx, sgh,
26✔
1134
                    PrefilterPktNonPFHookFlowStart, // TODO no longer needed to have a dedicated
26✔
1135
                                                    // callback
1136
                    pkt_hook_flow_start_mask, hook, (void *)data, PrefilterNonPFDataFree,
26✔
1137
                    "packet:flow_start:non_pf") < 0) {
26✔
1138
            SCFree(data);
×
1139
            goto error;
×
1140
        }
×
1141
    }
26✔
1142
    if (frame_non_pf_array_size) {
5,473✔
1143
        SCLogDebug("%u frame non-pf sigs", frame_non_pf_array_size);
4✔
1144
        struct PrefilterNonPFData *data =
4✔
1145
                SCCalloc(1, sizeof(*data) + frame_non_pf_array_size * sizeof(data->array[0]));
4✔
1146
        if (data == NULL)
4✔
1147
            goto error;
×
1148
        data->size = frame_non_pf_array_size;
4✔
1149
        memcpy((uint8_t *)&data->array, frame_non_pf_array,
4✔
1150
                frame_non_pf_array_size * sizeof(data->array[0]));
4✔
1151
        if (PrefilterAppendFrameEngine(de_ctx, sgh, PrefilterFrameNonPF, ALPROTO_UNKNOWN,
4✔
1152
                    FRAME_ANY_TYPE, (void *)data, PrefilterNonPFDataFree, "frame:non_pf") < 0) {
4✔
1153
            SCFree(data);
×
1154
            goto error;
×
1155
        }
×
1156
    }
4✔
1157

1158
    SCFree(pkt_hook_flow_start_non_pf_array);
5,473✔
1159
    pkt_hook_flow_start_non_pf_array = NULL;
5,473✔
1160
    SCFree(pkt_non_pf_array);
5,473✔
1161
    pkt_non_pf_array = NULL;
5,473✔
1162
    SCFree(frame_non_pf_array);
5,473✔
1163
    frame_non_pf_array = NULL;
5,473✔
1164
    return 0;
5,473✔
1165

1166
error:
×
1167
    if (tx_engines_hash) {
×
1168
        HashListTableFree(tx_engines_hash);
×
1169
    }
×
1170
    SCFree(pkt_hook_flow_start_non_pf_array);
×
1171
    SCFree(pkt_non_pf_array);
×
1172
    SCFree(frame_non_pf_array);
×
1173
    return -1;
×
1174
}
5,473✔
1175

1176
int PrefilterSetupRuleGroup(DetectEngineCtx *de_ctx, SigGroupHead *sgh)
1177
{
5,473✔
1178
    int r = PatternMatchPrepareGroup(de_ctx, sgh);
5,473✔
1179
    if (r != 0) {
5,473✔
1180
        FatalError("failed to set up pattern matching");
×
1181
    }
×
1182

1183
    /* set up engines if needed - when prefilter is set to auto we run
1184
     * all engines, otherwise only those that have been forced by the
1185
     * prefilter keyword. */
1186
    const enum DetectEnginePrefilterSetting setting = de_ctx->prefilter_setting;
5,473✔
1187
    for (int i = 0; i < DETECT_TBLSIZE; i++) {
2,687,243✔
1188
        if (sigmatch_table[i].SetupPrefilter != NULL &&
2,681,770✔
1189
                (setting == DETECT_PREFILTER_AUTO || de_ctx->sm_types_prefilter[i])) {
2,681,770✔
1190
            sigmatch_table[i].SetupPrefilter(de_ctx, sgh);
317✔
1191
        }
317✔
1192
    }
2,681,770✔
1193

1194
    if (SetupNonPrefilter(de_ctx, sgh) != 0) {
5,473✔
1195
        return -1;
×
1196
    }
×
1197

1198
    /* we have lists of engines in sgh->init now. Lets setup the
1199
     * match arrays */
1200
    PrefilterEngineList *el;
5,473✔
1201
    if (sgh->init->pkt_engines != NULL) {
5,473✔
1202
        uint32_t cnt = 0;
1,851✔
1203
        for (el = sgh->init->pkt_engines ; el != NULL; el = el->next) {
3,757✔
1204
            cnt++;
1,906✔
1205
        }
1,906✔
1206
        sgh->pkt_engines = SCMallocAligned(cnt * sizeof(PrefilterEngine), CLS);
1,851✔
1207
        if (sgh->pkt_engines == NULL) {
1,851✔
1208
            return -1;
×
1209
        }
×
1210
        memset(sgh->pkt_engines, 0x00, (cnt * sizeof(PrefilterEngine)));
1,851✔
1211

1212
        PrefilterEngine *e = sgh->pkt_engines;
1,851✔
1213
        for (el = sgh->init->pkt_engines ; el != NULL; el = el->next) {
3,757✔
1214
            e->local_id = el->id;
1,906✔
1215
            e->cb.Prefilter = el->Prefilter;
1,906✔
1216
            e->ctx.pkt.mask = el->pkt_mask;
1,906✔
1217
            // TODO right now we represent the hook in a u8 in the prefilter engine for space
1218
            // reasons.
1219
            BUG_ON(el->pkt_hook >= 8);
1,906✔
1220
            e->ctx.pkt.hook = (uint8_t)el->pkt_hook;
1,906✔
1221
            e->pectx = el->pectx;
1,906✔
1222
            el->pectx = NULL; // e now owns the ctx
1,906✔
1223
            e->gid = el->gid;
1,906✔
1224
            if (el->next == NULL) {
1,906✔
1225
                e->is_last = true;
1,851✔
1226
            }
1,851✔
1227
            e++;
1,906✔
1228
        }
1,906✔
1229
    }
1,851✔
1230
    if (sgh->init->payload_engines != NULL) {
5,473✔
1231
        uint32_t cnt = 0;
1,476✔
1232
        for (el = sgh->init->payload_engines ; el != NULL; el = el->next) {
3,255✔
1233
            cnt++;
1,779✔
1234
        }
1,779✔
1235
        sgh->payload_engines = SCMallocAligned(cnt * sizeof(PrefilterEngine), CLS);
1,476✔
1236
        if (sgh->payload_engines == NULL) {
1,476✔
1237
            return -1;
×
1238
        }
×
1239
        memset(sgh->payload_engines, 0x00, (cnt * sizeof(PrefilterEngine)));
1,476✔
1240

1241
        PrefilterEngine *e = sgh->payload_engines;
1,476✔
1242
        for (el = sgh->init->payload_engines ; el != NULL; el = el->next) {
3,255✔
1243
            e->local_id = el->id;
1,779✔
1244
            e->cb.Prefilter = el->Prefilter;
1,779✔
1245
            e->ctx.pkt.mask = el->pkt_mask;
1,779✔
1246
            // TODO right now we represent the hook in a u8 in the prefilter engine for space
1247
            // reasons.
1248
            BUG_ON(el->pkt_hook >= 8);
1,779✔
1249
            e->ctx.pkt.hook = (uint8_t)el->pkt_hook;
1,779✔
1250
            e->pectx = el->pectx;
1,779✔
1251
            el->pectx = NULL; // e now owns the ctx
1,779✔
1252
            e->gid = el->gid;
1,779✔
1253
            if (el->next == NULL) {
1,779✔
1254
                e->is_last = true;
1,476✔
1255
            }
1,476✔
1256
            e++;
1,779✔
1257
        }
1,779✔
1258
    }
1,476✔
1259
    if (sgh->init->tx_engines != NULL) {
5,473✔
1260
        uint32_t cnt = 0;
2,781✔
1261
        for (el = sgh->init->tx_engines ; el != NULL; el = el->next) {
13,722✔
1262
            cnt++;
10,941✔
1263
        }
10,941✔
1264
        sgh->tx_engines = SCMallocAligned(cnt * sizeof(PrefilterEngine), CLS);
2,781✔
1265
        if (sgh->tx_engines == NULL) {
2,781✔
1266
            return -1;
×
1267
        }
×
1268
        memset(sgh->tx_engines, 0x00, (cnt * sizeof(PrefilterEngine)));
2,781✔
1269

1270
        uint16_t local_id = 0;
2,781✔
1271
        PrefilterEngine *e = sgh->tx_engines;
2,781✔
1272
        for (el = sgh->init->tx_engines ; el != NULL; el = el->next) {
13,722✔
1273
            e->local_id = local_id++;
10,941✔
1274
            e->alproto = el->alproto;
10,941✔
1275
            e->ctx.tx_min_progress = el->tx_min_progress;
10,941✔
1276
            e->cb.PrefilterTx = el->PrefilterTx;
10,941✔
1277
            e->pectx = el->pectx;
10,941✔
1278
            el->pectx = NULL; // e now owns the ctx
10,941✔
1279
            e->gid = el->gid;
10,941✔
1280
            e++;
10,941✔
1281
        }
10,941✔
1282

1283
        /* sort by tx_min_progress, then alproto, then local_id */
1284
        qsort(sgh->tx_engines, local_id, sizeof(PrefilterEngine),
2,781✔
1285
                PrefilterSetupRuleGroupSortHelper);
2,781✔
1286
        sgh->tx_engines[local_id - 1].is_last = true;
2,781✔
1287
        sgh->tx_engines[local_id - 1].is_last_for_progress = true;
2,781✔
1288

1289
        PrefilterEngine *engine;
2,781✔
1290

1291
        /* per alproto to set is_last_for_progress per alproto because the inspect
1292
         * loop skips over engines that are not the correct alproto */
1293
        for (AppProto a = ALPROTO_FAILED + 1; a < g_alproto_max; a++) {
108,459✔
1294
            int last_tx_progress = 0;
105,678✔
1295
            bool last_tx_progress_set = false;
105,678✔
1296
            PrefilterEngine *prev_engine = NULL;
105,678✔
1297
            engine = sgh->tx_engines;
105,678✔
1298
            do {
415,758✔
1299
                if (engine->ctx.tx_min_progress != -1)
415,758✔
1300
                    BUG_ON(engine->ctx.tx_min_progress < last_tx_progress);
415,758✔
1301
                if (engine->alproto == a) {
415,758✔
1302
                    if (last_tx_progress_set && engine->ctx.tx_min_progress > last_tx_progress) {
10,940✔
1303
                        if (prev_engine) {
758✔
1304
                            prev_engine->is_last_for_progress = true;
758✔
1305
                        }
758✔
1306
                    }
758✔
1307

1308
                    last_tx_progress_set = true;
10,940✔
1309
                    prev_engine = engine;
10,940✔
1310
                } else {
404,818✔
1311
                    if (prev_engine) {
404,818✔
1312
                        prev_engine->is_last_for_progress = true;
28,149✔
1313
                    }
28,149✔
1314
                }
404,818✔
1315
                last_tx_progress = engine->ctx.tx_min_progress;
415,758✔
1316
                if (engine->is_last)
415,758✔
1317
                    break;
105,678✔
1318
                engine++;
310,080✔
1319
            } while (1);
310,080✔
1320
        }
105,678✔
1321
#ifdef DEBUG
1322
        SCLogDebug("sgh %p", sgh);
1323
        engine = sgh->tx_engines;
1324
        do {
1325
            SCLogDebug("engine: gid %u alproto %s tx_min_progress %d is_last %s "
1326
                       "is_last_for_progress %s",
1327
                    engine->gid, AppProtoToString(engine->alproto), engine->ctx.tx_min_progress,
1328
                    engine->is_last ? "true" : "false",
1329
                    engine->is_last_for_progress ? "true" : "false");
1330
            if (engine->is_last)
1331
                break;
1332
            engine++;
1333
        } while (1);
1334
#endif
1335
    }
2,781✔
1336
    if (sgh->init->frame_engines != NULL) {
5,473✔
1337
        uint32_t cnt = 0;
69✔
1338
        for (el = sgh->init->frame_engines; el != NULL; el = el->next) {
216✔
1339
            cnt++;
147✔
1340
        }
147✔
1341
        sgh->frame_engines = SCMallocAligned(cnt * sizeof(PrefilterEngine), CLS);
69✔
1342
        if (sgh->frame_engines == NULL) {
69✔
1343
            return -1;
×
1344
        }
×
1345
        memset(sgh->frame_engines, 0x00, (cnt * sizeof(PrefilterEngine)));
69✔
1346

1347
        PrefilterEngine *e = sgh->frame_engines;
69✔
1348
        for (el = sgh->init->frame_engines; el != NULL; el = el->next) {
216✔
1349
            e->local_id = el->id;
147✔
1350
            e->ctx.frame_type = el->frame_type;
147✔
1351
            e->cb.PrefilterFrame = el->PrefilterFrame;
147✔
1352
            e->alproto = el->alproto;
147✔
1353
            e->pectx = el->pectx;
147✔
1354
            el->pectx = NULL; // e now owns the ctx
147✔
1355
            e->gid = el->gid;
147✔
1356
            if (el->next == NULL) {
147✔
1357
                e->is_last = true;
69✔
1358
            }
69✔
1359
            e++;
147✔
1360
        }
147✔
1361
    }
69✔
1362

1363
    if (sgh->init->post_rule_match_engines != NULL) {
5,473✔
1364
        uint32_t cnt = 0;
7✔
1365
        for (el = sgh->init->post_rule_match_engines; el != NULL; el = el->next) {
14✔
1366
            cnt++;
7✔
1367
        }
7✔
1368
        sgh->post_rule_match_engines = SCMallocAligned(cnt * sizeof(PrefilterEngine), CLS);
7✔
1369
        if (sgh->post_rule_match_engines == NULL) {
7✔
1370
            return -1;
×
1371
        }
×
1372
        memset(sgh->post_rule_match_engines, 0x00, (cnt * sizeof(PrefilterEngine)));
7✔
1373

1374
        uint16_t local_id = 0;
7✔
1375
        PrefilterEngine *e = sgh->post_rule_match_engines;
7✔
1376
        for (el = sgh->init->post_rule_match_engines; el != NULL; el = el->next) {
14✔
1377
            e->local_id = local_id++;
7✔
1378
            e->cb.PrefilterPostRule = el->PrefilterPostRule;
7✔
1379
            e->pectx = el->pectx;
7✔
1380
            el->pectx = NULL; // e now owns the ctx
7✔
1381
            e->gid = el->gid;
7✔
1382
            e->is_last = (el->next == NULL);
7✔
1383
            e++;
7✔
1384
        }
7✔
1385
        SCLogDebug("sgh %p max local_id %u", sgh, local_id);
7✔
1386
    }
7✔
1387

1388
    return 0;
5,473✔
1389
}
5,473✔
1390

1391
/* hash table for assigning a unique id to each engine type. */
1392

1393
static uint32_t PrefilterStoreHashFunc(HashListTable *ht, void *data, uint16_t datalen)
1394
{
18,526✔
1395
    PrefilterStore *ctx = data;
18,526✔
1396

1397
    uint32_t hash = (uint32_t)strlen(ctx->name);
18,526✔
1398

1399
    for (size_t u = 0; u < strlen(ctx->name); u++) {
270,966✔
1400
        hash += ctx->name[u];
252,440✔
1401
    }
252,440✔
1402

1403
    hash %= ht->array_size;
18,526✔
1404
    return hash;
18,526✔
1405
}
18,526✔
1406

1407
static char PrefilterStoreCompareFunc(void *data1, uint16_t len1,
1408
                                      void *data2, uint16_t len2)
1409
{
11,570✔
1410
    PrefilterStore *ctx1 = data1;
11,570✔
1411
    PrefilterStore *ctx2 = data2;
11,570✔
1412
    return (strcmp(ctx1->name, ctx2->name) == 0);
11,570✔
1413
}
11,570✔
1414

1415
static void PrefilterStoreFreeFunc(void *ptr)
1416
{
3,746✔
1417
    SCFree(ptr);
3,746✔
1418
}
3,746✔
1419

1420
void PrefilterDeinit(DetectEngineCtx *de_ctx)
1421
{
3,299✔
1422
    if (de_ctx->prefilter_hash_table != NULL) {
3,299✔
1423
        HashListTableFree(de_ctx->prefilter_hash_table);
3,296✔
1424
    }
3,296✔
1425
}
3,299✔
1426

1427
void PrefilterInit(DetectEngineCtx *de_ctx)
1428
{
3,355✔
1429
    BUG_ON(de_ctx->prefilter_hash_table != NULL);
3,355✔
1430

1431
    de_ctx->prefilter_hash_table = HashListTableInit(256,
3,355✔
1432
            PrefilterStoreHashFunc,
3,355✔
1433
            PrefilterStoreCompareFunc,
3,355✔
1434
            PrefilterStoreFreeFunc);
3,355✔
1435
    BUG_ON(de_ctx->prefilter_hash_table == NULL);
3,355✔
1436
}
3,355✔
1437

1438
static int PrefilterStoreGetId(DetectEngineCtx *de_ctx,
1439
        const char *name, void (*FreeFunc)(void *))
1440
{
14,780✔
1441
    PrefilterStore ctx = { name, FreeFunc, 0 };
14,780✔
1442

1443
    BUG_ON(de_ctx->prefilter_hash_table == NULL);
14,780✔
1444

1445
    SCLogDebug("looking up %s", name);
14,780✔
1446

1447
    PrefilterStore *rctx = HashListTableLookup(de_ctx->prefilter_hash_table, (void *)&ctx, 0);
14,780✔
1448
    if (rctx != NULL) {
14,780✔
1449
        return rctx->id;
11,034✔
1450
    }
11,034✔
1451

1452
    PrefilterStore *actx = SCCalloc(1, sizeof(*actx));
3,746✔
1453
    if (actx == NULL) {
3,746✔
1454
        return -1;
×
1455
    }
×
1456

1457
    actx->name = name;
3,746✔
1458
    actx->FreeFunc = FreeFunc;
3,746✔
1459
    actx->id = de_ctx->prefilter_id++;
3,746✔
1460
    SCLogDebug("prefilter engine %s has profile id %u", actx->name, actx->id);
3,746✔
1461

1462
    int ret = HashListTableAdd(de_ctx->prefilter_hash_table, actx, 0);
3,746✔
1463
    if (ret != 0) {
3,746✔
1464
        SCFree(actx);
×
1465
        return -1;
×
1466
    }
×
1467

1468
    int r = actx->id;
3,746✔
1469
    return r;
3,746✔
1470
}
3,746✔
1471

1472
/** \warning slow */
1473
static const PrefilterStore *PrefilterStoreGetStore(const DetectEngineCtx *de_ctx,
1474
        const uint32_t id)
1475
{
14,780✔
1476

1477
    const PrefilterStore *store = NULL;
14,780✔
1478
    if (de_ctx->prefilter_hash_table != NULL) {
14,780✔
1479
        HashListTableBucket *hb = HashListTableGetListHead(de_ctx->prefilter_hash_table);
14,780✔
1480
        for ( ; hb != NULL; hb = HashListTableGetListNext(hb)) {
122,630✔
1481
            PrefilterStore *ctx = HashListTableGetListData(hb);
122,630✔
1482
            if (ctx->id == id) {
122,630✔
1483
                store = ctx;
14,780✔
1484
                break;
14,780✔
1485
            }
14,780✔
1486
        }
122,630✔
1487
    }
14,780✔
1488
    return store;
14,780✔
1489
}
14,780✔
1490

1491
#include "util-print.h"
1492

1493
typedef struct PrefilterMpmCtx {
1494
    int list_id;
1495
    union {
1496
        InspectionBufferGetDataPtr GetData;
1497
        InspectionSingleBufferGetDataPtr GetDataSingle;
1498
    };
1499
    const MpmCtx *mpm_ctx;
1500
    const DetectEngineTransforms *transforms;
1501
} PrefilterMpmCtx;
1502

1503
/** \brief Generic Mpm prefilter callback for simple InspectionSingleBufferGetDataPtr
1504
 *
1505
 *  \param det_ctx detection engine thread ctx
1506
 *  \param p packet to inspect
1507
 *  \param f flow to inspect
1508
 *  \param txv tx to inspect
1509
 *  \param pectx inspection context
1510
 */
1511
static void PrefilterMpmTxSingle(DetectEngineThreadCtx *det_ctx, const void *pectx, Packet *p,
1512
        Flow *f, void *txv, const uint64_t idx, const AppLayerTxData *_txd, const uint8_t flags)
1513
{
1,869✔
1514
    SCEnter();
1,869✔
1515

1516
    const PrefilterMpmCtx *ctx = (const PrefilterMpmCtx *)pectx;
1,869✔
1517
    const MpmCtx *mpm_ctx = ctx->mpm_ctx;
1,869✔
1518
    SCLogDebug("running on list %d", ctx->list_id);
1,869✔
1519

1520
    InspectionBuffer *buffer = DetectGetSingleData(
1,869✔
1521
            det_ctx, ctx->transforms, f, flags, txv, ctx->list_id, ctx->GetDataSingle);
1,869✔
1522
    if (buffer == NULL)
1,869✔
1523
        return;
1,265✔
1524

1525
    const uint32_t data_len = buffer->inspect_len;
604✔
1526
    const uint8_t *data = buffer->inspect;
604✔
1527

1528
    SCLogDebug("mpm'ing buffer:");
604✔
1529
    // PrintRawDataFp(stdout, data, data_len);
1530

1531
    if (data != NULL && data_len >= mpm_ctx->minlen) {
604✔
1532
        (void)mpm_table[mpm_ctx->mpm_type].Search(
490✔
1533
                mpm_ctx, &det_ctx->mtc, &det_ctx->pmq, data, data_len);
490✔
1534
        PREFILTER_PROFILING_ADD_BYTES(det_ctx, data_len);
490✔
1535
    }
490✔
1536
}
604✔
1537

1538
/** \brief Generic Mpm prefilter callback
1539
 *
1540
 *  \param det_ctx detection engine thread ctx
1541
 *  \param p packet to inspect
1542
 *  \param f flow to inspect
1543
 *  \param txv tx to inspect
1544
 *  \param pectx inspection context
1545
 */
1546
static void PrefilterMpm(DetectEngineThreadCtx *det_ctx, const void *pectx, Packet *p, Flow *f,
1547
        void *txv, const uint64_t idx, const AppLayerTxData *_txd, const uint8_t flags)
1548
{
72,349✔
1549
    SCEnter();
72,349✔
1550

1551
    const PrefilterMpmCtx *ctx = (const PrefilterMpmCtx *)pectx;
72,349✔
1552
    const MpmCtx *mpm_ctx = ctx->mpm_ctx;
72,349✔
1553
    SCLogDebug("running on list %d", ctx->list_id);
72,349✔
1554

1555
    InspectionBuffer *buffer = ctx->GetData(det_ctx, ctx->transforms, f, flags, txv, ctx->list_id);
72,349✔
1556
    if (buffer == NULL)
72,349✔
1557
        return;
25,438✔
1558

1559
    const uint32_t data_len = buffer->inspect_len;
46,911✔
1560
    const uint8_t *data = buffer->inspect;
46,911✔
1561

1562
    SCLogDebug("mpm'ing buffer:");
46,911✔
1563
    //PrintRawDataFp(stdout, data, data_len);
1564

1565
    if (data != NULL && data_len >= mpm_ctx->minlen) {
46,914✔
1566
        (void)mpm_table[mpm_ctx->mpm_type].Search(
45,196✔
1567
                mpm_ctx, &det_ctx->mtc, &det_ctx->pmq, data, data_len);
45,196✔
1568
        PREFILTER_PROFILING_ADD_BYTES(det_ctx, data_len);
45,196✔
1569
    }
45,196✔
1570
}
46,911✔
1571

1572
static void PrefilterGenericMpmFree(void *ptr)
1573
{
4,589✔
1574
    SCFree(ptr);
4,589✔
1575
}
4,589✔
1576

1577
int PrefilterGenericMpmRegister(DetectEngineCtx *de_ctx, SigGroupHead *sgh, MpmCtx *mpm_ctx,
1578
        const DetectBufferMpmRegistry *mpm_reg, int list_id)
1579
{
4,592✔
1580
    SCEnter();
4,592✔
1581
    PrefilterMpmCtx *pectx = SCCalloc(1, sizeof(*pectx));
4,592✔
1582
    if (pectx == NULL)
4,592✔
1583
        return -1;
×
1584
    pectx->list_id = list_id;
4,592✔
1585
    pectx->GetData = mpm_reg->app_v2.GetData;
4,592✔
1586
    pectx->mpm_ctx = mpm_ctx;
4,592✔
1587
    pectx->transforms = &mpm_reg->transforms;
4,592✔
1588

1589
    int r = PrefilterAppendTxEngine(de_ctx, sgh, PrefilterMpm,
4,592✔
1590
        mpm_reg->app_v2.alproto, mpm_reg->app_v2.tx_min_progress,
4,592✔
1591
        pectx, PrefilterGenericMpmFree, mpm_reg->pname);
4,592✔
1592
    if (r != 0) {
4,592✔
1593
        SCFree(pectx);
×
1594
    }
×
1595
    return r;
4,592✔
1596
}
4,592✔
1597

1598
int PrefilterSingleMpmRegister(DetectEngineCtx *de_ctx, SigGroupHead *sgh, MpmCtx *mpm_ctx,
1599
        const DetectBufferMpmRegistry *mpm_reg, int list_id)
1600
{
240✔
1601
    SCEnter();
240✔
1602
    PrefilterMpmCtx *pectx = SCCalloc(1, sizeof(*pectx));
240✔
1603
    if (pectx == NULL)
240✔
1604
        return -1;
×
1605
    pectx->list_id = list_id;
240✔
1606
    pectx->GetDataSingle = mpm_reg->app_v2.GetDataSingle;
240✔
1607
    pectx->mpm_ctx = mpm_ctx;
240✔
1608
    pectx->transforms = &mpm_reg->transforms;
240✔
1609

1610
    int r = PrefilterAppendTxEngine(de_ctx, sgh, PrefilterMpmTxSingle, mpm_reg->app_v2.alproto,
240✔
1611
            mpm_reg->app_v2.tx_min_progress, pectx, PrefilterGenericMpmFree, mpm_reg->pname);
240✔
1612
    if (r != 0) {
240✔
1613
        SCFree(pectx);
×
1614
    }
×
1615
    return r;
240✔
1616
}
240✔
1617

1618
static void PrefilterMultiGenericMpmFree(void *ptr)
1619
{
948✔
1620
    // PrefilterMpmListId
1621
    SCFree(ptr);
948✔
1622
}
948✔
1623

1624
static void PrefilterMultiMpm(DetectEngineThreadCtx *det_ctx, const void *pectx, Packet *p, Flow *f,
1625
        void *txv, const uint64_t idx, const AppLayerTxData *_txd, const uint8_t flags)
1626
{
5,981✔
1627
    SCEnter();
5,981✔
1628

1629
    const PrefilterMpmListId *ctx = (const PrefilterMpmListId *)pectx;
5,981✔
1630
    const MpmCtx *mpm_ctx = ctx->mpm_ctx;
5,981✔
1631
    SCLogDebug("running on list %d", ctx->list_id);
5,981✔
1632
    uint32_t local_id = 0;
5,981✔
1633

1634
    do {
13,190✔
1635
        // loop until we get a NULL
1636
        InspectionBuffer *buffer = DetectGetMultiData(
13,190✔
1637
                det_ctx, ctx->transforms, f, flags, txv, ctx->list_id, local_id, ctx->GetData);
13,190✔
1638
        if (buffer == NULL)
13,190✔
1639
            break;
5,982✔
1640

1641
        if (buffer->inspect_len >= mpm_ctx->minlen) {
7,208✔
1642
            (void)mpm_table[mpm_ctx->mpm_type].Search(
7,013✔
1643
                    mpm_ctx, &det_ctx->mtc, &det_ctx->pmq, buffer->inspect, buffer->inspect_len);
7,013✔
1644
            PREFILTER_PROFILING_ADD_BYTES(det_ctx, buffer->inspect_len);
7,013✔
1645
        }
7,013✔
1646

1647
        local_id++;
7,208✔
1648
    } while (1);
7,208✔
1649
}
5,981✔
1650

1651
int PrefilterMultiGenericMpmRegister(DetectEngineCtx *de_ctx, SigGroupHead *sgh, MpmCtx *mpm_ctx,
1652
        const DetectBufferMpmRegistry *mpm_reg, int list_id)
1653
{
948✔
1654
    SCEnter();
948✔
1655
    PrefilterMpmListId *pectx = SCCalloc(1, sizeof(*pectx));
948✔
1656
    if (pectx == NULL)
948✔
1657
        return -1;
×
1658
    pectx->list_id = list_id;
948✔
1659
    pectx->GetData = mpm_reg->app_v2.GetMultiData;
948✔
1660
    pectx->mpm_ctx = mpm_ctx;
948✔
1661
    pectx->transforms = &mpm_reg->transforms;
948✔
1662

1663
    int r = PrefilterAppendTxEngine(de_ctx, sgh, PrefilterMultiMpm, mpm_reg->app_v2.alproto,
948✔
1664
            mpm_reg->app_v2.tx_min_progress, pectx, PrefilterMultiGenericMpmFree, mpm_reg->pname);
948✔
1665
    if (r != 0) {
948✔
1666
        SCFree(pectx);
×
1667
    }
×
1668
    return r;
948✔
1669
}
948✔
1670

1671
/* generic mpm for pkt engines */
1672

1673
typedef struct PrefilterMpmPktCtx {
1674
    int list_id;
1675
    InspectionBufferGetPktDataPtr GetData;
1676
    const MpmCtx *mpm_ctx;
1677
    const DetectEngineTransforms *transforms;
1678
} PrefilterMpmPktCtx;
1679

1680
/** \brief Generic Mpm prefilter callback
1681
 *
1682
 *  \param det_ctx detection engine thread ctx
1683
 *  \param p packet to inspect
1684
 *  \param f flow to inspect
1685
 *  \param txv tx to inspect
1686
 *  \param pectx inspection context
1687
 */
1688
static void PrefilterMpmPkt(DetectEngineThreadCtx *det_ctx,
1689
        Packet *p, const void *pectx)
1690
{
217✔
1691
    SCEnter();
217✔
1692

1693
    const PrefilterMpmPktCtx *ctx = (const PrefilterMpmPktCtx *)pectx;
217✔
1694
    const MpmCtx *mpm_ctx = ctx->mpm_ctx;
217✔
1695
    SCLogDebug("running on list %d", ctx->list_id);
217✔
1696

1697
    InspectionBuffer *buffer = ctx->GetData(det_ctx, ctx->transforms,
217✔
1698
            p, ctx->list_id);
217✔
1699
    if (buffer == NULL)
217✔
UNCOV
1700
        return;
×
1701

1702
    const uint32_t data_len = buffer->inspect_len;
217✔
1703
    const uint8_t *data = buffer->inspect;
217✔
1704

1705
    SCLogDebug("mpm'ing buffer:");
217✔
1706
    //PrintRawDataFp(stdout, data, data_len);
1707

1708
    if (data != NULL && data_len >= mpm_ctx->minlen) {
217✔
1709
        (void)mpm_table[mpm_ctx->mpm_type].Search(
212✔
1710
                mpm_ctx, &det_ctx->mtc, &det_ctx->pmq, data, data_len);
212✔
1711
        PREFILTER_PROFILING_ADD_BYTES(det_ctx, data_len);
212✔
1712
    }
212✔
1713
}
217✔
1714

1715
static void PrefilterMpmPktFree(void *ptr)
1716
{
36✔
1717
    SCFree(ptr);
36✔
1718
}
36✔
1719

1720
int PrefilterGenericMpmPktRegister(DetectEngineCtx *de_ctx, SigGroupHead *sgh, MpmCtx *mpm_ctx,
1721
        const DetectBufferMpmRegistry *mpm_reg, int list_id)
1722
{
36✔
1723
    SCEnter();
36✔
1724
    PrefilterMpmPktCtx *pectx = SCCalloc(1, sizeof(*pectx));
36✔
1725
    if (pectx == NULL)
36✔
1726
        return -1;
×
1727
    pectx->list_id = list_id;
36✔
1728
    pectx->GetData = mpm_reg->pkt_v1.GetData;
36✔
1729
    pectx->mpm_ctx = mpm_ctx;
36✔
1730
    pectx->transforms = &mpm_reg->transforms;
36✔
1731

1732
    enum SignatureHookPkt hook = SIGNATURE_HOOK_PKT_NOT_SET; // TODO review
36✔
1733
    int r = PrefilterAppendEngine(
36✔
1734
            de_ctx, sgh, PrefilterMpmPkt, 0, hook, pectx, PrefilterMpmPktFree, mpm_reg->pname);
36✔
1735
    if (r != 0) {
36✔
1736
        SCFree(pectx);
×
1737
    }
×
1738
    return r;
36✔
1739
}
36✔
1740

1741
#define QUEUE_STEP 16
5✔
1742

1743
void PostRuleMatchWorkQueueAppend(
1744
        DetectEngineThreadCtx *det_ctx, const Signature *s, const int type, const uint32_t value)
1745
{
9✔
1746
    if (det_ctx->post_rule_work_queue.q == NULL) {
9✔
1747
        det_ctx->post_rule_work_queue.q =
5✔
1748
                SCCalloc(1, sizeof(PostRuleMatchWorkQueueItem) * QUEUE_STEP);
5✔
1749
        if (det_ctx->post_rule_work_queue.q == NULL) {
5✔
1750
            DetectEngineSetEvent(det_ctx, DETECT_EVENT_POST_MATCH_QUEUE_FAILED);
×
1751
            return;
×
1752
        }
×
1753
        det_ctx->post_rule_work_queue.size = QUEUE_STEP;
5✔
1754
    } else if (det_ctx->post_rule_work_queue.len == det_ctx->post_rule_work_queue.size) {
5✔
1755
        void *ptr = SCRealloc(
×
1756
                det_ctx->post_rule_work_queue.q, (det_ctx->post_rule_work_queue.size + QUEUE_STEP) *
×
1757
                                                         sizeof(PostRuleMatchWorkQueueItem));
×
1758
        if (ptr == NULL) {
×
1759
            DetectEngineSetEvent(det_ctx, DETECT_EVENT_POST_MATCH_QUEUE_FAILED);
×
1760
            return;
×
1761
        }
×
1762
        det_ctx->post_rule_work_queue.q = ptr;
×
1763
        det_ctx->post_rule_work_queue.size += QUEUE_STEP;
×
1764
    }
×
1765
    det_ctx->post_rule_work_queue.q[det_ctx->post_rule_work_queue.len].sm_type = type;
9✔
1766
    det_ctx->post_rule_work_queue.q[det_ctx->post_rule_work_queue.len].value = value;
9✔
1767
#ifdef DEBUG
1768
    det_ctx->post_rule_work_queue.q[det_ctx->post_rule_work_queue.len].id = s->iid;
1769
#endif
1770
    det_ctx->post_rule_work_queue.len++;
9✔
1771
    SCLogDebug("det_ctx->post_rule_work_queue.len %u", det_ctx->post_rule_work_queue.len);
9✔
1772
}
9✔
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