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

OISF / suricata / 23374838686

21 Mar 2026 07:29AM UTC coverage: 59.341% (-20.0%) from 79.315%
23374838686

Pull #15075

github

web-flow
Merge 90b4e834f into 6587e363a
Pull Request #15075: Stack 8001 v16.4

38 of 70 new or added lines in 10 files covered. (54.29%)

34165 existing lines in 563 files now uncovered.

119621 of 201584 relevant lines covered (59.34%)

650666.92 hits per line

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

70.61
/src/detect-engine-build.c
1
/* Copyright (C) 2007-2025 Open Information Security Foundation
2
 *
3
 * You can copy, redistribute or modify this Program under the terms of
4
 * the GNU General Public License version 2 as published by the Free
5
 * Software Foundation.
6
 *
7
 * This program is distributed in the hope that it will be useful,
8
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10
 * GNU General Public License for more details.
11
 *
12
 * You should have received a copy of the GNU General Public License
13
 * version 2 along with this program; if not, write to the Free Software
14
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
15
 * 02110-1301, USA.
16
 */
17

18
#include "suricata-common.h"
19
#include "detect.h"
20
#include "detect-engine.h"
21
#include "detect-parse.h"
22
#include "detect-content.h"
23

24
#include "detect-engine-build.h"
25
#include "detect-engine-address.h"
26
#include "detect-engine-analyzer.h"
27
#include "detect-engine-iponly.h"
28
#include "detect-engine-mpm.h"
29
#include "detect-engine-siggroup.h"
30
#include "detect-engine-port.h"
31
#include "detect-engine-prefilter.h"
32
#include "detect-engine-proto.h"
33
#include "detect-engine-threshold.h"
34

35
#include "detect-dsize.h"
36
#include "detect-tcp-flags.h"
37
#include "detect-flow.h"
38
#include "detect-config.h"
39
#include "detect-flowbits.h"
40

41
#include "app-layer-events.h"
42

43
#include "util-port-interval-tree.h"
44
#include "util-profiling.h"
45
#include "util-validate.h"
46
#include "util-var-name.h"
47
#include "util-conf.h"
48

49
/* Magic numbers to make the rules of a certain order fall in the same group */
50
#define DETECT_PGSCORE_RULE_PORT_PRIORITIZED 111 /* Rule port group contains a priority port */
1,120✔
51
#define DETECT_PGSCORE_RULE_MPM_FAST_PATTERN 99  /* Rule contains an MPM fast pattern */
2,539✔
52
#define DETECT_PGSCORE_RULE_MPM_NEGATED      77  /* Rule contains a negated MPM */
479✔
53
#define DETECT_PGSCORE_RULE_NO_MPM           55  /* Rule does not contain MPM */
702✔
54
#define DETECT_PGSCORE_RULE_SYN_ONLY         33  /* Rule needs SYN check */
×
55

56
void SigCleanSignatures(DetectEngineCtx *de_ctx)
57
{
35,466✔
58
    if (de_ctx == NULL)
35,466✔
59
        return;
×
60

61
    for (Signature *s = de_ctx->sig_list; s != NULL;) {
154,777✔
62
        Signature *ns = s->next;
119,311✔
63
        SigFree(de_ctx, s);
119,311✔
64
        s = ns;
119,311✔
65
    }
119,311✔
66
    de_ctx->sig_list = NULL;
35,466✔
67

68
    DetectEngineResetMaxSigId(de_ctx);
35,466✔
69
    de_ctx->sig_list = NULL;
35,466✔
70
}
35,466✔
71

72
/** \brief Find a specific signature by sid and gid
73
 *  \param de_ctx detection engine ctx
74
 *  \param sid the signature id
75
 *  \param gid the signature group id
76
 *
77
 *  \retval s sig found
78
 *  \retval NULL sig not found
79
 */
80
Signature *SigFindSignatureBySidGid(DetectEngineCtx *de_ctx, uint32_t sid, uint32_t gid)
UNCOV
81
{
×
UNCOV
82
    if (de_ctx == NULL)
×
83
        return NULL;
×
84

UNCOV
85
    for (Signature *s = de_ctx->sig_list; s != NULL; s = s->next) {
×
UNCOV
86
        if (s->id == sid && s->gid == gid)
×
UNCOV
87
            return s;
×
UNCOV
88
    }
×
89

UNCOV
90
    return NULL;
×
UNCOV
91
}
×
92

93
/**
94
 *  \brief Check if a signature contains the filestore keyword.
95
 *
96
 *  \param s signature
97
 *
98
 *  \retval 0 no
99
 *  \retval 1 yes
100
 */
101
int SignatureIsFilestoring(const Signature *s)
102
{
510,888✔
103
    if (s == NULL)
510,888✔
104
        return 0;
×
105

106
    if (s->flags & SIG_FLAG_FILESTORE)
510,888✔
107
        return 1;
21,508✔
108

109
    return 0;
489,380✔
110
}
510,888✔
111

112
/**
113
 *  \brief Check if a signature contains the filemagic keyword.
114
 *
115
 *  \param s signature
116
 *
117
 *  \retval 0 no
118
 *  \retval 1 yes
119
 */
120
int SignatureIsFilemagicInspecting(const Signature *s)
121
{
510,888✔
122
    if (s == NULL)
510,888✔
123
        return 0;
×
124

125
    if (s->file_flags & FILE_SIG_NEED_MAGIC)
510,888✔
126
        return 1;
964✔
127

128
    return 0;
509,924✔
129
}
510,888✔
130

131
/**
132
 *  \brief Check if a signature contains the filemd5 keyword.
133
 *
134
 *  \param s signature
135
 *
136
 *  \retval 0 no
137
 *  \retval 1 yes
138
 */
139
int SignatureIsFileMd5Inspecting(const Signature *s)
140
{
510,888✔
141
    if ((s != NULL) && (s->file_flags & FILE_SIG_NEED_MD5))
510,888✔
UNCOV
142
        return 1;
×
143

144
    return 0;
510,888✔
145
}
510,888✔
146

147
/**
148
 *  \brief Check if a signature contains the filesha1 keyword.
149
 *
150
 *  \param s signature
151
 *
152
 *  \retval 0 no
153
 *  \retval 1 yes
154
 */
155
int SignatureIsFileSha1Inspecting(const Signature *s)
156
{
510,888✔
157
    if ((s != NULL) && (s->file_flags & FILE_SIG_NEED_SHA1))
510,888✔
158
        return 1;
×
159

160
    return 0;
510,888✔
161
}
510,888✔
162

163
/**
164
 *  \brief Check if a signature contains the filesha256 keyword.
165
 *
166
 *  \param s signature
167
 *
168
 *  \retval 0 no
169
 *  \retval 1 yes
170
 */
171
int SignatureIsFileSha256Inspecting(const Signature *s)
172
{
510,888✔
173
    if ((s != NULL) && (s->file_flags & FILE_SIG_NEED_SHA256))
510,888✔
UNCOV
174
        return 1;
×
175

176
    return 0;
510,888✔
177
}
510,888✔
178

179
static bool SignatureInspectsBuffers(const Signature *s)
180
{
506,182✔
181
    return (s->init_data->buffer_index > 0);
506,182✔
182
}
506,182✔
183

184
/** \brief Test is a initialized signature is IP only
185
 *  \param de_ctx detection engine ctx
186
 *  \param s the signature
187
 *  \retval 1 sig is ip only
188
 *  \retval 2 sig is like ip only
189
 *  \retval 0 sig is not ip only
190
 */
191
int SignatureIsIPOnly(DetectEngineCtx *de_ctx, const Signature *s)
192
{
903,463✔
193
    /* explicit hook means no IP-only */
194
    if (s->init_data->hook.type != SIGNATURE_HOOK_TYPE_NOT_SET)
903,463✔
195
        return 0;
1✔
196

197
    if (s->alproto != ALPROTO_UNKNOWN)
903,462✔
198
        return 0;
584,009✔
199

200
    if (s->init_data->smlists[DETECT_SM_LIST_PMATCH] != NULL)
319,453✔
201
        return 0;
121,526✔
202

203
    // may happen for 'config' keyword, postmatch
204
    if (s->flags & SIG_FLAG_APPLAYER)
197,927✔
205
        return 0;
1,577✔
206

207
    /* if flow dir is set we can't process it in ip-only */
208
    if (!(((s->flags & (SIG_FLAG_TOSERVER|SIG_FLAG_TOCLIENT)) == 0) ||
196,350✔
209
            (s->flags & (SIG_FLAG_TOSERVER|SIG_FLAG_TOCLIENT)) ==
196,350✔
210
            (SIG_FLAG_TOSERVER|SIG_FLAG_TOCLIENT)))
196,350✔
211
        return 0;
4,391✔
212

213
    /* for now assume that all registered buffer types are incompatible */
214
    if (SignatureInspectsBuffers(s)) {
191,959✔
215
        SCReturnInt(0);
28,189✔
216
    }
28,189✔
217

218
    /* TMATCH list can be ignored, it contains TAGs and
219
     * tags are compatible to IP-only. */
220

221
    SigMatch *sm = s->init_data->smlists[DETECT_SM_LIST_MATCH];
163,770✔
222
    for (; sm != NULL; sm = sm->next) {
163,926✔
223
        if (!(sigmatch_table[sm->type].flags & SIGMATCH_IPONLY_COMPAT))
77,183✔
224
            return 0;
66,308✔
225
        /* we have enabled flowbits to be compatible with ip only sigs, as long
226
         * as the sig only has a "set" flowbits */
227
        if (sm->type == DETECT_FLOWBITS &&
10,875✔
228
                (((DetectFlowbitsData *)sm->ctx)->cmd != DETECT_FLOWBITS_CMD_SET)) {
10,875✔
229
            return 0;
10,719✔
230
        }
10,719✔
231
    }
10,875✔
232
    sm = s->init_data->smlists[DETECT_SM_LIST_POSTMATCH];
86,743✔
233
    for ( ; sm != NULL; sm = sm->next) {
92,926✔
234
        if ( !(sigmatch_table[sm->type].flags & SIGMATCH_IPONLY_COMPAT))
8,251✔
235
            return 0;
320✔
236
        /* we have enabled flowbits to be compatible with ip only sigs, as long
237
         * as the sig only has a "set" flowbits */
238
        if (sm->type == DETECT_FLOWBITS &&
7,931✔
239
            (((DetectFlowbitsData *)sm->ctx)->cmd != DETECT_FLOWBITS_CMD_SET) ) {
7,931✔
240
            return 0;
1,748✔
241
        }
1,748✔
242
    }
7,931✔
243

244
    if (s->init_data->src_contains_negation || s->init_data->dst_contains_negation) {
84,675✔
245
        /* Rule is IP only, but contains negated addresses. */
246
        return 2;
2,104✔
247
    }
2,104✔
248
    if (!(de_ctx->flags & DE_QUIET)) {
82,571✔
249
        SCLogDebug("IP-ONLY (%" PRIu32 "): source %s, dest %s", s->id,
80,677✔
250
                   s->flags & SIG_FLAG_SRC_ANY ? "ANY" : "SET",
80,677✔
251
                   s->flags & SIG_FLAG_DST_ANY ? "ANY" : "SET");
80,677✔
252
    }
80,677✔
253
    return 1;
82,571✔
254
}
84,675✔
255

256
/** \internal
257
 *  \brief Test is a initialized signature is inspecting protocol detection only
258
 *  \param de_ctx detection engine ctx
259
 *  \param s the signature
260
 *  \retval 1 sig is dp only
261
 *  \retval 0 sig is not dp only
262
 */
263
static int SignatureIsPDOnly(const DetectEngineCtx *de_ctx, const Signature *s)
264
{
906,507✔
265
    /* explicit hook means no PD-only */
266
    if (s->init_data->hook.type != SIGNATURE_HOOK_TYPE_NOT_SET)
906,507✔
267
        return 0;
1✔
268

269
    if (s->alproto != ALPROTO_UNKNOWN)
906,506✔
270
        return 0;
584,009✔
271

272
    if (s->init_data->smlists[DETECT_SM_LIST_PMATCH] != NULL)
322,497✔
273
        return 0;
121,526✔
274

275
    /* for now assume that all registered buffer types are incompatible */
276
    if (SignatureInspectsBuffers(s)) {
200,971✔
277
        SCReturnInt(0);
30,116✔
278
    }
30,116✔
279

280
    /* TMATCH list can be ignored, it contains TAGs and
281
     * tags are compatible to DP-only. */
282

283
    /* match list matches may be compatible to DP only. We follow the same
284
     * logic as IP-only so we can use that flag */
285

286
    SigMatch *sm = s->init_data->smlists[DETECT_SM_LIST_MATCH];
170,855✔
287
    if (sm == NULL)
170,855✔
288
        return 0;
86,732✔
289

290
    int pd = 0;
84,123✔
291
    for ( ; sm != NULL; sm = sm->next) {
88,018✔
292
        if (sm->type == DETECT_APP_LAYER_PROTOCOL) {
84,888✔
293
            pd = 1;
3,739✔
294
        } else {
81,149✔
295
            /* flowbits are supported for dp only sigs, as long
296
             * as the sig only has a "set" flowbits */
297
            if (sm->type == DETECT_FLOWBITS) {
81,149✔
298
                if ((((DetectFlowbitsData *)sm->ctx)->cmd != DETECT_FLOWBITS_CMD_SET) ) {
13,703✔
299
                    SCLogDebug("%u: not PD-only: flowbit settings other than 'set'", s->id);
13,703✔
300
                    return 0;
13,703✔
301
                }
13,703✔
302
            } else if (sm->type == DETECT_FLOW) {
67,446✔
303
                if (((DetectFlowData *)sm->ctx)->flags & ~(DETECT_FLOW_FLAG_TOSERVER|DETECT_FLOW_FLAG_TOCLIENT)) {
2,548✔
304
                    SCLogDebug("%u: not PD-only: flow settings other than toserver/toclient", s->id);
2,548✔
305
                    return 0;
2,548✔
306
                }
2,548✔
307
            } else if ( !(sigmatch_table[sm->type].flags & SIGMATCH_IPONLY_COMPAT)) {
64,898✔
308
                SCLogDebug("%u: not PD-only: %s not PD/IP-only compat", s->id, sigmatch_table[sm->type].name);
64,742✔
309
                return 0;
64,742✔
310
            }
64,742✔
311
        }
81,149✔
312
    }
84,888✔
313

314
    if (pd) {
3,130✔
315
        SCLogDebug("PD-ONLY (%" PRIu32 ")", s->id);
3,044✔
316
    }
3,044✔
317
    return pd;
3,130✔
318
}
84,123✔
319

320
/**
321
 *  \internal
322
 *  \brief Check if the initialized signature is inspecting the packet payload
323
 *  \param de_ctx detection engine ctx
324
 *  \param s the signature
325
 *  \retval 1 sig is inspecting the payload
326
 *  \retval 0 sig is not inspecting the payload
327
 */
328
static int SignatureIsInspectingPayload(DetectEngineCtx *de_ctx, const Signature *s)
329
{
113,188✔
330

331
    if (s->init_data->smlists[DETECT_SM_LIST_PMATCH] != NULL) {
113,188✔
332
        return 1;
18,381✔
333
    }
18,381✔
334
    return 0;
94,807✔
335
}
113,188✔
336

337
/**
338
 *  \internal
339
 *  \brief check if a signature is decoder event matching only
340
 *  \param de_ctx detection engine
341
 *  \param s the signature to test
342
 *  \retval 0 not a DEOnly sig
343
 *  \retval 1 DEOnly sig
344
 */
