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

jasonish / suricata / 22802647571

07 Mar 2026 04:23PM UTC coverage: 75.827% (-3.4%) from 79.233%
22802647571

push

github

jasonish
github-ci: add schema ordering check for yaml schema

253365 of 334137 relevant lines covered (75.83%)

3384729.69 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,953,885✔
70
    if (n < 2)
1,953,885✔
71
        return;
1,090,522✔
72
    SigIntId p = sids[n / 2];
863,363✔
73
    SigIntId *l = sids;
863,363✔
74
    SigIntId *r = sids + n - 1;
863,363✔
75
    while (l <= r) {
12,292,526✔
76
        if (*l < p)
11,429,163✔
77
            l++;
5,441,989✔
78
        else if (*r > p)
5,987,174✔
79
            r--;
4,997,284✔
80
        else {
989,890✔
81
            SigIntId t = *l;
989,890✔
82
            *l = *r;
989,890✔
83
            *r = t;
989,890✔
84
            l++;
989,890✔
85
            r--;
989,890✔
86
        }
989,890✔
87
    }
11,429,163✔
88
    QuickSortSigIntId(sids, (uint32_t)(r - sids) + 1);
863,363✔
89
    QuickSortSigIntId(l, (uint32_t)(sids + n - l));
863,363✔
90
}
863,363✔
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,245✔
104
    /* reset rule store */
105
    det_ctx->pmq.rule_id_array_cnt = 0;
116,245✔
106

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

110
    PrefilterEngine *engine = sgh->tx_engines;
116,245✔
111
    do {
2,233,471✔
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,233,471✔
114
        if (tx_ptr == NULL) {
2,233,471✔
115
            // incompatible engine->alproto with flow alproto
116
            goto next;
1,967,727✔
117
        }
1,967,727✔
118

119
        if (engine->ctx.tx_min_progress != -1) {
267,960✔
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)
267,960✔
128
                break;
8,196✔
129
            if (tx->tx_progress > engine->ctx.tx_min_progress) {
259,764✔
130
                SCLogDebug("tx->tx_progress %u > engine->ctx.tx_min_progress %d", tx->tx_progress,
225,922✔
131
                        engine->ctx.tx_min_progress);
225,922✔
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,922✔
136
                    SCLogDebug("tx already marked progress as beyond engine: %u > %u",
118,406✔
137
                            tx->detect_progress, engine->ctx.tx_min_progress);
118,406✔
138
                    goto next;
118,406✔
139
                } else {
127,369✔
140
                    SCLogDebug("tx->tx_progress %u > engine->ctx.tx_min_progress %d: "
107,516✔
141
                               "tx->detect_progress %u",
107,516✔
142
                            tx->tx_progress, engine->ctx.tx_min_progress, tx->detect_progress);
107,516✔
143
                }
107,516✔
144
            }
225,922✔
145
#ifdef DEBUG
146
            uint32_t old = det_ctx->pmq.rule_id_array_cnt;
147
#endif
148
            PREFILTER_PROFILING_START(det_ctx);
141,358✔
149
            engine->cb.PrefilterTx(det_ctx, engine->pectx, p, p->flow, tx_ptr, tx->tx_id,
141,358✔
150
                    tx->tx_data_ptr, flow_flags);
141,358✔
151
            PREFILTER_PROFILING_END(det_ctx, engine->gid);
141,358✔
152
            SCLogDebug("engine %p min_progress %d %s:%s: results %u", engine,
141,358✔
153
                    engine->ctx.tx_min_progress, AppProtoToString(engine->alproto), pname,
141,358✔
154
                    det_ctx->pmq.rule_id_array_cnt - old);
141,358✔
155

156
            if (tx->tx_progress > engine->ctx.tx_min_progress && engine->is_last_for_progress) {
141,358✔
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,515✔
161
                SCLogDebug("tx->tx_progress %d engine->ctx.tx_min_progress %d "
39,515✔
162
                           "engine->is_last_for_progress %d => tx->detect_progress updated to %02x",
39,515✔
163
                        tx->tx_progress, engine->ctx.tx_min_progress, engine->is_last_for_progress,
39,515✔
164
                        tx->detect_progress);
39,515✔
165
            }
