Coverage Report

Created: 2026-09-06 07:25

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/suricata8/src/detect-flowbits.c
Line
Count
Source
1
/* Copyright (C) 2007-2025 Open Information Security Foundation
2
 *
3
 * You can copy, redistribute or modify this Program under the terms of
4
 * the GNU General Public License version 2 as published by the Free
5
 * Software Foundation.
6
 *
7
 * This program is distributed in the hope that it will be useful,
8
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10
 * GNU General Public License for more details.
11
 *
12
 * You should have received a copy of the GNU General Public License
13
 * version 2 along with this program; if not, write to the Free Software
14
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
15
 * 02110-1301, USA.
16
 */
17
18
/**
19
 * \file
20
 *
21
 *  \author Victor Julien <victor@inliniac.net>
22
 *  \author Breno Silva <breno.silva@gmail.com>
23
 *
24
 * Implements the flowbits keyword
25
 */
26
27
#include "suricata-common.h"
28
#include "decode.h"
29
#include "action-globals.h"
30
#include "detect.h"
31
#include "threads.h"
32
#include "flow.h"
33
#include "flow-bit.h"
34
#include "flow-util.h"
35
#include "detect-flowbits.h"
36
#include "util-spm.h"
37
#include "rust.h"
38
39
#include "app-layer-parser.h"
40
41
#include "detect-parse.h"
42
#include "detect-engine.h"
43
#include "detect-engine-mpm.h"
44
#include "detect-engine-state.h"
45
#include "detect-engine-build.h"
46
#include "detect-engine-prefilter.h"
47
48
#include "tree.h"
49
50
#include "util-var-name.h"
51
#include "util-unittest.h"
52
#include "util-debug.h"
53
#include "util-conf.h"
54
55
79
#define PARSE_REGEX         "^([a-z]+)(?:,\\s*(.*))?"
56
static DetectParseRegex parse_regex;
57
58
5.94k
#define MAX_TOKENS 100
59
60
int DetectFlowbitMatch (DetectEngineThreadCtx *, Packet *,
61
        const Signature *, const SigMatchCtx *);
62
static int DetectFlowbitSetup (DetectEngineCtx *, Signature *, const char *);
63
static int FlowbitOrAddData(DetectEngineCtx *, DetectFlowbitsData *, char *);
64
void DetectFlowbitFree (DetectEngineCtx *, void *);
65
#ifdef UNITTESTS
66
void FlowBitsRegisterTests(void);
67
#endif
68
static bool PrefilterFlowbitIsPrefilterable(const Signature *s);
69
static int PrefilterSetupFlowbits(DetectEngineCtx *de_ctx, SigGroupHead *sgh);
70
71
void DetectFlowbitsRegister (void)
72
79
{
73
79
    sigmatch_table[DETECT_FLOWBITS].name = "flowbits";
74
79
    sigmatch_table[DETECT_FLOWBITS].desc = "operate on flow flag";
75
79
    sigmatch_table[DETECT_FLOWBITS].url = "/rules/flow-keywords.html#flowbits";
76
79
    sigmatch_table[DETECT_FLOWBITS].Match = DetectFlowbitMatch;
77
79
    sigmatch_table[DETECT_FLOWBITS].Setup = DetectFlowbitSetup;
78
79
    sigmatch_table[DETECT_FLOWBITS].Free  = DetectFlowbitFree;
79
#ifdef UNITTESTS
80
    sigmatch_table[DETECT_FLOWBITS].RegisterTests = FlowBitsRegisterTests;
81
#endif
82
    /* this is compatible to ip-only signatures */
83
79
    sigmatch_table[DETECT_FLOWBITS].flags |= (SIGMATCH_IPONLY_COMPAT | SIGMATCH_SUPPORT_FIREWALL);
84
85
79
    sigmatch_table[DETECT_FLOWBITS].SupportsPrefilter = PrefilterFlowbitIsPrefilterable;
86
79
    sigmatch_table[DETECT_FLOWBITS].SetupPrefilter = PrefilterSetupFlowbits;
87
    /* all but pre_flow */
88
79
    sigmatch_table[DETECT_FLOWBITS].tables =
89
79
            DETECT_TABLE_PACKET_PRE_STREAM_FLAG | DETECT_TABLE_PACKET_FILTER_FLAG |
90
79
            DETECT_TABLE_PACKET_TD_FLAG | DETECT_TABLE_APP_FILTER_FLAG | DETECT_TABLE_APP_TD_FLAG;
91
79
    DetectSetupParseRegexes(PARSE_REGEX, &parse_regex);
92
79
}
93
94
static int FlowbitOrAddData(DetectEngineCtx *de_ctx, DetectFlowbitsData *cd, char *arrptr)
95
2.12k
{
96
2.12k
    char *strarr[MAX_TOKENS];
97
2.12k
    char *token;
98
2.12k
    char *saveptr = NULL;
99
2.12k
    uint8_t i = 0;
100
101
8.35k
    while ((token = strtok_r(arrptr, "|", &saveptr))) {
102
        // Check for leading/trailing spaces in the token
103
6.30k
        while(isspace((unsigned char)*token))
104
4.84k
            token++;
105
6.30k
        if (*token == 0)
106
285
            goto next;
107
6.02k
        char *end = token + strlen(token) - 1;
108
6.71k
        while(end > token && isspace((unsigned char)*end))
109
691
            *(end--) = '\0';
110
111
        // Check for spaces in between the flowbit names
112
6.02k
        if (strchr(token, ' ') != NULL) {
113
74
            SCLogError("Spaces are not allowed in flowbit names.");
114
74
            return -1;
115
74
        }
116
117
5.94k
        if (i == MAX_TOKENS) {
118
0
            SCLogError("Number of flowbits exceeds "
119
0
                       "maximum allowed: %d.",
120
0
                    MAX_TOKENS);
121
0
            return -1;
122
0
        }
123
5.94k
        strarr[i++] = token;
124
6.23k
    next:
125
6.23k
        arrptr = NULL;
126
6.23k
    }
127
2.04k
    if (i == 0) {
128
0
        SCLogError("No valid flowbits specified");
129
0
        return -1;
130
0
    }
131
132
2.04k
    cd->or_list_size = i;
133
2.04k
    cd->or_list = SCCalloc(cd->or_list_size, sizeof(uint32_t));
134
2.04k
    if (unlikely(cd->or_list == NULL))
135
0
        return -1;
136
7.95k
    for (uint8_t j = 0; j < cd->or_list_size ; j++) {
137
5.91k
        cd->or_list[j] = VarNameStoreRegister(strarr[j], VAR_TYPE_FLOW_BIT);
138
5.91k
        de_ctx->max_fb_id = MAX(cd->or_list[j], de_ctx->max_fb_id);
139
5.91k
    }
140
141
2.04k
    return 1;
142
2.04k
}
143
144
static int DetectFlowbitMatchToggle (Packet *p, const DetectFlowbitsData *fd)
145
2
{
146
2
    if (p->flow == NULL)
147
2
        return -1;
148
149
0
    return FlowBitToggle(p->flow, fd->idx);
150
2
}
151
152
static int DetectFlowbitMatchUnset (Packet *p, const DetectFlowbitsData *fd)
153
4.21k
{
154
4.21k
    if (p->flow == NULL)
155
293
        return 0;
156
157
3.91k
    FlowBitUnset(p->flow,fd->idx);
158
159
3.91k
    return 1;
160
4.21k
}
161
162
static int DetectFlowbitMatchSet (Packet *p, const DetectFlowbitsData *fd)
163
25.9k
{
164
25.9k
    if (p->flow == NULL)
165
6.45k
        return -1;
166
167
19.4k
    int r = FlowBitSet(p->flow, fd->idx);
168
19.4k
    SCLogDebug("set %u", fd->idx);
169
19.4k
    return r;
170
25.9k
}
171
172
static int DetectFlowbitMatchIsset (Packet *p, const DetectFlowbitsData *fd)
173
6.15k
{
174
6.15k
    if (p->flow == NULL)
175
0
        return 0;
176
6.15k
    if (fd->or_list_size > 0) {
177
10.1k
        for (uint8_t i = 0; i < fd->or_list_size; i++) {
178
7.43k
            if (FlowBitIsset(p->flow, fd->or_list[i]) == 1)
179
1.56k
                return 1;
180
7.43k
        }
181
2.70k
        return 0;
182
4.26k
    }
183
184
1.89k
    return FlowBitIsset(p->flow,fd->idx);
185
6.15k
}
186
187
static int DetectFlowbitMatchIsnotset (Packet *p, const DetectFlowbitsData *fd)
188
18.1k
{
189
18.1k
    if (p->flow == NULL)
190
0
        return 0;
191
18.1k
    if (fd->or_list_size > 0) {
192
6.48k
        for (uint8_t i = 0; i < fd->or_list_size; i++) {
193
6.09k
            if (FlowBitIsnotset(p->flow, fd->or_list[i]) == 1)
194
4.53k
                return 1;
195
6.09k
        }
196
388
        return 0;
197
4.92k
    }
198
13.1k
    return FlowBitIsnotset(p->flow,fd->idx);
199
18.1k
}
200
201
/*
202
 * returns 0: no match (or error)
203
 *         1: match
204
 */
205
206
int DetectFlowbitMatch (DetectEngineThreadCtx *det_ctx, Packet *p,
207
        const Signature *s, const SigMatchCtx *ctx)
208
519
{
209
519
    const DetectFlowbitsData *fd = (const DetectFlowbitsData *)ctx;
210
519
    if (fd == NULL)
211
0
        return 0;
212
213
519
    switch (fd->cmd) {
214
112
        case DETECT_FLOWBITS_CMD_ISSET:
215
112
            return DetectFlowbitMatchIsset(p,fd);
216
178
        case DETECT_FLOWBITS_CMD_ISNOTSET:
217
178
            return DetectFlowbitMatchIsnotset(p,fd);
218
169
        case DETECT_FLOWBITS_CMD_SET: {
219
169
            int r = DetectFlowbitMatchSet(p, fd);
220
            /* only on a new "set" invoke the prefilter */
221
169
            if (r == 1 && fd->post_rule_match_prefilter) {
222
0
                SCLogDebug("flowbit set, appending to work queue");
223
0
                PostRuleMatchWorkQueueAppend(det_ctx, s, DETECT_FLOWBITS, fd->idx);
224
0
            }
225
169
            return (r != -1);
226
0
        }
227
58
        case DETECT_FLOWBITS_CMD_UNSET:
228
58
            return DetectFlowbitMatchUnset(p,fd);
229
2
        case DETECT_FLOWBITS_CMD_TOGGLE: {
230
2
            int r = DetectFlowbitMatchToggle(p, fd);
231
2
            if (r == 1 && fd->post_rule_match_prefilter) {
232
0
                SCLogDebug("flowbit set (by toggle), appending to work queue");
233
0
                PostRuleMatchWorkQueueAppend(det_ctx, s, DETECT_FLOWBITS, fd->idx);
234
0
            }
235
2
            return (r != -1);
236
0
        }
237
0
        default:
238
0
            SCLogError("unknown cmd %" PRIu32 "", fd->cmd);
239
0
            return 0;
240
519
    }
241
242
0
    return 0;
243
519
}
244
245
static int DetectFlowbitParse(const char *str, char *cmd, int cmd_len, char *name,
246
    int name_len)