345
static int SignatureIsDEOnly(DetectEngineCtx *de_ctx, const Signature *s)
346
{
818,788✔
347
    /* explicit hook means no DE-only */
348
    if (s->init_data->hook.type != SIGNATURE_HOOK_TYPE_NOT_SET)
818,788✔
349
        SCReturnInt(0);
1✔
350

351
    if (s->alproto != ALPROTO_UNKNOWN) {
818,787✔
352
        SCReturnInt(0);
584,009✔
353
    }
584,009✔
354

355
    if (s->init_data->smlists[DETECT_SM_LIST_PMATCH] != NULL)
234,778✔
356
    {
121,526✔
357
        SCReturnInt(0);
121,526✔
358
    }
121,526✔
359

360
    /* for now assume that all registered buffer types are incompatible */
361
    if (SignatureInspectsBuffers(s)) {
113,252✔
362
        SCReturnInt(0);
30,116✔
363
    }
30,116✔
364

365
    /* check for conflicting keywords */
366
    SigMatch *sm = s->init_data->smlists[DETECT_SM_LIST_MATCH];
83,136✔
367
    for ( ;sm != NULL; sm = sm->next) {
107,145✔
368
        if ( !(sigmatch_table[sm->type].flags & SIGMATCH_DEONLY_COMPAT))
81,182✔
369
            SCReturnInt(0);
57,173✔
370
    }
81,182✔
371

372
    /* need at least one decode event keyword to be considered decode event. */
373
    sm = s->init_data->smlists[DETECT_SM_LIST_MATCH];
25,963✔
374
    for ( ;sm != NULL; sm = sm->next) {
25,963✔
375
        if (sm->type == DETECT_DECODE_EVENT)
23,820✔
376
            goto deonly;
23,820✔
377
        if (sm->type == DETECT_ENGINE_EVENT)
×
378
            goto deonly;
×
379
        if (sm->type == DETECT_STREAM_EVENT)
×
380
            goto deonly;
×
381
    }
×
382

383
    SCReturnInt(0);
25,963✔
384

385
deonly:
23,820✔
386
    if (!(de_ctx->flags & DE_QUIET)) {
23,820✔
387
        SCLogDebug("DE-ONLY (%" PRIu32 "): source %s, dest %s", s->id,
23,790✔
388
                   s->flags & SIG_FLAG_SRC_ANY ? "ANY" : "SET",
23,790✔
389
                   s->flags & SIG_FLAG_DST_ANY ? "ANY" : "SET");
23,790✔
390
    }
23,790✔
391

392
    SCReturnInt(1);
23,820✔
393
}
25,963✔
394

395
#define MASK_TCP_INITDEINIT_FLAGS   (TH_SYN|TH_RST|TH_FIN)
726,176✔
396
#define MASK_TCP_UNUSUAL_FLAGS      (TH_URG|TH_ECN|TH_CWR)
726,176✔
397

398
/* Create mask for this packet + it's flow if it has one
399
 */
400
void
401
PacketCreateMask(Packet *p, SignatureMask *mask, AppProto alproto,
402
        bool app_decoder_events)
403
{
829,949✔
404
    if (!(PKT_IS_PSEUDOPKT(p))) {
829,949✔
405
        (*mask) |= SIG_MASK_REQUIRE_REAL_PKT;
819,273✔
406
    }
819,273✔
407
    if (!(p->flags & PKT_NOPAYLOAD_INSPECTION) && p->payload_len > 0) {
829,949✔
408
        SCLogDebug("packet has payload");
563,612✔
409
        (*mask) |= SIG_MASK_REQUIRE_PAYLOAD;
563,612✔
410
    } else if (p->flags & PKT_DETECT_HAS_STREAMDATA) {
563,612✔
411
        SCLogDebug("stream data available");
6,362✔
412
        (*mask) |= SIG_MASK_REQUIRE_PAYLOAD;
6,362✔
413
    } else {
259,975✔
414
        SCLogDebug("packet has no payload");
259,975✔
415
        (*mask) |= SIG_MASK_REQUIRE_NO_PAYLOAD;
259,975✔
416
    }
259,975✔
417

418
    if (p->events.cnt > 0 || app_decoder_events != 0 ||
829,949✔
419
            (p->app_layer_events != NULL && p->app_layer_events->cnt)) {
829,949✔
420
        SCLogDebug("packet/flow has events set");
72,261✔
421
        (*mask) |= SIG_MASK_REQUIRE_ENGINE_EVENT;
72,261✔
422
    }
72,261✔
423

424
    if (!(PKT_IS_PSEUDOPKT(p)) && PacketIsTCP(p)) {
829,949✔
425
        const TCPHdr *tcph = PacketGetTCP(p);
725,441✔
426
        if ((tcph->th_flags & MASK_TCP_INITDEINIT_FLAGS) != 0) {
725,441✔
427
            (*mask) |= SIG_MASK_REQUIRE_FLAGS_INITDEINIT;
46,358✔
428
        }
46,358✔
429
        if ((tcph->th_flags & MASK_TCP_UNUSUAL_FLAGS) != 0) {
725,441✔
430
            (*mask) |= SIG_MASK_REQUIRE_FLAGS_UNUSUAL;
95,681✔
431
        }
95,681✔
432
    }
725,441✔
433

434
    if (p->flags & PKT_HAS_FLOW) {
829,949✔
435
        SCLogDebug("packet has flow");
822,184✔
436
        (*mask) |= SIG_MASK_REQUIRE_FLOW;
822,184✔
437
    }
822,184✔
438
}
829,949✔
439

440
static int SignatureCreateMask(Signature *s)
441
{
119,313✔
442
    SCEnter();
119,313✔
443

444
    if ((s->flags & (SIG_FLAG_REQUIRE_PACKET | SIG_FLAG_REQUIRE_STREAM)) ==
119,313✔
445
            SIG_FLAG_REQUIRE_PACKET) {
119,313✔
446
        s->mask |= SIG_MASK_REQUIRE_REAL_PKT;
12,009✔
447
    }
12,009✔
448
    if (s->init_data->smlists[DETECT_SM_LIST_PMATCH] != NULL) {
119,313✔
449
        s->mask |= SIG_MASK_REQUIRE_PAYLOAD;
18,381✔
450
        SCLogDebug("sig requires payload");
18,381✔
451
    }
18,381✔
452

453
    SigMatch *sm;
119,313✔
454
    for (sm = s->init_data->smlists[DETECT_SM_LIST_MATCH] ; sm != NULL; sm = sm->next) {
170,675✔
455
        switch(sm->type) {
51,362✔
456
            case DETECT_FLOWBITS:
2,184✔
457
            {
2,184✔
458
                /* figure out what flowbit action */
459
                DetectFlowbitsData *fb = (DetectFlowbitsData *)sm->ctx;
2,184✔
460
                if (fb->cmd == DETECT_FLOWBITS_CMD_ISSET) {
2,184✔
461
                    /* not a mask flag, but still set it here */
462
                    s->flags |= SIG_FLAG_REQUIRE_FLOWVAR;
1,528✔
463

464
                    SCLogDebug("SIG_FLAG_REQUIRE_FLOWVAR set as sig has "
1,528✔
465
                            "flowbit isset option.");
1,528✔
466
                }
1,528✔
467

468
                /* flow is required for any flowbit manipulation */
469
                s->mask |= SIG_MASK_REQUIRE_FLOW;
2,184✔
470
                SCLogDebug("sig requires flow to be able to manipulate "
2,184✔
471
                        "flowbit(s)");
2,184✔
472
                break;
2,184✔
473
            }
×
474
            case DETECT_FLOWINT:
1,649✔
475
                /* flow is required for any flowint manipulation */
476
                s->mask |= SIG_MASK_REQUIRE_FLOW;
1,649✔
477
                SCLogDebug("sig requires flow to be able to manipulate "
1,649✔
478
                        "flowint(s)");
1,649✔
479
                break;
1,649✔
480
            case DETECT_FLAGS:
735✔
481
            {
735✔
482
                DetectU8Data *fl = (DetectU8Data *)sm->ctx;
735✔
483

484
                uint8_t arg = 0;
735✔
485
                if (fl->mode == DetectUintModeBitmask) {
735✔
486
                    arg = fl->arg2;
735✔
487
                } else if (fl->mode == DetectUintModeNegBitmask && fl->arg2 == 0) {
735✔
488
                    arg = fl->arg1;
×
489
                } else if (fl->mode == DetectUintModeEqual) {
×
490
                    arg = fl->arg1;
×
491
                }
×
492
                if (arg & MASK_TCP_INITDEINIT_FLAGS) {
735✔
493
                    s->mask |= SIG_MASK_REQUIRE_FLAGS_INITDEINIT;
546✔
494
                    SCLogDebug("sig requires SIG_MASK_REQUIRE_FLAGS_INITDEINIT");
546✔
495
                }
546✔
496
                if (arg & MASK_TCP_UNUSUAL_FLAGS) {
735✔
497
                    s->mask |= SIG_MASK_REQUIRE_FLAGS_UNUSUAL;
187✔
498
                    SCLogDebug("sig requires SIG_MASK_REQUIRE_FLAGS_UNUSUAL");
187✔
499
                }
187✔
500

501
                break;
735✔
502
            }
×
503
            case DETECT_DSIZE:
1,561✔
504
            {
1,561✔
505
                DetectU16Data *ds = (DetectU16Data *)sm->ctx;
1,561✔
506
                /* LT will include 0, so no payload.
507
                 * if GT is used in the same rule the
508
                 * flag will be set anyway. */
509
                if (ds->mode == DETECT_UINT_RA || ds->mode == DETECT_UINT_GT ||
1,561✔
510
                        ds->mode == DETECT_UINT_NE || ds->mode == DETECT_UINT_GTE) {
1,561✔
511

512
                    s->mask |= SIG_MASK_REQUIRE_PAYLOAD;
445✔
513
                    SCLogDebug("sig requires payload");
445✔
514

515
                } else if (ds->mode == DETECT_UINT_EQ) {
1,116✔
516
                    if (ds->arg1 > 0) {
754✔
517
                        s->mask |= SIG_MASK_REQUIRE_PAYLOAD;
751✔
518
                        SCLogDebug("sig requires payload");
751✔
519
                    } else {
751✔
520
                        s->mask |= SIG_MASK_REQUIRE_NO_PAYLOAD;
3✔
521
                        SCLogDebug("sig requires no payload");
3✔
522
                    }
3✔
523
                }
754✔
524
                break;
1,561✔
525
            }
×
526
            case DETECT_DECODE_EVENT:
12,777✔
527
                // fallthrough
528
            case DETECT_STREAM_EVENT:
18,195✔
529
                // fallthrough
530
            case DETECT_APP_LAYER_EVENT:
19,583✔
531
                // fallthrough
532
            case DETECT_ENGINE_EVENT:
19,583✔
533
                s->mask |= SIG_MASK_REQUIRE_ENGINE_EVENT;
19,583✔
534
                break;
19,583✔
535
        }
51,362✔
536
    }
51,362✔
537

538
    for (sm = s->init_data->smlists[DETECT_SM_LIST_POSTMATCH]; sm != NULL; sm = sm->next) {
134,035✔
539
        switch (sm->type) {
14,722✔
540
            case DETECT_CONFIG: {
44✔
541
                DetectConfigData *fd = (DetectConfigData *)sm->ctx;
44✔
542
                if (fd->scope == CONFIG_SCOPE_FLOW) {
44✔
543
                    s->mask |= SIG_MASK_REQUIRE_FLOW;
×
544
                }
×
545
                break;
44✔
546
            }
×
547
        }
14,722✔
548
    }
14,722✔
549

550
    if (s->init_data->init_flags & SIG_FLAG_INIT_FLOW) {
119,313✔
551
        s->mask |= SIG_MASK_REQUIRE_FLOW;
16,765✔
552
        SCLogDebug("sig requires flow");
16,765✔
553
    }
16,765✔
554

555
    if (s->flags & SIG_FLAG_APPLAYER) {
119,313✔
556
        s->mask |= SIG_MASK_REQUIRE_FLOW;
72,496✔
557
        SCLogDebug("sig requires flow");
72,496✔
558
    }
72,496✔
559

560
    SCLogDebug("mask %02X", s->mask);
119,313✔
561
    SCReturnInt(0);
119,313✔
562
}
119,313✔
563

564
static void SigInitStandardMpmFactoryContexts(DetectEngineCtx *de_ctx)
565
{
35,438✔
566
    DetectMpmInitializeBuiltinMpms(de_ctx);
35,438✔
567
}
35,438✔
568

569
/** \brief Pure-PCRE or bytetest rule */
570
static bool RuleInspectsPayloadHasNoMpm(const Signature *s)
571
{
19,128✔
572
    if (s->init_data->mpm_sm == NULL && s->init_data->smlists[DETECT_SM_LIST_PMATCH] != NULL)
19,128✔
573
        return true;
2,539✔
574
    return false;
16,589✔
575
}
19,128✔
576

577
static int RuleGetMpmPatternSize(const Signature *s)
578
{
3,344✔
579
    if (s->init_data->mpm_sm == NULL)
3,344✔
580
        return -1;
×
581
    int mpm_list = s->init_data->mpm_sm_list;
3,344✔
582
    if (mpm_list < 0)
3,344✔
583
        return -1;
×
584
    const DetectContentData *cd = (const DetectContentData *)s->init_data->mpm_sm->ctx;
3,344✔
585
    if (cd == NULL)
3,344✔
586
        return -1;
×
587
    return (int)cd->content_len;
3,344✔
588
}
3,344✔
589

590
static bool RuleMpmIsNegated(const Signature *s)
591
{
135,902✔
592
    if (s->flags & SIG_FLAG_MPM_NEG)
135,902✔
593
        return true;
479✔
594
    if (s->init_data->mpm_sm == NULL)
135,423✔
595
        return false;
93,995✔
596
    int mpm_list = s->init_data->mpm_sm_list;
41,428✔
597
    if (mpm_list < 0)
41,428✔
598
        return false;
×
599
    const DetectContentData *cd = (const DetectContentData *)s->init_data->mpm_sm->ctx;
41,428✔
600
    if (cd == NULL)
41,428✔
601
        return false;
×
602
    return (cd->flags & DETECT_CONTENT_NEGATED) ? true : false;
41,428✔
603
}
41,428✔
604

605
static SCJsonBuilder *RulesGroupPrintSghStats(const DetectEngineCtx *de_ctx,
606
        const SigGroupHead *sgh, const int add_rules, const int add_mpm_stats)