39,515✔
166
        } else {
4,294,968,101✔
167
            PREFILTER_PROFILING_START(det_ctx);
4,294,967,294✔
168
            engine->cb.PrefilterTx(det_ctx, engine->pectx, p, p->flow, tx_ptr, tx->tx_id,
4,294,967,294✔
169
                    tx->tx_data_ptr, flow_flags);
4,294,967,294✔
170
            PREFILTER_PROFILING_END(det_ctx, engine->gid);
4,294,967,294✔
171
        }
4,294,967,294✔
172
    next:
2,224,815✔
173
        if (engine->is_last)
2,224,815✔
174
            break;
108,108✔
175
        engine++;
2,116,707✔
176
    } while (1);
2,116,707✔
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,785✔
181
        PACKET_PROFILING_DETECT_START(p, PROF_DETECT_PF_SORT1);
6,453✔
182
        QuickSortSigIntId(det_ctx->pmq.rule_id_array, det_ctx->pmq.rule_id_array_cnt);
6,453✔
183
        PACKET_PROFILING_DETECT_END(p, PROF_DETECT_PF_SORT1);
6,453✔
184
    }
6,453✔
185
}
115,785✔
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,500,630✔
219
    SCEnter();
2,500,630✔
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,500,630✔
231
        PACKET_PROFILING_DETECT_START(p, PROF_DETECT_PF_PKT);
1,504,557✔
232
        /* run packet engines */
233
        PrefilterEngine *engine = sgh->pkt_engines;
1,504,557✔
234
        do {
1,637,142✔
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,142✔
240
                    (engine->ctx.pkt.hook == 0 || (p->pkt_hooks & BIT_U16(engine->ctx.pkt.hook)))) {
1,637,143✔
241
                PREFILTER_PROFILING_START(det_ctx);
1,474,908✔
242
                engine->cb.Prefilter(det_ctx, p, engine->pectx);
1,474,908✔
243
                PREFILTER_PROFILING_END(det_ctx, engine->gid);
1,474,908✔
244
            }
1,474,908✔
245

246
            if (engine->is_last)
1,637,142✔
247
                break;
1,504,505✔
248
            engine++;
132,637✔
249
        } while (1);
132,637✔
250
        PACKET_PROFILING_DETECT_END(p, PROF_DETECT_PF_PKT);
816,965✔
251
    }
1,504,557✔
252

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

265
            if (engine->is_last)
1,384,712✔
266
                break;
1,052,597✔
267
            engine++;
332,115✔
268
        }
332,115✔
269
        PACKET_PROFILING_DETECT_END(p, PROF_DETECT_PF_PAYLOAD);
1,052,571✔
270
    }
1,052,571✔
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,500,630✔
275
        PACKET_PROFILING_DETECT_START(p, PROF_DETECT_PF_SORT1);
220,661✔
276
        QuickSortSigIntId(det_ctx->pmq.rule_id_array, det_ctx->pmq.rule_id_array_cnt);
220,661✔
277
        PACKET_PROFILING_DETECT_END(p, PROF_DETECT_PF_SORT1);
220,661✔
278
    }
220,661✔
279
    SCReturn;
2,500,630✔
280
}
2,500,630✔
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,885✔
286
    if (sgh == NULL || PrefilterFunc == NULL || pectx == NULL)
1,885✔
287
        return -1;
×
288

289
    PrefilterEngineList *e = SCMallocAligned(sizeof(*e), CLS);
1,885✔
290
    if (e == NULL)
1,885✔
291
        return -1;
×
292
    memset(e, 0x00, sizeof(*e));
1,885✔
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,885✔
296

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

303
    if (sgh->init->pkt_engines == NULL) {
1,885✔
304
        sgh->init->pkt_engines = e;
1,830✔
305
    } else {
1,830✔
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,885✔
316
    e->gid = PrefilterStoreGetId(de_ctx, e->name, e->Free);
1,885✔
317
    return 0;
1,885✔
318
}
1,885✔
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,933✔
356
    if (sgh == NULL || PrefilterTxFunc == NULL || pectx == NULL)