247
59.9k
{
248
59.9k
    int rc;
249
59.9k
    size_t pcre2len;
250
59.9k
    pcre2_match_data *match = NULL;
251
252
59.9k
    int count = DetectParsePcreExec(&parse_regex, &match, str, 0, 0);
253
59.9k
    if (count != 2 && count != 3) {
254
173
        SCLogError("\"%s\" is not a valid setting for flowbits.", str);
255
173
        goto error;
256
173
    }
257
258
59.8k
    pcre2len = cmd_len;
259
59.8k
    rc = pcre2_substring_copy_bynumber(match, 1, (PCRE2_UCHAR8 *)cmd, &pcre2len);
260
59.8k
    if (rc < 0) {
261
52
        SCLogError("pcre2_substring_copy_bynumber failed");
262
52
        goto error;
263
52
    }
264
265
59.7k
    if (count == 3) {
266
51.8k
        pcre2len = name_len;
267
51.8k
        rc = pcre2_substring_copy_bynumber(match, 2, (PCRE2_UCHAR8 *)name, &pcre2len);
268
51.8k
        if (rc < 0) {
269
114
            SCLogError("pcre2_substring_copy_bynumber failed");
270
114
            goto error;
271
114
        }
272
273
        /* Trim trailing whitespace. */
274
52.1k
        while (strlen(name) > 0 && isblank(name[strlen(name) - 1])) {
275
413
            name[strlen(name) - 1] = '\0';
276
413
        }
277
278
51.7k
        if (strchr(name, '|') == NULL) {
279
            /* Validate name, spaces are not allowed. */
280
625k
            for (size_t i = 0; i < strlen(name); i++) {
281
589k
                if (isblank(name[i])) {
282
1.56k
                    SCLogError("spaces not allowed in flowbit names");
283
1.56k
                    goto error;
284
1.56k
                }
285
589k
            }
286
37.5k
        }
287
51.7k
    }
288
289
59.7k
    pcre2_match_data_free(match);
290
58.0k
    return 1;
291
292
1.90k
error:
293
1.90k
    if (match) {
294
1.90k
        pcre2_match_data_free(match);
295
1.90k
    }
296
1.90k
    return 0;
297
59.7k
}
298
299
int DetectFlowbitSetup (DetectEngineCtx *de_ctx, Signature *s, const char *rawstr)
300
8.37k
{
301
8.37k
    DetectFlowbitsData *cd = NULL;
302
8.37k
    uint8_t fb_cmd = 0;
303
8.37k
    char fb_cmd_str[16] = "", fb_name[256] = "";
304
305
8.37k
    if (!DetectFlowbitParse(rawstr, fb_cmd_str, sizeof(fb_cmd_str), fb_name,
306
8.37k
            sizeof(fb_name))) {
307
279
        return -1;
308
279
    }
309
310
8.10k
    if (strcmp(fb_cmd_str,"noalert") == 0) {
311
633
        if (strlen(fb_name) != 0)
312
0
            goto error;
313
633
        s->action &= ~ACTION_ALERT;
314
633
        return 0;
315
7.46k
    } else if (strcmp(fb_cmd_str,"isset") == 0) {
316
2.42k
        fb_cmd = DETECT_FLOWBITS_CMD_ISSET;
317
5.04k
    } else if (strcmp(fb_cmd_str,"isnotset") == 0) {
318
1.93k
        fb_cmd = DETECT_FLOWBITS_CMD_ISNOTSET;
319
3.10k
    } else if (strcmp(fb_cmd_str,"set") == 0) {
320
940
        fb_cmd = DETECT_FLOWBITS_CMD_SET;
321
2.16k
    } else if (strcmp(fb_cmd_str,"unset") == 0) {
322
1.27k
        fb_cmd = DETECT_FLOWBITS_CMD_UNSET;
323
1.27k
    } else if (strcmp(fb_cmd_str,"toggle") == 0) {
324
271
        fb_cmd = DETECT_FLOWBITS_CMD_TOGGLE;
325
271
        if ((de_ctx->flags & DE_WARN_FLOWBITS_TOGGLE_DEPRECATION) == 0) {
326
35
            de_ctx->flags |= DE_WARN_FLOWBITS_TOGGLE_DEPRECATION;
327
35
            SCLogWarning("flowbits \"toggle\" command is deprecated and will be removed in "
328
35
                         "Suricata 9 (see ticket #8595)");
329
35
        }
330
625
    } else {
331
625
        SCLogError("ERROR: flowbits action \"%s\" is not supported.", fb_cmd_str);
332
625
        goto error;
333
625
    }
334
335
6.84k
    switch (fb_cmd) {
336
1.93k
        case DETECT_FLOWBITS_CMD_ISNOTSET:
337
4.35k
        case DETECT_FLOWBITS_CMD_ISSET:
338
5.29k
        case DETECT_FLOWBITS_CMD_SET:
339
6.57k
        case DETECT_FLOWBITS_CMD_UNSET:
340
6.84k
        case DETECT_FLOWBITS_CMD_TOGGLE:
341
6.84k
        default:
342
6.84k
            if (strlen(fb_name) == 0)
343
205
                goto error;
344
6.63k
            break;
345
6.84k
    }
346
347
6.63k
    cd = SCCalloc(1, sizeof(DetectFlowbitsData));
348
6.63k
    if (unlikely(cd == NULL))
349
0
        goto error;
350
6.63k
    if (strchr(fb_name, '|') != NULL) {
351
2.12k
        int retval = FlowbitOrAddData(de_ctx, cd, fb_name);
352
2.12k
        if (retval == -1) {
353
74
            goto error;
354
74
        }
355
2.04k
        cd->cmd = fb_cmd;
356
4.51k
    } else {
357
4.51k
        cd->idx = VarNameStoreRegister(fb_name, VAR_TYPE_FLOW_BIT);
358
4.51k
        de_ctx->max_fb_id = MAX(cd->idx, de_ctx->max_fb_id);
359
4.51k
        cd->cmd = fb_cmd;
360
4.51k
        cd->or_list_size = 0;
361
4.51k
        cd->or_list = NULL;
362
4.51k
        SCLogDebug("idx %" PRIu32 ", cmd %s, name %s",
363
4.51k
            cd->idx, fb_cmd_str, strlen(fb_name) ? fb_name : "(none)");
364
4.51k
    }
365
    /* Okay so far so good, lets get this into a SigMatch
366
     * and put it in the Signature. */
367
368
6.56k
    switch (fb_cmd) {
369
        /* noalert can't happen here */
370
1.92k
        case DETECT_FLOWBITS_CMD_ISNOTSET:
371
4.14k
        case DETECT_FLOWBITS_CMD_ISSET:
372
            /* checks, so packet list */
373
4.14k
            if (SCSigMatchAppendSMToList(de_ctx, s, DETECT_FLOWBITS, (SigMatchCtx *)cd,
374
4.14k
                        DETECT_SM_LIST_MATCH) == NULL) {
375
0
                goto error;
376
0
            }
377
4.14k
            break;
378
379
4.14k
        case DETECT_FLOWBITS_CMD_SET:
380
2.15k
        case DETECT_FLOWBITS_CMD_UNSET:
381
2.42k
        case DETECT_FLOWBITS_CMD_TOGGLE:
382
            /* modifiers, only run when entire sig has matched */
383
2.42k
            if (SCSigMatchAppendSMToList(de_ctx, s, DETECT_FLOWBITS, (SigMatchCtx *)cd,
384
2.42k
                        DETECT_SM_LIST_POSTMATCH) == NULL) {
385
0
                goto error;
386
0
            }
387
2.42k
            break;
388
389
        // suppress coverity warning as scan-build-7 warns w/o this.
390
        // coverity[deadcode : FALSE]
391
2.42k
        default:
392
0
            goto error;
393
6.56k
    }
394
395
6.56k
    return 0;
396
397
904
error:
398
904
    if (cd != NULL)
399
74
        DetectFlowbitFree(de_ctx, cd);
400
904
    return -1;
401
6.56k
}
402
403
void DetectFlowbitFree (DetectEngineCtx *de_ctx, void *ptr)
404
48.0k
{
405
48.0k
    DetectFlowbitsData *fd = (DetectFlowbitsData *)ptr;
406
48.0k
    if (fd == NULL)
407
0
        return;
408
48.0k
    VarNameStoreUnregister(fd->idx, VAR_TYPE_FLOW_BIT);
409
48.0k
    if (fd->or_list != NULL) {
410
55.1k
        for (uint8_t i = 0; i < fd->or_list_size; i++) {
411
41.8k
            VarNameStoreUnregister(fd->or_list[i], VAR_TYPE_FLOW_BIT);
412
41.8k
        }
413
13.3k
        SCFree(fd->or_list);
414
13.3k
    }
415
48.0k
    SCFree(fd);
416
48.0k
}
417
418
struct FBAnalyzer {
419
    struct FBAnalyze *array;
420
    uint32_t array_size;
421
};
422
423
struct FBAnalyze {
424
    uint16_t cnts[DETECT_FLOWBITS_CMD_MAX];
425
    uint16_t state_cnts[DETECT_FLOWBITS_CMD_MAX];
426
427
    uint32_t *set_sids;
428
    uint32_t set_sids_idx;
429
    uint32_t set_sids_size;
430
431
    uint32_t *isset_sids;
432
    uint32_t isset_sids_idx;
433
    uint32_t isset_sids_size;
434
435
    uint32_t *isnotset_sids;
436
    uint32_t isnotset_sids_idx;
437
    uint32_t isnotset_sids_size;
438
439
    uint32_t *unset_sids;
440
    uint32_t unset_sids_idx;
441
    uint32_t unset_sids_size;
442
443
    uint32_t *toggle_sids;
444
    uint32_t toggle_sids_idx;
445
    uint32_t toggle_sids_size;
446
};
447
448
extern bool rule_engine_analysis_set;
449
static void DetectFlowbitsAnalyzeDump(const DetectEngineCtx *de_ctx,
450
        struct FBAnalyze *array, uint32_t elements);