UNCOV
607
{
×
UNCOV
608
    uint32_t prefilter_cnt = 0;
×
UNCOV
609
    uint32_t mpm_cnt = 0;
×
UNCOV
610
    uint32_t nonmpm_cnt = 0;
×
UNCOV
611
    uint32_t mpm_depth_cnt = 0;
×
UNCOV
612
    uint32_t mpm_endswith_cnt = 0;
×
UNCOV
613
    uint32_t negmpm_cnt = 0;
×
UNCOV
614
    uint32_t any5_cnt = 0;
×
UNCOV
615
    uint32_t payload_no_mpm_cnt = 0;
×
UNCOV
616
    uint32_t syn_cnt = 0;
×
617

UNCOV
618
    uint32_t mpms_min = 0;
×
UNCOV
619
    uint32_t mpms_max = 0;
×
620

UNCOV
621
    int max_buffer_type_id = de_ctx->buffer_type_id;
×
622

UNCOV
623
    struct {
×
UNCOV
624
        uint32_t total;
×
UNCOV
625
        uint32_t cnt;
×
UNCOV
626
        uint32_t min;
×
UNCOV
627
        uint32_t max;
×
UNCOV
628
    } mpm_stats[max_buffer_type_id];
×
UNCOV
629
    memset(mpm_stats, 0x00, sizeof(mpm_stats));
×
630

UNCOV
631
    uint32_t alstats[g_alproto_max];
×
UNCOV
632
    memset(alstats, 0, g_alproto_max * sizeof(uint32_t));
×
UNCOV
633
    uint32_t mpm_sizes[max_buffer_type_id][256];
×
UNCOV
634
    memset(mpm_sizes, 0, sizeof(mpm_sizes));
×
UNCOV
635
    uint32_t alproto_mpm_bufs[g_alproto_max][max_buffer_type_id];
×
UNCOV
636
    memset(alproto_mpm_bufs, 0, sizeof(alproto_mpm_bufs));
×
637

UNCOV
638
    DEBUG_VALIDATE_BUG_ON(sgh->init == NULL);
×
UNCOV
639
    if (sgh->init == NULL)
×
640
        return NULL;
×
641

UNCOV
642
    SCJsonBuilder *js = SCJbNewObject();
×
UNCOV
643
    if (unlikely(js == NULL))
×
644
        return NULL;
×
645

UNCOV
646
    SCJbSetUint(js, "id", sgh->id);
×
647

UNCOV
648
    SCJbOpenArray(js, "rules");
×
UNCOV
649
    for (uint32_t x = 0; x < sgh->init->sig_cnt; x++) {
×
UNCOV
650
        const Signature *s = sgh->init->match_array[x];
×
UNCOV
651
        if (s == NULL)
×
652
            continue;
×
653

UNCOV
654
        int any = 0;
×
UNCOV
655
        if (s->proto.flags & DETECT_PROTO_ANY) {
×
656
            any++;
×
657
        }
×
UNCOV
658
        if (s->flags & SIG_FLAG_DST_ANY) {
×
UNCOV
659
            any++;
×
UNCOV
660
        }
×
UNCOV
661
        if (s->flags & SIG_FLAG_SRC_ANY) {
×
UNCOV
662
            any++;
×
UNCOV
663
        }
×
UNCOV
664
        if (s->flags & SIG_FLAG_DP_ANY) {
×
UNCOV
665
            any++;
×
UNCOV
666
        }
×
UNCOV
667
        if (s->flags & SIG_FLAG_SP_ANY) {
×
UNCOV
668
            any++;
×
UNCOV
669
        }
×
UNCOV
670
        if (any == 5) {
×
671
            any5_cnt++;
×
672
        }
×
673

UNCOV
674
        prefilter_cnt += (s->init_data->prefilter_sm != NULL);
×
UNCOV
675
        if (s->init_data->mpm_sm == NULL) {
×
UNCOV
676
            nonmpm_cnt++;
×
677

UNCOV
678
            if (s->sm_arrays[DETECT_SM_LIST_MATCH] != NULL) {
×
679
                SCLogDebug("SGH %p Non-MPM inspecting only packets. Rule %u", sgh, s->id);
×
680
            }
×
681

UNCOV
682
            DetectPort *sp = s->sp;
×
UNCOV
683
            DetectPort *dp = s->dp;
×
684

UNCOV
685
            if (s->flags & SIG_FLAG_TOSERVER && (dp->port == 0 && dp->port2 == 65535)) {
×
686
                SCLogDebug("SGH %p Non-MPM toserver and to 'any'. Rule %u", sgh, s->id);
×
687
            }
×
UNCOV
688
            if (s->flags & SIG_FLAG_TOCLIENT && (sp->port == 0 && sp->port2 == 65535)) {
×
689
                SCLogDebug("SGH %p Non-MPM toclient and to 'any'. Rule %u", sgh, s->id);
×
690
            }
×
691

UNCOV
692
            if (DetectFlagsSignatureNeedsSynPackets(s)) {
×
693
                syn_cnt++;
×
694
            }
×
695

UNCOV
696
        } else {
×
UNCOV
697
            int mpm_list = s->init_data->mpm_sm_list;
×
UNCOV
698
            BUG_ON(mpm_list < 0);
×
UNCOV
699
            const DetectContentData *cd = (const DetectContentData *)s->init_data->mpm_sm->ctx;
×
UNCOV
700
            uint32_t size = cd->content_len < 256 ? cd->content_len : 255;
×
701

UNCOV
702
            mpm_sizes[mpm_list][size]++;
×
UNCOV
703
            alproto_mpm_bufs[s->alproto][mpm_list]++;
×
704

UNCOV
705
            if (mpm_list == DETECT_SM_LIST_PMATCH) {
×
UNCOV
706
                if (size == 1) {
×
707
                    DetectPort *sp = s->sp;
×
708
                    DetectPort *dp = s->dp;
×
709
                    if (s->flags & SIG_FLAG_TOSERVER) {
×
710
                        if (dp->port == 0 && dp->port2 == 65535) {
×
711
                            SCLogDebug("SGH %p toserver 1byte fast_pattern to ANY. Rule %u", sgh, s->id);
×
712
                        } else {
×
713
                            SCLogDebug("SGH %p toserver 1byte fast_pattern to port(s) %u-%u. Rule %u", sgh, dp->port, dp->port2, s->id);
×
714
                        }
×
715
                    }
×
716
                    if (s->flags & SIG_FLAG_TOCLIENT) {
×
717
                        if (sp->port == 0 && sp->port2 == 65535) {
×
718
                            SCLogDebug("SGH %p toclient 1byte fast_pattern to ANY. Rule %u", sgh, s->id);
×
719
                        } else {
×
720
                            SCLogDebug("SGH %p toclient 1byte fast_pattern to port(s) %u-%u. Rule %u", sgh, sp->port, sp->port2, s->id);
×
721
                        }
×
722
                    }
×
723
                }
×
UNCOV
724
            }
×
725

UNCOV
726
            uint32_t w = PatternStrength(cd->content, cd->content_len);
×
UNCOV
727
            if (mpms_min == 0)
×
UNCOV
728
                mpms_min = w;
×
UNCOV
729
            if (w < mpms_min)
×
730
                mpms_min = w;
×
UNCOV
731
            if (w > mpms_max)
×
UNCOV
732
                mpms_max = w;
×
733

UNCOV
734
            BUG_ON(mpm_list >= max_buffer_type_id);
×
UNCOV
735
            mpm_stats[mpm_list].total += w;
×
UNCOV
736
            mpm_stats[mpm_list].cnt++;
×
UNCOV
737
            if (mpm_stats[mpm_list].min == 0 || w < mpm_stats[mpm_list].min)
×
UNCOV
738
                mpm_stats[mpm_list].min = w;
×
UNCOV
739
            if (w > mpm_stats[mpm_list].max)
×
UNCOV
740
                mpm_stats[mpm_list].max = w;
×
741

UNCOV
742
            mpm_cnt++;
×
743

UNCOV
744
            if (w < 10) {
×
UNCOV
745
                SCLogDebug("SGH %p Weak MPM Pattern on %s. Rule %u", sgh, DetectListToString(mpm_list), s->id);
×
UNCOV
746
            }
×
UNCOV
747
            if (w < 10 && any == 5) {
×
748
                SCLogDebug("SGH %p Weak MPM Pattern on %s, rule is 5xAny. Rule %u", sgh, DetectListToString(mpm_list), s->id);
×
749
            }
×
750

UNCOV
751
            if (cd->flags & DETECT_CONTENT_NEGATED) {
×
752
                SCLogDebug("SGH %p MPM Pattern on %s, is negated. Rule %u", sgh, DetectListToString(mpm_list), s->id);
×
753
                negmpm_cnt++;
×
754
            }
×
UNCOV
755
            if (cd->flags & DETECT_CONTENT_ENDS_WITH) {
×
756
                mpm_endswith_cnt++;
×
757
            }
×
UNCOV
758
            if (cd->flags & DETECT_CONTENT_DEPTH) {
×
759
                mpm_depth_cnt++;
×
760
            }
×
UNCOV
761
        }
×
762

UNCOV
763
        if (RuleInspectsPayloadHasNoMpm(s)) {
×
764
            SCLogDebug("SGH %p No MPM. Payload inspecting. Rule %u", sgh, s->id);
×
765
            payload_no_mpm_cnt++;
×
766
        }
×
767

UNCOV
768
        alstats[s->alproto]++;
×
769

UNCOV
770
        if (add_rules) {
×
UNCOV
771
            SCJsonBuilder *e = SCJbNewObject();
×
UNCOV
772
            if (e != NULL) {
×
UNCOV
773
                SCJbSetUint(e, "sig_id", s->id);
×
UNCOV
774
                SCJbClose(e);
×
UNCOV
775
                SCJbAppendObject(js, e);
×
UNCOV
776
                SCJbFree(e);
×
UNCOV
777
            }
×
UNCOV
778
        }
×
UNCOV
779
    }
×
UNCOV
780
    SCJbClose(js);
×
781

UNCOV
782
    SCJbOpenObject(js, "stats");
×
UNCOV
783
    SCJbSetUint(js, "total", sgh->init->sig_cnt);
×
784

UNCOV
785
    SCJbOpenObject(js, "types");
×
UNCOV
786
    SCJbSetUint(js, "mpm", mpm_cnt);
×
UNCOV
787
    SCJbSetUint(js, "non_mpm", nonmpm_cnt);
×
UNCOV
788
    SCJbSetUint(js, "mpm_depth", mpm_depth_cnt);
×
UNCOV
789
    SCJbSetUint(js, "mpm_endswith", mpm_endswith_cnt);
×
UNCOV
790
    SCJbSetUint(js, "negated_mpm", negmpm_cnt);
×
UNCOV
791
    SCJbSetUint(js, "payload_but_no_mpm", payload_no_mpm_cnt);
×
UNCOV
792
    SCJbSetUint(js, "prefilter", prefilter_cnt);
×
UNCOV
793
    SCJbSetUint(js, "syn", syn_cnt);
×
UNCOV
794
    SCJbSetUint(js, "any5", any5_cnt);
×
UNCOV
795
    SCJbClose(js);
×
796

UNCOV
797
    for (AppProto i = 0; i < g_alproto_max; i++) {
×
UNCOV
798
        if (alstats[i] > 0) {
×
UNCOV
799
            const char *proto_name = (i == ALPROTO_UNKNOWN) ? "payload" : AppProtoToString(i);
×
UNCOV
800
            SCJbOpenObject(js, proto_name);
×
UNCOV
801
            SCJbSetUint(js, "total", alstats[i]);
×
802

UNCOV
803
            for (int y = 0; y < max_buffer_type_id; y++) {
×
UNCOV
804
                if (alproto_mpm_bufs[i][y] == 0)
×
UNCOV
805
                    continue;
×
806

UNCOV
807
                const char *name;
×
UNCOV
808
                if (y < DETECT_SM_LIST_DYNAMIC_START)
×
UNCOV
809
                    name = DetectListToHumanString(y);
×
810
                else
×
811
                    name = DetectEngineBufferTypeGetNameById(de_ctx, y);
×
812

UNCOV
813
                SCJbSetUint(js, name, alproto_mpm_bufs[i][y]);
×
UNCOV
814
            }
×
UNCOV
815
            SCJbClose(js);
×
UNCOV
816
        }
×
UNCOV
817
    }
×
818

UNCOV
819
    if (add_mpm_stats) {
×
UNCOV
820
        SCJbOpenObject(js, "mpm");
×
821

UNCOV
822
        for (int i = 0; i < max_buffer_type_id; i++) {
×
UNCOV
823
            if (mpm_stats[i].cnt > 0) {
×
UNCOV
824
                const char *name;
×
UNCOV
825
                if (i < DETECT_SM_LIST_DYNAMIC_START)
×
UNCOV
826
                    name = DetectListToHumanString(i);
×
827
                else
×
828
                    name = DetectEngineBufferTypeGetNameById(de_ctx, i);
×
829

UNCOV
830
                SCJbOpenArray(js, name);
×
831

UNCOV
832
                for (int y = 0; y < 256; y++) {
×
UNCOV
833
                    if (mpm_sizes[i][y] == 0)
×
UNCOV
834
                        continue;
×
835

UNCOV
836
                    SCJsonBuilder *e = SCJbNewObject();
×
UNCOV
837
                    if (e != NULL) {
×
UNCOV
838
                        SCJbSetUint(e, "size", y);
×
UNCOV
839
                        SCJbSetUint(e, "count", mpm_sizes[i][y]);
×
UNCOV
840
                        SCJbClose(e);
×
UNCOV
841
                        SCJbAppendObject(js, e);
×
UNCOV
842
                        SCJbFree(e);
×
UNCOV
843
                    }
×
UNCOV
844
                }
×
845

UNCOV
846
                SCJsonBuilder *e = SCJbNewObject();
×
UNCOV
847
                if (e != NULL) {
×
UNCOV
848
                    SCJbSetUint(e, "total", mpm_stats[i].cnt);
×
UNCOV
849
                    SCJbSetUint(e, "avg_strength", mpm_stats[i].total / mpm_stats[i].cnt);
×
UNCOV
850
                    SCJbSetUint(e, "min_strength", mpm_stats[i].min);
×
UNCOV
851
                    SCJbSetUint(e, "max_strength", mpm_stats[i].max);
×
UNCOV
852
                    SCJbClose(e);
×
UNCOV
853
                    SCJbAppendObject(js, e);
×
UNCOV
854
                    SCJbFree(e);
×
UNCOV
855
                }
×
856

UNCOV
857
                SCJbClose(js);
×
UNCOV
858
            }
×
UNCOV
859
        }
×
UNCOV
860
        SCJbClose(js);
×
UNCOV
861
    }
×
UNCOV
862
    SCJbClose(js);
×
863

UNCOV
864
    SCJbSetUint(js, "score", sgh->init->score);
×
UNCOV
865
    SCJbClose(js);
×
866

UNCOV
867
    return js;
×
UNCOV
868
}
×
869

870
static void RulesDumpGrouping(const DetectEngineCtx *de_ctx,
871
                       const int add_rules, const int add_mpm_stats)