10,933✔
357
        return -1;
×
358

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

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

372
    if (sgh->init->tx_engines == NULL) {
10,933✔
373
        sgh->init->tx_engines = e;
2,777✔
374
    } else {
8,423✔
375
        PrefilterEngineList *t = sgh->init->tx_engines;
8,156✔
376
        while (t->next != NULL) {
126,231✔
377
            t = t->next;
118,075✔
378
        }
118,075✔
379

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

384
    e->name = name;
10,933✔
385
    e->gid = PrefilterStoreGetId(de_ctx, e->name, e->Free);
10,933✔
386
    return 0;
10,933✔
387
}
10,933✔
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,751✔
459
    if (e->Free && e->pectx) {
14,751✔
460
        e->Free(e->pectx);
×
461
    }
×
462
    SCFreeAligned(e);
14,751✔
463
}
14,751✔
464

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

469
    while (t != NULL) {
590,791✔
470
        PrefilterEngineList *next = t->next;
14,751✔
471
        PrefilterFreeEngineList(t);
14,751✔
472
        t = next;
14,751✔
473
    }
14,751✔
474
}
576,040✔
475

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

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

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

493
void PrefilterCleanupRuleGroup(const DetectEngineCtx *de_ctx, SigGroupHead *sgh)
494
{
115,208✔
495
    if (sgh->pkt_engines) {
115,208✔
496
        PrefilterFreeEngines(de_ctx, sgh->pkt_engines);
1,830✔
497
        sgh->pkt_engines = NULL;
1,830✔
498
    }
1,830✔
499
    if (sgh->payload_engines) {
115,208✔
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,208✔
504
        PrefilterFreeEngines(de_ctx, sgh->tx_engines);
2,777✔
505
        sgh->tx_engines = NULL;
2,777✔
506
    }
2,777✔
507
    if (sgh->frame_engines) {
115,208✔
508
        PrefilterFreeEngines(de_ctx, sgh->frame_engines);
69✔
509
        sgh->frame_engines = NULL;
69✔
510
    }
69✔
511
    if (sgh->post_rule_match_engines) {
115,208✔
512
        PrefilterFreeEngines(de_ctx, sgh->post_rule_match_engines);
7✔
513
        sgh->post_rule_match_engines = NULL;
7✔
514
    }
7✔
515
}
115,208✔
516