451
452
static void FBAnalyzerArrayFree(struct FBAnalyze *array, const uint32_t array_size)
453
9.13k
{
454
9.13k
    if (array) {
455
4.88M
        for (uint32_t i = 0; i < array_size; i++) {
456
4.87M
            SCFree(array[i].set_sids);
457
4.87M
            SCFree(array[i].unset_sids);
458
4.87M
            SCFree(array[i].isset_sids);
459
4.87M
            SCFree(array[i].isnotset_sids);
460
4.87M
            SCFree(array[i].toggle_sids);
461
4.87M
        }
462
9.13k
        SCFree(array);
463
9.13k
    }
464
9.13k
}
465
466
static void FBAnalyzerFree(struct FBAnalyzer *fba)
467
584
{
468
584
    if (fba && fba->array) {
469
584
        FBAnalyzerArrayFree(fba->array, fba->array_size);
470
584
        fba->array = NULL;
471
584
        fba->array_size = 0;
472
584
    }
473
584
}
474
475
#define MAX_SIDS 8
476
static bool CheckExpand(const uint32_t sids_idx, uint32_t **sids, uint32_t *sids_size)
477
47.7k
{
478
47.7k
    if (sids_idx >= *sids_size) {
479
25.8k
        const uint32_t old_size = *sids_size;
480
25.8k
        const uint32_t new_size = MAX(2 * old_size, MAX_SIDS);
481
482
25.8k
        void *ptr = SCRealloc(*sids, new_size * sizeof(uint32_t));
483
25.8k
        if (ptr == NULL)
484
0
            return false;
485
25.8k
        *sids_size = new_size;
486
25.8k
        *sids = ptr;
487
25.8k
    }
488
47.7k
    return true;
489
47.7k
}
490
491
static int DetectFlowbitsAnalyzeSignature(const Signature *s, struct FBAnalyzer *fba)
492
17.0k
{
493
17.0k
    struct FBAnalyze *array = fba->array;
494
17.0k
    if (array == NULL)
495
0
        return -1;
496
497
    /* see if the signature uses stateful matching TODO is there not a flag? */
498
17.0k
    bool has_state = (s->init_data->buffer_index != 0);
499
500
29.5k
    for (const SigMatch *sm = s->init_data->smlists[DETECT_SM_LIST_MATCH]; sm != NULL;
501
17.0k
            sm = sm->next) {
502
12.4k
        if (sm->type != DETECT_FLOWBITS)
503
11.7k
            continue;
504
        /* figure out the flowbit action */
505
754
        const DetectFlowbitsData *fb = (DetectFlowbitsData *)sm->ctx;
506
        // Handle flowbit array in case of ORed flowbits
507
1.13k
        for (uint8_t k = 0; k < fb->or_list_size; k++) {
508
379
            struct FBAnalyze *fa = &array[fb->or_list[k]];
509
379
            fa->cnts[fb->cmd]++;
510
379
            fa->state_cnts[fb->cmd] += has_state;
511
512
379
            if (fb->cmd == DETECT_FLOWBITS_CMD_ISSET) {
513
153
                if (!CheckExpand(fa->isset_sids_idx, &fa->isset_sids, &fa->isset_sids_size))
514
0
                    return -1;
515
153
                fa->isset_sids[fa->isset_sids_idx] = s->iid;
516
153
                fa->isset_sids_idx++;
517
226
            } else if (fb->cmd == DETECT_FLOWBITS_CMD_ISNOTSET) {
518
226
                if (!CheckExpand(
519
226
                            fa->isnotset_sids_idx, &fa->isnotset_sids, &fa->isnotset_sids_size))
520
0
                    return -1;
521
226
                fa->isnotset_sids[fa->isnotset_sids_idx] = s->iid;
522
226
                fa->isnotset_sids_idx++;
523
226
            }
524
379
        }
525
754
        if (fb->or_list_size == 0) {
526
572
            struct FBAnalyze *fa = &array[fb->idx];
527
572
            fa->cnts[fb->cmd]++;
528
572
            fa->state_cnts[fb->cmd] += has_state;
529
530
572
            if (fb->cmd == DETECT_FLOWBITS_CMD_ISSET) {
531
525
                if (!CheckExpand(fa->isset_sids_idx, &fa->isset_sids, &fa->isset_sids_size))
532
0
                    return -1;
533
525
                fa->isset_sids[fa->isset_sids_idx] = s->iid;
534
525
                fa->isset_sids_idx++;
535
525
            } else if (fb->cmd == DETECT_FLOWBITS_CMD_ISNOTSET) {
536
47
                if (!CheckExpand(
537
47
                            fa->isnotset_sids_idx, &fa->isnotset_sids, &fa->isnotset_sids_size))
538
0
                    return -1;
539
47
                fa->isnotset_sids[fa->isnotset_sids_idx] = s->iid;
540
47
                fa->isnotset_sids_idx++;
541
47
            }
542
572
        }
543
754
    }
544
19.1k
    for (const SigMatch *sm = s->init_data->smlists[DETECT_SM_LIST_POSTMATCH]; sm != NULL;
545
17.0k
            sm = sm->next) {
546
2.10k
        if (sm->type != DETECT_FLOWBITS)
547
1.63k
            continue;
548
        /* figure out what flowbit action */
549
470
        const DetectFlowbitsData *fb = (DetectFlowbitsData *)sm->ctx;
550
470
        struct FBAnalyze *fa = &array[fb->idx];
551
470
        fa->cnts[fb->cmd]++;
552
470
        fa->state_cnts[fb->cmd] += has_state;
553
554
470
        if (fb->cmd == DETECT_FLOWBITS_CMD_SET) {
555
313
            if (!CheckExpand(fa->set_sids_idx, &fa->set_sids, &fa->set_sids_size))
556
0
                return -1;
557
313
            fa->set_sids[fa->set_sids_idx] = s->iid;
558
313
            fa->set_sids_idx++;
559
313
        } else if (fb->cmd == DETECT_FLOWBITS_CMD_UNSET) {
560
31
            if (!CheckExpand(fa->unset_sids_idx, &fa->unset_sids, &fa->unset_sids_size))
561
0
                return -1;
562
31
            fa->unset_sids[fa->unset_sids_idx] = s->iid;
563
31
            fa->unset_sids_idx++;
564
126
        } else if (fb->cmd == DETECT_FLOWBITS_CMD_TOGGLE) {
565
126
            if (!CheckExpand(fa->toggle_sids_idx, &fa->toggle_sids, &fa->toggle_sids_size))
566
0
                return -1;
567
126
            fa->toggle_sids[fa->toggle_sids_idx] = s->iid;
568
126
            fa->toggle_sids_idx++;
569
126
        }
570
470
    }
571
17.0k
    return 0;
572
17.0k
}
573
574
int DetectFlowbitsAnalyze(DetectEngineCtx *de_ctx)
575
17.5k
{
576
17.5k
    const uint32_t max_fb_id = de_ctx->max_fb_id;
577
17.5k
    if (max_fb_id == 0)
578
17.2k
        return 0;
579
580
285
    struct FBAnalyzer fba = { .array = NULL, .array_size = 0 };
581
285
    const uint32_t array_size = max_fb_id + 1;
582
285
    struct FBAnalyze *array = SCCalloc(array_size, sizeof(struct FBAnalyze));
583
285
    if (array == NULL) {
584
0
        SCLogError("Unable to allocate flowbit analyze array");
585
0
        return -1;
586
0
    }
587
285
    fba.array = array;
588
285
    fba.array_size = array_size;
589
590
285
    SCLogDebug("fb analyzer array size: %"PRIu64,
591
285
            (uint64_t)(array_size * sizeof(struct FBAnalyze)));
592
593
    /* fill flowbit array, updating counters per sig */
594
7.80k
    for (uint32_t i = 0; i < de_ctx->sig_array_len; i++) {
595
7.51k
        const Signature *s = de_ctx->sig_array[i];
596
597
7.51k
        int r = DetectFlowbitsAnalyzeSignature(s, &fba);
598
7.51k
        if (r < 0) {
599
0
            FBAnalyzerFree(&fba);
600
0
            return -1;
601
0
        }
602
7.51k
    }
603
604
    /* walk array to see if all bits make sense */
605
18.8k
    for (uint32_t i = 0; i < array_size; i++) {
606
18.5k
        const char *varname = VarNameStoreSetupLookup(i, VAR_TYPE_FLOW_BIT);
607
18.5k
        if (varname == NULL)
608
5.56k
            continue;
609
610
18.5k
        bool to_state = false;
611
612
13.0k
        if (array[i].cnts[DETECT_FLOWBITS_CMD_ISSET] &&
613
241
            array[i].cnts[DETECT_FLOWBITS_CMD_TOGGLE] == 0 &&
614
225
            array[i].cnts[DETECT_FLOWBITS_CMD_SET] == 0) {
615
616
204
            const Signature *s = de_ctx->sig_array[array[i].isset_sids[0]];
617
204
            SCLogWarning("flowbit '%s' is checked but not "
618
204
                         "set. Checked in %u and %u other sigs",
619
204
                    varname, s->id, array[i].isset_sids_idx - 1);
620
204
        }
621
13.0k
        if (array[i].state_cnts[DETECT_FLOWBITS_CMD_ISSET] &&
622
35
            array[i].state_cnts[DETECT_FLOWBITS_CMD_SET] == 0)
623
33
        {
624
33
            SCLogDebug("flowbit %s/%u: isset in state, set not in state", varname, i);
625
33
        }
626
627
        /* if signature depends on 'stateful' flowbits, then turn the
628
         * sig into a stateful sig itself */
629
13.0k
        if (array[i].cnts[DETECT_FLOWBITS_CMD_ISSET] > 0 &&
630
241
            array[i].state_cnts[DETECT_FLOWBITS_CMD_ISSET] == 0 &&
631
206
            array[i].state_cnts[DETECT_FLOWBITS_CMD_SET])
632
0
        {
633
0
            SCLogDebug("flowbit %s/%u: isset not in state, set in state", varname, i);
634
0
            to_state = true;
635
0
        }
636
637
13.0k
        SCLogDebug("ALL flowbit %s/%u: sets %u toggles %u unsets %u isnotsets %u issets %u", varname, i,
638
13.0k
                array[i].cnts[DETECT_FLOWBITS_CMD_SET], array[i].cnts[DETECT_FLOWBITS_CMD_TOGGLE],
639
13.0k
                array[i].cnts[DETECT_FLOWBITS_CMD_UNSET], array[i].cnts[DETECT_FLOWBITS_CMD_ISNOTSET],
640
13.0k
                array[i].cnts[DETECT_FLOWBITS_CMD_ISSET]);
641
13.0k
        SCLogDebug("STATE flowbit %s/%u: sets %u toggles %u unsets %u isnotsets %u issets %u", varname, i,
642
13.0k
                array[i].state_cnts[DETECT_FLOWBITS_CMD_SET], array[i].state_cnts[DETECT_FLOWBITS_CMD_TOGGLE],
643
13.0k
                array[i].state_cnts[DETECT_FLOWBITS_CMD_UNSET], array[i].state_cnts[DETECT_FLOWBITS_CMD_ISNOTSET],
644
13.0k
                array[i].state_cnts[DETECT_FLOWBITS_CMD_ISSET]);
645
13.1k
        for (uint32_t x = 0; x < array[i].set_sids_idx; x++) {
646
169
            SCLogDebug("SET flowbit %s/%u: SID %u", varname, i,
647
169
                    de_ctx->sig_array[array[i].set_sids[x]]->id);
648
169
        }
649
13.0k
        if (to_state) {
650
0
            for (uint32_t x = 0; x < array[i].isset_sids_idx; x++) {
651
0
                Signature *s = de_ctx->sig_array[array[i].isset_sids[x]];
652
0
                SCLogDebug("GET flowbit %s/%u: SID %u", varname, i, s->id);
653
654
0
                s->init_data->init_flags |= SIG_FLAG_INIT_STATE_MATCH;
655
0
                s->init_data->is_rule_state_dependant = true;
656
657
0
                uint32_t sids_array_size = array[i].set_sids_idx;
658
659
                // save information about flowbits that affect this rule's state
660
0
                if (s->init_data->rule_state_dependant_sids_array == NULL) {
661
0
                    s->init_data->rule_state_dependant_sids_array =
662
0
                            SCCalloc(sids_array_size, sizeof(uint32_t));
663
0
                    if (s->init_data->rule_state_dependant_sids_array == NULL) {
664
0
                        SCLogError("Failed to allocate memory for rule_state_dependant_ids");
665
0
                        goto error;
666
0
                    }
667
0
                    s->init_data->rule_state_flowbits_ids_size = 1;
668
0
                    s->init_data->rule_state_flowbits_ids_array =
669
0
                            SCCalloc(s->init_data->rule_state_flowbits_ids_size, sizeof(uint32_t));
670
0
                    if (s->init_data->rule_state_flowbits_ids_array == NULL) {
671
0
                        SCLogError("Failed to allocate memory for rule_state_variable_idx");
672
0
                        goto error;
673
0
                    }
674
0
                    s->init_data->rule_state_dependant_sids_size = sids_array_size;
675
0
                    SCLogDebug("alloc'ed array for rule dependency and fbs idx array, sid %u, "
676
0
                               "sizes are %u and %u",
677
0
                            s->id, s->init_data->rule_state_dependant_sids_size,
678
0
                            s->init_data->rule_state_flowbits_ids_size);
679
0
                } else {
680
0
                    uint32_t new_array_size =
681
0
                            s->init_data->rule_state_dependant_sids_size + sids_array_size;
682
0
                    void *tmp_ptr = SCRealloc(s->init_data->rule_state_dependant_sids_array,
683
0
                            new_array_size * sizeof(uint32_t));
684
0
                    if (tmp_ptr == NULL) {
685
0
                        SCLogError("Failed to allocate memory for rule_state_variable_idx");
686
0
                        goto error;
687
0
                    }
688
0
                    s->init_data->rule_state_dependant_sids_array = tmp_ptr;
689
0
                    s->init_data->rule_state_dependant_sids_size = new_array_size;
690
0
                    SCLogDebug("realloc'ed array for rule dependency, sid %u, new size is %u",
691
0
                            s->id, s->init_data->rule_state_dependant_sids_size);
692
0
                    uint32_t new_fb_array_size = s->init_data->rule_state_flowbits_ids_size + 1;
693
0
                    void *tmp_fb_ptr = SCRealloc(s->init_data->rule_state_flowbits_ids_array,
694
0
                            new_fb_array_size * sizeof(uint32_t));
695
0
                    s->init_data->rule_state_flowbits_ids_array = tmp_fb_ptr;
696
0
                    if (s->init_data->rule_state_flowbits_ids_array == NULL) {
697
0
                        SCLogError("Failed to reallocate memory for rule_state_variable_idx");
698
0
                        goto error;
699
0
                    }
700
0
                    SCLogDebug(
701
0
                            "realloc'ed array for flowbits ids, new size is %u", new_fb_array_size);
702
0
                    s->init_data->rule_state_dependant_sids_size = new_array_size;
703
0
                    s->init_data->rule_state_flowbits_ids_size = new_fb_array_size;
704
0
                }
705
0
                for (uint32_t idx = 0; idx < s->init_data->rule_state_dependant_sids_size; idx++) {
706
0
                    if (idx < array[i].set_sids_idx) {
707
0
                        s->init_data->rule_state_dependant_sids_array
708
0
                                [s->init_data->rule_state_dependant_sids_idx] =
709
0
                                de_ctx->sig_array[array[i].set_sids[idx]]->id;
710
0
                        s->init_data->rule_state_dependant_sids_idx++;
711
0
                    }
712
0
                }
713
0
                s->init_data
714
0
                        ->rule_state_flowbits_ids_array[s->init_data->rule_state_flowbits_ids_size -
715
0
                                                        1] = i;
716
0
                s->init_data->rule_state_flowbits_ids_size += 1;
717
                // flowbit info saving for rule made stateful rule work finished
718
719
0
                SCLogDebug("made SID %u stateful because it depends on "
720
0
                        "stateful rules that set flowbit %s", s->id, varname);
721
0
            }
722
0
        }
723
13.0k
    }
724
725
285
    if (rule_engine_analysis_set) {
726
0
        DetectFlowbitsAnalyzeDump(de_ctx, array, array_size);
727
0
    }
728
729
285
    FBAnalyzerFree(&fba);
730
285
    return 0;
731
0
error:
732
0
    FBAnalyzerFree(&fba);
733
0
    return -1;
734
285
}
735
736
// TODO misses IPOnly rules. IPOnly flowbit rules are set only though.
737
static struct FBAnalyzer DetectFlowbitsAnalyzeForGroup(
738
        const DetectEngineCtx *de_ctx, SigGroupHead *sgh)