UNCOV
872
{
×
UNCOV
873
    SCJsonBuilder *js = SCJbNewObject();
×
UNCOV
874
    if (unlikely(js == NULL))
×
875
        return;
×
876

UNCOV
877
    for (int p = 0; p < 256; p++) {
×
UNCOV
878
        if (p == IPPROTO_TCP || p == IPPROTO_UDP) {
×
UNCOV
879
            const char *name = (p == IPPROTO_TCP) ? "tcp" : "udp";
×
880

UNCOV
881
            SCJbOpenObject(js, name);
×
UNCOV
882
            SCJbOpenArray(js, "toserver");
×
UNCOV
883
            const DetectPort *list =
×
UNCOV
884
                    (p == IPPROTO_TCP) ? de_ctx->flow_gh[1].tcp : de_ctx->flow_gh[1].udp;
×
UNCOV
885
            while (list != NULL) {
×
UNCOV
886
                SCJsonBuilder *port = SCJbNewObject();
×
UNCOV
887
                SCJbSetUint(port, "port", list->port);
×
UNCOV
888
                SCJbSetUint(port, "port2", list->port2);
×
889

UNCOV
890
                SCJsonBuilder *stats =
×
UNCOV
891
                        RulesGroupPrintSghStats(de_ctx, list->sh, add_rules, add_mpm_stats);
×
UNCOV
892
                SCJbSetObject(port, "rulegroup", stats);
×
UNCOV
893
                SCJbFree(stats);
×
UNCOV
894
                SCJbClose(port);
×
UNCOV
895
                SCJbAppendObject(js, port);
×
UNCOV
896
                SCJbFree(port);
×
897

UNCOV
898
                list = list->next;
×
UNCOV
899
            }
×
UNCOV
900
            SCJbClose(js); // toserver array
×
901

UNCOV
902
            SCJbOpenArray(js, "toclient");
×
UNCOV
903
            list = (p == IPPROTO_TCP) ? de_ctx->flow_gh[0].tcp :
×
UNCOV
904
                                        de_ctx->flow_gh[0].udp;
×
UNCOV
905
            while (list != NULL) {
×
UNCOV
906
                SCJsonBuilder *port = SCJbNewObject();
×
UNCOV
907
                SCJbSetUint(port, "port", list->port);
×
UNCOV
908
                SCJbSetUint(port, "port2", list->port2);
×
909

UNCOV
910
                SCJsonBuilder *stats =
×
UNCOV
911
                        RulesGroupPrintSghStats(de_ctx, list->sh, add_rules, add_mpm_stats);
×
UNCOV
912
                SCJbSetObject(port, "rulegroup", stats);
×
UNCOV
913
                SCJbFree(stats);
×
UNCOV
914
                SCJbClose(port);
×
UNCOV
915
                SCJbAppendObject(js, port);
×
UNCOV
916
                SCJbFree(port);
×
917

UNCOV
918
                list = list->next;
×
UNCOV
919
            }
×
UNCOV
920
            SCJbClose(js); // toclient array
×
UNCOV
921
            SCJbClose(js);
×
UNCOV
922
        } else if (p == IPPROTO_ICMP || p == IPPROTO_ICMPV6) {
×
UNCOV
923
            const char *name = (p == IPPROTO_ICMP) ? "icmpv4" : "icmpv6";
×
UNCOV
924
            SCJbOpenObject(js, name);
×
UNCOV
925
            if (de_ctx->flow_gh[1].sgh[p]) {
×
926
                SCJbOpenObject(js, "toserver");
×
927
                SCJsonBuilder *stats = RulesGroupPrintSghStats(
×
928
                        de_ctx, de_ctx->flow_gh[1].sgh[p], add_rules, add_mpm_stats);
×
929
                SCJbSetObject(js, "rulegroup", stats);
×
930
                SCJbFree(stats);
×
931
                SCJbClose(js);
×
932
            }
×
UNCOV
933
            if (de_ctx->flow_gh[0].sgh[p]) {
×
934
                SCJbOpenObject(js, "toclient");
×
935
                SCJsonBuilder *stats = RulesGroupPrintSghStats(
×
936
                        de_ctx, de_ctx->flow_gh[0].sgh[p], add_rules, add_mpm_stats);
×
937
                SCJbSetObject(js, "rulegroup", stats);
×
938
                SCJbFree(stats);
×
939
                SCJbClose(js);
×
940
            }
×
UNCOV
941
            SCJbClose(js);
×
UNCOV
942
        }
×
UNCOV
943
    }
×
UNCOV
944
    SCJbClose(js);
×
945

UNCOV
946
    const char *filename = "rule_group.json";
×
UNCOV
947
    const char *log_dir = SCConfigGetLogDirectory();
×
UNCOV
948
    char log_path[PATH_MAX] = "";
×
UNCOV
949
    snprintf(log_path, sizeof(log_path), "%s/%s", log_dir, filename);
×
950

UNCOV
951
    FILE *fp = fopen(log_path, "w");
×
UNCOV
952
    if (fp != NULL) {
×
UNCOV
953
        fwrite(SCJbPtr(js), SCJbLen(js), 1, fp);
×
UNCOV
954
        (void)fclose(fp);
×
UNCOV
955
    }
×
UNCOV
956
    SCJbFree(js);
×
UNCOV
957
}
×
958

959
static int RulesGroupByIPProto(DetectEngineCtx *de_ctx)
960
{
35,438✔
961
    Signature *s = de_ctx->sig_list;
35,438✔
962

963
    SigGroupHead *sgh_ts[256] = {NULL};
35,438✔
964
    SigGroupHead *sgh_tc[256] = {NULL};
35,438✔
965

966
    for ( ; s != NULL; s = s->next) {
154,751✔
967
        if (s->type == SIG_TYPE_IPONLY)
119,313✔
968
            continue;
5,651✔
969

970
        /* traverse over IP protocol list from libc */
971
        for (int p = 0; p < 256; p++) {
29,211,134✔
972
            if (p == IPPROTO_TCP || p == IPPROTO_UDP) {
29,097,472✔
973
                continue;
227,324✔
974
            }
227,324✔
975
            if (!(s->proto.proto[p / 8] & (1<<(p % 8)) || (s->proto.flags & DETECT_PROTO_ANY))) {
28,870,148✔
976
                continue;
23,535,889✔
977
            }
23,535,889✔
978

979
            /* Signatures that are ICMP, SCTP, not IP only are handled here */
980
            if (s->flags & SIG_FLAG_TOCLIENT) {
5,334,259✔
981
                SigGroupHeadAppendSig(de_ctx, &sgh_tc[p], s);
5,311,651✔
982
            }
5,311,651✔
983
            if (s->flags & SIG_FLAG_TOSERVER) {
5,334,259✔
984
                SigGroupHeadAppendSig(de_ctx, &sgh_ts[p], s);
5,323,229✔
985
            }
5,323,229✔
986
        }
5,334,259✔
987
    }
113,662✔
988

989
    /* lets look at deduplicating this list */
990
    SigGroupHeadHashFree(de_ctx);
35,438✔
991
    SigGroupHeadHashInit(de_ctx);
35,438✔
992

993
    uint32_t cnt = 0;
35,438✔
994
    uint32_t own = 0;
35,438✔
995
    uint32_t ref = 0;
35,438✔
996
    int p;
35,438✔
997
    for (p = 0; p < 256; p++) {
9,107,566✔
998
        if (p == IPPROTO_TCP || p == IPPROTO_UDP)
9,072,128✔
999
            continue;
70,876✔
1000
        if (sgh_ts[p] == NULL)
9,001,252✔
1001
            continue;
8,097,989✔
1002

1003
        cnt++;
903,263✔
1004

1005
        SigGroupHead *lookup_sgh = SigGroupHeadHashLookup(de_ctx, sgh_ts[p]);
903,263✔
1006
        if (lookup_sgh == NULL) {
903,263✔
1007
            SCLogDebug("proto group %d sgh %p is the original", p, sgh_ts[p]);
3,996✔
1008

1009
            SigGroupHeadSetSigCnt(sgh_ts[p], 0);
3,996✔
1010
            SigGroupHeadBuildMatchArray(de_ctx, sgh_ts[p], 0);
3,996✔
1011

1012
            SigGroupHeadHashAdd(de_ctx, sgh_ts[p]);
3,996✔
1013
            SigGroupHeadStore(de_ctx, sgh_ts[p]);
3,996✔
1014
            own++;
3,996✔
1015
        } else {
899,267✔
1016
            SCLogDebug("proto group %d sgh %p is a copy", p, sgh_ts[p]);
899,267✔
1017

1018
            SigGroupHeadFree(de_ctx, sgh_ts[p]);
899,267✔
1019
            sgh_ts[p] = lookup_sgh;
899,267✔
1020
            ref++;
899,267✔
1021
        }
899,267✔
1022
    }
903,263✔
1023
    SCLogPerf("OTHER %s: %u proto groups, %u unique SGH's, %u copies",
35,438✔
1024
            "toserver", cnt, own, ref);
35,438✔
1025

1026
    cnt = 0;
35,438✔
1027
    own = 0;
35,438✔
1028
    ref = 0;
35,438✔
1029
    for (p = 0; p < 256; p++) {
9,107,566✔
1030
        if (p == IPPROTO_TCP || p == IPPROTO_UDP)
9,072,128✔
1031
            continue;
70,876✔
1032
        if (sgh_tc[p] == NULL)
9,001,252✔
1033
            continue;
8,102,339✔
1034

1035
        cnt++;
898,913✔
1036

1037
        SigGroupHead *lookup_sgh = SigGroupHeadHashLookup(de_ctx, sgh_tc[p]);
898,913✔
1038
        if (lookup_sgh == NULL) {
898,913✔
1039
            SCLogDebug("proto group %d sgh %p is the original", p, sgh_tc[p]);
79✔
1040

1041
            SigGroupHeadSetSigCnt(sgh_tc[p], 0);
79✔
1042
            SigGroupHeadBuildMatchArray(de_ctx, sgh_tc[p], 0);
79✔
1043

1044
            SigGroupHeadHashAdd(de_ctx, sgh_tc[p]);
79✔
1045
            SigGroupHeadStore(de_ctx, sgh_tc[p]);
79✔
1046
            own++;
79✔
1047

1048
        } else {
898,834✔
1049
            SCLogDebug("proto group %d sgh %p is a copy", p, sgh_tc[p]);
898,834✔
1050

1051
            SigGroupHeadFree(de_ctx, sgh_tc[p]);
898,834✔
1052
            sgh_tc[p] = lookup_sgh;
898,834✔
1053
            ref++;
898,834✔
1054
        }
898,834✔
1055
    }
898,913✔
1056
    SCLogPerf("OTHER %s: %u proto groups, %u unique SGH's, %u copies",
35,438✔
1057
            "toclient", cnt, own, ref);
35,438✔
1058

1059
    for (p = 0; p < 256; p++) {
9,107,566✔
1060
        if (p == IPPROTO_TCP || p == IPPROTO_UDP)
9,072,128✔
1061
            continue;
70,876✔
1062

1063
        de_ctx->flow_gh[0].sgh[p] = sgh_tc[p];
9,001,252✔
1064
        de_ctx->flow_gh[1].sgh[p] = sgh_ts[p];
9,001,252✔
1065
    }
9,001,252✔
1066

1067
    return 0;
35,438✔
1068
}
35,438✔
1069

1070
static int PortIsPriority(const DetectEngineCtx *de_ctx, const DetectPort *a, int ipproto)
1071
{
302,620✔
1072
    DetectPort *w = de_ctx->tcp_priorityports;
302,620✔
1073
    if (ipproto == IPPROTO_UDP)
302,620✔
1074
        w = de_ctx->udp_priorityports;
104,432✔
1075

1076
    while (w) {
2,787,085✔
1077
        /* Make sure the priority port falls in the port range of a */
1078
        DEBUG_VALIDATE_BUG_ON(a->port > a->port2);
2,485,585✔
1079
        if (a->port == w->port && w->port2 == a->port2) {
2,485,585✔
1080
            return 1;
1,120✔
1081
        }
1,120✔
1082
        w = w->next;
2,484,465✔
1083
    }
2,484,465✔
1084

1085
    return 0;
301,500✔
1086
}
302,620✔
1087

1088
static int RuleSetScore(Signature *s)
1089
{
119,313✔
1090
    DetectPort *p = NULL;
119,313✔
1091
    if (s->flags & SIG_FLAG_TOSERVER)
119,313✔
1092
        p = s->dp;
109,365✔
1093
    else if (s->flags & SIG_FLAG_TOCLIENT)
9,948✔
1094
        p = s->sp;
9,948✔
1095
    else
×
1096
        return 0;
×
1097

1098
    /* for sigs that don't use 'any' as port, see if we want to
1099
     * prioritize poor sigs */
1100
    int wl = 0;
119,313✔
1101
    if (!(p->port == 0 && p->port2 == 65535)) {
119,313✔
1102
        /* pure pcre, bytetest, etc rules */
1103
        if (RuleInspectsPayloadHasNoMpm(s)) {
19,128✔
1104
            SCLogDebug("Rule %u MPM has 1 byte fast_pattern. Prioritizing SGH's.", s->id);
2,539✔
1105
            wl = DETECT_PGSCORE_RULE_MPM_FAST_PATTERN;
2,539✔
1106

1107
        } else if (RuleMpmIsNegated(s)) {
16,589✔
1108
            SCLogDebug("Rule %u MPM is negated. Prioritizing SGH's.", s->id);
479✔
1109
            wl = DETECT_PGSCORE_RULE_MPM_NEGATED;
479✔
1110

1111
            /* one byte pattern in packet/stream payloads */
1112
        } else if (s->init_data->mpm_sm != NULL &&
16,110✔
1113
                   s->init_data->mpm_sm_list == DETECT_SM_LIST_PMATCH &&
16,110✔
1114
                   RuleGetMpmPatternSize(s) == 1) {
16,110✔
1115
            SCLogDebug("Rule %u No MPM. Payload inspecting. Prioritizing SGH's.", s->id);
702✔
1116
            wl = DETECT_PGSCORE_RULE_NO_MPM;
702✔
1117

1118
        } else if (DetectFlagsSignatureNeedsSynOnlyPackets(s)) {
15,408✔
1119
            SCLogDebug("Rule %u Needs SYN, so inspected often. Prioritizing SGH's.", s->id);
×
1120
            wl = DETECT_PGSCORE_RULE_SYN_ONLY;
×
1121
        }
×
1122
    }
19,128✔
1123

1124
    s->init_data->score = wl;
119,313✔
1125
    return wl;
119,313✔
1126
}
119,313✔
1127

1128
static int SortCompare(const void *a, const void *b)
1129
{
170,052✔
1130
    const DetectPort *pa = *(const DetectPort **)a;
170,052✔
1131
    const DetectPort *pb = *(const DetectPort **)b;
170,052✔
1132

1133
    if (pa->sh->init->score < pb->sh->init->score) {
170,052✔
1134
        return 1;
7,912✔
1135
    } else if (pa->sh->init->score > pb->sh->init->score) {
162,140✔
1136
        return -1;
7,963✔
1137
    }
7,963✔
1138

1139
    if (pa->sh->init->sig_cnt < pb->sh->init->sig_cnt) {
154,177✔
1140
        return 1;
57,333✔
1141
    } else if (pa->sh->init->sig_cnt > pb->sh->init->sig_cnt) {
96,844✔
1142
        return -1;
44,805✔
1143
    }
44,805✔
1144

1145
    /* Hack to make the qsort output deterministic across platforms.
1146
     * This had to be done because the order of equal elements sorted
1147
     * by qsort is undeterministic and showed different output on BSD,
1148
     * MacOS and Windows. Sorting based on id makes it deterministic. */
1149
    if (pa->sh->id < pb->sh->id)
52,039✔
1150
        return -1;
52,039✔
1151

1152
    return 1;
×
1153
}
52,039✔
1154

1155
static inline void SortGroupList(
1156
        uint32_t *groups, DetectPort **list, int (*CompareFunc)(const void *, const void *))