517
static int PrefilterSetupRuleGroupSortHelper(const void *a, const void *b)
518
{
26,736✔
519
    const PrefilterEngine *s0 = a;
26,736✔
520
    const PrefilterEngine *s1 = b;
26,736✔
521
    if (s1->ctx.tx_min_progress == s0->ctx.tx_min_progress) {
26,736✔
522
        if (s1->alproto == s0->alproto) {
12,678✔
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,038✔
526
        }
4,038✔
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,736✔
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,765✔
567
    SCFree(data);
2,765✔
568
}
2,765✔
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
{
6,959✔
573
    const struct PrefilterNonPFDataTx *data = (const struct PrefilterNonPFDataTx *)pectx;
6,959✔
574
    SCLogDebug("adding %u sids", data->size);
6,959✔
575
    PrefilterAddSids(&det_ctx->pmq, data->array, data->size);
6,959✔
576
}
6,959✔
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
{
12,888✔
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
}
12,888✔
602

603
static void PrefilterPktNonPF(DetectEngineThreadCtx *det_ctx, Packet *p, const void *pectx)
604
{
1,338,284✔
605
    const uint16_t alproto = p->flow ? p->flow->alproto : ALPROTO_UNKNOWN;
1,338,284✔
606
    const SignatureMask mask = p->sig_mask;
1,338,284✔
607
    const struct PrefilterNonPFData *data = (const struct PrefilterNonPFData *)pectx;
1,338,284✔
608
    SCLogDebug("adding %u sids", data->size);
1,338,284✔
609
    NONPF_PKT_STATS_INCR(prefilter_pkt_nonpf_called);
1,338,284✔
610
    for (uint32_t i = 0; i < data->size; i++) {
7,732,404✔
611
        const struct PrefilterNonPFDataSig *ds = &data->array[i];
6,394,250✔
612
        const SignatureMask rule_mask = ds->pkt.sig_mask;
6,394,250✔
613
        if ((rule_mask & mask) == rule_mask) {
6,394,250✔
614
            switch (ds->type) {
2,445,926✔
615
                case 0:
2,340,078✔
616
                    if (ds->value == ALPROTO_UNKNOWN || AppProtoEquals(ds->value, alproto)) {
2,340,079✔
617
                        const uint32_t sid = ds->sid;
1,668,239✔
618
                        PrefilterAddSids(&det_ctx->pmq, &sid, 1);
1,668,239✔
619
                        NONPF_PKT_STATS_INCR(prefilter_pkt_nonpf_sids);
1,668,239✔
620
                    } else {
2,149,791,941✔
621
                        NONPF_PKT_STATS_INCR(prefilter_pkt_nonpf_alproto_fail);
2,148,155,487✔
622
                    }
2,148,155,487✔
623
                    break;
2,340,078✔
624
                case 1:
23,226✔
625
                    if (ds->value == p->dp) {
23,226✔
626
                        const uint32_t sid = ds->sid;
6,979✔
627
                        PrefilterAddSids(&det_ctx->pmq, &sid, 1);
6,979✔
628
                        NONPF_PKT_STATS_INCR(prefilter_pkt_nonpf_sids);
6,979✔
629
                    } else {
16,247✔
630
                        NONPF_PKT_STATS_INCR(prefilter_pkt_nonpf_dport_fail);
16,247✔
631
                    }
16,247✔
632
                    break;
23,226✔
633
                case 2:
82,634✔
634
                    if (ds->value == p->payload_len) {
82,634✔
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,199✔
639
                        NONPF_PKT_STATS_INCR(prefilter_pkt_nonpf_dsize_fail);
81,542✔
640
                    }
81,542✔
641
                    break;
82,634✔
642
            }
2,445,926✔
643
        } else {
5,620,697✔
644
            NONPF_PKT_STATS_INCR(prefilter_pkt_nonpf_mask_fail);
3,948,324✔
645
        }
3,948,324✔
646
    }
6,394,250✔
647
}
1,338,284✔
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,414✔
682
    const char *str = data;
1,414✔
683
    return StringHashDjb2((const uint8_t *)str, (uint16_t)strlen(str)) % h->array_size;
1,414✔
684
}
1,414✔
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,414✔
695
    SCFree(data);
1,414✔
696
}
1,414✔
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,123✔
713
    struct TxNonPFData *d = data;
4,123✔
714
    return (d->alproto + d->progress + d->dir + d->sig_list) % h->array_size;
4,123✔
715
}
4,123✔
716

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

725
static void TxNonPFFree(void *data)
726
{
1,414✔
727
    struct TxNonPFData *d = data;
1,414✔
728
    SCFree(d->sigs);
1,414✔
729
    SCFree(d);
1,414✔
730
}
1,414✔
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,709✔
736
    const uint32_t max_sids = DetectEngineGetMaxSigId(de_ctx);
2,709✔
737

738
    struct TxNonPFData lookup = {
2,709✔
739
        .alproto = alproto,
2,709✔
740
        .dir = dir,
2,709✔
741
        .progress = progress,
2,709✔
742
        .sig_list = sig_list,
2,709✔
743
        .sigs_cnt = 0,
2,709✔
744
        .sigs = NULL,
2,709✔
745
        .engine_name = NULL,
2,709✔
746
    };
2,709✔
747
    struct TxNonPFData *e = HashListTableLookup(tx_engines_hash, &lookup, 0);