739
5.25k
{
740
5.25k
    struct FBAnalyzer fba = { .array = NULL, .array_size = 0 };
741
742
5.25k
    const uint32_t max_fb_id = de_ctx->max_fb_id;
743
5.25k
    if (max_fb_id == 0)
744
0
        return fba;
745
746
5.25k
    uint32_t array_size = max_fb_id + 1;
747
5.25k
    struct FBAnalyze *array = SCCalloc(array_size, sizeof(struct FBAnalyze));
748
5.25k
    if (array == NULL) {
749
0
        SCLogError("Unable to allocate flowbit analyze array");
750
0
        return fba;
751
0
    }
752
5.25k
    SCLogDebug(
753
5.25k
            "fb analyzer array size: %" PRIu64, (uint64_t)(array_size * sizeof(struct FBAnalyze)));
754
5.25k
    fba.array = array;
755
5.25k
    fba.array_size = array_size;
756
757
    /* fill flowbit array, updating counters per sig */
758
70.8k
    for (uint32_t i = 0; i < sgh->init->sig_cnt; i++) {
759
65.6k
        const Signature *s = sgh->init->match_array[i];
760
65.6k
        SCLogDebug("sgh %p: s->id %u", sgh, s->id);
761
762
65.6k
        int r = DetectFlowbitsAnalyzeSignature(s, &fba);
763
65.6k
        if (r < 0) {
764
0
            FBAnalyzerFree(&fba);
765
0
            return fba;
766
0
        }
767
65.6k
    }
768
769
    /* walk array to see if all bits make sense */
770
3.24M
    for (uint32_t i = 0; i < array_size; i++) {
771
3.23M
        const char *varname = VarNameStoreSetupLookup(i, VAR_TYPE_FLOW_BIT);
772
3.23M
        if (varname == NULL)
773
1.34M
            continue;
774
775
3.23M
        bool to_state = false;
776
1.89M
        if (array[i].state_cnts[DETECT_FLOWBITS_CMD_ISSET] &&
777
3.04k
                array[i].state_cnts[DETECT_FLOWBITS_CMD_SET] == 0) {
778
2.99k
            SCLogDebug("flowbit %s/%u: isset in state, set not in state", varname, i);
779
2.99k
        }
780
781
        /* if signature depends on 'stateful' flowbits, then turn the
782
         * sig into a stateful sig itself */
783
1.89M
        if (array[i].cnts[DETECT_FLOWBITS_CMD_ISSET] > 0 &&
784
7.48k
                array[i].state_cnts[DETECT_FLOWBITS_CMD_ISSET] == 0 &&
785
4.43k
                array[i].state_cnts[DETECT_FLOWBITS_CMD_SET]) {
786
13
            SCLogDebug("flowbit %s/%u: isset not in state, set in state", varname, i);
787
13
            to_state = true;
788
13
        }
789
790
1.89M
        SCLogDebug("ALL flowbit %s/%u: sets %u toggles %u unsets %u isnotsets %u issets %u",
791
1.89M
                varname, i, array[i].cnts[DETECT_FLOWBITS_CMD_SET],
792
1.89M
                array[i].cnts[DETECT_FLOWBITS_CMD_TOGGLE], array[i].cnts[DETECT_FLOWBITS_CMD_UNSET],
793
1.89M
                array[i].cnts[DETECT_FLOWBITS_CMD_ISNOTSET],
794
1.89M
                array[i].cnts[DETECT_FLOWBITS_CMD_ISSET]);
795
1.89M
        SCLogDebug("STATE flowbit %s/%u: sets %u toggles %u unsets %u isnotsets %u issets %u",
796
1.89M
                varname, i, array[i].state_cnts[DETECT_FLOWBITS_CMD_SET],
797
1.89M
                array[i].state_cnts[DETECT_FLOWBITS_CMD_TOGGLE],
798
1.89M
                array[i].state_cnts[DETECT_FLOWBITS_CMD_UNSET],
799
1.89M
                array[i].state_cnts[DETECT_FLOWBITS_CMD_ISNOTSET],
800
1.89M
                array[i].state_cnts[DETECT_FLOWBITS_CMD_ISSET]);
801
1.90M
        for (uint32_t x = 0; x < array[i].set_sids_idx; x++) {
802
1.92k
            SCLogDebug("SET flowbit %s/%u: SID %u", varname, i,
803
1.92k
                    de_ctx->sig_array[array[i].set_sids[x]]->id);
804
1.92k
        }
805
1.91M
        for (uint32_t x = 0; x < array[i].isset_sids_idx; x++) {
806
11.8k
            Signature *s = de_ctx->sig_array[array[i].isset_sids[x]];
807
11.8k
            SCLogDebug("GET flowbit %s/%u: SID %u", varname, i, s->id);
808
809
11.8k
            if (to_state) {
810
13
                s->init_data->init_flags |= SIG_FLAG_INIT_STATE_MATCH;
811
13
                SCLogDebug("made SID %u stateful because it depends on "
812
13
                           "stateful rules that set flowbit %s",
813
13
                        s->id, varname);
814
13
            }
815
11.8k
        }
816
1.89M
    }
817
818
5.25k
    return fba;
819
5.25k
}
820
821
SCMutex g_flowbits_dump_write_m = SCMUTEX_INITIALIZER;
822
static void DetectFlowbitsAnalyzeDump(const DetectEngineCtx *de_ctx,
823
        struct FBAnalyze *array, uint32_t elements)