1157
{
141,752✔
1158
    int cnt = 0;
141,752✔
1159
    for (DetectPort *x = *list; x != NULL; x = x->next) {
246,841✔
1160
        DEBUG_VALIDATE_BUG_ON(x->port > x->port2);
105,089✔
1161
        cnt++;
105,089✔
1162
    }
105,089✔
1163
    if (cnt <= 1)
141,752✔
1164
        return;
124,694✔
1165

1166
    /* build temporary array to sort with qsort */
1167
    DetectPort **array = (DetectPort **)SCCalloc(cnt, sizeof(DetectPort *));
17,058✔
1168
    if (array == NULL)
17,058✔
1169
        return;
×
1170

1171
    int idx = 0;
17,058✔
1172
    for (DetectPort *x = *list; x != NULL;) {
90,677✔
1173
        /* assign a temporary id to resolve otherwise equal groups */
1174
        x->sh->id = idx + 1;
73,619✔
1175
        SigGroupHeadSetSigCnt(x->sh, 0);
73,619✔
1176
        DetectPort *next = x->next;
73,619✔
1177
        x->next = x->prev = x->last = NULL;
73,619✔
1178
        DEBUG_VALIDATE_BUG_ON(x->port > x->port2);
73,619✔
1179
        array[idx++] = x;
73,619✔
1180
        x = next;
73,619✔
1181
    }
73,619✔
1182
    DEBUG_VALIDATE_BUG_ON(cnt != idx);
17,058✔
1183

1184
    qsort(array, idx, sizeof(DetectPort *), SortCompare);
17,058✔
1185

1186
    /* rebuild the list based on the qsort-ed array */
1187
    DetectPort *new_list = NULL, *tail = NULL;
17,058✔
1188
    for (int i = 0; i < idx; i++) {
90,677✔
1189
        DetectPort *p = array[i];
73,619✔
1190
        /* unset temporary group id */
1191
        p->sh->id = 0;
73,619✔
1192

1193
        if (new_list == NULL) {
73,619✔
1194
            new_list = p;
17,058✔
1195
        }
17,058✔
1196
        if (tail != NULL) {
73,619✔
1197
            tail->next = p;
56,561✔
1198
        }
56,561✔
1199
        p->prev = tail;
73,619✔
1200
        tail = p;
73,619✔
1201
    }
73,619✔
1202

1203
    *list = new_list;
17,058✔
1204
    *groups = idx;
17,058✔
1205

1206
#if DEBUG
1207
    int dbgcnt = 0;
1208
    SCLogDebug("SORTED LIST:");
1209
    for (DetectPort *tmp = *list; tmp != NULL; tmp = tmp->next) {
1210
        SCLogDebug("item:= [%u:%u]; score: %d; sig_cnt: %d", tmp->port, tmp->port2,
1211
                tmp->sh->init->score, tmp->sh->init->sig_cnt);
1212
        dbgcnt++;
1213
        BUG_ON(dbgcnt > cnt);
1214
    }
1215
#endif
1216
    SCFree(array);
17,058✔
1217
}
17,058✔
1218
/** \internal
1219
 *  \brief Create a list of DetectPort objects sorted based on CompareFunc's
1220
 *         logic.
1221
 *
1222
 *  List can limit the number of groups. In this case an extra "join" group
1223
 *  is created that contains the sigs belonging to that. It's *appended* to
1224
 *  the list, meaning that if the list is walked linearly it's found last.
1225
 *  The joingr is meant to be a catch all.
1226
 *
1227
 */
1228
static int CreateGroupedPortList(DetectEngineCtx *de_ctx, DetectPort *port_list,
1229
        DetectPort **newhead, uint32_t unique_groups,
1230
        int (*CompareFunc)(const void *, const void *))
1231
{
141,752✔
1232
    DetectPort *tmplist = NULL, *joingr = NULL;
141,752✔
1233
    uint32_t groups = 0;
141,752✔
1234

1235
    /* insert the ports into the tmplist, where it will
1236
     * be sorted descending on 'cnt' and on whether a group
1237
     * is prioritized. */
1238
    tmplist = port_list;
141,752✔
1239
    SortGroupList(&groups, &tmplist, SortCompare);
141,752✔
1240
    uint32_t left = unique_groups;
141,752✔
1241
    if (left == 0)
141,752✔
1242
        left = groups;
×
1243

1244
    /* create another list: take the port groups from above
1245
     * and add them to the 2nd list until we have met our
1246
     * count. The rest is added to the 'join' group. */
1247
    DetectPort *tmplist2 = NULL, *tmplist2_tail = NULL;
141,752✔
1248
    DetectPort *gr, *next_gr;
141,752✔
1249
    for (gr = tmplist; gr != NULL;) {
246,841✔
1250
        next_gr = gr->next;
105,089✔
1251

1252
        SCLogDebug("temp list gr %p %u:%u", gr, gr->port, gr->port2);
105,089✔
1253
        DetectPortPrint(gr);
105,089✔
1254

1255
        /* if we've set up all the unique groups, add the rest to the
1256
         * catch-all joingr */
1257
        if (left == 0) {
105,089✔
1258
            if (joingr == NULL) {
171✔
1259
                DetectPortParse(de_ctx, &joingr, "0:65535");
79✔
1260
                if (joingr == NULL) {
79✔
1261
                    goto error;
×
1262
                }
×
1263
                SCLogDebug("joingr => %u-%u", joingr->port, joingr->port2);
79✔
1264
                joingr->next = NULL;
79✔
1265
            }
79✔
1266
            SigGroupHeadCopySigs(de_ctx, gr->sh, &joingr->sh);
171✔
1267

1268
            /* when a group's sigs are added to the joingr, we can free it */
1269
            gr->next = NULL;
171✔
1270
            DetectPortFree(de_ctx, gr);
171✔
1271
            /* append */
1272
        } else {
104,918✔
1273
            gr->next = NULL;
104,918✔
1274

1275
            if (tmplist2 == NULL) {
104,918✔
1276
                tmplist2 = gr;
48,528✔
1277
                tmplist2_tail = gr;
48,528✔
1278
            } else {
56,390✔
1279
                tmplist2_tail->next = gr;
56,390✔
1280
                tmplist2_tail = gr;
56,390✔
1281
            }
56,390✔
1282
        }
104,918✔
1283

1284
        if (left > 0)
105,089✔
1285
            left--;
104,918✔
1286

1287
        gr = next_gr;
105,089✔
1288
    }
105,089✔
1289

1290
    /* if present, append the joingr that covers the rest */
1291
    if (joingr != NULL) {
141,752✔
1292
        SCLogDebug("appending joingr %p %u:%u", joingr, joingr->port, joingr->port2);
79✔
1293

1294
        if (tmplist2 == NULL) {
79✔
1295
            tmplist2 = joingr;
×
1296
            // tmplist2_tail = joingr;
1297
        } else {
79✔
1298
            tmplist2_tail->next = joingr;
79✔
1299
            // tmplist2_tail = joingr;
1300
        }
79✔
1301
    } else {
141,673✔
1302
        SCLogDebug("no joingr");
141,673✔
1303
    }
141,673✔
1304

1305
    /* pass back our new list to the caller */
1306
    *newhead = tmplist2;
141,752✔
1307
    DetectPortPrintList(*newhead);
141,752✔
1308

1309
    return 0;
141,752✔
1310
error:
×
1311
    return -1;
×
1312
}
141,752✔
1313

1314
#define UNDEFINED_PORT 0
255,247✔
1315
#define RANGE_PORT  1
2,147,483,647✔
1316
#define SINGLE_PORT 2
2,147,483,647✔
1317

1318
typedef struct UniquePortPoint_ {
1319
    uint16_t port; /* value of the port */
1320
    bool single;   /* is the port single or part of a range */
1321
} UniquePortPoint;
1322

1323
/**
1324
 * \brief Function to set unique port points. Consider all the ports
1325
 *        flattened out on one line, set the points that correspond
1326
 *        to a valid port. Also store whether the port point stored
1327
 *        was a single port or part of a range.
1328
 *
1329
 * \param p Port object to be set
1330
 * \param unique_list List of unique port points to be updated
1331
 * \param size_list Current size of the list
1332
 *
1333
 * \return Updated size of the list
1334
 */
1335
static inline uint32_t SetUniquePortPoints(
1336
        const DetectPort *p, uint8_t *unique_list, uint32_t size_list)
1337
{
122,543✔
1338
    if (unique_list[p->port] == UNDEFINED_PORT) {
122,543✔
1339
        if (p->port == p->port2) {
106,437✔
1340
            unique_list[p->port] = SINGLE_PORT;
41,496✔
1341
        } else {
64,941✔
1342
            unique_list[p->port] = RANGE_PORT;
64,941✔
1343
        }
64,941✔
1344
        size_list++;
106,437✔
1345
    } else if (((unique_list[p->port] == SINGLE_PORT) && (p->port != p->port2)) ||
106,437✔
1346
               ((unique_list[p->port] == RANGE_PORT) && (p->port == p->port2))) {
16,106✔
1347
        if ((p->port != UINT16_MAX) && (unique_list[p->port + 1] == UNDEFINED_PORT)) {
10,161✔
1348
            unique_list[p->port + 1] = RANGE_PORT;
5,500✔
1349
            size_list++;
5,500✔
1350
        }
5,500✔
1351
    }
10,161✔
1352

1353
    /* Treat right boundary as single point to avoid creating unneeded
1354
     * ranges later on */
1355
    if (unique_list[p->port2] == UNDEFINED_PORT) {
122,543✔
1356
        size_list++;
65,823✔
1357
    }
65,823✔
1358
    unique_list[p->port2] = SINGLE_PORT;
122,543✔
1359
    return size_list;
122,543✔
1360
}
122,543✔
1361

1362
/**
1363
 * \brief Function to set the *final* unique port points and save them
1364
 *        for later use. The points are already sorted because of the way
1365
 *        they have been retrieved and saved earlier for use at this point.
1366
 *
1367
 * \param unique_list List of the unique port points to be used
1368
 * \param size_unique_arr Number of unique port points
1369
 * \param final_arr List of the final unique port points to be created
1370
 */
1371
static inline void SetFinalUniquePortPoints(
1372
        const uint8_t *unique_list, const uint32_t size_unique_arr, UniquePortPoint *final_arr)
1373
{
48,528✔
1374
    for (uint32_t i = 0, j = 0; i < (UINT16_MAX + 1); i++) {
2,147,483,647✔
1375
        DEBUG_VALIDATE_BUG_ON(j > size_unique_arr);
2,147,483,647✔
1376
        if (unique_list[i] == RANGE_PORT) {
2,147,483,647✔
1377
            final_arr[j].port = (uint16_t)i;
66,024✔
1378
            final_arr[j++].single = false;
66,024✔
1379
        } else if (unique_list[i] == SINGLE_PORT) {
2,147,483,647✔
1380
            final_arr[j].port = (uint16_t)i;
111,736✔
1381
            final_arr[j++].single = true;
111,736✔
1382
        }
111,736✔
1383
    }
2,147,483,647✔
1384
}
48,528✔
1385

1386
/**
1387
 * \brief Function to create the list of ports with the smallest ranges
1388
 *        by resolving overlaps and end point conditions. These contain the
1389
 *        correct SGHs as well after going over the interval tree to find
1390
 *        any range overlaps.
1391
 *
1392
 * \param de_ctx Detection Engine Context
1393
 * \param unique_list Final list of unique port points
1394
 * \param size_list Size of the unique_list
1395
 * \param it Pointer to the interval tree
1396
 * \param list Pointer to the list where final ports will be stored
1397
 *
1398
 * \return 0 on success, -1 otherwise
1399
 */
1400
static inline int CreatePortList(DetectEngineCtx *de_ctx, const uint8_t *unique_list,
1401
        const uint32_t size_list, SCPortIntervalTree *it, DetectPort **list)
1402
{
141,752✔
1403
    /* Only do the operations if there is at least one unique port */
1404
    if (size_list == 0)
141,752✔
1405
        return 0;
93,224✔
1406
    UniquePortPoint *final_unique_points =
48,528✔
1407
            (UniquePortPoint *)SCCalloc(size_list, sizeof(UniquePortPoint));
48,528✔
1408
    if (final_unique_points == NULL)
48,528✔
1409
        return -1;
×
1410
    SetFinalUniquePortPoints(unique_list, size_list, final_unique_points);
48,528✔
1411
    /* Handle edge case when there is just one unique port */
1412
    if (size_list == 1) {
48,528✔
1413
        SCPortIntervalFindOverlappingRanges(
1,057✔
1414
                de_ctx, final_unique_points[0].port, final_unique_points[0].port, &it->tree, list);
1,057✔
1415
    } else {
47,471✔
1416
        UniquePortPoint *p1 = &final_unique_points[0];
47,471✔
1417
        UniquePortPoint *p2 = &final_unique_points[1];
47,471✔
1418
        uint16_t port = p1 ? p1->port : 0; // just for cppcheck
47,471✔
1419
        uint16_t port2 = p2->port;
47,471✔
1420
        for (uint32_t i = 1; i < size_list; i++) {
176,703✔
1421
            DEBUG_VALIDATE_BUG_ON(port > port2);
129,232✔
1422
            if ((p1 && p1->single) && p2->single) {
129,232✔
1423
                SCPortIntervalFindOverlappingRanges(de_ctx, port, port, &it->tree, list);
5,526✔
1424
                SCPortIntervalFindOverlappingRanges(de_ctx, port2, port2, &it->tree, list);
5,526✔
1425
                port = port2 + 1;
5,526✔
1426
            } else if (p1 && p1->single) {
123,706✔
1427
                SCPortIntervalFindOverlappingRanges(de_ctx, port, port, &it->tree, list);
4,131✔
1428
                if ((port2 > port + 1)) {
4,131✔
1429
                    SCPortIntervalFindOverlappingRanges(
672✔
1430
                            de_ctx, port + 1, port2 - 1, &it->tree, list);
672✔
1431
                    port = port2;
672✔
1432
                } else {
3,459✔
1433
                    port = port + 1;
3,459✔
1434
                }
3,459✔
1435
            } else if (p2->single) {
119,575✔
1436
                /* If port2 is boundary and less or equal to port + 1, create a range
1437
                 * keeping the boundary away as it is single port */
1438
                if ((port2 >= port + 1)) {
95,496✔
1439
                    SCPortIntervalFindOverlappingRanges(de_ctx, port, port2 - 1, &it->tree, list);
77,736✔
1440
                }
77,736✔
1441
                /* Deal with port2 as it is a single port */
1442
                SCPortIntervalFindOverlappingRanges(de_ctx, port2, port2, &it->tree, list);
95,496✔
1443
                port = port2 + 1;
95,496✔
1444
            } else {
95,496✔
1445
                if ((port2 > port + 1)) {
24,079✔
1446
                    SCPortIntervalFindOverlappingRanges(de_ctx, port, port2 - 1, &it->tree, list);
2,775✔
1447
                    port = port2;
2,775✔
1448
                } else {
21,304✔
1449
                    SCPortIntervalFindOverlappingRanges(de_ctx, port, port2, &it->tree, list);
21,304✔
1450
                    port = port2 + 1;
21,304✔
1451
                }
21,304✔
1452
            }
24,079✔
1453
            /* if the current port matches the p2->port, assign it to p1 so that
1454
             * there is a UniquePortPoint object to check other info like whether
1455
             * the port with this value is single */
1456
            if (port == p2->port) {
129,232✔
1457
                p1 = p2;
6,906✔
1458
            } else {
122,326✔
1459
                p1 = NULL;
122,326✔
1460
            }
122,326✔
1461
            if (i + 1 < size_list) {
129,232✔
1462
                p2 = &final_unique_points[i + 1];
81,761✔
1463
                port2 = p2->port;
81,761✔
1464
            }
81,761✔
1465
        }
129,232✔
1466
    }
47,471✔
1467
    /* final_unique_points array is no longer needed */
1468
    SCFree(final_unique_points);
48,528✔
1469
    return 0;
48,528✔
1470
}
48,528✔
1471