2,709✔
748
    if (e != NULL) {
2,709✔
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,414✔
767
    if (add == NULL) {
1,414✔
768
        return -1;
×
769
    }
×
770
    add->dir = dir;
1,414✔
771
    add->alproto = alproto;
1,414✔
772
    add->progress = progress;
1,414✔
773
    add->sig_list = sig_list;
1,414✔
774
    add->sigs = SCCalloc(max_sids, sizeof(struct PrefilterNonPFDataSig));
1,414✔
775
    if (add->sigs == NULL) {
1,414✔
776
        SCFree(add);
×
777
        return -1;
×
778
    }
×
779
    add->sigs_cnt = 0;
1,414✔
780
    add->sigs[add->sigs_cnt].sid = s->iid;
1,414✔
781
    add->sigs[add->sigs_cnt].value = alproto;
1,414✔
782
    add->sigs_cnt++;
1,414✔
783

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

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

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

811
    return 0;
1,414✔
812
}
1,414✔
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,448✔
823
    const uint32_t max_sids = DetectEngineGetMaxSigId(de_ctx);
5,448✔
824
    SCLogDebug("max_sids %u", max_sids);
5,448✔
825
    struct PrefilterNonPFDataSig *pkt_non_pf_array = SCCalloc(max_sids, sizeof(*pkt_non_pf_array));
5,448✔
826
    if (pkt_non_pf_array == NULL) {
5,448✔
827
        return -1;
×
828
    }
×
829
    uint32_t pkt_non_pf_array_size = 0;
5,448✔
830
    struct PrefilterNonPFDataSig *frame_non_pf_array =
5,448✔
831
            SCCalloc(max_sids, sizeof(*frame_non_pf_array));
5,448✔
832
    if (frame_non_pf_array == NULL) {
5,448✔
833
        SCFree(pkt_non_pf_array);
×
834
        return -1;
×
835
    }
×
836
    uint32_t frame_non_pf_array_size = 0;
5,448✔
837

838
    struct PrefilterNonPFDataSig *pkt_hook_flow_start_non_pf_array =
5,448✔
839
            SCCalloc(max_sids, sizeof(*pkt_hook_flow_start_non_pf_array));