824
{
825
    SCJsonBuilder *js = SCJbNewObject();
826
    if (js == NULL)
827
        return;
828
829
    SCJbOpenArray(js, "flowbits");
830
    for (uint32_t x = 0; x < elements; x++) {
831
        const char *varname = VarNameStoreSetupLookup(x, VAR_TYPE_FLOW_BIT);
832
        if (varname == NULL)
833
            continue;
834
835
        const struct FBAnalyze *e = &array[x];
836
837
        SCJbStartObject(js);
838
        SCJbSetString(js, "name", varname);
839
        SCJbSetUint(js, "internal_id", x);
840
        SCJbSetUint(js, "set_cnt", e->cnts[DETECT_FLOWBITS_CMD_SET]);
841
        SCJbSetUint(js, "unset_cnt", e->cnts[DETECT_FLOWBITS_CMD_UNSET]);
842
        SCJbSetUint(js, "toggle_cnt", e->cnts[DETECT_FLOWBITS_CMD_TOGGLE]);
843
        SCJbSetUint(js, "isset_cnt", e->cnts[DETECT_FLOWBITS_CMD_ISSET]);
844
        SCJbSetUint(js, "isnotset_cnt", e->cnts[DETECT_FLOWBITS_CMD_ISNOTSET]);
845
846
        // sets
847
        if (e->cnts[DETECT_FLOWBITS_CMD_SET]) {
848
            SCJbOpenArray(js, "sets");
849
            for (uint32_t i = 0; i < e->set_sids_idx; i++) {
850
                const Signature *s = de_ctx->sig_array[e->set_sids[i]];
851
                SCJbAppendUint(js, s->id);
852
            }
853
            SCJbClose(js);
854
        }
855
        // gets
856
        if (e->cnts[DETECT_FLOWBITS_CMD_ISSET]) {
857
            SCJbOpenArray(js, "isset");
858
            for (uint32_t i = 0; i < e->isset_sids_idx; i++) {
859
                const Signature *s = de_ctx->sig_array[e->isset_sids[i]];
860
                SCJbAppendUint(js, s->id);
861
            }
862
            SCJbClose(js);
863
        }
864
        // isnotset
865
        if (e->cnts[DETECT_FLOWBITS_CMD_ISNOTSET]) {
866
            SCJbOpenArray(js, "isnotset");
867
            for (uint32_t i = 0; i < e->isnotset_sids_idx; i++) {
868
                const Signature *s = de_ctx->sig_array[e->isnotset_sids[i]];
869
                SCJbAppendUint(js, s->id);
870
            }
871
            SCJbClose(js);
872
        }
873
        // unset
874
        if (e->cnts[DETECT_FLOWBITS_CMD_UNSET]) {
875
            SCJbOpenArray(js, "unset");
876
            for (uint32_t i = 0; i < e->unset_sids_idx; i++) {
877
                const Signature *s = de_ctx->sig_array[e->unset_sids[i]];
878
                SCJbAppendUint(js, s->id);
879
            }
880
            SCJbClose(js);
881
        }
882
        // toggle
883
        if (e->cnts[DETECT_FLOWBITS_CMD_TOGGLE]) {
884
            SCJbOpenArray(js, "toggle");
885
            for (uint32_t i = 0; i < e->toggle_sids_idx; i++) {
886
                const Signature *s = de_ctx->sig_array[e->toggle_sids[i]];
887
                SCJbAppendUint(js, s->id);
888
            }
889
            SCJbClose(js);
890
        }
891
        SCJbClose(js);
892
    }
893
    SCJbClose(js); // array
894
    SCJbClose(js); // object
895
896
    const char *filename = "flowbits.json";
897
    const char *log_dir = SCConfigGetLogDirectory();
898
    char log_path[PATH_MAX] = "";
899
    snprintf(log_path, sizeof(log_path), "%s/%s", log_dir, filename);
900
901
    SCMutexLock(&g_flowbits_dump_write_m);
902
    FILE *fp = fopen(log_path, "w");
903
    if (fp != NULL) {
904
        fwrite(SCJbPtr(js), SCJbLen(js), 1, fp);
905
        fprintf(fp, "\n");
906
        fclose(fp);
907
    }
908
    SCMutexUnlock(&g_flowbits_dump_write_m);
909
910
    SCJbFree(js);
911
}
912
913
static bool PrefilterFlowbitIsPrefilterable(const Signature *s)
914
0
{
915
0
    SCLogDebug("sid:%u: checking", s->id);
916
917
0
    for (const SigMatch *sm = s->init_data->smlists[DETECT_SM_LIST_MATCH]; sm != NULL;
918
0
            sm = sm->next) {
919
0
        switch (sm->type) {
920
0
            case DETECT_FLOWBITS: {
921
0
                const DetectFlowbitsData *fb = (DetectFlowbitsData *)sm->ctx;
922
0
                if (fb->cmd == DETECT_FLOWBITS_CMD_ISSET) {
923
0
                    SCLogDebug("sid:%u: FLOWBITS ISSET can prefilter", s->id);
924
0
                    return true;
925
0
                }
926
0
                break;
927
0
            }
928
0
        }
929
0
    }
930
0
    SCLogDebug("sid:%u: no flowbit prefilter", s->id);
931
0
    return false;
932
0
}
933
934
/** core flowbit data structure: map a flowbit id to the signatures that need inspecting after it is
935
 * found. Part of a rb-tree. */
936
typedef struct PrefilterFlowbit {
937
    uint32_t id;           /**< flowbit id */
938
    uint32_t rule_id_size; /**< size in elements of `rule_id` */
939
    uint32_t rule_id_cnt;  /**< usage in elements of `rule_id` */
940
    uint32_t *rule_id;     /**< array of signature iid that are part of this prefilter */
941
    RB_ENTRY(PrefilterFlowbit) __attribute__((__packed__)) rb;
942
} __attribute__((__packed__)) PrefilterFlowbit;
943
944
static int PrefilterFlowbitCompare(const PrefilterFlowbit *a, const PrefilterFlowbit *b)
945
94.2k
{
946
94.2k
    if (a->id > b->id)
947
47.8k
        return 1;
948
46.3k
    else if (a->id < b->id)
949
38.8k
        return -1;
950
7.51k
    else
951
7.51k
        return 0;
952
94.2k
}
953
954
/** red-black tree prototype for PFB (Prefilter Flow Bits) */
955
RB_HEAD(PFB, PrefilterFlowbit);
956
RB_PROTOTYPE(PFB, PrefilterFlowbit, rb, PrefilterFlowbitCompare);
957
257k
RB_GENERATE(PFB, PrefilterFlowbit, rb, PrefilterFlowbitCompare);
PFB_RB_INSERT_COLOR
Line
Count
Source
957
RB_GENERATE(PFB, PrefilterFlowbit, rb, PrefilterFlowbitCompare);
PFB_RB_REMOVE_COLOR
Line
Count
Source
957
RB_GENERATE(PFB, PrefilterFlowbit, rb, PrefilterFlowbitCompare);
PFB_RB_INSERT
Line
Count
Source
957
RB_GENERATE(PFB, PrefilterFlowbit, rb, PrefilterFlowbitCompare);
PFB_RB_REMOVE
Line
Count
Source
957
RB_GENERATE(PFB, PrefilterFlowbit, rb, PrefilterFlowbitCompare);
PFB_RB_FIND
Line
Count
Source
957
RB_GENERATE(PFB, PrefilterFlowbit, rb, PrefilterFlowbitCompare);
Unexecuted instantiation: PFB_RB_NFIND
PFB_RB_MINMAX
Line
Count
Source
957
RB_GENERATE(PFB, PrefilterFlowbit, rb, PrefilterFlowbitCompare);
958
257k
959
257k
struct PrefilterEngineFlowbits {
960
257k
    struct PFB fb_tree;
961
257k
};
962
257k
963
257k
static void PrefilterFlowbitFree(void *vctx)
964
257k
{
965
4.82k
    struct PrefilterEngineFlowbits *ctx = vctx;
966
4.82k
    struct PrefilterFlowbit *rec, *safe = NULL;
967
17.0k
    RB_FOREACH_SAFE (rec, PFB, &ctx->fb_tree, safe) {
968
17.0k
        PFB_RB_REMOVE(&ctx->fb_tree, rec);
969
17.0k
        SCFree(rec->rule_id);
970
17.0k
        SCFree(rec);
971
17.0k
    }
972
973
4.82k
    SCFree(ctx);
974
4.82k
}
975
976
static void PrefilterFlowbitMatch(DetectEngineThreadCtx *det_ctx, Packet *p, const void *pectx)
977
4.27k
{
978
4.27k
    struct PrefilterEngineFlowbits *ctx = (struct PrefilterEngineFlowbits *)pectx;
979
4.27k
    SCLogDebug("%" PRIu64 ": ctx %p", p->pcap_cnt, ctx);
980
981
4.27k
    if (p->flow == NULL) {
982
0
        SCReturn;
983
0
    }
984
985
6.03k
    for (GenericVar *gv = p->flow->flowvar; gv != NULL; gv = gv->next) {
986
1.75k
        if (gv->type != DETECT_FLOWBITS)
987
260
            continue;
988
989
1.49k
        PrefilterFlowbit lookup;
990
1.49k
        memset(&lookup, 0, sizeof(lookup));
991
1.49k
        lookup.id = gv->idx;
992
1.49k
        SCLogDebug("flowbit %u", gv->idx);
993
994
1.49k
        PrefilterFlowbit *b = PFB_RB_FIND(&ctx->fb_tree, &lookup);
995
1.49k
        if (b == NULL) {
996
578
            SCLogDebug("flowbit %u not in the tree", lookup.id);
997
921
        } else {
998
921
            SCLogDebug("flowbit %u found in the tree: %u", lookup.id, b->id);
999
1000
921
            PrefilterAddSids(&det_ctx->pmq, b->rule_id, b->rule_id_cnt);
1001
#ifdef DEBUG
1002
            for (uint32_t x = 0; x < b->rule_id_cnt; x++) {
1003
                const Signature *s = det_ctx->de_ctx->sig_array[b->rule_id[x]];
1004
                SCLogDebug("flowbit %u -> sig %u", gv->idx, s->id);
1005
            }
1006
#endif
1007
921
        }
1008
1.49k
    }
1009
4.27k
}
1010
1011
static void PrefilterFlowbitPostRuleMatch(
1012
        DetectEngineThreadCtx *det_ctx, const void *pectx, Packet *p, Flow *f)
1013
36
{
1014
36
    struct PrefilterEngineFlowbits *ctx = (struct PrefilterEngineFlowbits *)pectx;
1015
36
    SCLogDebug("%" PRIu64 ": ctx %p", p->pcap_cnt, ctx);
1016
1017
36
    if (p->flow == NULL) {
1018
0
        SCReturn;
1019
0
    }
1020
1021
82
    for (uint32_t i = 0; i < det_ctx->post_rule_work_queue.len; i++) {
1022
46
        const PostRuleMatchWorkQueueItem *w = &det_ctx->post_rule_work_queue.q[i];
1023
46
        if (w->sm_type != DETECT_FLOWBITS)
1024
0
            continue;
1025
1026
46
        PrefilterFlowbit lookup;
1027
46
        memset(&lookup, 0, sizeof(lookup));
1028
46
        lookup.id = w->value;
1029
1030
46
        PrefilterFlowbit *b = PFB_RB_FIND(&ctx->fb_tree, &lookup);
1031
46
        if (b == NULL) {
1032
0
            SCLogDebug("flowbit %u not in the tree", lookup.id);
1033
46
        } else {
1034
46
            SCLogDebug("flowbit %u found in the tree: %u. Adding %u sids", lookup.id, b->id,
1035
46
                    b->rule_id_cnt);
1036
46
            PrefilterAddSids(&det_ctx->pmq, b->rule_id, b->rule_id_cnt);
1037
#ifdef DEBUG
1038
            // SCLogDebug("b %u", b->rule_id_cnt);
1039
            for (uint32_t x = 0; x < b->rule_id_cnt; x++) {
1040
                Signature *s = det_ctx->de_ctx->sig_array[b->rule_id[x]];
1041
                SCLogDebug("flowbit %u -> sig %u (triggered by %u)", w->value, s->id,
1042
                        det_ctx->de_ctx->sig_array[w->id]->id);
1043
            }
1044
#endif
1045
46
        }
1046
46
    }
1047
36
}
1048
1049
16.8k
#define BLOCK_SIZE 8
1050
1051
static int AddBitAndSid(
1052
        struct PrefilterEngineFlowbits *ctx, const Signature *s, const uint32_t flowbit_id)