1472
static DetectPort *RulesGroupByPorts(DetectEngineCtx *de_ctx, uint8_t ipproto, uint32_t direction)
1473
{
141,752✔
1474
    /* step 1: create a hash of 'DetectPort' objects based on all the
1475
     *         rules. Each object will have a SGH with the sigs added
1476
     *         that belong to the SGH. */
1477
    DetectPortHashInit(de_ctx);
141,752✔
1478

1479
    uint32_t size_unique_port_arr = 0;
141,752✔
1480
    const Signature *s = de_ctx->sig_list;
141,752✔
1481
    DetectPort *list = NULL;
141,752✔
1482

1483
    uint8_t *unique_port_points = (uint8_t *)SCCalloc(UINT16_MAX + 1, sizeof(uint8_t));
141,752✔
1484
    if (unique_port_points == NULL)
141,752✔
1485
        return NULL;
×
1486

1487
    while (s) {
619,004✔
1488
        /* IP Only rules are handled separately */
1489
        if (s->type == SIG_TYPE_IPONLY)
477,252✔
1490
            goto next;
22,604✔
1491
        /* Protocol does not match the Signature protocol and is neither IP or pkthdr */
1492
        if (!(s->proto.proto[ipproto / 8] & (1<<(ipproto % 8)) || (s->proto.flags & DETECT_PROTO_ANY)))
454,648✔
1493
            goto next;
149,226✔
1494
        /* Direction does not match Signature direction */
1495
        if (direction == SIG_FLAG_TOSERVER) {
305,422✔
1496
            if (!(s->flags & SIG_FLAG_TOSERVER))
152,711✔
1497
                goto next;
11,089✔
1498
        } else if (direction == SIG_FLAG_TOCLIENT) {
152,711✔
1499
            if (!(s->flags & SIG_FLAG_TOCLIENT))
152,711✔
1500
                goto next;
36,899✔
1501
        }
152,711✔
1502

1503
        /* see if we want to exclude directionless sigs that really care only for
1504
         * to_server syn scans/floods */
1505
        if ((direction == SIG_FLAG_TOCLIENT) && DetectFlagsSignatureNeedsSynOnlyPackets(s) &&
257,434✔
1506
                ((s->flags & (SIG_FLAG_TOSERVER | SIG_FLAG_TOCLIENT)) ==
257,434✔
1507
                        (SIG_FLAG_TOSERVER | SIG_FLAG_TOCLIENT)) &&
×
1508
                (!(s->dp->port == 0 && s->dp->port2 == 65535))) {
257,434✔
1509
            SCLogWarning("rule %u: SYN-only to port(s) %u:%u "
×
1510
                         "w/o direction specified, disabling for toclient direction",
×
1511
                    s->id, s->dp->port, s->dp->port2);
×
1512
            goto next;
×
1513
        }
×
1514

1515
        DetectPort *p = NULL;
257,434✔
1516
        if (direction == SIG_FLAG_TOSERVER)
257,434✔
1517
            p = s->dp;
141,622✔
1518
        else if (direction == SIG_FLAG_TOCLIENT)
115,812✔
1519
            p = s->sp;
115,812✔
1520
        else
×
1521
            BUG_ON(1);
115,812✔
1522

1523
        int wl = s->init_data->score;
257,434✔
1524
        while (p) {
560,054✔
1525
            int pwl = PortIsPriority(de_ctx, p, ipproto) ? DETECT_PGSCORE_RULE_PORT_PRIORITIZED : 0;
302,620✔
1526
            pwl = MAX(wl,pwl);
302,620✔
1527

1528
            DetectPort *lookup = DetectPortHashLookup(de_ctx, p);
302,620✔
1529
            if (lookup) {
302,620✔
1530
                SigGroupHeadAppendSig(de_ctx, &lookup->sh, s);
180,077✔
1531
                lookup->sh->init->score = MAX(lookup->sh->init->score, pwl);
180,077✔
1532
            } else {
180,077✔
1533
                DetectPort *tmp2 = DetectPortCopySingle(de_ctx, p);
122,543✔
1534
                BUG_ON(tmp2 == NULL);
122,543✔
1535
                SigGroupHeadAppendSig(de_ctx, &tmp2->sh, s);
122,543✔
1536
                tmp2->sh->init->score = pwl;
122,543✔
1537
                DetectPortHashAdd(de_ctx, tmp2);
122,543✔
1538
                size_unique_port_arr =
122,543✔
1539
                        SetUniquePortPoints(tmp2, unique_port_points, size_unique_port_arr);
122,543✔
1540
            }
122,543✔
1541

1542
            p = p->next;
302,620✔
1543
        }
302,620✔
1544
    next:
477,252✔
1545
        s = s->next;
477,252✔
1546
    }
477,252✔
1547

1548
    /* step 2: create a list of the smallest port ranges with
1549
     * appropriate SGHs */
1550

1551
    /* Create an interval tree of all the given ports to make the search
1552
     * for overlaps later on easier */
1553
    SCPortIntervalTree *it = SCPortIntervalTreeInit();
141,752✔
1554
    if (it == NULL)
141,752✔
1555
        goto error;
×
1556

1557
    HashListTableBucket *htb = NULL;
141,752✔
1558
    for (htb = HashListTableGetListHead(de_ctx->dport_hash_table); htb != NULL;
264,295✔
1559
            htb = HashListTableGetListNext(htb)) {
141,752✔
1560
        DetectPort *p = HashListTableGetListData(htb);
122,543✔
1561
        if (SCPortIntervalInsert(de_ctx, it, p) != SC_OK) {
122,543✔
1562
            SCLogDebug("Port was not inserted in the tree");
×
1563
            goto error;
×
1564
        }
×
1565
    }
122,543✔
1566

1567
    /* Create a sorted list of ports in ascending order after resolving overlaps
1568
     * and corresponding SGHs */
1569
    if (CreatePortList(de_ctx, unique_port_points, size_unique_port_arr, it, &list) < 0)
141,752✔
1570
        goto error;
×
1571

1572
    /* unique_port_points array is no longer needed */
1573
    SCFree(unique_port_points);
141,752✔
1574

1575
    /* Port hashes are no longer needed */
1576
    DetectPortHashFree(de_ctx);
141,752✔
1577

1578
    SCLogDebug("rules analyzed");
141,752✔
1579

1580
    /* step 3: group the list and shrink it if necessary */
1581
    DetectPort *newlist = NULL;
141,752✔
1582
    uint16_t groupmax = (direction == SIG_FLAG_TOCLIENT) ? de_ctx->max_uniq_toclient_groups :
141,752✔
1583
                                                           de_ctx->max_uniq_toserver_groups;
141,752✔
1584
    CreateGroupedPortList(de_ctx, list, &newlist, groupmax, SortCompare);
141,752✔
1585
    list = newlist;
141,752✔
1586

1587
    /* step 4: deduplicate the SGH's */
1588
    SigGroupHeadHashFree(de_ctx);
141,752✔
1589
    SigGroupHeadHashInit(de_ctx);
141,752✔
1590

1591
    uint32_t cnt = 0;
141,752✔
1592
    uint32_t own = 0;
141,752✔
1593
    uint32_t ref = 0;
141,752✔
1594
    DetectPort *iter;
141,752✔
1595
    for (iter = list ; iter != NULL; iter = iter->next) {
246,749✔
1596
        BUG_ON (iter->sh == NULL);
104,997✔
1597
        DEBUG_VALIDATE_BUG_ON(own + ref != cnt);
104,997✔
1598
        cnt++;
104,997✔
1599

1600
        SigGroupHead *lookup_sgh = SigGroupHeadHashLookup(de_ctx, iter->sh);
104,997✔
1601
        if (lookup_sgh == NULL) {
104,997✔
1602
            SCLogDebug("port group %p sgh %p is the original", iter, iter->sh);
85,802✔
1603

1604
            SigGroupHeadSetSigCnt(iter->sh, 0);
85,802✔
1605
            SigGroupHeadBuildMatchArray(de_ctx, iter->sh, 0);
85,802✔
1606
            SigGroupHeadSetProtoAndDirection(iter->sh, ipproto, direction);
85,802✔
1607
            SigGroupHeadHashAdd(de_ctx, iter->sh);
85,802✔
1608
            SigGroupHeadStore(de_ctx, iter->sh);
85,802✔
1609
            iter->flags |= PORT_SIGGROUPHEAD_COPY;
85,802✔
1610
            own++;
85,802✔
1611
        } else {
85,802✔
1612
            SCLogDebug("port group %p sgh %p is a copy", iter, iter->sh);
19,195✔
1613

1614
            SigGroupHeadFree(de_ctx, iter->sh);
19,195✔
1615
            iter->sh = lookup_sgh;
19,195✔
1616
            iter->flags |= PORT_SIGGROUPHEAD_COPY;
19,195✔
1617
            ref++;
19,195✔
1618
        }
19,195✔
1619
    }
104,997✔
1620
#if 0
1621
    for (iter = list ; iter != NULL; iter = iter->next) {
1622
        SCLogInfo("PORT %u-%u %p (sgh=%s, prioritized=%s/%d)",
1623
                iter->port, iter->port2, iter->sh,
1624
                iter->flags & PORT_SIGGROUPHEAD_COPY ? "ref" : "own",
1625
                iter->sh->init->score ? "true" : "false",
1626
                iter->sh->init->score);
1627
    }
1628
#endif
1629
    SCLogPerf("%s %s: %u port groups, %u unique SGH's, %u copies",
141,752✔
1630
            ipproto == 6 ? "TCP" : "UDP",
141,752✔
1631
            direction == SIG_FLAG_TOSERVER ? "toserver" : "toclient",
141,752✔
1632
            cnt, own, ref);
141,752✔
1633
    SCPortIntervalTreeFree(de_ctx, it);
141,752✔
1634
    return list;
141,752✔
1635

1636
error:
×
1637
    if (unique_port_points != NULL)
×
1638
        SCFree(unique_port_points);
×
1639
    if (it != NULL)
×
1640
        SCPortIntervalTreeFree(de_ctx, it);
×
1641

1642
    return NULL;
×
1643
}
141,752✔
1644

1645
void SignatureSetType(DetectEngineCtx *de_ctx, Signature *s)
1646
{
906,513✔
1647
    BUG_ON(s->type != SIG_TYPE_NOT_SET);
906,513✔
1648
    int iponly = 0;
906,513✔
1649

1650
    if (s->init_data->hook.type == SIGNATURE_HOOK_TYPE_APP) {
906,513✔
1651
        s->type = SIG_TYPE_APP_TX;
6✔
1652
        SCLogDebug("%u: set to app_tx due to hook type app", s->id);
6✔
1653
        SCReturn;
6✔
1654
    }
6✔
1655

1656
    /* see if the sig is dp only */
1657
    if (SignatureIsPDOnly(de_ctx, s) == 1) {
906,507✔
1658
        s->type = SIG_TYPE_PDONLY;
3,044✔
1659

1660
        /* see if the sig is ip only */
1661
    } else if ((iponly = SignatureIsIPOnly(de_ctx, s)) > 0) {
903,463✔
1662
        if (iponly == 1) {
84,675✔
1663
            s->type = SIG_TYPE_IPONLY;
82,571✔
1664
        } else if (iponly == 2) {
82,571✔
1665
            s->type = SIG_TYPE_LIKE_IPONLY;
2,104✔
1666
        }
2,104✔
1667
    } else if (SignatureIsDEOnly(de_ctx, s) == 1) {
818,788✔
1668
        s->type = SIG_TYPE_DEONLY;
23,820✔
1669

1670
    } else {
794,968✔
1671
        const bool has_match = s->init_data->smlists[DETECT_SM_LIST_MATCH] != NULL;
794,968✔
1672
        const bool has_pmatch = s->init_data->smlists[DETECT_SM_LIST_PMATCH] != NULL;
794,968✔
1673
        bool has_buffer_frame_engine = false;
794,968✔
1674
        bool has_buffer_packet_engine = false;
794,968✔
1675
        bool has_buffer_app_engine = false;
794,968✔
1676

1677
        for (uint32_t x = 0; x < s->init_data->buffer_index; x++) {
1,373,392✔
1678
            const uint32_t id = s->init_data->buffers[x].id;
578,424✔
1679

1680
            if (DetectEngineBufferTypeSupportsPacketGetById(de_ctx, id)) {
578,424✔
1681
                has_buffer_packet_engine = true;
10,011✔
1682
            } else if (DetectEngineBufferTypeSupportsFramesGetById(de_ctx, id)) {
568,413✔
1683
                has_buffer_frame_engine = true;
23,569✔
1684
            } else {
544,844✔
1685
                has_buffer_app_engine = true;
544,844✔
1686
            }
544,844✔
1687
        }
578,424✔
1688

1689
        if (has_buffer_packet_engine) {
794,968✔
1690
            s->type = SIG_TYPE_PKT;
9,578✔
1691
        } else if (has_buffer_frame_engine || has_buffer_app_engine) {
785,390✔
1692
            s->type = SIG_TYPE_APP_TX;
521,217✔
1693
        } else if (has_pmatch) {
521,217✔
1694
            if ((s->flags & (SIG_FLAG_REQUIRE_PACKET | SIG_FLAG_REQUIRE_STREAM)) ==
133,079✔
1695
                    SIG_FLAG_REQUIRE_PACKET) {
133,079✔
1696
                s->type = SIG_TYPE_PKT;
42,977✔
1697
            } else if ((s->flags & (SIG_FLAG_REQUIRE_PACKET | SIG_FLAG_REQUIRE_STREAM)) ==
90,102✔
1698
                       SIG_FLAG_REQUIRE_STREAM) {
90,102✔
1699
                s->type = SIG_TYPE_STREAM;
78,123✔
1700
            } else {
78,123✔
1701
                s->type = SIG_TYPE_PKT_STREAM;
11,979✔
1702
            }
11,979✔
1703
        } else if (has_match) {
133,079✔
1704
            s->type = SIG_TYPE_PKT;
94,193✔
1705

1706
            /* app-layer but no inspect engines */
1707
        } else if (s->flags & SIG_FLAG_APPLAYER) {
94,193✔
1708
            s->type = SIG_TYPE_APPLAYER;
34,757✔
1709
        } else {
34,757✔
1710
            s->type = SIG_TYPE_PKT;
2,144✔
1711
        }
2,144✔
1712
    }
794,968✔
1713
}
906,507✔
1714

1715
/**
1716
 * \brief Preprocess signature, classify ip-only, etc, build sig array
1717
 *
1718
 * \param de_ctx Pointer to the Detection Engine Context
1719
 *
1720
 * \retval  0 on success
1721
 * \retval -1 on failure
1722
 */