5,448✔
840
    if (pkt_hook_flow_start_non_pf_array == NULL) {
5,448✔
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,448✔
846
    SignatureMask pkt_hook_flow_start_mask = 0;
5,448✔
847
    bool pkt_hook_flow_start_mask_init = false;
5,448✔
848

849
    HashListTable *tx_engines_hash =
5,448✔
850
            HashListTableInit(256, TxNonPFHash, TxNonPFCompare, TxNonPFFree);
5,448✔
851
    if (tx_engines_hash == NULL) {
5,448✔
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,448✔
859
        de_ctx->non_pf_engine_names =
2,481✔
860
                HashTableInit(512, NonPFNamesHash, NonPFNamesCompare, NonPFNamesFree);
2,481✔
861
        if (de_ctx->non_pf_engine_names == NULL) {
2,481✔
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,481✔
869

870
    SignatureMask pkt_mask = 0;
5,448✔
871
    bool pkt_mask_init = false;
5,448✔
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,448✔
878
    SCLogDebug("app_events_list_id %d", app_events_list_id);
5,448✔
879
    const int app_state_list_id = DetectBufferTypeGetByName("app-layer-state");
5,448✔
880
    SCLogDebug("app_state_list_id %d", app_state_list_id);
5,448✔
881
    for (uint32_t sig = 0; sig < sgh->init->sig_cnt; sig++) {
3,361,284✔
882
        Signature *s = sgh->init->match_array[sig];
3,355,836✔
883
        if (s == NULL)
3,355,836✔
884
            continue;
×
885
        SCLogDebug("checking sid %u for non-prefilter", s->id);
3,355,836✔
886
        if (s->init_data->mpm_sm != NULL && (s->flags & SIG_FLAG_MPM_NEG) == 0)
3,355,836✔
887
            continue;
3,010,165✔
888
        if (s->init_data->prefilter_sm != NULL)
345,671✔
889
            continue;
96✔
890
        if ((s->flags & (SIG_FLAG_PREFILTER | SIG_FLAG_MPM_NEG)) == SIG_FLAG_PREFILTER)
345,575✔
891
            continue;
×
892
        SCLogDebug("setting up sid %u for non-prefilter", s->id);
345,575✔
893

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

899
        if (s->init_data->hook.type == SIGNATURE_HOOK_TYPE_PKT &&
345,575✔
900
                s->init_data->hook.t.pkt.ph == SIGNATURE_HOOK_PKT_FLOW_START) {
345,575✔
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,146✔
937
            const int list_id = s->init_data->buffers[x].id;
4,609✔
938
            const DetectBufferType *buf = DetectEngineBufferTypeGetById(de_ctx, list_id);
4,609✔
939
            if (buf == NULL)
4,609✔
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,609✔
944
            if (list_id == app_events_list_id)
4,609✔
945
                continue;
3,647✔
946
            if (buf->packet) {
962✔
947
                SCLogDebug("packet buf");
56✔
948
                /* packet is handled below */
949
                pkt_non_pf = true;
56✔
950
            } else if (buf->frame) {
906✔
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 {
902✔
971
                SCLogDebug("x %u list_id %d", x, list_id);
902✔
972
                for (DetectEngineAppInspectionEngine *app = de_ctx->app_inspect_engines;
902✔
973
                        app != NULL; app = app->next) {
619,875✔
974
                    SCLogDebug("app %p proto %s list_d %d sig dir %0x", app,
618,973✔
975
                            AppProtoToString(app->alproto), app->sm_list,
618,973✔
976
                            s->flags & (SIG_FLAG_TOSERVER | SIG_FLAG_TOCLIENT));
618,973✔
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) ||
618,973✔
983
                                  ((s->flags & SIG_FLAG_TOCLIENT) != 0 && app->dir == 1)) &&
618,973✔
984
                                list_id == (int)app->sm_list &&
618,973✔
985
                                (s->alproto == ALPROTO_UNKNOWN || app->alproto == ALPROTO_UNKNOWN ||
618,973✔
986
                                        AppProtoEquals(s->alproto, app->alproto))))
4,089✔
987
                        continue;
616,428✔
988

989
                    int sig_list = 0;
2,545✔
990
                    if (list_id == app_state_list_id)
2,545✔
991
                        sig_list = app_state_list_id;
1✔
992
                    if (TxNonPFAddSig(de_ctx, tx_engines_hash, app->alproto, app->dir,
2,545✔
993
                                app->progress, sig_list, buf->name, s) != 0) {
2,545✔
994
                        goto error;
×
995
                    }
×
996
                    tx_non_pf = true;
2,545✔
997
                }
2,545✔
998
            }
902✔
999
        }
962✔
1000
        /* handle hook only rules */
1001
        if (!tx_non_pf && s->init_data->hook.type == SIGNATURE_HOOK_TYPE_APP) {
345,537✔
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,537✔
1020
            if (!pkt_non_pf) {
344,519✔
1021
                SCLogDebug("not frame, not tx, so pkt");
344,476✔
1022
            }
344,476✔
1023
            pkt_non_pf = true;
344,519✔
1024
        }
344,519✔
1025

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

1029
        if (pkt_non_pf) {
345,537✔
1030
            /* for pkt non prefilter, we have some space in the structure,
1031
             * so we can squeeze another filter */
1032
            uint8_t type;
344,528✔
1033
            uint16_t value;
344,528✔
1034
            if ((s->flags & SIG_FLAG_DSIZE) && s->dsize_mode == DETECT_UINT_EQ) {
344,528✔
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,344✔
1042
                type = 1;
365✔
1043
                value = s->dp->port;
365✔
1044
#ifdef NONPF_PKT_STATS
1045
                nonpf_pkt_dport++;
1046
#endif
1047
            } else {
343,979✔
1048
                type = 0;
343,979✔
1049
                value = s->alproto;
343,979✔
1050
#ifdef NONPF_PKT_STATS
1051
                nonpf_pkt_alproto++;
1052
#endif
1053
            }
343,979✔
1054

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

1061
            if (pkt_mask_init) {
344,528✔
1062
                pkt_mask &= s->mask;
342,777✔
1063
            } else {
342,864✔
1064
                pkt_mask = s->mask;
1,751✔
1065
                pkt_mask_init = true;
1,751✔
1066
            }
1,751✔
1067
        }
344,528✔
1068
    }