1053
23.0k
{
1054
23.0k
    PrefilterFlowbit x;
1055
23.0k
    memset(&x, 0, sizeof(x));
1056
23.0k
    x.id = flowbit_id;
1057
1058
23.0k
    PrefilterFlowbit *pfb = PFB_RB_FIND(&ctx->fb_tree, &x);
1059
23.0k
    if (pfb == NULL) {
1060
16.5k
        PrefilterFlowbit *add = SCCalloc(1, sizeof(*add));
1061
16.5k
        if (add == NULL)
1062
0
            return -1;
1063
1064
16.5k
        add->id = flowbit_id;
1065
16.5k
        add->rule_id = SCCalloc(1, BLOCK_SIZE * sizeof(uint32_t));
1066
16.5k
        if (add->rule_id == NULL) {
1067
0
            SCFree(add);
1068
0
            return -1;
1069
0
        }
1070
16.5k
        add->rule_id_size = BLOCK_SIZE;
1071
16.5k
        add->rule_id_cnt = 1;
1072
16.5k
        add->rule_id[0] = s->iid;
1073
1074
16.5k
        PrefilterFlowbit *res = PFB_RB_INSERT(&ctx->fb_tree, add);
1075
16.5k
        SCLogDebug("not found, so added (res %p)", res);
1076
16.5k
        if (res != NULL) {
1077
            // duplicate, shouldn't be possible after the FIND above
1078
0
            BUG_ON(1);
1079
0
            return -1;
1080
0
        }
1081
16.5k
    } else {
1082
6.51k
        SCLogDebug("found! pfb %p id %u", pfb, pfb->id);
1083
1084
6.51k
        if (pfb->rule_id_cnt < pfb->rule_id_size) {
1085
6.40k
            pfb->rule_id[pfb->rule_id_cnt++] = s->iid;
1086
6.40k
        } else {
1087
105
            uint32_t *ptr =
1088
105
                    SCRealloc(pfb->rule_id, (pfb->rule_id_size + BLOCK_SIZE) * sizeof(uint32_t));
1089
105
            if (ptr == NULL) {
1090
                // memory stays in the tree
1091
0
                return -1;
1092
0
            }
1093
105
            pfb->rule_id = ptr;
1094
105
            pfb->rule_id_size += BLOCK_SIZE;
1095
105
            pfb->rule_id[pfb->rule_id_cnt++] = s->iid;
1096
105
        }
1097
6.51k
    }
1098
23.0k
    return 0;
1099
23.0k
}
1100
1101
static int AddBitsAndSid(const DetectEngineCtx *de_ctx, struct PrefilterEngineFlowbits *ctx,
1102
        const DetectFlowbitsData *fb, const Signature *s)
1103
5.60k
{
1104
5.60k
    if (fb->or_list_size == 0) {
1105
4.06k
        if (AddBitAndSid(ctx, s, fb->idx) < 0) {
1106
0
            return -1;
1107
0
        }
1108
4.06k
    } else {
1109
20.5k
        for (uint8_t i = 0; i < fb->or_list_size; i++) {
1110
18.9k
            SCLogDebug("flowbit OR: bit %u", fb->or_list[i]);
1111
18.9k
            if (AddBitAndSid(ctx, s, fb->or_list[i]) < 0) {
1112
0
                return -1;
1113
0
            }
1114
18.9k
        }
1115
1.53k
    }
1116
5.60k
    return 0;
1117
5.60k
}
1118
1119
static uint32_t NextMultiple(const uint32_t v, const uint32_t m)
1120
3.14k
{
1121
3.14k
    return v + (m - v % m);
1122
3.14k
}
1123
1124
/** \internal
1125
 *  \brief adds sids for 'isset' prefilter flowbits
1126
 *  \retval int 1 if we added sid(s), 0 if we didn't, -1 on error */
1127
// TODO skip sids that aren't set by this sgh
1128
// TODO skip sids that doesn't have a isset in the same direction
1129
static int AddIssetSidsForBit(const DetectEngineCtx *de_ctx, const struct FBAnalyzer *fba,
1130
        const DetectFlowbitsData *fb, PrefilterFlowbit *add)
1131
216
{
1132
216
    int added = 0;
1133
407
    for (uint32_t i = 0; i < fba->array[fb->idx].isset_sids_idx; i++) {
1134
191
        const uint32_t sig_iid = fba->array[fb->idx].isset_sids[i];
1135
191
        const Signature *s = de_ctx->sig_array[sig_iid];
1136
191
        SCLogDebug("flowbit: %u => considering sid %u (iid:%u)", fb->idx, s->id, s->iid);
1137
1138
        /* Skip sids that aren't prefilter. These would just run all the time. */
1139
191
        if (s->init_data->prefilter_sm == NULL ||
1140
96
                s->init_data->prefilter_sm->type != DETECT_FLOWBITS) {
1141
#ifdef DEBUG
1142
            const char *name = s->init_data->prefilter_sm
1143
                                       ? sigmatch_table[s->init_data->prefilter_sm->type].name
1144
                                       : "none";
1145
            SCLogDebug("flowbit: %u => rejected sid %u (iid:%u). No prefilter or prefilter not "
1146
                       "flowbits (%p, %s, %d)",
1147
                    fb->idx, s->id, sig_iid, s->init_data->prefilter_sm, name,
1148
                    s->init_data->prefilter_sm ? s->init_data->prefilter_sm->type : -1);
1149
#endif
1150
95
            continue;
1151
95
        }
1152
1153
        /* only add sids that match our bit */
1154
96
        const DetectFlowbitsData *fs_fb =
1155
96
                (const DetectFlowbitsData *)s->init_data->prefilter_sm->ctx;
1156
96
        if (fs_fb->idx != fb->idx) {
1157
0
            SCLogDebug(
1158
0
                    "flowbit: %u => rejected sid %u (iid:%u). Sig prefilters on different bit %u",
1159
0
                    fb->idx, s->id, sig_iid, fs_fb->idx);
1160
0
            continue;
1161
0
        }
1162
1163
96
        bool dup = false;
1164
106
        for (uint32_t x = 0; x < add->rule_id_cnt; x++) {
1165
10
            if (add->rule_id[x] == sig_iid) {
1166
0
                dup = true;
1167
0
            }
1168
10
        }
1169
1170
96
        if (!dup) {
1171
96
            if (add->rule_id_cnt < add->rule_id_size) {
1172
96
                add->rule_id[add->rule_id_cnt++] = sig_iid;
1173
96
            } else {
1174
0
                uint32_t *ptr = SCRealloc(
1175
0
                        add->rule_id, (add->rule_id_size + BLOCK_SIZE) * sizeof(uint32_t));
1176
0
                if (ptr == NULL) {
1177
0
                    return -1;
1178
0
                }
1179
0
                add->rule_id = ptr;
1180
0
                add->rule_id_size += BLOCK_SIZE;
1181
0
                add->rule_id[add->rule_id_cnt++] = sig_iid;
1182
0
            }
1183
96
            added = 1;
1184
96
            SCLogDebug("flowbit: %u => accepted sid %u (iid:%u)", fb->idx, s->id, sig_iid);
1185
96
        }
1186
96
    }
1187
216
    return added;
1188
216
}
1189
1190
/* TODO shouldn't add sids for which Signature::num is < our num. Is this possible after sorting? */
1191
1192
/** \brief For set/toggle flowbits, build "set" post-rule-match engine
1193
 *
1194
 *  For set/toggle flowbits, a special post-rule-match engine is constructed
1195
 *  to update the running match array during rule matching.
1196
 */
1197
static int AddBitSetToggle(const DetectEngineCtx *de_ctx, struct FBAnalyzer *fba,
1198
        struct PrefilterEngineFlowbits *ctx, const DetectFlowbitsData *fb, const Signature *s)
1199
216
{
1200
216
    PrefilterFlowbit x;
1201
216
    memset(&x, 0, sizeof(x));
1202
216
    x.id = fb->idx;
1203
216
    PrefilterFlowbit *pfb = PFB_RB_FIND(&ctx->fb_tree, &x);
1204
216
    if (pfb == NULL) {
1205
216
        PrefilterFlowbit *add = SCCalloc(1, sizeof(*add));
1206
216
        if (add == NULL)
1207
0
            return -1;
1208
1209
216
        add->id = fb->idx;
1210
216
        add->rule_id_size = NextMultiple(fba->array[fb->idx].isset_sids_idx, BLOCK_SIZE);
1211
216
        add->rule_id = SCCalloc(1, add->rule_id_size * sizeof(uint32_t));
1212
216
        if (add->rule_id == NULL) {
1213
0
            SCFree(add);
1214
0
            return -1;
1215
0
        }
1216
1217
216
        if (AddIssetSidsForBit(de_ctx, fba, fb, add) != 1) {
1218
130
            SCLogDebug("no sids added");
1219
130
            SCFree(add->rule_id);
1220
130
            SCFree(add);
1221
130
            return 0;
1222
130
        }
1223
86
        PrefilterFlowbit *res = PFB_RB_INSERT(&ctx->fb_tree, add);
1224
86
        SCLogDebug("not found, so added (res %p)", res);
1225
86
        BUG_ON(res != NULL); // TODO if res != NULL we have a duplicate which should be impossible
1226
86
    } else {
1227
0
        SCLogDebug("found! pfb %p id %u", pfb, pfb->id);
1228
1229
0
        int r = AddIssetSidsForBit(de_ctx, fba, fb, pfb);
1230
0
        if (r < 0) {
1231
0
            return -1;
1232
0
        } else if (r == 0) {
1233
0
            SCLogDebug("no sids added");
1234
0
            return 0;
1235
0
        }
1236
0
    }
1237
86
    return 1;
1238
216
}
1239
1240
/** \brief build flowbit prefilter state(s)
1241
 *
1242
 *  Build "set" and "isset" states.
1243
 *
1244
 *  For each flowbit "isset" in the sgh, we need to check:
1245
 *  1. is it supported
1246
 *  2. is prefilter enabled
1247
 *  3. does it match in the same dir or only opposing dir
1248
 */