1723
int SigPrepareStage1(DetectEngineCtx *de_ctx)
1724
{
35,438✔
1725
    uint32_t cnt_iponly = 0;
35,438✔
1726
    uint32_t cnt_payload = 0;
35,438✔
1727
    uint32_t cnt_packet = 0;
35,438✔
1728
    uint32_t cnt_packet_stream = 0;
35,438✔
1729
    uint32_t cnt_applayer = 0;
35,438✔
1730
    uint32_t cnt_deonly = 0;
35,438✔
1731

1732
    if (!(de_ctx->flags & DE_QUIET)) {
35,438✔
1733
        SCLogDebug("building signature grouping structure, stage 1: "
35,438✔
1734
                   "preprocessing rules...");
35,438✔
1735
    }
35,438✔
1736

1737
    de_ctx->sig_array_len = DetectEngineGetMaxSigId(de_ctx);
35,438✔
1738
    de_ctx->sig_array = (Signature **)SCCalloc(de_ctx->sig_array_len, sizeof(Signature *));
35,438✔
1739
    if (de_ctx->sig_array == NULL)
35,438✔
1740
        goto error;
×
1741

1742
    /* now for every rule add the source group */
1743
    for (Signature *s = de_ctx->sig_list; s != NULL; s = s->next) {
154,751✔
1744
        de_ctx->sig_array[s->iid] = s;
119,313✔
1745

1746
        SCLogDebug("Signature %" PRIu32 ", internal id %" PRIu32 ", ptrs %p %p ", s->id, s->iid, s,
119,313✔
1747
                de_ctx->sig_array[s->iid]);
119,313✔
1748

1749
        if (s->type == SIG_TYPE_PDONLY) {
119,313✔
1750
            SCLogDebug("Signature %"PRIu32" is considered \"PD only\"", s->id);
474✔
1751
        } else if (s->type == SIG_TYPE_IPONLY) {
118,839✔
1752
            SCLogDebug("Signature %"PRIu32" is considered \"IP only\"", s->id);
5,651✔
1753
            cnt_iponly++;
5,651✔
1754
        } else if (SignatureIsInspectingPayload(de_ctx, s) == 1) {
113,188✔
1755
            SCLogDebug("Signature %"PRIu32" is considered \"Payload inspecting\"", s->id);
18,381✔
1756
            cnt_payload++;
18,381✔
1757
        } else if (s->type == SIG_TYPE_PKT) {
94,807✔
1758
            SCLogDebug("Signature %" PRIu32 " is considered \"Packet inspecting\"", s->id);
15,861✔
1759
            cnt_packet++;
15,861✔
1760
        } else if (s->type == SIG_TYPE_PKT_STREAM) {
78,946✔
1761
            SCLogDebug("Signature %" PRIu32 " is considered \"Packet-stream inspecting\"", s->id);
×
1762
            cnt_packet_stream++;
×
1763
        } else if (s->type == SIG_TYPE_DEONLY) {
78,946✔
1764
            SCLogDebug("Signature %"PRIu32" is considered \"Decoder Event only\"", s->id);
12,752✔
1765
            cnt_deonly++;
12,752✔
1766
        } else if (s->flags & SIG_FLAG_APPLAYER) {
66,194✔
1767
            SCLogDebug("Signature %"PRIu32" is considered \"Applayer inspecting\"", s->id);
64,101✔
1768
            cnt_applayer++;
64,101✔
1769
        }
64,101✔
1770

1771
#ifdef DEBUG
1772
        if (SCLogDebugEnabled()) {
1773
            uint16_t colen = 0;
1774
            char copresent = 0;
1775
            SigMatch *sm;
1776
            DetectContentData *co;
1777
            for (sm = s->init_data->smlists[DETECT_SM_LIST_MATCH]; sm != NULL; sm = sm->next) {
1778
                if (sm->type != DETECT_CONTENT)
1779
                    continue;
1780

1781
                copresent = 1;
1782
                co = (DetectContentData *)sm->ctx;
1783
                if (co->content_len > colen)
1784
                    colen = co->content_len;
1785
            }
1786

1787
            if (copresent && colen == 1) {
1788
                SCLogDebug("signature %8u content maxlen 1", s->id);
1789
                for (int proto = 0; proto < 256; proto++) {
1790
                    if (s->proto.proto[(proto/8)] & (1<<(proto%8)))
1791
                        SCLogDebug("=> proto %" PRId32 "", proto);
1792
                }
1793
            }
1794
        }
1795
#endif /* DEBUG */
1796

1797
        if (RuleMpmIsNegated(s)) {
119,313✔
1798
            s->flags |= SIG_FLAG_MPM_NEG;
1,436✔
1799
        }
1,436✔
1800

1801
        SignatureCreateMask(s);
119,313✔
1802
        DetectContentPropagateLimits(s);
119,313✔
1803
        SigParseApplyDsizeToContent(s);
119,313✔
1804

1805
        RuleSetScore(s);
119,313✔
1806

1807
        /* run buffer type callbacks if any */
1808
        for (int x = 0; x < DETECT_SM_LIST_MAX; x++) {
954,504✔
1809
            if (s->init_data->smlists[x])
835,191✔
1810
                DetectEngineBufferRunSetupCallback(de_ctx, x, s);
76,929✔
1811
        }
835,191✔
1812
        for (uint32_t x = 0; x < s->init_data->buffer_index; x++) {
188,582✔
1813
            DetectEngineBufferRunSetupCallback(de_ctx, s->init_data->buffers[x].id, s);
69,269✔
1814
        }
69,269✔
1815

1816
        de_ctx->sig_cnt++;
119,313✔
1817
    }
119,313✔
1818

1819
    if (!(de_ctx->flags & DE_QUIET)) {
35,438✔
1820
        if (strlen(de_ctx->config_prefix) > 0)
35,438✔
UNCOV
1821
            SCLogInfo("tenant id %d: %" PRIu32 " signatures processed. %" PRIu32 " are IP-only "
×
1822
                      "rules, %" PRIu32 " are inspecting packet payload, %" PRIu32
35,438✔
1823
                      " inspect application layer, %" PRIu32 " are decoder event only, %" PRIu32
35,438✔
1824
                      " are packet inspecting,"
35,438✔
1825
                      " %" PRIu32 " are packet-stream inspecting",
35,438✔
1826
                    de_ctx->tenant_id, de_ctx->sig_cnt, cnt_iponly, cnt_payload, cnt_applayer,
35,438✔
1827
                    cnt_deonly, cnt_packet, cnt_packet_stream);
35,438✔
1828
        else
35,438✔
1829
            SCLogInfo("%" PRIu32 " signatures processed. %" PRIu32 " are IP-only "
35,438✔
1830
                      "rules, %" PRIu32 " are inspecting packet payload, %" PRIu32
35,438✔
1831
                      " inspect application layer, %" PRIu32 " are decoder event only %" PRIu32
35,438✔
1832
                      " are packet inspecting,"
35,438✔
1833
                      " %" PRIu32 " are packet-stream inspecting",
35,438✔
1834
                    de_ctx->sig_cnt, cnt_iponly, cnt_payload, cnt_applayer, cnt_deonly, cnt_packet,
35,438✔
1835
                    cnt_packet_stream);
35,438✔
1836

1837
        SCLogConfig("building signature grouping structure, stage 1: "
35,438✔
1838
               "preprocessing rules... complete");
35,438✔
1839
    }
35,438✔
1840

1841
    if (DetectFlowbitsAnalyze(de_ctx) != 0)
35,438✔
1842
        goto error;
×
1843

1844
    return 0;
35,438✔
1845

1846
error:
×
1847
    return -1;
×
1848
}
35,438✔
1849

1850
/**
1851
 *  \internal
1852
 *  \brief add a decoder event signature to the detection engine ctx
1853
 */
1854
static void DetectEngineAddDecoderEventSig(DetectEngineCtx *de_ctx, Signature *s)
1855
{
12,752✔
1856
    SCLogDebug("adding signature %"PRIu32" to the decoder event sgh", s->id);
12,752✔
1857
    SigGroupHeadAppendSig(de_ctx, &de_ctx->decoder_event_sgh, s);
12,752✔
1858
}
12,752✔
1859

1860
static void DetectEngineAddSigToPreStreamHook(DetectEngineCtx *de_ctx, Signature *s)
UNCOV
1861
{
×
UNCOV
1862
    SCLogDebug("adding signature %" PRIu32 " to the pre_stream hook sgh", s->id);
×
1863

UNCOV
1864
    if ((s->flags & (SIG_FLAG_TOSERVER | SIG_FLAG_TOCLIENT)) ==
×
UNCOV
1865
            (SIG_FLAG_TOSERVER | SIG_FLAG_TOCLIENT)) {
×
1866
        SigGroupHeadAppendSig(de_ctx, &de_ctx->pre_stream_sgh[0], s);
×
1867
        SigGroupHeadAppendSig(de_ctx, &de_ctx->pre_stream_sgh[1], s);
×
UNCOV
1868
    } else if (s->flags & SIG_FLAG_TOSERVER) {
×
UNCOV
1869
        SigGroupHeadAppendSig(de_ctx, &de_ctx->pre_stream_sgh[0], s);
×
UNCOV
1870
    } else if (s->flags & SIG_FLAG_TOCLIENT) {
×
1871
        SigGroupHeadAppendSig(de_ctx, &de_ctx->pre_stream_sgh[1], s);
×
1872
    }
×
UNCOV
1873
}
×
1874

1875
static void DetectEngineAddSigToPreFlowHook(DetectEngineCtx *de_ctx, Signature *s)
UNCOV
1876
{
×
UNCOV
1877
    SCLogDebug("adding signature %" PRIu32 " to the pre_flow hook sgh", s->id);
×
UNCOV
1878
    SigGroupHeadAppendSig(de_ctx, &de_ctx->pre_flow_sgh, s);
×
UNCOV
1879
}
×
1880

1881
/**
1882
 * \brief Fill the global src group head, with the sigs included
1883
 *
1884
 * \param de_ctx Pointer to the Detection Engine Context whose Signatures have
1885
 *               to be processed
1886
 *
1887
 * \retval  0 On success
1888
 * \retval -1 On failure
1889
 */
1890
int SigPrepareStage2(DetectEngineCtx *de_ctx)
1891
{
35,438✔
1892
    SCLogDebug("building signature grouping structure, stage 2: "
35,438✔
1893
            "building source address lists...");
35,438✔
1894

1895
    IPOnlyInit(de_ctx, &de_ctx->io_ctx);
35,438✔
1896

1897
    de_ctx->flow_gh[1].tcp = RulesGroupByPorts(de_ctx, IPPROTO_TCP, SIG_FLAG_TOSERVER);
35,438✔
1898
    de_ctx->flow_gh[0].tcp = RulesGroupByPorts(de_ctx, IPPROTO_TCP, SIG_FLAG_TOCLIENT);
35,438✔
1899
    de_ctx->flow_gh[1].udp = RulesGroupByPorts(de_ctx, IPPROTO_UDP, SIG_FLAG_TOSERVER);
35,438✔
1900
    de_ctx->flow_gh[0].udp = RulesGroupByPorts(de_ctx, IPPROTO_UDP, SIG_FLAG_TOCLIENT);
35,438✔
1901

1902
    /* Setup the other IP Protocols (so not TCP/UDP) */
1903
    RulesGroupByIPProto(de_ctx);
35,438✔
1904

1905
    /* now for every rule add the source group to our temp lists */
1906
    for (Signature *s = de_ctx->sig_list; s != NULL; s = s->next) {
154,751✔
1907
        SCLogDebug("s->id %"PRIu32, s->id);
119,313✔
1908
        if (s->type == SIG_TYPE_IPONLY) {
119,313✔
1909
            IPOnlyAddSignature(de_ctx, &de_ctx->io_ctx, s);
5,651✔
1910
        } else if (s->type == SIG_TYPE_DEONLY) {
113,662✔
1911
            DetectEngineAddDecoderEventSig(de_ctx, s);
12,752✔
1912
        } else if (s->type == SIG_TYPE_PKT && s->init_data->hook.type == SIGNATURE_HOOK_TYPE_PKT &&
100,910✔
1913
                   s->init_data->hook.t.pkt.ph == SIGNATURE_HOOK_PKT_PRE_STREAM) {
100,910✔
UNCOV
1914
            DetectEngineAddSigToPreStreamHook(de_ctx, s);
×
1915
        } else if (s->type == SIG_TYPE_PKT && s->init_data->hook.type == SIGNATURE_HOOK_TYPE_PKT &&
100,910✔
1916
                   s->init_data->hook.t.pkt.ph == SIGNATURE_HOOK_PKT_PRE_FLOW) {
100,910✔
UNCOV
1917
            DetectEngineAddSigToPreFlowHook(de_ctx, s);
×
UNCOV
1918
        }
×
1919
    }
119,313✔
1920

1921
    IPOnlyPrepare(de_ctx);
35,438✔
1922
    IPOnlyPrint(de_ctx, &de_ctx->io_ctx);
35,438✔
1923
    return 0;
35,438✔
1924
}
35,438✔
1925

1926
static void DetectEngineBuildDecoderEventSgh(DetectEngineCtx *de_ctx)
1927
{
35,438✔
1928
    if (de_ctx->decoder_event_sgh == NULL)
35,438✔
1929
        return;
35,022✔
1930

1931
    uint32_t max_idx = DetectEngineGetMaxSigId(de_ctx);
416✔
1932
    SigGroupHeadSetSigCnt(de_ctx->decoder_event_sgh, max_idx);
416✔
1933
    SigGroupHeadBuildMatchArray(de_ctx, de_ctx->decoder_event_sgh, max_idx);
416✔
1934
}
416✔
1935

1936
static void DetectEngineBuildPreStreamHookSghs(DetectEngineCtx *de_ctx)
1937
{
35,438✔
1938
    uint32_t max_idx = DetectEngineGetMaxSigId(de_ctx);
35,438✔
1939
    if (de_ctx->pre_stream_sgh[0] != NULL) {
35,438✔
UNCOV
1940
        SigGroupHeadSetSigCnt(de_ctx->pre_stream_sgh[0], max_idx);
×
UNCOV
1941
        SigGroupHeadBuildMatchArray(de_ctx, de_ctx->pre_stream_sgh[0], max_idx);
×
UNCOV
1942
        PrefilterSetupRuleGroup(de_ctx, de_ctx->pre_stream_sgh[0]);
×
UNCOV
1943
    }
×
1944
    if (de_ctx->pre_stream_sgh[1] != NULL) {
35,438✔
1945
        SigGroupHeadSetSigCnt(de_ctx->pre_stream_sgh[1], max_idx);
×
1946
        SigGroupHeadBuildMatchArray(de_ctx, de_ctx->pre_stream_sgh[1], max_idx);
×
1947
        PrefilterSetupRuleGroup(de_ctx, de_ctx->pre_stream_sgh[1]);
×
1948
    }
×
1949

1950
    if (de_ctx->pre_stream_sgh[0] != NULL || de_ctx->pre_stream_sgh[1] != NULL) {
35,438✔
UNCOV
1951
        de_ctx->PreStreamHook = DetectPreStream;
×
UNCOV
1952
    }
×
1953
}
35,438✔
1954

1955
static void DetectEngineBuildPreFlowHookSghs(DetectEngineCtx *de_ctx)
1956
{
35,438✔
1957
    if (de_ctx->pre_flow_sgh != NULL) {
35,438✔
UNCOV
1958
        uint32_t max_idx = DetectEngineGetMaxSigId(de_ctx);
×
UNCOV
1959
        SigGroupHeadSetSigCnt(de_ctx->pre_flow_sgh, max_idx);
×
UNCOV
1960
        SigGroupHeadBuildMatchArray(de_ctx, de_ctx->pre_flow_sgh, max_idx);
×
UNCOV
1961
        PrefilterSetupRuleGroup(de_ctx, de_ctx->pre_flow_sgh);
×
UNCOV
1962
        de_ctx->PreFlowHook = DetectPreFlow;
×
UNCOV
1963
    }
×
1964
}
35,438✔
1965

1966
int SigPrepareStage3(DetectEngineCtx *de_ctx)
1967
{
35,438✔
1968
    /* prepare the decoder event sgh */
1969
    DetectEngineBuildDecoderEventSgh(de_ctx);
35,438✔
1970

1971
    /* pre_flow hook sgh */
1972
    DetectEngineBuildPreFlowHookSghs(de_ctx);
35,438✔
1973

1974
    /* pre_stream hook sghs */
1975
    DetectEngineBuildPreStreamHookSghs(de_ctx);
35,438✔
1976

1977
    return 0;
35,438✔
1978
}
35,438✔
1979