345,537✔
1069

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

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

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

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

1108
    if (pkt_non_pf_array_size) {
5,448✔
1109
        struct PrefilterNonPFData *data =
1,751✔
1110
                SCCalloc(1, sizeof(*data) + pkt_non_pf_array_size * sizeof(data->array[0]));
1,751✔
1111
        if (data == NULL)
1,751✔
1112
            goto error;
×
1113
        data->size = pkt_non_pf_array_size;
1,751✔
1114
        memcpy((uint8_t *)&data->array, pkt_non_pf_array,
1,751✔
1115
                pkt_non_pf_array_size * sizeof(data->array[0]));
1,751✔
1116
        enum SignatureHookPkt hook = SIGNATURE_HOOK_PKT_NOT_SET; // TODO review
1,751✔
1117
        if (PrefilterAppendEngine(de_ctx, sgh, PrefilterPktNonPF, pkt_mask, hook, (void *)data,
1,751✔
1118
                    PrefilterNonPFDataFree, "packet:non_pf") < 0) {
1,751✔
1119
            SCFree(data);
×
1120
            goto error;
×
1121
        }
×
1122
    }
1,751✔
1123
    if (pkt_hook_flow_start_non_pf_array_size) {
5,448✔
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,448✔
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,448✔
1159
    pkt_hook_flow_start_non_pf_array = NULL;
5,448✔
1160
    SCFree(pkt_non_pf_array);
5,448✔
1161
    pkt_non_pf_array = NULL;
5,448✔
1162
    SCFree(frame_non_pf_array);
5,448✔
1163
    frame_non_pf_array = NULL;
5,448✔
1164
    return 0;
5,448✔
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,448✔
1175

1176
int PrefilterSetupRuleGroup(DetectEngineCtx *de_ctx, SigGroupHead *sgh)
1177
{
5,448✔
1178
    int r = PatternMatchPrepareGroup(de_ctx, sgh);
5,448✔
1179
    if (r != 0) {
5,448✔
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,448✔
1187
    for (int i = 0; i < DETECT_TBLSIZE; i++) {
2,674,968✔
1188
        if (sigmatch_table[i].SetupPrefilter != NULL &&
2,669,520✔
1189
                (setting == DETECT_PREFILTER_AUTO || de_ctx->sm_types_prefilter[i])) {
2,669,520✔
1190
            sigmatch_table[i].SetupPrefilter(de_ctx, sgh);
317✔
1191
        }
317✔
1192
    }
2,669,520✔
1193

1194
    if (SetupNonPrefilter(de_ctx, sgh) != 0) {
5,448✔
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,448✔
1201
    if (sgh->init->pkt_engines != NULL) {
5,448✔
1202
        uint32_t cnt = 0;
1,830✔
1203
        for (el = sgh->init->pkt_engines ; el != NULL; el = el->next) {
3,715✔
1204
            cnt++;
1,885✔
1205
        }
1,885✔
1206
        sgh->pkt_engines = SCMallocAligned(cnt * sizeof(PrefilterEngine), CLS);
1,830✔
1207
        if (sgh->pkt_engines == NULL) {
1,830✔
1208
            return -1;
×
1209
        }
×
1210
        memset(sgh->pkt_engines, 0x00, (cnt * sizeof(PrefilterEngine)));
1,830✔
1211

1212
        PrefilterEngine *e = sgh->pkt_engines;
1,830✔
1213
        for (el = sgh->init->pkt_engines ; el != NULL; el = el->next) {
3,715✔
1214
            e->local_id = el->id;
1,885✔
1215
            e->cb.Prefilter = el->Prefilter;
1,885✔
1216
            e->ctx.pkt.mask = el->pkt_mask;
1,885✔
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,885✔
1220
            e->ctx.pkt.hook = (uint8_t)el->pkt_hook;
1,885✔
1221
            e->pectx = el->pectx;
1,885✔
1222
            el->pectx = NULL; // e now owns the ctx
1,885✔
1223
            e->gid = el->gid;
1,885✔
1224
            if (el->next == NULL) {
1,885✔
1225
                e->is_last = true;
1,830✔
1226
            }
1,830✔
1227
            e++;
1,885✔
1228
        }
1,885✔
1229
    }
1,830✔
1230
    if (sgh->init->payload_engines != NULL) {
5,448✔
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,448✔
1260
        uint32_t cnt = 0;
2,777✔
1261
        for (el = sgh->init->tx_engines ; el != NULL; el = el->next) {
13,710✔
1262
            cnt++;
10,933✔
1263
        }
10,933✔
1264
        sgh->tx_engines = SCMallocAligned(cnt * sizeof(PrefilterEngine), CLS);
2,777✔
1265
        if (sgh->tx_engines == NULL) {
2,777✔
1266
            return -1;
×
1267
        }
×
1268
        memset(sgh->tx_engines, 0x00, (cnt * sizeof(PrefilterEngine)));
2,777✔
1269

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

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

1289
        PrefilterEngine *engine;
2,777✔
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,303✔
1294
            int last_tx_progress = 0;
105,526✔
1295
            bool last_tx_progress_set = false;
105,526✔
1296
            PrefilterEngine *prev_engine = NULL;
105,526✔
1297
            engine = sgh->tx_engines;
105,526✔
1298
            do {
415,454✔
1299
                if (engine->ctx.tx_min_progress != -1)
415,454✔
1300
                    BUG_ON(engine->ctx.tx_min_progress < last_tx_progress);
415,454✔
1301
                if (engine->alproto == a) {
415,454✔
1302
                    if (last_tx_progress_set && engine->ctx.tx_min_progress > last_tx_progress) {
10,932✔
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,932✔
1309
                    prev_engine = engine;
10,932✔
1310
                } else {
404,522✔
1311
                    if (prev_engine) {
404,522✔
1312
                        prev_engine->is_last_for_progress = true;
28,145✔
1313
                    }
28,145✔
1314
                }
404,522✔
1315
                last_tx_progress = engine->ctx.tx_min_progress;
415,454✔
1316
                if (engine->is_last)
415,454✔
1317
                    break;
105,526✔
1318
                engine++;
309,928✔
1319
            } while (1);
309,928✔
1320
        }
105,526✔
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,777✔
1336
    if (sgh->init->frame_engines != NULL) {
5,448✔
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,448✔
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,448✔
1389
}
5,448✔
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,472✔
1395
    PrefilterStore *ctx = data;
18,472✔
1396

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

1399
    for (size_t u = 0; u < strlen(ctx->name); u++) {
269,976✔
1400
        hash += ctx->name[u];
251,504✔
1401
    }
251,504✔
1402

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1477
    const PrefilterStore *store = NULL;
14,751✔
1478
    if (de_ctx->prefilter_hash_table != NULL) {
14,751✔
1479
        HashListTableBucket *hb = HashListTableGetListHead(de_ctx->prefilter_hash_table);
14,751✔
1480
        for ( ; hb != NULL; hb = HashListTableGetListNext(hb)) {
122,589✔
1481
            PrefilterStore *ctx = HashListTableGetListData(hb);
122,589✔
1482
            if (ctx->id == id) {
122,589✔
1483
                store = ctx;
14,751✔
1484
                break;
14,751✔
1485
            }
14,751✔
1486
        }
122,589✔
1487
    }
14,751✔
1488
    return store;
14,751✔
1489
}
14,751✔
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,396✔
1549
    SCEnter();
72,396✔
1550

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

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

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

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

1565
    if (data != NULL && data_len >= mpm_ctx->minlen) {
46,938✔
1566
        (void)mpm_table[mpm_ctx->mpm_type].Search(
45,201✔
1567
                mpm_ctx, &det_ctx->mtc, &det_ctx->pmq, data, data_len);
45,201✔
1568
        PREFILTER_PROFILING_ADD_BYTES(det_ctx, data_len);
45,201✔
1569
    }
45,201✔
1570
}
46,938✔
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,977✔
1627
    SCEnter();
5,977✔
1628

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

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

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

1647
        local_id++;
7,205✔
1648
    } while (1);
7,205✔
1649
}
5,977✔
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✔
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