1249
static int PrefilterSetupFlowbits(DetectEngineCtx *de_ctx, SigGroupHead *sgh)
1250
299
{
1251
299
    if (sgh == NULL)
1252
0
        return 0;
1253
1254
299
    SCLogDebug("sgh %p: setting up prefilter", sgh);
1255
299
    struct PrefilterEngineFlowbits *isset_ctx = NULL;
1256
299
    struct PrefilterEngineFlowbits *set_ctx = NULL;
1257
1258
299
    struct FBAnalyzer fb_analysis = DetectFlowbitsAnalyzeForGroup(de_ctx, sgh);
1259
299
    if (fb_analysis.array == NULL)
1260
0
        goto error;
1261
1262
9.83k
    for (uint32_t i = 0; i < sgh->init->sig_cnt; i++) {
1263
9.53k
        Signature *s = sgh->init->match_array[i];
1264
9.53k
        if (s == NULL)
1265
0
            continue;
1266
1267
9.53k
        SCLogDebug("checking sid %u", s->id);
1268
1269
        /* first build the 'set' state */
1270
10.3k
        for (SigMatch *sm = s->init_data->smlists[DETECT_SM_LIST_POSTMATCH]; sm != NULL;
1271
9.53k
                sm = sm->next) {
1272
823
            if (sm->type != DETECT_FLOWBITS) {
1273
596
                SCLogDebug("skip non flowbits sm");
1274
596
                continue;
1275
596
            }
1276
1277
227
            DetectFlowbitsData *fb = (DetectFlowbitsData *)sm->ctx;
1278
227
            if (fb->cmd == DETECT_FLOWBITS_CMD_SET) {
1279
133
                SCLogDebug(
1280
133
                        "DETECT_SM_LIST_POSTMATCH: sid %u DETECT_FLOWBITS set %u", s->id, fb->idx);
1281
133
            } else if (fb->cmd == DETECT_FLOWBITS_CMD_TOGGLE) {
1282
94
                SCLogDebug("DETECT_SM_LIST_POSTMATCH: sid %u DETECT_FLOWBITS toggle %u", s->id,
1283
94
                        fb->idx);
1284
94
            } else {
1285
0
                SCLogDebug("unsupported flowbits setting");
1286
0
                continue;
1287
0
            }
1288
1289
227
            if (fb_analysis.array[fb->idx].isnotset_sids_idx ||
1290
216
                    fb_analysis.array[fb->idx].unset_sids_idx) {
1291
11
                SCLogDebug("flowbit %u not supported: unset in use", fb->idx);
1292
11
                continue;
1293
11
            }
1294
1295
216
            if (set_ctx == NULL) {
1296
79
                set_ctx = SCCalloc(1, sizeof(*set_ctx));
1297
79
                if (set_ctx == NULL)
1298
0
                    goto error;
1299
79
            }
1300
1301
216
            SCLogDebug("setting up sets/toggles for sid %u", s->id);
1302
216
            if (AddBitSetToggle(de_ctx, &fb_analysis, set_ctx, fb, s) == 1) {
1303
                // flag the set/toggle to trigger the post-rule match logic
1304
86
                SCLogDebug("set up sets/toggles for sid %u", s->id);
1305
86
                fb->post_rule_match_prefilter = true;
1306
86
            }
1307
1308
            // TODO don't add for sigs that don't have isset in this sgh. Reasoning:
1309
            // prefilter post match logic only makes sense in the same dir as otherwise
1310
            // the regular 'isset' logic can simply run with the regular prefilters
1311
            // before the rule loop
1312
216
        }
1313
1314
        /* next, build the 'isset' state */
1315
9.53k
        if (s->init_data->prefilter_sm == NULL ||
1316
9.29k
                s->init_data->prefilter_sm->type != DETECT_FLOWBITS) {
1317
9.29k
            SCLogDebug("no prefilter or prefilter not flowbits");
1318
9.29k
            continue;
1319
9.29k
        }
1320
1321
240
        const DetectFlowbitsData *fb = (DetectFlowbitsData *)s->init_data->prefilter_sm->ctx;
1322
240
        if (fb_analysis.array[fb->idx].isnotset_sids_idx ||
1323
240
                fb_analysis.array[fb->idx].unset_sids_idx) {
1324
0
            SCLogDebug("flowbit %u not supported: toggle or unset in use", fb->idx);
1325
0
            s->init_data->prefilter_sm = NULL;
1326
0
            s->flags &= ~SIG_FLAG_PREFILTER;
1327
0
            continue;
1328
0
        }
1329
1330
240
        SCLogDebug("isset: adding sid %u, flowbit %u", s->id, fb->idx);
1331
1332
240
        if (isset_ctx == NULL) {
1333
113
            isset_ctx = SCCalloc(1, sizeof(*isset_ctx));
1334
113
            if (isset_ctx == NULL)
1335
0
                goto error;
1336
113
        }
1337
240
        if (AddBitsAndSid(de_ctx, isset_ctx, fb, s) < 0) {
1338
0
            goto error;
1339
0
        }
1340
240
    }
1341
1342
    /* finally, register the states with their engines */
1343
299
    static const char *g_prefilter_flowbits_isset = "flowbits:isset";
1344
299
    if (isset_ctx != NULL) {
1345
113
        enum SignatureHookPkt hook = SIGNATURE_HOOK_PKT_NOT_SET; // TODO review
1346
113
        PrefilterAppendEngine(de_ctx, sgh, PrefilterFlowbitMatch, SIG_MASK_REQUIRE_FLOW, hook,
1347
113
                isset_ctx, PrefilterFlowbitFree, g_prefilter_flowbits_isset);
1348
113
        SCLogDebug("isset: added prefilter engine");
1349
1350
113
        if (set_ctx != NULL && !RB_EMPTY(&set_ctx->fb_tree)) {
1351
48
            static const char *g_prefilter_flowbits_set = "flowbits:set";
1352
48
            PrefilterAppendPostRuleEngine(de_ctx, sgh, PrefilterFlowbitPostRuleMatch, set_ctx,
1353
48
                    PrefilterFlowbitFree, g_prefilter_flowbits_set);
1354
48
            SCLogDebug("set/toggle: added prefilter engine");
1355
65
        } else {
1356
65
            if (set_ctx) {
1357
4
                PrefilterFlowbitFree(set_ctx);
1358
4
            }
1359
65
            SCLogDebug("set/toggle: NO prefilter engine added");
1360
65
        }
1361
186
    } else if (set_ctx != NULL) {
1362
27
        PrefilterFlowbitFree(set_ctx);
1363
27
    }
1364
299
    FBAnalyzerFree(&fb_analysis);
1365
299
    return 0;
1366
1367
0
error:
1368
0
    if (set_ctx) {
1369
0
        PrefilterFlowbitFree(set_ctx);
1370
0
    }
1371
0
    if (isset_ctx) {
1372
0
        PrefilterFlowbitFree(isset_ctx);
1373
0
    }
1374
0
    FBAnalyzerFree(&fb_analysis);
1375
0
    return -1;
1376
299
}
1377
1378
#ifdef UNITTESTS
1379
1380
static int FlowBitsTestParse01(void)
1381
{
1382
    char command[16] = "", name[16] = "";
1383
1384
    /* Single argument version. */
1385
    FAIL_IF(!DetectFlowbitParse("noalert", command, sizeof(command), name,
1386
            sizeof(name)));
1387
    FAIL_IF(strcmp(command, "noalert") != 0);
1388
1389
    /* No leading or trailing spaces. */
1390
    FAIL_IF(!DetectFlowbitParse("set,flowbit", command, sizeof(command), name,
1391
            sizeof(name)));
1392
    FAIL_IF(strcmp(command, "set") != 0);
1393
    FAIL_IF(strcmp(name, "flowbit") != 0);
1394
1395
    /* Leading space. */
1396
    FAIL_IF(!DetectFlowbitParse("set, flowbit", command, sizeof(command), name,
1397
            sizeof(name)));
1398
    FAIL_IF(strcmp(command, "set") != 0);
1399
    FAIL_IF(strcmp(name, "flowbit") != 0);
1400
1401
    /* Trailing space. */
1402
    FAIL_IF(!DetectFlowbitParse("set,flowbit ", command, sizeof(command), name,
1403
            sizeof(name)));
1404
    FAIL_IF(strcmp(command, "set") != 0);
1405
    FAIL_IF(strcmp(name, "flowbit") != 0);
1406
1407
    /* Leading and trailing space. */
1408
    FAIL_IF(!DetectFlowbitParse("set, flowbit ", command, sizeof(command), name,
1409
            sizeof(name)));
1410
    FAIL_IF(strcmp(command, "set") != 0);
1411
    FAIL_IF(strcmp(name, "flowbit") != 0);
1412
1413
    /* Spaces are not allowed in the name. */
1414
    FAIL_IF(DetectFlowbitParse("set,namewith space", command, sizeof(command),
1415
            name, sizeof(name)));
1416
1417
    PASS;
1418
}
1419
1420
/**
1421
 * \test FlowBitsTestSig01 is a test for a valid noalert flowbits option
1422
 *
1423
 *  \retval 1 on success
1424
 *  \retval 0 on failure
1425
 */
1426
1427
static int FlowBitsTestSig01(void)
1428
{
1429
    Signature *s = NULL;
1430
    DetectEngineCtx *de_ctx = NULL;
1431
1432
    de_ctx = DetectEngineCtxInit();
1433
    FAIL_IF_NULL(de_ctx);
1434
1435
    de_ctx->flags |= DE_QUIET;
1436
1437
    s = de_ctx->sig_list = SigInit(de_ctx,"alert ip any any -> any any (msg:\"Noalert\"; flowbits:noalert,wrongusage; content:\"GET \"; sid:1;)");
1438
    FAIL_IF_NOT_NULL(s);
1439
1440
    SigGroupBuild(de_ctx);
1441
    DetectEngineCtxFree(de_ctx);
1442
    PASS;
1443
}
1444
1445
/**
1446
 * \test FlowBitsTestSig02 is a test for a valid isset,set,isnotset,unset,toggle flowbits options
1447
 *
1448
 *  \retval 1 on success
1449
 *  \retval 0 on failure
1450
 */
1451
1452
static int FlowBitsTestSig02(void)
1453
{
1454
    Signature *s = NULL;
1455
    ThreadVars th_v;
1456
    DetectEngineCtx *de_ctx = NULL;
1457
1458
    memset(&th_v, 0, sizeof(th_v));
1459
1460
    de_ctx = DetectEngineCtxInit();
1461
    FAIL_IF_NULL(de_ctx);
1462
1463
    de_ctx->flags |= DE_QUIET;
1464
1465
    s = de_ctx->sig_list = SigInit(de_ctx,"alert ip any any -> any any (msg:\"isset rule need an option\"; flowbits:isset; content:\"GET \"; sid:1;)");
1466
    FAIL_IF_NOT_NULL(s);
1467
1468
    s = de_ctx->sig_list = SigInit(de_ctx,"alert ip any any -> any any (msg:\"isnotset rule need an option\"; flowbits:isnotset; content:\"GET \"; sid:2;)");
1469
    FAIL_IF_NOT_NULL(s);
1470
1471
    s = de_ctx->sig_list = SigInit(de_ctx,"alert ip any any -> any any (msg:\"set rule need an option\"; flowbits:set; content:\"GET \"; sid:3;)");
1472
    FAIL_IF_NOT_NULL(s);
1473
1474
    s = de_ctx->sig_list = SigInit(de_ctx,"alert ip any any -> any any (msg:\"unset rule need an option\"; flowbits:unset; content:\"GET \"; sid:4;)");
1475
    FAIL_IF_NOT_NULL(s);
1476
1477
    s = de_ctx->sig_list = SigInit(de_ctx,"alert ip any any -> any any (msg:\"toggle rule need an option\"; flowbits:toggle; content:\"GET \"; sid:5;)");
1478
    FAIL_IF_NOT_NULL(s);
1479
1480
    s = de_ctx->sig_list = SigInit(de_ctx,"alert ip any any -> any any (msg:\"!set is not an option\"; flowbits:!set,myerr; content:\"GET \"; sid:6;)");
1481
    FAIL_IF_NOT_NULL(s);
1482
1483
    SigGroupBuild(de_ctx);
1484
    DetectEngineCtxFree(de_ctx);
1485
1486
    PASS;
1487
}
1488
1489
/**
1490
 * \test FlowBitsTestSig03 is a test for a invalid flowbits option
1491
 *
1492
 *  \retval 1 on success
1493
 *  \retval 0 on failure
1494
 */
1495
1496
static int FlowBitsTestSig03(void)
1497
{
1498
    Signature *s = NULL;
1499
    DetectEngineCtx *de_ctx = NULL;
1500
1501
    de_ctx = DetectEngineCtxInit();
1502
    FAIL_IF_NULL(de_ctx);
1503
1504
    de_ctx->flags |= DE_QUIET;
1505
1506
    s = de_ctx->sig_list = SigInit(de_ctx,"alert ip any any -> any any (msg:\"Unknown cmd\"; flowbits:wrongcmd; content:\"GET \"; sid:1;)");
1507
    FAIL_IF_NOT_NULL(s);
1508
1509
    SigGroupBuild(de_ctx);
1510
    DetectEngineCtxFree(de_ctx);
1511
    PASS;
1512
}
1513
1514
/**
1515
 * \test FlowBitsTestSig04 is a test check idx value
1516
 *
1517
 *  \retval 1 on success
1518
 *  \retval 0 on failure
1519
 */