1980
int SigAddressCleanupStage1(DetectEngineCtx *de_ctx)
1981
{
35,466✔
1982
    BUG_ON(de_ctx == NULL);
35,466✔
1983

1984
    SCLogDebug("cleaning up signature grouping structure...");
35,466✔
1985

1986
    if (de_ctx->decoder_event_sgh)
35,466✔
1987
        SigGroupHeadFree(de_ctx, de_ctx->decoder_event_sgh);
416✔
1988
    de_ctx->decoder_event_sgh = NULL;
35,466✔
1989
    if (de_ctx->pre_flow_sgh)
35,466✔
UNCOV
1990
        SigGroupHeadFree(de_ctx, de_ctx->pre_flow_sgh);
×
1991
    de_ctx->pre_flow_sgh = NULL;
35,466✔
1992
    if (de_ctx->pre_stream_sgh[0])
35,466✔
UNCOV
1993
        SigGroupHeadFree(de_ctx, de_ctx->pre_stream_sgh[0]);
×
1994
    de_ctx->pre_stream_sgh[0] = NULL;
35,466✔
1995
    if (de_ctx->pre_stream_sgh[1])
35,466✔
1996
        SigGroupHeadFree(de_ctx, de_ctx->pre_stream_sgh[1]);
×
1997
    de_ctx->pre_stream_sgh[1] = NULL;
35,466✔
1998

1999
    for (int f = 0; f < FLOW_STATES; f++) {
106,398✔
2000
        for (int p = 0; p < 256; p++) {
18,229,524✔
2001
            de_ctx->flow_gh[f].sgh[p] = NULL;
18,158,592✔
2002
        }
18,158,592✔
2003

2004
        /* free lookup lists */
2005
        DetectPortCleanupList(de_ctx, de_ctx->flow_gh[f].tcp);
70,932✔
2006
        de_ctx->flow_gh[f].tcp = NULL;
70,932✔
2007
        DetectPortCleanupList(de_ctx, de_ctx->flow_gh[f].udp);
70,932✔
2008
        de_ctx->flow_gh[f].udp = NULL;
70,932✔
2009
    }
70,932✔
2010

2011
    for (uint32_t idx = 0; idx < de_ctx->sgh_array_cnt; idx++) {
125,342✔
2012
        SigGroupHead *sgh = de_ctx->sgh_array[idx];
89,876✔
2013
        if (sgh == NULL)
89,876✔
2014
            continue;
×
2015

2016
        SCLogDebug("sgh %p", sgh);
89,876✔
2017
        SigGroupHeadFree(de_ctx, sgh);
89,876✔
2018
    }
89,876✔
2019
    SCFree(de_ctx->sgh_array);
35,466✔
2020
    de_ctx->sgh_array = NULL;
35,466✔
2021
    de_ctx->sgh_array_cnt = 0;
35,466✔
2022
    de_ctx->sgh_array_size = 0;
35,466✔
2023

2024
    IPOnlyDeinit(de_ctx, &de_ctx->io_ctx);
35,466✔
2025

2026
    SCLogDebug("cleaning up signature grouping structure... complete");
35,466✔
2027
    return 0;
35,466✔
2028
}
35,466✔
2029

2030
#if 0
2031
static void DbgPrintSigs(DetectEngineCtx *de_ctx, SigGroupHead *sgh)
2032
{
2033
    if (sgh == NULL) {
2034
        printf("\n");
2035
        return;
2036
    }
2037

2038
    uint32_t sig;
2039
    for (sig = 0; sig < sgh->sig_cnt; sig++) {
2040
        printf("%" PRIu32 " ", sgh->match_array[sig]->id);
2041
    }
2042
    printf("\n");
2043
}
2044

2045
static void DbgPrintSigs2(DetectEngineCtx *de_ctx, SigGroupHead *sgh)
2046
{
2047
    if (sgh == NULL || sgh->init == NULL) {
2048
        printf("\n");
2049
        return;
2050
    }
2051

2052
    uint32_t sig;
2053
    for (sig = 0; sig < DetectEngineGetMaxSigId(de_ctx); sig++) {
2054
        if (sgh->init->sig_array[(sig/8)] & (1<<(sig%8))) {
2055
            printf("%" PRIu32 " ", de_ctx->sig_array[sig]->id);
2056
        }
2057
    }
2058
    printf("\n");
2059
}
2060
#endif
2061

2062
/** \brief finalize preparing sgh's */
2063
int SigPrepareStage4(DetectEngineCtx *de_ctx)
2064
{
35,438✔
2065
    SCEnter();
35,438✔
2066

2067
    //SCLogInfo("sgh's %"PRIu32, de_ctx->sgh_array_cnt);
2068

2069
    uint32_t cnt = 0;
35,438✔
2070
    for (uint32_t idx = 0; idx < de_ctx->sgh_array_cnt; idx++) {
125,315✔
2071
        SigGroupHead *sgh = de_ctx->sgh_array[idx];
89,877✔
2072
        if (sgh == NULL)
89,877✔
2073
            continue;
×
2074

2075
        SCLogDebug("sgh %p", sgh);
89,877✔
2076

2077
        SigGroupHeadSetupFiles(de_ctx, sgh);
89,877✔
2078
        SCLogDebug("filestore count %u", sgh->filestore_cnt);
89,877✔
2079

2080
        PrefilterSetupRuleGroup(de_ctx, sgh);
89,877✔
2081

2082
        sgh->id = idx;
89,877✔
2083
        cnt++;
89,877✔
2084
    }
89,877✔
2085
    SCLogPerf("Unique rule groups: %u", cnt);
35,438✔
2086

2087
    MpmStoreReportStats(de_ctx);
35,438✔
2088

2089
    if (de_ctx->decoder_event_sgh != NULL) {
35,438✔
2090
        /* no need to set filestore count here as that would make a
2091
         * signature not decode event only. */
2092
        PrefilterSetupRuleGroup(de_ctx, de_ctx->decoder_event_sgh);
416✔
2093
    }
416✔
2094

2095
    int dump_grouping = 0;
35,438✔
2096
    (void)SCConfGetBool("detect.profiling.grouping.dump-to-disk", &dump_grouping);
35,438✔
2097

2098
    if (dump_grouping) {
35,438✔
UNCOV
2099
        int add_rules = 0;
×
UNCOV
2100
        (void)SCConfGetBool("detect.profiling.grouping.include-rules", &add_rules);
×
UNCOV
2101
        int add_mpm_stats = 0;
×
UNCOV
2102
        (void)SCConfGetBool("detect.profiling.grouping.include-mpm-stats", &add_mpm_stats);
×
2103

UNCOV
2104
        RulesDumpGrouping(de_ctx, add_rules, add_mpm_stats);
×
UNCOV
2105
    }
×
2106

2107
    for (uint32_t idx = 0; idx < de_ctx->sgh_array_cnt; idx++) {
125,315✔
2108
        SigGroupHead *sgh = de_ctx->sgh_array[idx];
89,877✔
2109
        if (sgh == NULL)
89,877✔
2110
            continue;
×
2111
        SigGroupHeadInitDataFree(sgh->init);
89,877✔
2112
        sgh->init = NULL;
89,877✔
2113
    }
89,877✔
2114
    /* cleanup the hashes now since we won't need them
2115
     * after the initialization phase. */
2116
    SigGroupHeadHashFree(de_ctx);
35,438✔
2117

2118
#ifdef PROFILING
2119
    SCProfilingSghInitCounters(de_ctx);
2120
#endif
2121
    SCReturnInt(0);
35,438✔
2122
}
35,438✔
2123

2124
extern bool rule_engine_analysis_set;
2125
/** \internal
2126
 *  \brief perform final per signature setup tasks
2127
 *
2128
 *  - Create SigMatchData arrays from the init only SigMatch lists
2129
 *  - Setup per signature inspect engines
2130
 *  - remove signature init data.
2131
 */
2132
static int SigMatchPrepare(DetectEngineCtx *de_ctx)
2133
{
35,438✔
2134
    SCEnter();
35,438✔
2135

2136
    Signature *s = de_ctx->sig_list;
35,438✔
2137
    for (; s != NULL; s = s->next) {
154,751✔
2138
        /* set up inspect engines */
2139
        DetectEngineAppInspectionEngine2Signature(de_ctx, s);
119,313✔
2140

2141
        /* built-ins */
2142
        for (int type = 0; type < DETECT_SM_LIST_MAX; type++) {
954,504✔
2143
            /* skip PMATCH if it is used in a stream 'app engine' instead */
2144
            if (type == DETECT_SM_LIST_PMATCH && (s->init_data->init_flags & SIG_FLAG_INIT_STATE_MATCH))
835,191✔
2145
                continue;
59,961✔
2146
            SigMatch *sm = s->init_data->smlists[type];
775,230✔
2147
            s->sm_arrays[type] = SigMatchList2DataArray(sm);
775,230✔
2148
        }
775,230✔
2149
        /* set up the pkt inspection engines */
2150
        DetectEnginePktInspectionSetup(s);
119,313✔
2151

2152
        if (rule_engine_analysis_set) {
119,313✔
UNCOV
2153
            EngineAnalysisAddAllRulePatterns(de_ctx, s);
×
UNCOV
2154
            EngineAnalysisRules2(de_ctx, s);
×
UNCOV
2155
        }
×
2156
        /* free lists. Ctx' are xferred to sm_arrays so won't get freed */
2157
        for (uint32_t i = 0; i < DETECT_SM_LIST_MAX; i++) {
954,504✔
2158
            SigMatch *sm = s->init_data->smlists[i];
835,191✔
2159
            while (sm != NULL) {
934,077✔
2160
                SigMatch *nsm = sm->next;
98,886✔
2161
                SigMatchFree(de_ctx, sm);
98,886✔
2162
                sm = nsm;
98,886✔
2163
            }
98,886✔
2164
        }
835,191✔
2165
        for (uint32_t i = 0; i < (uint32_t)s->init_data->transforms.cnt; i++) {
119,316✔
2166
            if (s->init_data->transforms.transforms[i].options) {
3✔
2167
                int transform = s->init_data->transforms.transforms[i].transform;
3✔
2168
                sigmatch_table[transform].Free(
3✔
2169
                        de_ctx, s->init_data->transforms.transforms[i].options);
3✔
2170
                s->init_data->transforms.transforms[i].options = NULL;
3✔
2171
            }
3✔
2172
        }
3✔
2173
        for (uint32_t x = 0; x < s->init_data->buffer_index; x++) {
188,582✔
2174
            SigMatch *sm = s->init_data->buffers[x].head;
69,269✔
2175
            while (sm != NULL) {
145,624✔
2176
                SigMatch *nsm = sm->next;
76,355✔
2177
                SigMatchFree(de_ctx, sm);
76,355✔
2178
                sm = nsm;
76,355✔
2179
            }
76,355✔
2180
        }
69,269✔
2181
        if (s->init_data->cidr_dst != NULL)
119,313✔
2182
            IPOnlyCIDRListFree(s->init_data->cidr_dst);
×
2183

2184
        if (s->init_data->cidr_src != NULL)
119,313✔
2185
            IPOnlyCIDRListFree(s->init_data->cidr_src);
×
2186

2187
        SCFree(s->init_data->buffers);
119,313✔
2188
        SCFree(s->init_data->rule_state_dependant_sids_array);
119,313✔
2189
        SCFree(s->init_data->rule_state_flowbits_ids_array);
119,313✔
2190
        SCFree(s->init_data);
119,313✔
2191
        s->init_data = NULL;
119,313✔
2192
    }
119,313✔
2193

2194
    DumpPatterns(de_ctx);
35,438✔
2195
    SCReturnInt(0);
35,438✔
2196
}
35,438✔
2197

2198
/**
2199
 * \brief Convert the signature list into the runtime match structure.
2200
 *
2201
 * \param de_ctx Pointer to the Detection Engine Context whose Signatures have
2202
 *               to be processed
2203
 *
2204
 * \retval  0 On Success.
2205
 * \retval -1 On failure.
2206
 */
2207
int SigGroupBuild(DetectEngineCtx *de_ctx)
2208
{
35,438✔
2209
    Signature *s = de_ctx->sig_list;
35,438✔
2210

2211
    /* Assign the unique order id of signatures after sorting,
2212
     * so the IP Only engine process them in order too.  Also
2213
     * reset the old signums and assign new signums.  We would
2214
     * have experienced Sig reordering by now, hence the new
2215
     * signums. */
2216
    de_ctx->signum = 0;
35,438✔
2217
    while (s != NULL) {
154,751✔
2218
        s->iid = de_ctx->signum++;
119,313✔
2219

2220
        s = s->next;
119,313✔
2221
    }
119,313✔
2222

2223
    if (DetectSetFastPatternAndItsId(de_ctx) < 0)
35,438✔
2224
        return -1;
×
2225

2226
    SigInitStandardMpmFactoryContexts(de_ctx);
35,438✔
2227

2228
    if (SigPrepareStage1(de_ctx) != 0) {
35,438✔
2229
        FatalError("initializing the detection engine failed");
×
2230
    }
×
2231

2232
    if (SigPrepareStage2(de_ctx) != 0) {
35,438✔
2233
        FatalError("initializing the detection engine failed");
×
2234
    }
×
2235

2236
    if (SigPrepareStage3(de_ctx) != 0) {
35,438✔
2237
        FatalError("initializing the detection engine failed");
×
2238
    }
×
2239
    if (SigPrepareStage4(de_ctx) != 0) {
35,438✔
2240
        FatalError("initializing the detection engine failed");
×
2241
    }
×
2242

2243
    int r = DetectMpmPrepareBuiltinMpms(de_ctx);
35,438✔
2244
    r |= DetectMpmPrepareAppMpms(de_ctx);
35,438✔
2245
    r |= DetectMpmPreparePktMpms(de_ctx);
35,438✔
2246
    r |= DetectMpmPrepareFrameMpms(de_ctx);
35,438✔
2247
    if (r != 0) {
35,438✔
2248
        FatalError("initializing the detection engine failed");
×
2249
    }
×
2250

2251
    if (SigMatchPrepare(de_ctx) != 0) {
35,438✔
2252
        FatalError("initializing the detection engine failed");
×
2253
    }
×
2254

2255
#ifdef PROFILING
2256
    SCProfilingKeywordInitCounters(de_ctx);
2257
    SCProfilingPrefilterInitCounters(de_ctx);
2258
    de_ctx->profile_match_logging_threshold = UINT_MAX; // disabled
2259

2260
    intmax_t v = 0;
2261
    if (SCConfGetInt("detect.profiling.inspect-logging-threshold", &v) == 1)
2262
        de_ctx->profile_match_logging_threshold = (uint32_t)v;
2263
#endif
2264
#ifdef PROFILE_RULES
2265
    SCProfilingRuleInitCounters(de_ctx);
2266
#endif
2267

2268
    if (!DetectEngineMultiTenantEnabled()) {
35,438✔
2269
        VarNameStoreActivate();
35,438✔
2270
    }
35,438✔
2271

2272
    if (EngineModeIsFirewall()) {
35,438✔
UNCOV
2273
        FirewallAnalyzer(de_ctx);
×
UNCOV
2274
    }
×
2275
    return 0;
35,438✔
2276
}
35,438✔
2277

2278
int SigGroupCleanup (DetectEngineCtx *de_ctx)
2279
{
35,466✔
2280
    SigAddressCleanupStage1(de_ctx);
35,466✔
2281

2282
    return 0;
35,466✔
2283
}
35,466✔
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