1520
1521
static int FlowBitsTestSig04(void)
1522
{
1523
    Signature *s = NULL;
1524
    DetectEngineCtx *de_ctx = NULL;
1525
    int idx = 0;
1526
    de_ctx = DetectEngineCtxInit();
1527
    FAIL_IF_NULL(de_ctx);
1528
1529
    de_ctx->flags |= DE_QUIET;
1530
1531
    s = de_ctx->sig_list = SigInit(de_ctx,"alert ip any any -> any any (msg:\"isset option\"; flowbits:isset,fbt; content:\"GET \"; sid:1;)");
1532
    FAIL_IF_NULL(s);
1533
1534
    idx = VarNameStoreRegister("fbt", VAR_TYPE_FLOW_BIT);
1535
    FAIL_IF(idx == 0);
1536
1537
    SigGroupBuild(de_ctx);
1538
    DetectEngineCtxFree(de_ctx);
1539
    PASS;
1540
}
1541
1542
/**
1543
 * \test FlowBitsTestSig05 is a test check noalert flag
1544
 *
1545
 *  \retval 1 on success
1546
 *  \retval 0 on failure
1547
 */
1548
1549
static int FlowBitsTestSig05(void)
1550
{
1551
    Signature *s = NULL;
1552
    DetectEngineCtx *de_ctx = NULL;
1553
1554
    de_ctx = DetectEngineCtxInit();
1555
    FAIL_IF_NULL(de_ctx);
1556
1557
    de_ctx->flags |= DE_QUIET;
1558
1559
    s = de_ctx->sig_list = SigInit(de_ctx,"alert ip any any -> any any (msg:\"Noalert\"; flowbits:noalert; content:\"GET \"; sid:1;)");
1560
    FAIL_IF_NULL(s);
1561
    FAIL_IF((s->action & ACTION_ALERT) != 0);
1562
1563
    SigGroupBuild(de_ctx);
1564
    DetectEngineCtxFree(de_ctx);
1565
    PASS;
1566
}
1567
1568
/**
1569
 * \test FlowBitsTestSig06 is a test set flowbits option
1570
 *
1571
 *  \retval 1 on success
1572
 *  \retval 0 on failure
1573
 */
1574
1575
static int FlowBitsTestSig06(void)
1576
{
1577
    uint8_t *buf = (uint8_t *)
1578
                    "GET /one/ HTTP/1.1\r\n"
1579
                    "Host: one.example.org\r\n"
1580
                    "\r\n";
1581
    uint16_t buflen = strlen((char *)buf);
1582
    Packet *p = PacketGetFromAlloc();
1583
    FAIL_IF_NULL(p);
1584
    Signature *s = NULL;
1585
    ThreadVars th_v;
1586
    DetectEngineThreadCtx *det_ctx = NULL;
1587
    DetectEngineCtx *de_ctx = NULL;
1588
    Flow f;
1589
    GenericVar flowvar, *gv = NULL;
1590
    int result = 0;
1591
    uint32_t idx = 0;
1592
1593
    memset(&th_v, 0, sizeof(th_v));
1594
    memset(&f, 0, sizeof(Flow));
1595
    memset(&flowvar, 0, sizeof(GenericVar));
1596
1597
    FLOW_INITIALIZE(&f);
1598
    p->flow = &f;
1599
    p->flow->flowvar = &flowvar;
1600
1601
    p->src.family = AF_INET;
1602
    p->dst.family = AF_INET;
1603
    p->payload = buf;
1604
    p->payload_len = buflen;
1605
    p->proto = IPPROTO_TCP;
1606
    p->flags |= PKT_HAS_FLOW;
1607
    p->flowflags |= (FLOW_PKT_TOSERVER | FLOW_PKT_TOSERVER_FIRST);
1608
1609
    de_ctx = DetectEngineCtxInit();
1610
    FAIL_IF_NULL(de_ctx);
1611
1612
    de_ctx->flags |= DE_QUIET;
1613
1614
    s = de_ctx->sig_list = SigInit(de_ctx,"alert ip any any -> any any (msg:\"Flowbit set\"; flowbits:set,myflow; sid:10;)");
1615
    FAIL_IF_NULL(s);
1616
1617
    idx = VarNameStoreRegister("myflow", VAR_TYPE_FLOW_BIT);
1618
    SigGroupBuild(de_ctx);
1619
    DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
1620
1621
    SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
1622
1623
    gv = p->flow->flowvar;
1624
    FAIL_IF_NULL(gv);
1625
    for ( ; gv != NULL; gv = gv->next) {
1626
        if (gv->type == DETECT_FLOWBITS && gv->idx == idx) {
1627
                result = 1;
1628
        }
1629
    }
1630
    FAIL_IF_NOT(result);
1631
1632
    DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
1633
    DetectEngineCtxFree(de_ctx);
1634
1635
    FLOW_DESTROY(&f);
1636
1637
    SCFree(p);
1638
    PASS;
1639
}
1640
1641
/**
1642
 * \test FlowBitsTestSig07 is a test unset flowbits option
1643
 *
1644
 *  \retval 1 on success
1645
 *  \retval 0 on failure
1646
 */
1647
1648
static int FlowBitsTestSig07(void)
1649
{
1650
    uint8_t *buf = (uint8_t *)
1651
                    "GET /one/ HTTP/1.1\r\n"
1652
                    "Host: one.example.org\r\n"
1653
                    "\r\n";
1654
    uint16_t buflen = strlen((char *)buf);
1655
    Packet *p = PacketGetFromAlloc();
1656
    FAIL_IF_NULL(p);
1657
    Signature *s = NULL;
1658
    ThreadVars th_v;
1659
    DetectEngineThreadCtx *det_ctx = NULL;
1660
    DetectEngineCtx *de_ctx = NULL;
1661
    Flow f;
1662
    GenericVar flowvar, *gv = NULL;
1663
    int result = 0;
1664
    uint32_t idx = 0;
1665
1666
    memset(&th_v, 0, sizeof(th_v));
1667
    memset(&f, 0, sizeof(Flow));
1668
    memset(&flowvar, 0, sizeof(GenericVar));
1669
1670
    FLOW_INITIALIZE(&f);
1671
    p->flow = &f;
1672
    p->flow->flowvar = &flowvar;
1673
1674
    p->src.family = AF_INET;
1675
    p->dst.family = AF_INET;
1676
    p->payload = buf;
1677
    p->payload_len = buflen;
1678
    p->proto = IPPROTO_TCP;
1679
1680
    de_ctx = DetectEngineCtxInit();
1681
    FAIL_IF_NULL(de_ctx);
1682
1683
    de_ctx->flags |= DE_QUIET;
1684
1685
    s = de_ctx->sig_list = SigInit(de_ctx,"alert ip any any -> any any (msg:\"Flowbit set\"; flowbits:set,myflow2; sid:10;)");
1686
    FAIL_IF_NULL(s);
1687
1688
    s = s->next = SigInit(de_ctx,"alert ip any any -> any any (msg:\"Flowbit unset\"; flowbits:unset,myflow2; sid:11;)");
1689
    FAIL_IF_NULL(s);
1690
1691
    idx = VarNameStoreRegister("myflow", VAR_TYPE_FLOW_BIT);
1692
    SigGroupBuild(de_ctx);
1693
    DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
1694
1695
    SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
1696
1697
    gv = p->flow->flowvar;
1698
    FAIL_IF_NULL(gv);
1699
1700
    for ( ; gv != NULL; gv = gv->next) {
1701
        if (gv->type == DETECT_FLOWBITS && gv->idx == idx) {
1702
                result = 1;
1703
        }
1704
    }
1705
    FAIL_IF(result);
1706
1707
    DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
1708
    DetectEngineCtxFree(de_ctx);
1709
1710
    FLOW_DESTROY(&f);
1711
1712
    SCFree(p);
1713
    PASS;
1714
}
1715
1716
/**
1717
 * \test FlowBitsTestSig08 is a test toggle flowbits option
1718
 *
1719
 *  \retval 1 on success
1720
 *  \retval 0 on failure
1721
 */
1722
1723
static int FlowBitsTestSig08(void)
1724
{
1725
    uint8_t *buf = (uint8_t *)
1726
                    "GET /one/ HTTP/1.1\r\n"
1727
                    "Host: one.example.org\r\n"
1728
                    "\r\n";
1729
    uint16_t buflen = strlen((char *)buf);
1730
    Packet *p = PacketGetFromAlloc();
1731
    if (unlikely(p == NULL))
1732
        return 0;
1733
    Signature *s = NULL;
1734
    ThreadVars th_v;
1735
    DetectEngineThreadCtx *det_ctx = NULL;
1736
    DetectEngineCtx *de_ctx = NULL;
1737
    Flow f;
1738
    GenericVar flowvar, *gv = NULL;
1739
    int result = 0;
1740
    uint32_t idx = 0;
1741
1742
    memset(&th_v, 0, sizeof(th_v));
1743
    memset(&f, 0, sizeof(Flow));
1744
    memset(&flowvar, 0, sizeof(GenericVar));
1745
1746
    FLOW_INITIALIZE(&f);
1747
    p->flow = &f;
1748
    p->flow->flowvar = &flowvar;
1749
1750
    p->src.family = AF_INET;
1751
    p->dst.family = AF_INET;
1752
    p->payload = buf;
1753
    p->payload_len = buflen;
1754
    p->proto = IPPROTO_TCP;
1755
1756
    de_ctx = DetectEngineCtxInit();
1757
    FAIL_IF_NULL(de_ctx);
1758
1759
    de_ctx->flags |= DE_QUIET;
1760
1761
    s = de_ctx->sig_list = SigInit(de_ctx,"alert ip any any -> any any (msg:\"Flowbit set\"; flowbits:set,myflow2; sid:10;)");
1762
    FAIL_IF_NULL(s);
1763
1764
    s = s->next  = SigInit(de_ctx,"alert ip any any -> any any (msg:\"Flowbit unset\"; flowbits:toggle,myflow2; sid:11;)");
1765
    FAIL_IF_NULL(s);
1766
1767
    idx = VarNameStoreRegister("myflow", VAR_TYPE_FLOW_BIT);
1768
    SigGroupBuild(de_ctx);
1769
    DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
1770
1771
    SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
1772
1773
    gv = p->flow->flowvar;
1774
    FAIL_IF_NULL(gv);
1775
1776
    for ( ; gv != NULL; gv = gv->next) {
1777
        if (gv->type == DETECT_FLOWBITS && gv->idx == idx) {
1778
                result = 1;
1779
        }
1780
    }
1781
    FAIL_IF(result);
1782
1783
    DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
1784
    DetectEngineCtxFree(de_ctx);
1785
1786
    FLOW_DESTROY(&f);
1787
1788
    SCFree(p);
1789
    PASS;
1790
}
1791
1792
/**
1793
 * \brief this function registers unit tests for FlowBits
1794
 */
1795
void FlowBitsRegisterTests(void)
1796
{
1797
    UtRegisterTest("FlowBitsTestParse01", FlowBitsTestParse01);
1798
    UtRegisterTest("FlowBitsTestSig01", FlowBitsTestSig01);
1799
    UtRegisterTest("FlowBitsTestSig02", FlowBitsTestSig02);
1800
    UtRegisterTest("FlowBitsTestSig03", FlowBitsTestSig03);
1801
    UtRegisterTest("FlowBitsTestSig04", FlowBitsTestSig04);
1802
    UtRegisterTest("FlowBitsTestSig05", FlowBitsTestSig05);
1803
    UtRegisterTest("FlowBitsTestSig06", FlowBitsTestSig06);
1804
    UtRegisterTest("FlowBitsTestSig07", FlowBitsTestSig07);
1805
    UtRegisterTest("FlowBitsTestSig08", FlowBitsTestSig08);
1806
}
1807
#endif /* UNITTESTS */