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

OISF / suricata / 23338889526

20 Mar 2026 10:29AM UTC coverage: 76.331% (-3.0%) from 79.315%
23338889526

Pull #15053

github

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

106 of 127 new or added lines in 8 files covered. (83.46%)

9913 existing lines in 468 files now uncovered.

255689 of 334972 relevant lines covered (76.33%)

4170649.82 hits per line

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

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

18
/**
19
 * \file
20
 *
21
 *  \author Victor Julien <victor@inliniac.net>
22
 *  \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-enum.h"
51
#include "util-var-name.h"
52
#include "util-unittest.h"
53
#include "util-debug.h"
54
#include "util-conf.h"
55

56
#define PARSE_REGEX         "^([a-z]+)(?:,\\s*(.*))?"
2,216✔
57
static DetectParseRegex parse_regex;
58

59
#define MAX_TOKENS 100
32✔
60

61
int DetectFlowbitMatch (DetectEngineThreadCtx *, Packet *,
62
        const Signature *, const SigMatchCtx *);
63
static int DetectFlowbitSetup (DetectEngineCtx *, Signature *, const char *);
64
static int FlowbitOrAddData(DetectEngineCtx *, DetectFlowbitsData *, char *);
65
void DetectFlowbitFree (DetectEngineCtx *, void *);
66
#ifdef UNITTESTS
67
void FlowBitsRegisterTests(void);
68
#endif
69
static bool PrefilterFlowbitIsPrefilterable(const Signature *s);
70
static int PrefilterSetupFlowbits(DetectEngineCtx *de_ctx, SigGroupHead *sgh);
71

72
void DetectFlowbitsRegister (void)
73
{
2,216✔
74
    sigmatch_table[DETECT_FLOWBITS].name = "flowbits";
2,216✔
75
    sigmatch_table[DETECT_FLOWBITS].desc = "operate on flow flag";
2,216✔
76
    sigmatch_table[DETECT_FLOWBITS].url = "/rules/flow-keywords.html#flowbits";
2,216✔
77
    sigmatch_table[DETECT_FLOWBITS].Match = DetectFlowbitMatch;
2,216✔
78
    sigmatch_table[DETECT_FLOWBITS].Setup = DetectFlowbitSetup;
2,216✔
79
    sigmatch_table[DETECT_FLOWBITS].Free  = DetectFlowbitFree;
2,216✔
80
#ifdef UNITTESTS
3✔
81
    sigmatch_table[DETECT_FLOWBITS].RegisterTests = FlowBitsRegisterTests;
3✔
82
#endif
3✔
83
    /* this is compatible to ip-only signatures */
84
    sigmatch_table[DETECT_FLOWBITS].flags |= (SIGMATCH_IPONLY_COMPAT | SIGMATCH_SUPPORT_FIREWALL);
2,216✔
85

86
    sigmatch_table[DETECT_FLOWBITS].SupportsPrefilter = PrefilterFlowbitIsPrefilterable;
2,216✔
87
    sigmatch_table[DETECT_FLOWBITS].SetupPrefilter = PrefilterSetupFlowbits;
2,216✔
88
    /* all but pre_flow */
89
    sigmatch_table[DETECT_FLOWBITS].tables =
2,216✔
90
            DETECT_TABLE_PACKET_PRE_STREAM_FLAG | DETECT_TABLE_PACKET_FILTER_FLAG |
2,216✔
91
            DETECT_TABLE_PACKET_TD_FLAG | DETECT_TABLE_APP_FILTER_FLAG | DETECT_TABLE_APP_TD_FLAG;
2,216✔
92
    DetectSetupParseRegexes(PARSE_REGEX, &parse_regex);
2,216✔
93
}
2,216✔
94

95
static bool DetectFlowbitIsPostmatch(uint8_t cmd)
96
{
9,293✔
97
    DEBUG_VALIDATE_BUG_ON(cmd >= DETECT_FLOWBITS_CMD_MAX);
9,293✔
98

99
    switch (cmd) {
9,293✔
100
        case DETECT_FLOWBITS_CMD_TOGGLE:
1,932✔
101
        case DETECT_FLOWBITS_CMD_SET:
3,660✔
102
        case DETECT_FLOWBITS_CMD_UNSET:
5,628✔
103
            return true;
5,628✔
104
    }
9,293✔
105
    return false;
3,665✔
106
}
9,293✔
107

108
SCEnumCharMap flowbit_cmds[] = {
109
    { "set", DETECT_FLOWBITS_CMD_SET },
110
    { "toggle", DETECT_FLOWBITS_CMD_TOGGLE },
111
    { "unset", DETECT_FLOWBITS_CMD_UNSET },
112
    { "isnotset", DETECT_FLOWBITS_CMD_ISNOTSET },
113
    { "isset", DETECT_FLOWBITS_CMD_ISSET },
114
};
115

116
static inline int DetectFlowbitValidateDo(
117
        Signature *s, uint8_t cmd, uint8_t cmd2, uint32_t idx, bool err)
118
{
9,293✔
119
    bool postmatch = DetectFlowbitIsPostmatch(cmd);
9,293✔
120
    SigMatch *list = postmatch ? s->init_data->smlists[DETECT_SM_LIST_POSTMATCH]
9,293✔
121
                               : s->init_data->smlists[DETECT_SM_LIST_MATCH];
9,293✔
122

123
    for (SigMatch *sm = list; sm != NULL; sm = sm->next) {
13,337✔
124
        if (sm->type != DETECT_FLOWBITS)
4,056✔
125
            continue;
3,508✔
126

127
        DetectFlowbitsData *fd = (DetectFlowbitsData *)sm->ctx;
548✔
128
        if ((fd->idx == idx) && (fd->cmd == cmd)) {
548✔
129
            if (err) {
12✔
130
                SCLogError("invalid flowbit command combination in the same signature: isset and "
2✔
131
                           "isnotset");
2✔
132
                return -1;
2✔
133
            }
2✔
134
            SCLogWarning(
10✔
135
                    "inconsequential flowbit command combination in the same signature: %s and %s",
10✔
136
                    SCMapEnumValueToName(cmd, flowbit_cmds),
10✔
137
                    SCMapEnumValueToName(cmd2, flowbit_cmds));
10✔
138
            return 0;
10✔
139
        }
12✔
140
    }
548✔
141

142
    /* no invalid or inconsequential command pair was found */
143
    return 1;
9,281✔
144
}
9,293✔
145

146
static int DetectFlowbitValidate(Signature *s, DetectFlowbitsData *fd)
147
{
3,684✔
148
    struct DetectFlowbitInvalidCmdMap_ {
3,684✔
149
        uint8_t cmd1;
3,684✔
150
        uint8_t cmd2;
3,684✔
151
        bool err; /* Error out if rule is unsatisfiable at runtime */
3,684✔
152
    };
3,684✔
153

154
    struct DetectFlowbitInvalidCmdMap_ icmds_map[] = {
3,684✔
155
        /* POSTMATCH, MATCH combinations */
156
        { DETECT_FLOWBITS_CMD_UNSET, DETECT_FLOWBITS_CMD_ISNOTSET, false },
3,684✔
157
        { DETECT_FLOWBITS_CMD_SET, DETECT_FLOWBITS_CMD_ISSET, false },
3,684✔
158
        /* POSTMATCH, POSTMATCH combinations */
159
        { DETECT_FLOWBITS_CMD_SET, DETECT_FLOWBITS_CMD_TOGGLE, false },
3,684✔
160
        { DETECT_FLOWBITS_CMD_UNSET, DETECT_FLOWBITS_CMD_TOGGLE, false },
3,684✔
161
        { DETECT_FLOWBITS_CMD_SET, DETECT_FLOWBITS_CMD_UNSET, false },
3,684✔
162
        /* MATCH, MATCH combinations */
163
        { DETECT_FLOWBITS_CMD_ISSET, DETECT_FLOWBITS_CMD_ISNOTSET, true },
3,684✔
164
    };
3,684✔
165

166
    int ret = 0;
3,684✔
167

168
    for (uint8_t i = 0; i < ARRAY_SIZE(icmds_map); i++) {
25,746✔
169
        if (fd->cmd == icmds_map[i].cmd1) {
22,074✔
170
            ret = DetectFlowbitValidateDo(
7,328✔
171
                    s, icmds_map[i].cmd2, icmds_map[i].cmd1, fd->idx, icmds_map[i].err);
7,328✔
172
            if (ret != 1) {
7,328✔
173
                return ret;
6✔
174
            }
6✔
175
        } else if (fd->cmd == icmds_map[i].cmd2) {
14,746✔
176
            ret = DetectFlowbitValidateDo(
1,965✔
177
                    s, icmds_map[i].cmd1, icmds_map[i].cmd2, fd->idx, icmds_map[i].err);
1,965✔
178
            if (ret != 1) {
1,965✔
179
                return ret;
6✔
180
            }
6✔
181
        }
1,965✔
182
    }
22,074✔
183

184
    return 0;
3,672✔
185
}
3,684✔
186

187
static int FlowbitOrAddData(DetectEngineCtx *de_ctx, DetectFlowbitsData *cd, char *arrptr)
188
{
14✔
189
    char *strarr[MAX_TOKENS];
14✔
190
    char *token;
14✔
191
    char *saveptr = NULL;
14✔
192
    uint8_t i = 0;
14✔
193

194
    while ((token = strtok_r(arrptr, "|", &saveptr))) {
46✔
195
        // Check for leading/trailing spaces in the token
196
        while(isspace((unsigned char)*token))
32✔
UNCOV
197
            token++;
×
198
        if (*token == 0)
32✔
UNCOV
199
            goto next;
×
200
        char *end = token + strlen(token) - 1;
32✔
201
        while(end > token && isspace((unsigned char)*end))
32✔
UNCOV
202
            *(end--) = '\0';
×
203

204
        // Check for spaces in between the flowbit names
205
        if (strchr(token, ' ') != NULL) {
32✔
UNCOV
206
            SCLogError("Spaces are not allowed in flowbit names.");
×
UNCOV
207
            return -1;
×
UNCOV
208
        }
×
209

210
        if (i == MAX_TOKENS) {
32✔
UNCOV
211
            SCLogError("Number of flowbits exceeds "
×
UNCOV
212
                       "maximum allowed: %d.",
×
UNCOV
213
                    MAX_TOKENS);
×
UNCOV
214
            return -1;
×
UNCOV
215
        }
×
216
        strarr[i++] = token;
32✔
217
    next:
32✔
218
        arrptr = NULL;
32✔
219
    }
32✔
220
    if (i == 0) {
14✔
UNCOV
221
        SCLogError("No valid flowbits specified");
×
UNCOV
222
        return -1;
×
UNCOV
223
    }
×
224

225
    cd->or_list_size = i;
14✔
226
    cd->or_list = SCCalloc(cd->or_list_size, sizeof(uint32_t));
14✔
227
    if (unlikely(cd->or_list == NULL))
14✔
228
        return -1;
×
229
    for (uint8_t j = 0; j < cd->or_list_size ; j++) {
46✔
230
        uint32_t varname_id = VarNameStoreRegister(strarr[j], VAR_TYPE_FLOW_BIT);
32✔
231
        if (unlikely(varname_id == 0))
32✔
232
            return -1;
×
233
        cd->or_list[j] = varname_id;
32✔
234
        de_ctx->max_fb_id = MAX(cd->or_list[j], de_ctx->max_fb_id);
32✔
235
    }
32✔
236

237
    return 1;
14✔
238
}
14✔
239

240
static int DetectFlowbitMatchToggle (Packet *p, const DetectFlowbitsData *fd)
241
{
7✔
242
    if (p->flow == NULL)
7✔
UNCOV
243
        return -1;
×
244

245
    return FlowBitToggle(p->flow, fd->idx);
7✔
246
}
7✔
247

248
static int DetectFlowbitMatchUnset (Packet *p, const DetectFlowbitsData *fd)
249
{
5✔
250
    if (p->flow == NULL)
5✔
UNCOV
251
        return 0;
×
252

253
    FlowBitUnset(p->flow,fd->idx);
5✔
254

255
    return 1;
5✔
256
}
5✔
257

258
static int DetectFlowbitMatchSet (Packet *p, const DetectFlowbitsData *fd)
259
{
1,013✔
260
    if (p->flow == NULL)
1,013✔
UNCOV
261
        return -1;
×
262

263
    int r = FlowBitSet(p->flow, fd->idx);
1,013✔
264
    SCLogDebug("set %u", fd->idx);
1,013✔
265
    return r;
1,013✔
266
}
1,013✔
267

268
static int DetectFlowbitMatchIsset (Packet *p, const DetectFlowbitsData *fd)
269
{
2,592✔
270
    if (p->flow == NULL)
2,592✔
271
        return 0;
×
272
    if (fd->or_list_size > 0) {
2,592✔
273
        for (uint8_t i = 0; i < fd->or_list_size; i++) {
11✔
274
            if (FlowBitIsset(p->flow, fd->or_list[i]) == 1)
9✔
275
                return 1;
2✔
276
        }
9✔
277
        return 0;
2✔
278
    }
4✔
279

280
    return FlowBitIsset(p->flow,fd->idx);
2,588✔
281
}
2,592✔
282

283
static int DetectFlowbitMatchIsnotset (Packet *p, const DetectFlowbitsData *fd)
284
{
7,938✔
285
    if (p->flow == NULL)
7,938✔
286
        return 0;
×
287
    if (fd->or_list_size > 0) {
7,938✔
288
        for (uint8_t i = 0; i < fd->or_list_size; i++) {
3✔
289
            if (FlowBitIsnotset(p->flow, fd->or_list[i]) == 1)
2✔
UNCOV
290
                return 1;
×
291
        }
2✔
292
        return 0;
1✔
293
    }
1✔
294
    return FlowBitIsnotset(p->flow,fd->idx);
7,937✔
295
}
7,938✔
296

297
/*
298
 * returns 0: no match (or error)
299
 *         1: match
300
 */
301

302
int DetectFlowbitMatch (DetectEngineThreadCtx *det_ctx, Packet *p,
303
        const Signature *s, const SigMatchCtx *ctx)
304
{
11,555✔
305
    const DetectFlowbitsData *fd = (const DetectFlowbitsData *)ctx;
11,555✔
306
    if (fd == NULL)
11,555✔
307
        return 0;
×
308

309
    switch (fd->cmd) {
11,555✔
310
        case DETECT_FLOWBITS_CMD_ISSET:
2,592✔
311
            return DetectFlowbitMatchIsset(p,fd);
2,592✔
312
        case DETECT_FLOWBITS_CMD_ISNOTSET:
7,938✔
313
            return DetectFlowbitMatchIsnotset(p,fd);
7,938✔
314
        case DETECT_FLOWBITS_CMD_SET: {
1,013✔
315
            int r = DetectFlowbitMatchSet(p, fd);
1,013✔
316
            /* only on a new "set" invoke the prefilter */
317
            if (r == 1 && fd->post_rule_match_prefilter) {
1,013✔
318
                SCLogDebug("flowbit set, appending to work queue");
5✔
319
                PostRuleMatchWorkQueueAppend(det_ctx, s, DETECT_FLOWBITS, fd->idx);
5✔
320
            }
5✔
321
            return (r != -1);
1,013✔
322
        }
×
323
        case DETECT_FLOWBITS_CMD_UNSET:
5✔
324
            return DetectFlowbitMatchUnset(p,fd);
5✔
325
        case DETECT_FLOWBITS_CMD_TOGGLE: {
7✔
326
            int r = DetectFlowbitMatchToggle(p, fd);
7✔
327
            if (r == 1 && fd->post_rule_match_prefilter) {
7✔
328
                SCLogDebug("flowbit set (by toggle), appending to work queue");
4✔
329
                PostRuleMatchWorkQueueAppend(det_ctx, s, DETECT_FLOWBITS, fd->idx);
4✔
330
            }
4✔
331
            return (r != -1);
7✔
332
        }
×
333
        default:
×
334
            SCLogError("unknown cmd %" PRIu32 "", fd->cmd);
×
335
            return 0;
×
336
    }
11,555✔
337

338
    return 0;
×
339
}
11,555✔
340

341
static int DetectFlowbitParse(const char *str, char *cmd, int cmd_len, char *name,
342
    int name_len)
343
{
4,508✔
344
    int rc;
4,508✔
345
    size_t pcre2len;
4,508✔
346
    pcre2_match_data *match = NULL;
4,508✔
347

348
    int count = DetectParsePcreExec(&parse_regex, &match, str, 0, 0);
4,508✔
349
    if (count != 2 && count != 3) {
4,508✔
350
        SCLogError("\"%s\" is not a valid setting for flowbits.", str);
1✔
351
        goto error;
1✔
352
    }
1✔
353

354
    pcre2len = cmd_len;
4,507✔
355
    rc = pcre2_substring_copy_bynumber(match, 1, (PCRE2_UCHAR8 *)cmd, &pcre2len);
4,507✔
356
    if (rc < 0) {
4,507✔
UNCOV
357
        SCLogError("pcre2_substring_copy_bynumber failed");
×
UNCOV
358
        goto error;
×
UNCOV
359
    }
×
360

361
    if (count == 3) {
4,507✔
362
        pcre2len = name_len;
3,690✔
363
        rc = pcre2_substring_copy_bynumber(match, 2, (PCRE2_UCHAR8 *)name, &pcre2len);
3,690✔
364
        if (rc < 0) {
3,690✔
UNCOV
365
            SCLogError("pcre2_substring_copy_bynumber failed");
×
UNCOV
366
            goto error;
×
UNCOV
367
        }
×
368

369
        /* Trim trailing whitespace. */
370
        while (strlen(name) > 0 && isblank(name[strlen(name) - 1])) {
3,692✔
371
            name[strlen(name) - 1] = '\0';
2✔
372
        }
2✔
373

374
        if (strchr(name, '|') == NULL) {
3,690✔
375
            /* Validate name, spaces are not allowed. */
376
            for (size_t i = 0; i < strlen(name); i++) {
65,814✔
377
                if (isblank(name[i])) {
62,139✔
378
                    SCLogError("spaces not allowed in flowbit names");
1✔
379
                    goto error;
1✔
380
                }
1✔
381
            }
62,139✔
382
        }
3,676✔
383
    }
3,690✔
384

385
    pcre2_match_data_free(match);
4,507✔
386
    return 1;
4,506✔
387

388
error:
2✔
389
    if (match) {
2✔
390
        pcre2_match_data_free(match);
2✔
391
    }
2✔
392
    return 0;
2✔
393
}
4,507✔
394

395
int DetectFlowbitSetup (DetectEngineCtx *de_ctx, Signature *s, const char *rawstr)
396
{
4,502✔
397
    DetectFlowbitsData *cd = NULL;
4,502✔
398
    uint8_t fb_cmd = 0;
4,502✔
399
    char fb_cmd_str[16] = "", fb_name[256] = "";
4,502✔
400

401
    if (!DetectFlowbitParse(rawstr, fb_cmd_str, sizeof(fb_cmd_str), fb_name,
4,502✔
402
            sizeof(fb_name))) {
4,502✔
403
        return -1;
1✔
404
    }
1✔
405

406
    if (strcmp(fb_cmd_str,"noalert") == 0) {
4,501✔
407
        if (strlen(fb_name) != 0)
810✔
408
            goto error;
1✔
409
        s->action &= ~ACTION_ALERT;
809✔
410
        return 0;
809✔
411
    } else if (strcmp(fb_cmd_str,"isset") == 0) {
3,691✔
412
        fb_cmd = DETECT_FLOWBITS_CMD_ISSET;
1,623✔
413
    } else if (strcmp(fb_cmd_str,"isnotset") == 0) {
2,068✔
414
        fb_cmd = DETECT_FLOWBITS_CMD_ISNOTSET;
112✔
415
    } else if (strcmp(fb_cmd_str,"set") == 0) {
1,956✔
416
        fb_cmd = DETECT_FLOWBITS_CMD_SET;
1,844✔
417
    } else if (strcmp(fb_cmd_str,"unset") == 0) {
1,844✔
418
        fb_cmd = DETECT_FLOWBITS_CMD_UNSET;
93✔
419
    } else if (strcmp(fb_cmd_str,"toggle") == 0) {
93✔
420
        fb_cmd = DETECT_FLOWBITS_CMD_TOGGLE;
18✔
421
    } else {
18✔
422
        SCLogError("ERROR: flowbits action \"%s\" is not supported.", fb_cmd_str);
1✔
423
        goto error;
1✔
424
    }
1✔
425

426
    switch (fb_cmd) {
3,690✔
427
        case DETECT_FLOWBITS_CMD_ISNOTSET:
112✔
428
        case DETECT_FLOWBITS_CMD_ISSET:
1,735✔
429
        case DETECT_FLOWBITS_CMD_SET:
3,579✔
430
        case DETECT_FLOWBITS_CMD_UNSET:
3,672✔
431
        case DETECT_FLOWBITS_CMD_TOGGLE:
3,690✔
432
        default:
3,690✔
433
            if (strlen(fb_name) == 0)
3,690✔
434
                goto error;
6✔
435
            break;
3,684✔
436
    }
3,690✔
437

438
    cd = SCCalloc(1, sizeof(DetectFlowbitsData));
3,684✔
439
    if (unlikely(cd == NULL))
3,684✔
440
        goto error;
×
441
    if (strchr(fb_name, '|') != NULL) {
3,684✔
442
        int retval = FlowbitOrAddData(de_ctx, cd, fb_name);
14✔
443
        if (retval == -1) {
14✔
UNCOV
444
            goto error;
×
UNCOV
445
        }
×
446
        cd->cmd = fb_cmd;
14✔
447
    } else {
3,670✔
448
        uint32_t varname_id = VarNameStoreRegister(fb_name, VAR_TYPE_FLOW_BIT);
3,670✔
449
        if (unlikely(varname_id == 0))
3,670✔
450
            goto error;
×
451
        cd->idx = varname_id;
3,670✔
452
        de_ctx->max_fb_id = MAX(cd->idx, de_ctx->max_fb_id);
3,670✔
453
        cd->cmd = fb_cmd;
3,670✔
454
        cd->or_list_size = 0;
3,670✔
455
        cd->or_list = NULL;
3,670✔
456
        SCLogDebug("idx %" PRIu32 ", cmd %s, name %s",
3,670✔
457
            cd->idx, fb_cmd_str, strlen(fb_name) ? fb_name : "(none)");
3,670✔
458
    }
3,670✔
459

460
    if (DetectFlowbitValidate(s, cd) != 0) {
3,684✔
461
        goto error;
2✔
462
    }
2✔
463

464
    /* Okay so far so good, lets get this into a SigMatch
465
     * and put it in the Signature. */
466

467
    switch (fb_cmd) {
3,682✔
468
        /* noalert can't happen here */
469
        case DETECT_FLOWBITS_CMD_ISNOTSET:
110✔
470
        case DETECT_FLOWBITS_CMD_ISSET:
1,731✔
471
            /* checks, so packet list */
472
            if (SCSigMatchAppendSMToList(de_ctx, s, DETECT_FLOWBITS, (SigMatchCtx *)cd,
1,731✔
473
                        DETECT_SM_LIST_MATCH) == NULL) {
1,731✔
474
                goto error;
×
475
            }
×
476
            break;
1,731✔
477

478
        case DETECT_FLOWBITS_CMD_SET:
1,843✔
479
        case DETECT_FLOWBITS_CMD_UNSET:
1,934✔
480
        case DETECT_FLOWBITS_CMD_TOGGLE:
1,951✔
481
            /* modifiers, only run when entire sig has matched */
482
            if (SCSigMatchAppendSMToList(de_ctx, s, DETECT_FLOWBITS, (SigMatchCtx *)cd,
1,951✔
483
                        DETECT_SM_LIST_POSTMATCH) == NULL) {
1,951✔
484
                goto error;
×
485
            }
×
486
            break;
1,951✔
487

488
        // suppress coverity warning as scan-build-7 warns w/o this.
489
        // coverity[deadcode : FALSE]
490
        default:
1,951✔
491
            goto error;
×
492
    }
3,682✔
493

494
    return 0;
3,682✔
495

496
error:
10✔
497
    if (cd != NULL)
10✔
498
        DetectFlowbitFree(de_ctx, cd);
2✔
499
    return -1;
10✔
500
}
3,682✔
501

502
void DetectFlowbitFree (DetectEngineCtx *de_ctx, void *ptr)
503
{
3,684✔
504
    DetectFlowbitsData *fd = (DetectFlowbitsData *)ptr;
3,684✔
505
    if (fd == NULL)
3,684✔
506
        return;
×
507
    VarNameStoreUnregister(fd->idx, VAR_TYPE_FLOW_BIT);
3,684✔
508
    if (fd->or_list != NULL) {
3,684✔
509
        for (uint8_t i = 0; i < fd->or_list_size; i++) {
46✔
510
            VarNameStoreUnregister(fd->or_list[i], VAR_TYPE_FLOW_BIT);
32✔
511
        }
32✔
512
        SCFree(fd->or_list);
14✔
513
    }
14✔
514
    SCFree(fd);
3,684✔
515
}
3,684✔
516

517
struct FBAnalyzer {
518
    struct FBAnalyze *array;
519
    uint32_t array_size;
520
};
521

522
struct FBAnalyze {
523
    uint16_t cnts[DETECT_FLOWBITS_CMD_MAX];
524
    uint16_t state_cnts[DETECT_FLOWBITS_CMD_MAX];
525

526
    uint32_t *set_sids;
527
    uint32_t set_sids_idx;
528
    uint32_t set_sids_size;
529

530
    uint32_t *isset_sids;
531
    uint32_t isset_sids_idx;
532
    uint32_t isset_sids_size;
533

534
    uint32_t *isnotset_sids;
535
    uint32_t isnotset_sids_idx;
536
    uint32_t isnotset_sids_size;
537

538
    uint32_t *unset_sids;
539
    uint32_t unset_sids_idx;
540
    uint32_t unset_sids_size;
541

542
    uint32_t *toggle_sids;
543
    uint32_t toggle_sids_idx;
544
    uint32_t toggle_sids_size;
545
};
546

547
extern bool rule_engine_analysis_set;
548
static void DetectFlowbitsAnalyzeDump(const DetectEngineCtx *de_ctx,
549
        struct FBAnalyze *array, uint32_t elements);
550

551
static void FBAnalyzerArrayFree(struct FBAnalyze *array, const uint32_t array_size)
552
{
109✔
553
    if (array) {
109✔
554
        for (uint32_t i = 0; i < array_size; i++) {
1,298✔
555
            SCFree(array[i].set_sids);
1,189✔
556
            SCFree(array[i].unset_sids);
1,189✔
557
            SCFree(array[i].isset_sids);
1,189✔
558
            SCFree(array[i].isnotset_sids);
1,189✔
559
            SCFree(array[i].toggle_sids);
1,189✔
560
        }
1,189✔
561
        SCFree(array);
109✔
562
    }
109✔
563
}
109✔
564

565
static void FBAnalyzerFree(struct FBAnalyzer *fba)
566
{
111✔
567
    if (fba && fba->array) {
111✔
568
        FBAnalyzerArrayFree(fba->array, fba->array_size);
109✔
569
        fba->array = NULL;
109✔
570
        fba->array_size = 0;
109✔
571
    }
109✔
572
}
111✔
573

574
#define MAX_SIDS 8
575
static bool CheckExpand(const uint32_t sids_idx, uint32_t **sids, uint32_t *sids_size)
576
{
3,782✔
577
    if (sids_idx >= *sids_size) {
3,782✔
578
        const uint32_t old_size = *sids_size;
1,836✔
579
        const uint32_t new_size = MAX(2 * old_size, MAX_SIDS);
1,836✔
580

581
        void *ptr = SCRealloc(*sids, new_size * sizeof(uint32_t));
1,836✔
582
        if (ptr == NULL)
1,836✔
583
            return false;
×
584
        *sids_size = new_size;
1,836✔
585
        *sids = ptr;
1,836✔
586
    }
1,836✔
587
    return true;
3,782✔
588
}
3,782✔
589

590
static int DetectFlowbitsAnalyzeSignature(const Signature *s, struct FBAnalyzer *fba)
591
{
75,720✔
592
    struct FBAnalyze *array = fba->array;
75,720✔
593
    if (array == NULL)
75,720✔
594
        return -1;
×
595

596
    /* see if the signature uses stateful matching TODO is there not a flag? */
597
    bool has_state = (s->init_data->buffer_index != 0);
75,720✔
598

599
    for (const SigMatch *sm = s->init_data->smlists[DETECT_SM_LIST_MATCH]; sm != NULL;
137,501✔
600
            sm = sm->next) {
75,720✔
601
        if (sm->type != DETECT_FLOWBITS)
61,781✔
602
            continue;
59,993✔
603
        /* figure out the flowbit action */
604
        const DetectFlowbitsData *fb = (DetectFlowbitsData *)sm->ctx;
1,788✔
605
        // Handle flowbit array in case of ORed flowbits
606
        for (uint8_t k = 0; k < fb->or_list_size; k++) {
1,820✔
607
            struct FBAnalyze *fa = &array[fb->or_list[k]];
32✔
608
            fa->cnts[fb->cmd]++;
32✔
609
            fa->state_cnts[fb->cmd] += has_state;
32✔
610

611
            if (fb->cmd == DETECT_FLOWBITS_CMD_ISSET) {
32✔
612
                if (!CheckExpand(fa->isset_sids_idx, &fa->isset_sids, &fa->isset_sids_size))
24✔
613
                    return -1;
×
614
                fa->isset_sids[fa->isset_sids_idx] = s->iid;
24✔
615
                fa->isset_sids_idx++;
24✔
616
            } else if (fb->cmd == DETECT_FLOWBITS_CMD_ISNOTSET) {
24✔
617
                if (!CheckExpand(
8✔
618
                            fa->isnotset_sids_idx, &fa->isnotset_sids, &fa->isnotset_sids_size))
8✔
619
                    return -1;
×
620
                fa->isnotset_sids[fa->isnotset_sids_idx] = s->iid;
8✔
621
                fa->isnotset_sids_idx++;
8✔
622
            }
8✔
623
        }
32✔
624
        if (fb->or_list_size == 0) {
1,788✔
625
            struct FBAnalyze *fa = &array[fb->idx];
1,774✔
626
            fa->cnts[fb->cmd]++;
1,774✔
627
            fa->state_cnts[fb->cmd] += has_state;
1,774✔
628

629
            if (fb->cmd == DETECT_FLOWBITS_CMD_ISSET) {
1,774✔
630
                if (!CheckExpand(fa->isset_sids_idx, &fa->isset_sids, &fa->isset_sids_size))
1,673✔
631
                    return -1;
×
632
                fa->isset_sids[fa->isset_sids_idx] = s->iid;
1,673✔
633
                fa->isset_sids_idx++;
1,673✔
634
            } else if (fb->cmd == DETECT_FLOWBITS_CMD_ISNOTSET) {
1,673✔
635
                if (!CheckExpand(
101✔
636
                            fa->isnotset_sids_idx, &fa->isnotset_sids, &fa->isnotset_sids_size))
101✔
637
                    return -1;
×
638
                fa->isnotset_sids[fa->isnotset_sids_idx] = s->iid;
101✔
639
                fa->isnotset_sids_idx++;
101✔
640
            }
101✔
641
        }
1,774✔
642
    }
1,788✔
643
    for (const SigMatch *sm = s->init_data->smlists[DETECT_SM_LIST_POSTMATCH]; sm != NULL;
78,923✔
644
            sm = sm->next) {
75,720✔
645
        if (sm->type != DETECT_FLOWBITS)
3,203✔
646
            continue;
1,227✔
647
        /* figure out what flowbit action */
648
        const DetectFlowbitsData *fb = (DetectFlowbitsData *)sm->ctx;
1,976✔
649
        struct FBAnalyze *fa = &array[fb->idx];
1,976✔
650
        fa->cnts[fb->cmd]++;
1,976✔
651
        fa->state_cnts[fb->cmd] += has_state;
1,976✔
652

653
        if (fb->cmd == DETECT_FLOWBITS_CMD_SET) {
1,976✔
654
            if (!CheckExpand(fa->set_sids_idx, &fa->set_sids, &fa->set_sids_size))
1,865✔
655
                return -1;
×
656
            fa->set_sids[fa->set_sids_idx] = s->iid;
1,865✔
657
            fa->set_sids_idx++;
1,865✔
658
        } else if (fb->cmd == DETECT_FLOWBITS_CMD_UNSET) {
1,865✔
659
            if (!CheckExpand(fa->unset_sids_idx, &fa->unset_sids, &fa->unset_sids_size))
88✔
660
                return -1;
×
661
            fa->unset_sids[fa->unset_sids_idx] = s->iid;
88✔
662
            fa->unset_sids_idx++;
88✔
663
        } else if (fb->cmd == DETECT_FLOWBITS_CMD_TOGGLE) {
89✔
664
            if (!CheckExpand(fa->toggle_sids_idx, &fa->toggle_sids, &fa->toggle_sids_size))
23✔
665
                return -1;
×
666
            fa->toggle_sids[fa->toggle_sids_idx] = s->iid;
23✔
667
            fa->toggle_sids_idx++;
23✔
668
        }
23✔
669
    }
1,976✔
670
    return 0;
75,720✔
671
}
75,720✔
672

673
int DetectFlowbitsAnalyze(DetectEngineCtx *de_ctx)
674
{
2,663✔
675
    const uint32_t max_fb_id = de_ctx->max_fb_id;
2,663✔
676
    if (max_fb_id == 0)
2,663✔
677
        return 0;
2,578✔
678

679
    struct FBAnalyzer fba = { .array = NULL, .array_size = 0 };
85✔
680
    const uint32_t array_size = max_fb_id + 1;
85✔
681
    struct FBAnalyze *array = SCCalloc(array_size, sizeof(struct FBAnalyze));
85✔
682
    if (array == NULL) {
85✔
683
        SCLogError("Unable to allocate flowbit analyze array");
×
684
        return -1;
×
685
    }
×
686
    fba.array = array;
85✔
687
    fba.array_size = array_size;
85✔
688

689
    SCLogDebug("fb analyzer array size: %"PRIu64,
85✔
690
            (uint64_t)(array_size * sizeof(struct FBAnalyze)));
85✔
691

692
    /* fill flowbit array, updating counters per sig */
693
    for (uint32_t i = 0; i < de_ctx->sig_array_len; i++) {
75,676✔
694
        const Signature *s = de_ctx->sig_array[i];
75,591✔
695

696
        int r = DetectFlowbitsAnalyzeSignature(s, &fba);
75,591✔
697
        if (r < 0) {
75,591✔
698
            FBAnalyzerFree(&fba);
×
699
            return -1;
×
700
        }
×
701
    }
75,591✔
702

703
    /* walk array to see if all bits make sense */
704
    for (uint32_t i = 0; i < array_size; i++) {
1,194✔
705
        const char *varname = VarNameStoreSetupLookup(i, VAR_TYPE_FLOW_BIT);
1,109✔
706
        if (varname == NULL)
1,109✔
707
            continue;
173✔
708

709
        bool to_state = false;
1,109✔
710

711
        if (array[i].cnts[DETECT_FLOWBITS_CMD_ISSET] &&
936✔
712
            array[i].cnts[DETECT_FLOWBITS_CMD_TOGGLE] == 0 &&
936✔
713
            array[i].cnts[DETECT_FLOWBITS_CMD_SET] == 0) {
936✔
714

715
            const Signature *s = de_ctx->sig_array[array[i].isset_sids[0]];
13✔
716
            SCLogWarning("flowbit '%s' is checked but not "
13✔
717
                         "set. Checked in %u and %u other sigs",
13✔
718
                    varname, s->id, array[i].isset_sids_idx - 1);
13✔
719
        }
13✔
720
        if (array[i].state_cnts[DETECT_FLOWBITS_CMD_ISSET] &&
936✔
721
            array[i].state_cnts[DETECT_FLOWBITS_CMD_SET] == 0)
936✔
722
        {
11✔
723
            SCLogDebug("flowbit %s/%u: isset in state, set not in state", varname, i);
11✔
724
        }
11✔
725

726
        /* if signature depends on 'stateful' flowbits, then turn the
727
         * sig into a stateful sig itself */
728
        if (array[i].cnts[DETECT_FLOWBITS_CMD_ISSET] > 0 &&
936✔
729
            array[i].state_cnts[DETECT_FLOWBITS_CMD_ISSET] == 0 &&
936✔
730
            array[i].state_cnts[DETECT_FLOWBITS_CMD_SET])
936✔
731
        {
34✔
732
            SCLogDebug("flowbit %s/%u: isset not in state, set in state", varname, i);
34✔
733
            to_state = true;
34✔
734
        }
34✔
735

736
        SCLogDebug("ALL flowbit %s/%u: sets %u toggles %u unsets %u isnotsets %u issets %u", varname, i,
936✔
737
                array[i].cnts[DETECT_FLOWBITS_CMD_SET], array[i].cnts[DETECT_FLOWBITS_CMD_TOGGLE],
936✔
738
                array[i].cnts[DETECT_FLOWBITS_CMD_UNSET], array[i].cnts[DETECT_FLOWBITS_CMD_ISNOTSET],
936✔
739
                array[i].cnts[DETECT_FLOWBITS_CMD_ISSET]);
936✔
740
        SCLogDebug("STATE flowbit %s/%u: sets %u toggles %u unsets %u isnotsets %u issets %u", varname, i,
936✔
741
                array[i].state_cnts[DETECT_FLOWBITS_CMD_SET], array[i].state_cnts[DETECT_FLOWBITS_CMD_TOGGLE],
936✔
742
                array[i].state_cnts[DETECT_FLOWBITS_CMD_UNSET], array[i].state_cnts[DETECT_FLOWBITS_CMD_ISNOTSET],
936✔
743
                array[i].state_cnts[DETECT_FLOWBITS_CMD_ISSET]);
936✔
744
        for (uint32_t x = 0; x < array[i].set_sids_idx; x++) {
2,769✔
745
            SCLogDebug("SET flowbit %s/%u: SID %u", varname, i,
1,833✔
746
                    de_ctx->sig_array[array[i].set_sids[x]]->id);
1,833✔
747
        }
1,833✔
748
        if (to_state) {
936✔
749
            for (uint32_t x = 0; x < array[i].isset_sids_idx; x++) {
86✔
750
                Signature *s = de_ctx->sig_array[array[i].isset_sids[x]];
52✔
751
                SCLogDebug("GET flowbit %s/%u: SID %u", varname, i, s->id);
52✔
752

753
                s->init_data->init_flags |= SIG_FLAG_INIT_STATE_MATCH;
52✔
754
                s->init_data->is_rule_state_dependant = true;
52✔
755

756
                uint32_t sids_array_size = array[i].set_sids_idx;
52✔
757

758
                // save information about flowbits that affect this rule's state
759
                if (s->init_data->rule_state_dependant_sids_array == NULL) {
52✔
760
                    s->init_data->rule_state_dependant_sids_array =
48✔
761
                            SCCalloc(sids_array_size, sizeof(uint32_t));
48✔
762
                    if (s->init_data->rule_state_dependant_sids_array == NULL) {
48✔
763
                        SCLogError("Failed to allocate memory for rule_state_dependant_ids");
×
764
                        goto error;
×
765
                    }
×
766
                    s->init_data->rule_state_flowbits_ids_size = 1;
48✔
767
                    s->init_data->rule_state_flowbits_ids_array =
48✔
768
                            SCCalloc(s->init_data->rule_state_flowbits_ids_size, sizeof(uint32_t));
48✔
769
                    if (s->init_data->rule_state_flowbits_ids_array == NULL) {
48✔
770
                        SCLogError("Failed to allocate memory for rule_state_variable_idx");
×
771
                        goto error;
×
772
                    }
×
773
                    s->init_data->rule_state_dependant_sids_size = sids_array_size;
48✔
774
                    SCLogDebug("alloc'ed array for rule dependency and fbs idx array, sid %u, "
48✔
775
                               "sizes are %u and %u",
48✔
776
                            s->id, s->init_data->rule_state_dependant_sids_size,
48✔
777
                            s->init_data->rule_state_flowbits_ids_size);
48✔
778
                } else {
48✔
779
                    uint32_t new_array_size =
4✔
780
                            s->init_data->rule_state_dependant_sids_size + sids_array_size;
4✔
781
                    void *tmp_ptr = SCRealloc(s->init_data->rule_state_dependant_sids_array,
4✔
782
                            new_array_size * sizeof(uint32_t));
4✔
783
                    if (tmp_ptr == NULL) {
4✔
784
                        SCLogError("Failed to allocate memory for rule_state_variable_idx");
×
785
                        goto error;
×
786
                    }
×
787
                    s->init_data->rule_state_dependant_sids_array = tmp_ptr;
4✔
788
                    s->init_data->rule_state_dependant_sids_size = new_array_size;
4✔
789
                    SCLogDebug("realloc'ed array for rule dependency, sid %u, new size is %u",
4✔
790
                            s->id, s->init_data->rule_state_dependant_sids_size);
4✔
791
                    uint32_t new_fb_array_size = s->init_data->rule_state_flowbits_ids_size + 1;
4✔
792
                    void *tmp_fb_ptr = SCRealloc(s->init_data->rule_state_flowbits_ids_array,
4✔
793
                            new_fb_array_size * sizeof(uint32_t));
4✔
794
                    s->init_data->rule_state_flowbits_ids_array = tmp_fb_ptr;
4✔
795
                    if (s->init_data->rule_state_flowbits_ids_array == NULL) {
4✔
796
                        SCLogError("Failed to reallocate memory for rule_state_variable_idx");
×
797
                        goto error;
×
798
                    }
×
799
                    SCLogDebug(
4✔
800
                            "realloc'ed array for flowbits ids, new size is %u", new_fb_array_size);
4✔
801
                    s->init_data->rule_state_dependant_sids_size = new_array_size;
4✔
802
                    s->init_data->rule_state_flowbits_ids_size = new_fb_array_size;
4✔
803
                }
4✔
804
                for (uint32_t idx = 0; idx < s->init_data->rule_state_dependant_sids_size; idx++) {
110✔
805
                    if (idx < array[i].set_sids_idx) {
58✔
806
                        s->init_data->rule_state_dependant_sids_array
54✔
807
                                [s->init_data->rule_state_dependant_sids_idx] =
54✔
808
                                de_ctx->sig_array[array[i].set_sids[idx]]->id;
54✔
809
                        s->init_data->rule_state_dependant_sids_idx++;
54✔
810
                    }
54✔
811
                }
58✔
812
                s->init_data
52✔
813
                        ->rule_state_flowbits_ids_array[s->init_data->rule_state_flowbits_ids_size -
52✔
814
                                                        1] = i;
52✔
815
                s->init_data->rule_state_flowbits_ids_size += 1;
52✔
816
                // flowbit info saving for rule made stateful rule work finished
817

818
                SCLogDebug("made SID %u stateful because it depends on "
52✔
819
                        "stateful rules that set flowbit %s", s->id, varname);
52✔
820
            }
52✔
821
        }
34✔
822
    }
936✔
823

824
    if (rule_engine_analysis_set) {
85✔
825
        DetectFlowbitsAnalyzeDump(de_ctx, array, array_size);
18✔
826
    }
18✔
827

828
    FBAnalyzerFree(&fba);
85✔
829
    return 0;
85✔
830
error:
×
831
    FBAnalyzerFree(&fba);
×
832
    return -1;
×
833
}
85✔
834

835
// TODO misses IPOnly rules. IPOnly flowbit rules are set only though.
836
static struct FBAnalyzer DetectFlowbitsAnalyzeForGroup(
837
        const DetectEngineCtx *de_ctx, SigGroupHead *sgh)
838
{
26✔
839
    struct FBAnalyzer fba = { .array = NULL, .array_size = 0 };
26✔
840

841
    const uint32_t max_fb_id = de_ctx->max_fb_id;
26✔
842
    if (max_fb_id == 0)
26✔
843
        return fba;
2✔
844

845
    uint32_t array_size = max_fb_id + 1;
24✔
846
    struct FBAnalyze *array = SCCalloc(array_size, sizeof(struct FBAnalyze));
24✔
847
    if (array == NULL) {
24✔
848
        SCLogError("Unable to allocate flowbit analyze array");
×
849
        return fba;
×
850
    }
×
851
    SCLogDebug(
24✔
852
            "fb analyzer array size: %" PRIu64, (uint64_t)(array_size * sizeof(struct FBAnalyze)));
24✔
853
    fba.array = array;
24✔
854
    fba.array_size = array_size;
24✔
855

856
    /* fill flowbit array, updating counters per sig */
857
    for (uint32_t i = 0; i < sgh->init->sig_cnt; i++) {
153✔
858
        const Signature *s = sgh->init->match_array[i];
129✔
859
        SCLogDebug("sgh %p: s->id %u", sgh, s->id);
129✔
860

861
        int r = DetectFlowbitsAnalyzeSignature(s, &fba);
129✔
862
        if (r < 0) {
129✔
863
            FBAnalyzerFree(&fba);
×
864
            return fba;
×
865
        }
×
866
    }
129✔
867

868
    /* walk array to see if all bits make sense */
869
    for (uint32_t i = 0; i < array_size; i++) {
104✔
870
        const char *varname = VarNameStoreSetupLookup(i, VAR_TYPE_FLOW_BIT);
80✔
871
        if (varname == NULL)
80✔
872
            continue;
24✔
873

874
        bool to_state = false;
80✔
875
        if (array[i].state_cnts[DETECT_FLOWBITS_CMD_ISSET] &&
56✔
876
                array[i].state_cnts[DETECT_FLOWBITS_CMD_SET] == 0) {
56✔
877
            SCLogDebug("flowbit %s/%u: isset in state, set not in state", varname, i);
8✔
878
        }
8✔
879

880
        /* if signature depends on 'stateful' flowbits, then turn the
881
         * sig into a stateful sig itself */
882
        if (array[i].cnts[DETECT_FLOWBITS_CMD_ISSET] > 0 &&
56✔
883
                array[i].state_cnts[DETECT_FLOWBITS_CMD_ISSET] == 0 &&
56✔
884
                array[i].state_cnts[DETECT_FLOWBITS_CMD_SET]) {
56✔
885
            SCLogDebug("flowbit %s/%u: isset not in state, set in state", varname, i);
2✔
886
            to_state = true;
2✔
887
        }
2✔
888

889
        SCLogDebug("ALL flowbit %s/%u: sets %u toggles %u unsets %u isnotsets %u issets %u",
56✔
890
                varname, i, array[i].cnts[DETECT_FLOWBITS_CMD_SET],
56✔
891
                array[i].cnts[DETECT_FLOWBITS_CMD_TOGGLE], array[i].cnts[DETECT_FLOWBITS_CMD_UNSET],
56✔
892
                array[i].cnts[DETECT_FLOWBITS_CMD_ISNOTSET],
56✔
893
                array[i].cnts[DETECT_FLOWBITS_CMD_ISSET]);
56✔
894
        SCLogDebug("STATE flowbit %s/%u: sets %u toggles %u unsets %u isnotsets %u issets %u",
56✔
895
                varname, i, array[i].state_cnts[DETECT_FLOWBITS_CMD_SET],
56✔
896
                array[i].state_cnts[DETECT_FLOWBITS_CMD_TOGGLE],
56✔
897
                array[i].state_cnts[DETECT_FLOWBITS_CMD_UNSET],
56✔
898
                array[i].state_cnts[DETECT_FLOWBITS_CMD_ISNOTSET],
56✔
899
                array[i].state_cnts[DETECT_FLOWBITS_CMD_ISSET]);
56✔
900
        for (uint32_t x = 0; x < array[i].set_sids_idx; x++) {
88✔
901
            SCLogDebug("SET flowbit %s/%u: SID %u", varname, i,
32✔
902
                    de_ctx->sig_array[array[i].set_sids[x]]->id);
32✔
903
        }
32✔
904
        for (uint32_t x = 0; x < array[i].isset_sids_idx; x++) {
126✔
905
            Signature *s = de_ctx->sig_array[array[i].isset_sids[x]];
70✔
906
            SCLogDebug("GET flowbit %s/%u: SID %u", varname, i, s->id);
70✔
907

908
            if (to_state) {
70✔
909
                s->init_data->init_flags |= SIG_FLAG_INIT_STATE_MATCH;
2✔
910
                SCLogDebug("made SID %u stateful because it depends on "
2✔
911
                           "stateful rules that set flowbit %s",
2✔
912
                        s->id, varname);
2✔
913
            }
2✔
914
        }
70✔
915
    }
56✔
916

917
    return fba;
24✔
918
}
24✔
919

920
SCMutex g_flowbits_dump_write_m = SCMUTEX_INITIALIZER;
921
static void DetectFlowbitsAnalyzeDump(const DetectEngineCtx *de_ctx,
922
        struct FBAnalyze *array, uint32_t elements)
923
{
18✔
924
    SCJsonBuilder *js = SCJbNewObject();
18✔
925
    if (js == NULL)
18✔
926
        return;
×
927

928
    SCJbOpenArray(js, "flowbits");
18✔
929
    for (uint32_t x = 0; x < elements; x++) {
71✔
930
        const char *varname = VarNameStoreSetupLookup(x, VAR_TYPE_FLOW_BIT);
53✔
931
        if (varname == NULL)
53✔
932
            continue;
18✔
933

934
        const struct FBAnalyze *e = &array[x];
35✔
935

936
        SCJbStartObject(js);
35✔
937
        SCJbSetString(js, "name", varname);
35✔
938
        SCJbSetUint(js, "internal_id", x);
35✔
939
        SCJbSetUint(js, "set_cnt", e->cnts[DETECT_FLOWBITS_CMD_SET]);
35✔
940
        SCJbSetUint(js, "unset_cnt", e->cnts[DETECT_FLOWBITS_CMD_UNSET]);
35✔
941
        SCJbSetUint(js, "toggle_cnt", e->cnts[DETECT_FLOWBITS_CMD_TOGGLE]);
35✔
942
        SCJbSetUint(js, "isset_cnt", e->cnts[DETECT_FLOWBITS_CMD_ISSET]);
35✔
943
        SCJbSetUint(js, "isnotset_cnt", e->cnts[DETECT_FLOWBITS_CMD_ISNOTSET]);
35✔
944

945
        // sets
946
        if (e->cnts[DETECT_FLOWBITS_CMD_SET]) {
35✔
947
            SCJbOpenArray(js, "sets");
24✔
948
            for (uint32_t i = 0; i < e->set_sids_idx; i++) {
49✔
949
                const Signature *s = de_ctx->sig_array[e->set_sids[i]];
25✔
950
                SCJbAppendUint(js, s->id);
25✔
951
            }
25✔
952
            SCJbClose(js);
24✔
953
        }
24✔
954
        // gets
955
        if (e->cnts[DETECT_FLOWBITS_CMD_ISSET]) {
35✔
956
            SCJbOpenArray(js, "isset");
19✔
957
            for (uint32_t i = 0; i < e->isset_sids_idx; i++) {
60✔
958
                const Signature *s = de_ctx->sig_array[e->isset_sids[i]];
41✔
959
                SCJbAppendUint(js, s->id);
41✔
960
            }
41✔
961
            SCJbClose(js);
19✔
962
        }
19✔
963
        // isnotset
964
        if (e->cnts[DETECT_FLOWBITS_CMD_ISNOTSET]) {
35✔
965
            SCJbOpenArray(js, "isnotset");
8✔
966
            for (uint32_t i = 0; i < e->isnotset_sids_idx; i++) {
16✔
967
                const Signature *s = de_ctx->sig_array[e->isnotset_sids[i]];
8✔
968
                SCJbAppendUint(js, s->id);
8✔
969
            }
8✔
970
            SCJbClose(js);
8✔
971
        }
8✔
972
        // unset
973
        if (e->cnts[DETECT_FLOWBITS_CMD_UNSET]) {
35✔
974
            SCJbOpenArray(js, "unset");
7✔
975
            for (uint32_t i = 0; i < e->unset_sids_idx; i++) {
14✔
976
                const Signature *s = de_ctx->sig_array[e->unset_sids[i]];
7✔
977
                SCJbAppendUint(js, s->id);
7✔
978
            }
7✔
979
            SCJbClose(js);
7✔
980
        }
7✔
981
        // toggle
982
        if (e->cnts[DETECT_FLOWBITS_CMD_TOGGLE]) {
35✔
983
            SCJbOpenArray(js, "toggle");
5✔
984
            for (uint32_t i = 0; i < e->toggle_sids_idx; i++) {
10✔
985
                const Signature *s = de_ctx->sig_array[e->toggle_sids[i]];
5✔
986
                SCJbAppendUint(js, s->id);
5✔
987
            }
5✔
988
            SCJbClose(js);
5✔
989
        }
5✔
990
        SCJbClose(js);
35✔
991
    }
35✔
992
    SCJbClose(js); // array
18✔
993
    SCJbClose(js); // object
18✔
994

995
    const char *filename = "flowbits.json";
18✔
996
    const char *log_dir = SCConfigGetLogDirectory();
18✔
997
    char log_path[PATH_MAX] = "";
18✔
998
    snprintf(log_path, sizeof(log_path), "%s/%s", log_dir, filename);
18✔
999

1000
    SCMutexLock(&g_flowbits_dump_write_m);
18✔
1001
    FILE *fp = fopen(log_path, "w");
18✔
1002
    if (fp != NULL) {
18✔
1003
        fwrite(SCJbPtr(js), SCJbLen(js), 1, fp);
18✔
1004
        fprintf(fp, "\n");
18✔
1005
        fclose(fp);
18✔
1006
    }
18✔
1007
    SCMutexUnlock(&g_flowbits_dump_write_m);
18✔
1008

1009
    SCJbFree(js);
18✔
1010
}
18✔
1011

1012
static bool PrefilterFlowbitIsPrefilterable(const Signature *s)
1013
{
×
1014
    SCLogDebug("sid:%u: checking", s->id);
×
1015

1016
    for (const SigMatch *sm = s->init_data->smlists[DETECT_SM_LIST_MATCH]; sm != NULL;
×
1017
            sm = sm->next) {
×
1018
        switch (sm->type) {
×
1019
            case DETECT_FLOWBITS: {
×
1020
                const DetectFlowbitsData *fb = (DetectFlowbitsData *)sm->ctx;
×
1021
                if (fb->cmd == DETECT_FLOWBITS_CMD_ISSET) {
×
1022
                    SCLogDebug("sid:%u: FLOWBITS ISSET can prefilter", s->id);
×
1023
                    return true;
×
1024
                }
×
1025
                break;
×
1026
            }
×
1027
        }
×
1028
    }
×
1029
    SCLogDebug("sid:%u: no flowbit prefilter", s->id);
×
1030
    return false;
×
1031
}
×
1032

1033
/** core flowbit data structure: map a flowbit id to the signatures that need inspecting after it is
1034
 * found. Part of a rb-tree. */
1035
typedef struct PrefilterFlowbit {
1036
    uint32_t *rule_id;     /**< array of signature iid that are part of this prefilter */
1037
    RB_ENTRY(PrefilterFlowbit) __attribute__((__packed__)) rb;
1038
    uint32_t id;           /**< flowbit id */
1039
    uint32_t rule_id_size; /**< size in elements of `rule_id` */
1040
    uint32_t rule_id_cnt;  /**< usage in elements of `rule_id` */
1041
} __attribute__((__packed__)) PrefilterFlowbit;
1042

1043
static int PrefilterFlowbitCompare(const PrefilterFlowbit *a, const PrefilterFlowbit *b)
1044
{
132✔
1045
    if (a->id > b->id)
132✔
1046
        return 1;
34✔
1047
    else if (a->id < b->id)
98✔
1048
        return -1;
39✔
1049
    else
59✔
1050
        return 0;
59✔
1051
}
132✔
1052

1053
/** red-black tree prototype for PFB (Prefilter Flow Bits) */
1054
RB_HEAD(PFB, PrefilterFlowbit);
1055
RB_PROTOTYPE(PFB, PrefilterFlowbit, rb, PrefilterFlowbitCompare);
1056
RB_GENERATE(PFB, PrefilterFlowbit, rb, PrefilterFlowbitCompare);
466✔
1057

466✔
1058
struct PrefilterEngineFlowbits {
466✔
1059
    struct PFB fb_tree;
466✔
1060
};
466✔
1061

466✔
1062
static void PrefilterFlowbitFree(void *vctx)
466✔
1063
{
466✔
1064
    struct PrefilterEngineFlowbits *ctx = vctx;
31✔
1065
    struct PrefilterFlowbit *rec, *safe = NULL;
31✔
1066
    RB_FOREACH_SAFE (rec, PFB, &ctx->fb_tree, safe) {
42✔
1067
        PFB_RB_REMOVE(&ctx->fb_tree, rec);
42✔
1068
        SCFree(rec->rule_id);
42✔
1069
        SCFree(rec);
42✔
1070
    }
42✔
1071

1072
    SCFree(ctx);
31✔
1073
}
31✔
1074

1075
static void PrefilterFlowbitMatch(DetectEngineThreadCtx *det_ctx, Packet *p, const void *pectx)
1076
{
60✔
1077
    struct PrefilterEngineFlowbits *ctx = (struct PrefilterEngineFlowbits *)pectx;
60✔
1078
    SCLogDebug("%" PRIu64 ": ctx %p", PcapPacketCntGet(p), ctx);
60✔
1079

1080
    if (p->flow == NULL) {
60✔
1081
        SCReturn;
×
1082
    }
×
1083

1084
    for (GenericVar *gv = p->flow->flowvar; gv != NULL; gv = gv->next) {
101✔
1085
        if (gv->type != DETECT_FLOWBITS)
41✔
1086
            continue;
×
1087

1088
        PrefilterFlowbit lookup;
41✔
1089
        memset(&lookup, 0, sizeof(lookup));
41✔
1090
        lookup.id = gv->idx;
41✔
1091
        SCLogDebug("flowbit %u", gv->idx);
41✔
1092

1093
        PrefilterFlowbit *b = PFB_RB_FIND(&ctx->fb_tree, &lookup);
41✔
1094
        if (b == NULL) {
41✔
UNCOV
1095
            SCLogDebug("flowbit %u not in the tree", lookup.id);
×
1096
        } else {
41✔
1097
            SCLogDebug("flowbit %u found in the tree: %u", lookup.id, b->id);
41✔
1098

1099
            PrefilterAddSids(&det_ctx->pmq, b->rule_id, b->rule_id_cnt);
41✔
1100
#ifdef DEBUG
1101
            for (uint32_t x = 0; x < b->rule_id_cnt; x++) {
1102
                const Signature *s = det_ctx->de_ctx->sig_array[b->rule_id[x]];
1103
                SCLogDebug("flowbit %u -> sig %u", gv->idx, s->id);
1104
            }
1105
#endif
1106
        }
41✔
1107
    }
41✔
1108
}
60✔
1109

1110
static void PrefilterFlowbitPostRuleMatch(
1111
        DetectEngineThreadCtx *det_ctx, const void *pectx, Packet *p, Flow *f)
1112
{
5✔
1113
    struct PrefilterEngineFlowbits *ctx = (struct PrefilterEngineFlowbits *)pectx;
5✔
1114
    SCLogDebug("%" PRIu64 ": ctx %p", PcapPacketCntGet(p), ctx);
5✔
1115

1116
    if (p->flow == NULL) {
5✔
1117
        SCReturn;
×
1118
    }
×
1119

1120
    for (uint32_t i = 0; i < det_ctx->post_rule_work_queue.len; i++) {
14✔
1121
        const PostRuleMatchWorkQueueItem *w = &det_ctx->post_rule_work_queue.q[i];
9✔
1122
        if (w->sm_type != DETECT_FLOWBITS)
9✔
1123
            continue;
×
1124

1125
        PrefilterFlowbit lookup;
9✔
1126
        memset(&lookup, 0, sizeof(lookup));
9✔
1127
        lookup.id = w->value;
9✔
1128

1129
        PrefilterFlowbit *b = PFB_RB_FIND(&ctx->fb_tree, &lookup);
9✔
1130
        if (b == NULL) {
9✔
1131
            SCLogDebug("flowbit %u not in the tree", lookup.id);
×
1132
        } else {
9✔
1133
            SCLogDebug("flowbit %u found in the tree: %u. Adding %u sids", lookup.id, b->id,
9✔
1134
                    b->rule_id_cnt);
9✔
1135
            PrefilterAddSids(&det_ctx->pmq, b->rule_id, b->rule_id_cnt);
9✔
1136
#ifdef DEBUG
1137
            // SCLogDebug("b %u", b->rule_id_cnt);
1138
            for (uint32_t x = 0; x < b->rule_id_cnt; x++) {
1139
                Signature *s = det_ctx->de_ctx->sig_array[b->rule_id[x]];
1140
                SCLogDebug("flowbit %u -> sig %u (triggered by %u)", w->value, s->id,
1141
                        det_ctx->de_ctx->sig_array[w->id]->id);
1142
            }
1143
#endif
1144
        }
9✔
1145
    }
9✔
1146
}
5✔
1147

1148
#define BLOCK_SIZE 8
65✔
1149

1150
static int AddBitAndSid(
1151
        struct PrefilterEngineFlowbits *ctx, const Signature *s, const uint32_t flowbit_id)
1152
{
34✔
1153
    PrefilterFlowbit x;
34✔
1154
    memset(&x, 0, sizeof(x));
34✔
1155
    x.id = flowbit_id;
34✔
1156

1157
    PrefilterFlowbit *pfb = PFB_RB_FIND(&ctx->fb_tree, &x);
34✔
1158
    if (pfb == NULL) {
34✔
1159
        PrefilterFlowbit *add = SCCalloc(1, sizeof(*add));
27✔
1160
        if (add == NULL)
27✔
1161
            return -1;
×
1162

1163
        add->id = flowbit_id;
27✔
1164
        add->rule_id = SCCalloc(1, BLOCK_SIZE * sizeof(uint32_t));
27✔
1165
        if (add->rule_id == NULL) {
27✔
1166
            SCFree(add);
×
1167
            return -1;
×
1168
        }
×
1169
        add->rule_id_size = BLOCK_SIZE;
27✔
1170
        add->rule_id_cnt = 1;
27✔
1171
        add->rule_id[0] = s->iid;
27✔
1172

1173
        PrefilterFlowbit *res = PFB_RB_INSERT(&ctx->fb_tree, add);
27✔
1174
        SCLogDebug("not found, so added (res %p)", res);
27✔
1175
        if (res != NULL) {
27✔
1176
            // duplicate, shouldn't be possible after the FIND above
1177
            BUG_ON(1);
×
1178
            return -1;
×
1179
        }
×
1180
    } else {
27✔
1181
        SCLogDebug("found! pfb %p id %u", pfb, pfb->id);
7✔
1182

1183
        if (pfb->rule_id_cnt < pfb->rule_id_size) {
7✔
1184
            pfb->rule_id[pfb->rule_id_cnt++] = s->iid;
7✔
1185
        } else {
7✔
1186
            uint32_t *ptr =
×
1187
                    SCRealloc(pfb->rule_id, (pfb->rule_id_size + BLOCK_SIZE) * sizeof(uint32_t));
×
1188
            if (ptr == NULL) {
×
1189
                // memory stays in the tree
1190
                return -1;
×
1191
            }
×
1192
            pfb->rule_id = ptr;
×
1193
            pfb->rule_id_size += BLOCK_SIZE;
×
1194
            pfb->rule_id[pfb->rule_id_cnt++] = s->iid;
×
1195
        }
×
1196
    }
7✔
1197
    return 0;
34✔
1198
}
34✔
1199

1200
static int AddBitsAndSid(const DetectEngineCtx *de_ctx, struct PrefilterEngineFlowbits *ctx,
1201
        const DetectFlowbitsData *fb, const Signature *s)
1202
{
34✔
1203
    if (fb->or_list_size == 0) {
34✔
1204
        if (AddBitAndSid(ctx, s, fb->idx) < 0) {
34✔
1205
            return -1;
×
1206
        }
×
1207
    } else {
34✔
UNCOV
1208
        for (uint8_t i = 0; i < fb->or_list_size; i++) {
×
UNCOV
1209
            SCLogDebug("flowbit OR: bit %u", fb->or_list[i]);
×
UNCOV
1210
            if (AddBitAndSid(ctx, s, fb->or_list[i]) < 0) {
×
1211
                return -1;
×
1212
            }
×
UNCOV
1213
        }
×
UNCOV
1214
    }
×
1215
    return 0;
34✔
1216
}
34✔
1217

1218
static uint32_t NextMultiple(const uint32_t v, const uint32_t m)
1219
{
38✔
1220
    return v + (m - v % m);
38✔
1221
}
38✔
1222

1223
/** \internal
1224
 *  \brief adds sids for 'isset' prefilter flowbits
1225
 *  \retval int 1 if we added sid(s), 0 if we didn't, -1 on error */
1226
// TODO skip sids that aren't set by this sgh
1227
// TODO skip sids that doesn't have a isset in the same direction
1228
static int AddIssetSidsForBit(const DetectEngineCtx *de_ctx, const struct FBAnalyzer *fba,
1229
        const DetectFlowbitsData *fb, PrefilterFlowbit *add)
1230
{
40✔
1231
    int added = 0;
40✔
1232
    for (uint32_t i = 0; i < fba->array[fb->idx].isset_sids_idx; i++) {
85✔
1233
        const uint32_t sig_iid = fba->array[fb->idx].isset_sids[i];
45✔
1234
        const Signature *s = de_ctx->sig_array[sig_iid];
45✔
1235
        SCLogDebug("flowbit: %u => considering sid %u (iid:%u)", fb->idx, s->id, s->iid);
45✔
1236

1237
        /* Skip sids that aren't prefilter. These would just run all the time. */
1238
        if (s->init_data->prefilter_sm == NULL ||
45✔
1239
                s->init_data->prefilter_sm->type != DETECT_FLOWBITS) {
45✔
1240
#ifdef DEBUG
1241
            const char *name = s->init_data->prefilter_sm
1242
                                       ? sigmatch_table[s->init_data->prefilter_sm->type].name
1243
                                       : "none";
1244
            SCLogDebug("flowbit: %u => rejected sid %u (iid:%u). No prefilter or prefilter not "
1245
                       "flowbits (%p, %s, %d)",
1246
                    fb->idx, s->id, sig_iid, s->init_data->prefilter_sm, name,
1247
                    s->init_data->prefilter_sm ? s->init_data->prefilter_sm->type : -1);
1248
#endif
1249
            continue;
24✔
1250
        }
24✔
1251

1252
        /* only add sids that match our bit */
1253
        const DetectFlowbitsData *fs_fb =
21✔
1254
                (const DetectFlowbitsData *)s->init_data->prefilter_sm->ctx;
21✔
1255
        if (fs_fb->idx != fb->idx) {
21✔
UNCOV
1256
            SCLogDebug(
×
UNCOV
1257
                    "flowbit: %u => rejected sid %u (iid:%u). Sig prefilters on different bit %u",
×
UNCOV
1258
                    fb->idx, s->id, sig_iid, fs_fb->idx);
×
UNCOV
1259
            continue;
×
UNCOV
1260
        }
×
1261

1262
        bool dup = false;
21✔
1263
        for (uint32_t x = 0; x < add->rule_id_cnt; x++) {
27✔
1264
            if (add->rule_id[x] == sig_iid) {
6✔
1265
                dup = true;
2✔
1266
            }
2✔
1267
        }
6✔
1268

1269
        if (!dup) {
21✔
1270
            if (add->rule_id_cnt < add->rule_id_size) {
19✔
1271
                add->rule_id[add->rule_id_cnt++] = sig_iid;
19✔
1272
            } else {
19✔
1273
                uint32_t *ptr = SCRealloc(
×
1274
                        add->rule_id, (add->rule_id_size + BLOCK_SIZE) * sizeof(uint32_t));
×
1275
                if (ptr == NULL) {
×
1276
                    return -1;
×
1277
                }
×
1278
                add->rule_id = ptr;
×
1279
                add->rule_id_size += BLOCK_SIZE;
×
1280
                add->rule_id[add->rule_id_cnt++] = sig_iid;
×
1281
            }
×
1282
            added = 1;
19✔
1283
            SCLogDebug("flowbit: %u => accepted sid %u (iid:%u)", fb->idx, s->id, sig_iid);
19✔
1284
        }
19✔
1285
    }
21✔
1286
    return added;
40✔
1287
}
40✔
1288

1289
/* TODO shouldn't add sids for which Signature::num is < our num. Is this possible after sorting? */
1290

1291
/** \brief For set/toggle flowbits, build "set" post-rule-match engine
1292
 *
1293
 *  For set/toggle flowbits, a special post-rule-match engine is constructed
1294
 *  to update the running match array during rule matching.
1295
 */
1296
static int AddBitSetToggle(const DetectEngineCtx *de_ctx, struct FBAnalyzer *fba,
1297
        struct PrefilterEngineFlowbits *ctx, const DetectFlowbitsData *fb, const Signature *s)
1298
{
40✔
1299
    PrefilterFlowbit x;
40✔
1300
    memset(&x, 0, sizeof(x));
40✔
1301
    x.id = fb->idx;
40✔
1302
    PrefilterFlowbit *pfb = PFB_RB_FIND(&ctx->fb_tree, &x);
40✔
1303
    if (pfb == NULL) {
40✔
1304
        PrefilterFlowbit *add = SCCalloc(1, sizeof(*add));
38✔
1305
        if (add == NULL)
38✔
1306
            return -1;
×
1307

1308
        add->id = fb->idx;
38✔
1309
        add->rule_id_size = NextMultiple(fba->array[fb->idx].isset_sids_idx, BLOCK_SIZE);
38✔
1310
        add->rule_id = SCCalloc(1, add->rule_id_size * sizeof(uint32_t));
38✔
1311
        if (add->rule_id == NULL) {
38✔
1312
            SCFree(add);
×
1313
            return -1;
×
1314
        }
×
1315

1316
        if (AddIssetSidsForBit(de_ctx, fba, fb, add) != 1) {
38✔
1317
            SCLogDebug("no sids added");
23✔
1318
            SCFree(add->rule_id);
23✔
1319
            SCFree(add);
23✔
1320
            return 0;
23✔
1321
        }
23✔
1322
        PrefilterFlowbit *res = PFB_RB_INSERT(&ctx->fb_tree, add);
15✔
1323
        SCLogDebug("not found, so added (res %p)", res);
15✔
1324
        BUG_ON(res != NULL); // TODO if res != NULL we have a duplicate which should be impossible
15✔
1325
    } else {
15✔
1326
        SCLogDebug("found! pfb %p id %u", pfb, pfb->id);
2✔
1327

1328
        int r = AddIssetSidsForBit(de_ctx, fba, fb, pfb);
2✔
1329
        if (r < 0) {
2✔
1330
            return -1;
×
1331
        } else if (r == 0) {
2✔
1332
            SCLogDebug("no sids added");
2✔
1333
            return 0;
2✔
1334
        }
2✔
1335
    }
2✔
1336
    return 1;
15✔
1337
}
40✔
1338

1339
/** \brief build flowbit prefilter state(s)
1340
 *
1341
 *  Build "set" and "isset" states.
1342
 *
1343
 *  For each flowbit "isset" in the sgh, we need to check:
1344
 *  1. is it supported
1345
 *  2. is prefilter enabled
1346
 *  3. does it match in the same dir or only opposing dir
1347
 */
1348
static int PrefilterSetupFlowbits(DetectEngineCtx *de_ctx, SigGroupHead *sgh)
1349
{
26✔
1350
    if (sgh == NULL)
26✔
1351
        return 0;
×
1352

1353
    SCLogDebug("sgh %p: setting up prefilter", sgh);
26✔
1354
    struct PrefilterEngineFlowbits *isset_ctx = NULL;
26✔
1355
    struct PrefilterEngineFlowbits *set_ctx = NULL;
26✔
1356

1357
    struct FBAnalyzer fb_analysis = DetectFlowbitsAnalyzeForGroup(de_ctx, sgh);
26✔
1358
    if (fb_analysis.array == NULL)
26✔
1359
        goto error;
2✔
1360

1361
    for (uint32_t i = 0; i < sgh->init->sig_cnt; i++) {
153✔
1362
        Signature *s = sgh->init->match_array[i];
129✔
1363
        if (s == NULL)
129✔
1364
            continue;
×
1365

1366
        SCLogDebug("checking sid %u", s->id);
129✔
1367

1368
        /* first build the 'set' state */
1369
        for (SigMatch *sm = s->init_data->smlists[DETECT_SM_LIST_POSTMATCH]; sm != NULL;
169✔
1370
                sm = sm->next) {
129✔
1371
            if (sm->type != DETECT_FLOWBITS) {
40✔
UNCOV
1372
                SCLogDebug("skip non flowbits sm");
×
UNCOV
1373
                continue;
×
UNCOV
1374
            }
×
1375

1376
            DetectFlowbitsData *fb = (DetectFlowbitsData *)sm->ctx;
40✔
1377
            if (fb->cmd == DETECT_FLOWBITS_CMD_SET) {
40✔
1378
                SCLogDebug(
32✔
1379
                        "DETECT_SM_LIST_POSTMATCH: sid %u DETECT_FLOWBITS set %u", s->id, fb->idx);
32✔
1380
            } else if (fb->cmd == DETECT_FLOWBITS_CMD_TOGGLE) {
32✔
1381
                SCLogDebug("DETECT_SM_LIST_POSTMATCH: sid %u DETECT_FLOWBITS toggle %u", s->id,
8✔
1382
                        fb->idx);
8✔
1383
            } else {
8✔
UNCOV
1384
                SCLogDebug("unsupported flowbits setting");
×
UNCOV
1385
                continue;
×
UNCOV
1386
            }
×
1387

1388
            if (fb_analysis.array[fb->idx].isnotset_sids_idx ||
40✔
1389
                    fb_analysis.array[fb->idx].unset_sids_idx) {
40✔
UNCOV
1390
                SCLogDebug("flowbit %u not supported: unset in use", fb->idx);
×
UNCOV
1391
                continue;
×
UNCOV
1392
            }
×
1393

1394
            if (set_ctx == NULL) {
40✔
1395
                set_ctx = SCCalloc(1, sizeof(*set_ctx));
18✔
1396
                if (set_ctx == NULL)
18✔
1397
                    goto error;
×
1398
            }
18✔
1399

1400
            SCLogDebug("setting up sets/toggles for sid %u", s->id);
40✔
1401
            if (AddBitSetToggle(de_ctx, &fb_analysis, set_ctx, fb, s) == 1) {
40✔
1402
                // flag the set/toggle to trigger the post-rule match logic
1403
                SCLogDebug("set up sets/toggles for sid %u", s->id);
15✔
1404
                fb->post_rule_match_prefilter = true;
15✔
1405
            }
15✔
1406

1407
            // TODO don't add for sigs that don't have isset in this sgh. Reasoning:
1408
            // prefilter post match logic only makes sense in the same dir as otherwise
1409
            // the regular 'isset' logic can simply run with the regular prefilters
1410
            // before the rule loop
1411
        }
40✔
1412

1413
        /* next, build the 'isset' state */
1414
        if (s->init_data->prefilter_sm == NULL ||
129✔
1415
                s->init_data->prefilter_sm->type != DETECT_FLOWBITS) {
129✔
1416
            SCLogDebug("no prefilter or prefilter not flowbits");
95✔
1417
            continue;
95✔
1418
        }
95✔
1419

1420
        const DetectFlowbitsData *fb = (DetectFlowbitsData *)s->init_data->prefilter_sm->ctx;
34✔
1421
        if (fb_analysis.array[fb->idx].isnotset_sids_idx ||
34✔
1422
                fb_analysis.array[fb->idx].unset_sids_idx) {
34✔
1423
            SCLogDebug("flowbit %u not supported: toggle or unset in use", fb->idx);
×
1424
            s->init_data->prefilter_sm = NULL;
×
1425
            s->flags &= ~SIG_FLAG_PREFILTER;
×
1426
            continue;
×
1427
        }
×
1428

1429
        SCLogDebug("isset: adding sid %u, flowbit %u", s->id, fb->idx);
34✔
1430

1431
        if (isset_ctx == NULL) {
34✔
1432
            isset_ctx = SCCalloc(1, sizeof(*isset_ctx));
13✔
1433
            if (isset_ctx == NULL)
13✔
1434
                goto error;
×
1435
        }
13✔
1436
        if (AddBitsAndSid(de_ctx, isset_ctx, fb, s) < 0) {
34✔
1437
            goto error;
×
1438
        }
×
1439
    }
34✔
1440

1441
    /* finally, register the states with their engines */
1442
    static const char *g_prefilter_flowbits_isset = "flowbits:isset";
24✔
1443
    if (isset_ctx != NULL) {
24✔
1444
        enum SignatureHookPkt hook = SIGNATURE_HOOK_PKT_NOT_SET; // TODO review
13✔
1445
        PrefilterAppendEngine(de_ctx, sgh, PrefilterFlowbitMatch, SIG_MASK_REQUIRE_FLOW, hook,
13✔
1446
                isset_ctx, PrefilterFlowbitFree, g_prefilter_flowbits_isset);
13✔
1447
        SCLogDebug("isset: added prefilter engine");
13✔
1448

1449
        if (set_ctx != NULL && !RB_EMPTY(&set_ctx->fb_tree)) {
13✔
1450
            static const char *g_prefilter_flowbits_set = "flowbits:set";
7✔
1451
            PrefilterAppendPostRuleEngine(de_ctx, sgh, PrefilterFlowbitPostRuleMatch, set_ctx,
7✔
1452
                    PrefilterFlowbitFree, g_prefilter_flowbits_set);
7✔
1453
            SCLogDebug("set/toggle: added prefilter engine");
7✔
1454
        } else {
7✔
1455
            if (set_ctx) {
6✔
UNCOV
1456
                PrefilterFlowbitFree(set_ctx);
×
UNCOV
1457
            }
×
1458
            SCLogDebug("set/toggle: NO prefilter engine added");
6✔
1459
        }
6✔
1460
    } else if (set_ctx != NULL) {
13✔
1461
        PrefilterFlowbitFree(set_ctx);
11✔
1462
    }
11✔
1463
    FBAnalyzerFree(&fb_analysis);
24✔
1464
    return 0;
24✔
1465

1466
error:
2✔
1467
    if (set_ctx) {
2✔
1468
        PrefilterFlowbitFree(set_ctx);
×
1469
    }
×
1470
    if (isset_ctx) {
2✔
1471
        PrefilterFlowbitFree(isset_ctx);
×
1472
    }
×
1473
    FBAnalyzerFree(&fb_analysis);
2✔
1474
    return -1;
2✔
1475
}
24✔
1476

1477
#ifdef UNITTESTS
1478

1479
static int FlowBitsTestParse01(void)
1480
{
1✔
1481
    char command[16] = "", name[16] = "";
1✔
1482

1483
    /* Single argument version. */
1484
    FAIL_IF(!DetectFlowbitParse("noalert", command, sizeof(command), name,
1✔
1485
            sizeof(name)));
1✔
1486
    FAIL_IF(strcmp(command, "noalert") != 0);
1✔
1487

1488
    /* No leading or trailing spaces. */
1489
    FAIL_IF(!DetectFlowbitParse("set,flowbit", command, sizeof(command), name,
1✔
1490
            sizeof(name)));
1✔
1491
    FAIL_IF(strcmp(command, "set") != 0);
1✔
1492
    FAIL_IF(strcmp(name, "flowbit") != 0);
1✔
1493

1494
    /* Leading space. */
1495
    FAIL_IF(!DetectFlowbitParse("set, flowbit", command, sizeof(command), name,
1✔
1496
            sizeof(name)));
1✔
1497
    FAIL_IF(strcmp(command, "set") != 0);
1✔
1498
    FAIL_IF(strcmp(name, "flowbit") != 0);
1✔
1499

1500
    /* Trailing space. */
1501
    FAIL_IF(!DetectFlowbitParse("set,flowbit ", command, sizeof(command), name,
1✔
1502
            sizeof(name)));
1✔
1503
    FAIL_IF(strcmp(command, "set") != 0);
1✔
1504
    FAIL_IF(strcmp(name, "flowbit") != 0);
1✔
1505

1506
    /* Leading and trailing space. */
1507
    FAIL_IF(!DetectFlowbitParse("set, flowbit ", command, sizeof(command), name,
1✔
1508
            sizeof(name)));
1✔
1509
    FAIL_IF(strcmp(command, "set") != 0);
1✔
1510
    FAIL_IF(strcmp(name, "flowbit") != 0);
1✔
1511

1512
    /* Spaces are not allowed in the name. */
1513
    FAIL_IF(DetectFlowbitParse("set,namewith space", command, sizeof(command),
1✔
1514
            name, sizeof(name)));
1✔
1515

1516
    PASS;
1✔
1517
}
1✔
1518

1519
/**
1520
 * \test FlowBitsTestSig01 is a test for a valid noalert flowbits option
1521
 *
1522
 *  \retval 1 on success
1523
 *  \retval 0 on failure
1524
 */
1525

1526
static int FlowBitsTestSig01(void)
1527
{
1✔
1528
    Signature *s = NULL;
1✔
1529
    DetectEngineCtx *de_ctx = NULL;
1✔
1530

1531
    de_ctx = DetectEngineCtxInit();
1✔
1532
    FAIL_IF_NULL(de_ctx);
1✔
1533

1534
    de_ctx->flags |= DE_QUIET;
1✔
1535

1536
    s = de_ctx->sig_list = SigInit(de_ctx,"alert ip any any -> any any (msg:\"Noalert\"; flowbits:noalert,wrongusage; content:\"GET \"; sid:1;)");
1✔
1537
    FAIL_IF_NOT_NULL(s);
1✔
1538

1539
    SigGroupBuild(de_ctx);
1✔
1540
    DetectEngineCtxFree(de_ctx);
1✔
1541
    PASS;
1✔
1542
}
1✔
1543

1544
/**
1545
 * \test FlowBitsTestSig02 is a test for a valid isset,set,isnotset,unset,toggle flowbits options
1546
 *
1547
 *  \retval 1 on success
1548
 *  \retval 0 on failure
1549
 */
1550

1551
static int FlowBitsTestSig02(void)
1552
{
1✔
1553
    Signature *s = NULL;
1✔
1554
    ThreadVars th_v;
1✔
1555
    DetectEngineCtx *de_ctx = NULL;
1✔
1556

1557
    memset(&th_v, 0, sizeof(th_v));
1✔
1558

1559
    de_ctx = DetectEngineCtxInit();
1✔
1560
    FAIL_IF_NULL(de_ctx);
1✔
1561

1562
    de_ctx->flags |= DE_QUIET;
1✔
1563

1564
    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;)");
1✔
1565
    FAIL_IF_NOT_NULL(s);
1✔
1566

1567
    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;)");
1✔
1568
    FAIL_IF_NOT_NULL(s);
1✔
1569

1570
    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;)");
1✔
1571
    FAIL_IF_NOT_NULL(s);
1✔
1572

1573
    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;)");
1✔
1574
    FAIL_IF_NOT_NULL(s);
1✔
1575

1576
    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;)");
1✔
1577
    FAIL_IF_NOT_NULL(s);
1✔
1578

1579
    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;)");
1✔
1580
    FAIL_IF_NOT_NULL(s);
1✔
1581

1582
    SigGroupBuild(de_ctx);
1✔
1583
    DetectEngineCtxFree(de_ctx);
1✔
1584

1585
    PASS;
1✔
1586
}
1✔
1587

1588
/**
1589
 * \test FlowBitsTestSig03 is a test for a invalid flowbits option
1590
 *
1591
 *  \retval 1 on success
1592
 *  \retval 0 on failure
1593
 */
1594

1595
static int FlowBitsTestSig03(void)
1596
{
1✔
1597
    Signature *s = NULL;
1✔
1598
    DetectEngineCtx *de_ctx = NULL;
1✔
1599

1600
    de_ctx = DetectEngineCtxInit();
1✔
1601
    FAIL_IF_NULL(de_ctx);
1✔
1602

1603
    de_ctx->flags |= DE_QUIET;
1✔
1604

1605
    s = de_ctx->sig_list = SigInit(de_ctx,"alert ip any any -> any any (msg:\"Unknown cmd\"; flowbits:wrongcmd; content:\"GET \"; sid:1;)");
1✔
1606
    FAIL_IF_NOT_NULL(s);
1✔
1607

1608
    SigGroupBuild(de_ctx);
1✔
1609
    DetectEngineCtxFree(de_ctx);
1✔
1610
    PASS;
1✔
1611
}
1✔
1612

1613
/**
1614
 * \test FlowBitsTestSig04 is a test check idx value
1615
 *
1616
 *  \retval 1 on success
1617
 *  \retval 0 on failure
1618
 */
1619

1620
static int FlowBitsTestSig04(void)
1621
{
1✔
1622
    Signature *s = NULL;
1✔
1623
    DetectEngineCtx *de_ctx = NULL;
1✔
1624
    int idx = 0;
1✔
1625
    de_ctx = DetectEngineCtxInit();
1✔
1626
    FAIL_IF_NULL(de_ctx);
1✔
1627

1628
    de_ctx->flags |= DE_QUIET;
1✔
1629

1630
    s = de_ctx->sig_list = SigInit(de_ctx,"alert ip any any -> any any (msg:\"isset option\"; flowbits:isset,fbt; content:\"GET \"; sid:1;)");
1✔
1631
    FAIL_IF_NULL(s);
1✔
1632

1633
    idx = VarNameStoreRegister("fbt", VAR_TYPE_FLOW_BIT);
1✔
1634
    FAIL_IF(idx == 0);
1✔
1635

1636
    SigGroupBuild(de_ctx);
1✔
1637
    DetectEngineCtxFree(de_ctx);
1✔
1638
    PASS;
1✔
1639
}
1✔
1640

1641
/**
1642
 * \test FlowBitsTestSig05 is a test check noalert flag
1643
 *
1644
 *  \retval 1 on success
1645
 *  \retval 0 on failure
1646
 */
1647

1648
static int FlowBitsTestSig05(void)
1649
{
1✔
1650
    Signature *s = NULL;
1✔
1651
    DetectEngineCtx *de_ctx = NULL;
1✔
1652

1653
    de_ctx = DetectEngineCtxInit();
1✔
1654
    FAIL_IF_NULL(de_ctx);
1✔
1655

1656
    de_ctx->flags |= DE_QUIET;
1✔
1657

1658
    s = de_ctx->sig_list = SigInit(de_ctx,"alert ip any any -> any any (msg:\"Noalert\"; flowbits:noalert; content:\"GET \"; sid:1;)");
1✔
1659
    FAIL_IF_NULL(s);
1✔
1660
    FAIL_IF((s->action & ACTION_ALERT) != 0);
1✔
1661

1662
    SigGroupBuild(de_ctx);
1✔
1663
    DetectEngineCtxFree(de_ctx);
1✔
1664
    PASS;
1✔
1665
}
1✔
1666

1667
/**
1668
 * \test FlowBitsTestSig06 is a test set flowbits option
1669
 *
1670
 *  \retval 1 on success
1671
 *  \retval 0 on failure
1672
 */
1673

1674
static int FlowBitsTestSig06(void)
1675
{
1✔
1676
    uint8_t *buf = (uint8_t *)
1✔
1677
                    "GET /one/ HTTP/1.1\r\n"
1✔
1678
                    "Host: one.example.org\r\n"
1✔
1679
                    "\r\n";
1✔
1680
    uint16_t buflen = strlen((char *)buf);
1✔
1681
    Packet *p = PacketGetFromAlloc();
1✔
1682
    FAIL_IF_NULL(p);
1✔
1683
    Signature *s = NULL;
1✔
1684
    ThreadVars th_v;
1✔
1685
    DetectEngineThreadCtx *det_ctx = NULL;
1✔
1686
    DetectEngineCtx *de_ctx = NULL;
1✔
1687
    Flow f;
1✔
1688
    GenericVar flowvar, *gv = NULL;
1✔
1689
    int result = 0;
1✔
1690
    uint32_t idx = 0;
1✔
1691

1692
    memset(&th_v, 0, sizeof(th_v));
1✔
1693
    StatsThreadInit(&th_v.stats);
1✔
1694
    memset(&f, 0, sizeof(Flow));
1✔
1695
    memset(&flowvar, 0, sizeof(GenericVar));
1✔
1696

1697
    FLOW_INITIALIZE(&f);
1✔
1698
    p->flow = &f;
1✔
1699
    p->flow->flowvar = &flowvar;
1✔
1700

1701
    p->src.family = AF_INET;
1✔
1702
    p->dst.family = AF_INET;
1✔
1703
    p->payload = buf;
1✔
1704
    p->payload_len = buflen;
1✔
1705
    p->proto = IPPROTO_TCP;
1✔
1706
    p->flags |= PKT_HAS_FLOW;
1✔
1707
    p->flowflags |= (FLOW_PKT_TOSERVER | FLOW_PKT_TOSERVER_FIRST);
1✔
1708

1709
    de_ctx = DetectEngineCtxInit();
1✔
1710
    FAIL_IF_NULL(de_ctx);
1✔
1711

1712
    de_ctx->flags |= DE_QUIET;
1✔
1713

1714
    s = de_ctx->sig_list = SigInit(de_ctx,"alert ip any any -> any any (msg:\"Flowbit set\"; flowbits:set,myflow; sid:10;)");
1✔
1715
    FAIL_IF_NULL(s);
1✔
1716

1717
    idx = VarNameStoreRegister("myflow", VAR_TYPE_FLOW_BIT);
1✔
1718
    FAIL_IF_NOT(idx);
1✔
1719
    SigGroupBuild(de_ctx);
1✔
1720
    DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
1✔
1721

1722
    SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
1✔
1723

1724
    gv = p->flow->flowvar;
1✔
1725
    FAIL_IF_NULL(gv);
1✔
1726
    for ( ; gv != NULL; gv = gv->next) {
3✔
1727
        if (gv->type == DETECT_FLOWBITS && gv->idx == idx) {
2✔
1728
                result = 1;
1✔
1729
        }
1✔
1730
    }
2✔
1731
    FAIL_IF_NOT(result);
1✔
1732

1733
    PacketFree(p);
1✔
1734
    FLOW_DESTROY(&f);
1✔
1735

1736
    DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
1✔
1737
    DetectEngineCtxFree(de_ctx);
1✔
1738
    StatsThreadCleanup(&th_v.stats);
1✔
1739
    PASS;
1✔
1740
}
1✔
1741

1742
/**
1743
 * \test FlowBitsTestSig07 is a test unset flowbits option
1744
 *
1745
 *  \retval 1 on success
1746
 *  \retval 0 on failure
1747
 */
1748

1749
static int FlowBitsTestSig07(void)
1750
{
1✔
1751
    uint8_t *buf = (uint8_t *)
1✔
1752
                    "GET /one/ HTTP/1.1\r\n"
1✔
1753
                    "Host: one.example.org\r\n"
1✔
1754
                    "\r\n";
1✔
1755
    uint16_t buflen = strlen((char *)buf);
1✔
1756
    Packet *p = PacketGetFromAlloc();
1✔
1757
    FAIL_IF_NULL(p);
1✔
1758
    Signature *s = NULL;
1✔
1759
    ThreadVars th_v;
1✔
1760
    DetectEngineThreadCtx *det_ctx = NULL;
1✔
1761
    DetectEngineCtx *de_ctx = NULL;
1✔
1762
    Flow f;
1✔
1763
    GenericVar flowvar, *gv = NULL;
1✔
1764
    int result = 0;
1✔
1765
    uint32_t idx = 0;
1✔
1766

1767
    memset(&th_v, 0, sizeof(th_v));
1✔
1768
    StatsThreadInit(&th_v.stats);
1✔
1769
    memset(&f, 0, sizeof(Flow));
1✔
1770
    memset(&flowvar, 0, sizeof(GenericVar));
1✔
1771

1772
    FLOW_INITIALIZE(&f);
1✔
1773
    p->flow = &f;
1✔
1774
    p->flow->flowvar = &flowvar;
1✔
1775

1776
    p->src.family = AF_INET;
1✔
1777
    p->dst.family = AF_INET;
1✔
1778
    p->payload = buf;
1✔
1779
    p->payload_len = buflen;
1✔
1780
    p->proto = IPPROTO_TCP;
1✔
1781

1782
    de_ctx = DetectEngineCtxInit();
1✔
1783
    FAIL_IF_NULL(de_ctx);
1✔
1784

1785
    de_ctx->flags |= DE_QUIET;
1✔
1786

1787
    s = de_ctx->sig_list = SigInit(de_ctx,"alert ip any any -> any any (msg:\"Flowbit set\"; flowbits:set,myflow2; sid:10;)");
1✔
1788
    FAIL_IF_NULL(s);
1✔
1789

1790
    s = s->next = SigInit(de_ctx,"alert ip any any -> any any (msg:\"Flowbit unset\"; flowbits:unset,myflow2; sid:11;)");
1✔
1791
    FAIL_IF_NULL(s);
1✔
1792

1793
    idx = VarNameStoreRegister("myflow", VAR_TYPE_FLOW_BIT);
1✔
1794
    FAIL_IF_NOT(idx);
1✔
1795
    SigGroupBuild(de_ctx);
1✔
1796
    DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
1✔
1797

1798
    SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
1✔
1799

1800
    gv = p->flow->flowvar;
1✔
1801
    FAIL_IF_NULL(gv);
1✔
1802

1803
    for ( ; gv != NULL; gv = gv->next) {
2✔
1804
        if (gv->type == DETECT_FLOWBITS && gv->idx == idx) {
1✔
1805
                result = 1;
1806
        }
1807
    }
1✔
1808
    FAIL_IF(result);
1✔
1809

1810
    PacketFree(p);
1✔
1811
    FLOW_DESTROY(&f);
1✔
1812
    DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
1✔
1813
    DetectEngineCtxFree(de_ctx);
1✔
1814
    StatsThreadCleanup(&th_v.stats);
1✔
1815
    PASS;
1✔
1816
}
1✔
1817

1818
/**
1819
 * \test FlowBitsTestSig08 is a test toggle flowbits option
1820
 *
1821
 *  \retval 1 on success
1822
 *  \retval 0 on failure
1823
 */
1824

1825
static int FlowBitsTestSig08(void)
1826
{
1✔
1827
    uint8_t *buf = (uint8_t *)
1✔
1828
                    "GET /one/ HTTP/1.1\r\n"
1✔
1829
                    "Host: one.example.org\r\n"
1✔
1830
                    "\r\n";
1✔
1831
    uint16_t buflen = strlen((char *)buf);
1✔
1832
    Packet *p = PacketGetFromAlloc();
1✔
1833
    if (unlikely(p == NULL))
1✔
1834
        return 0;
1835
    Signature *s = NULL;
1✔
1836
    ThreadVars th_v;
1✔
1837
    DetectEngineThreadCtx *det_ctx = NULL;
1✔
1838
    DetectEngineCtx *de_ctx = NULL;
1✔
1839
    Flow f;
1✔
1840
    GenericVar flowvar, *gv = NULL;
1✔
1841
    int result = 0;
1✔
1842
    uint32_t idx = 0;
1✔
1843

1844
    memset(&th_v, 0, sizeof(th_v));
1✔
1845
    StatsThreadInit(&th_v.stats);
1✔
1846
    memset(&f, 0, sizeof(Flow));
1✔
1847
    memset(&flowvar, 0, sizeof(GenericVar));
1✔
1848

1849
    FLOW_INITIALIZE(&f);
1✔
1850
    p->flow = &f;
1✔
1851
    p->flow->flowvar = &flowvar;
1✔
1852

1853
    p->src.family = AF_INET;
1✔
1854
    p->dst.family = AF_INET;
1✔
1855
    p->payload = buf;
1✔
1856
    p->payload_len = buflen;
1✔
1857
    p->proto = IPPROTO_TCP;
1✔
1858

1859
    de_ctx = DetectEngineCtxInit();
1✔
1860
    FAIL_IF_NULL(de_ctx);
1✔
1861

1862
    de_ctx->flags |= DE_QUIET;
1✔
1863

1864
    s = de_ctx->sig_list = SigInit(de_ctx,"alert ip any any -> any any (msg:\"Flowbit set\"; flowbits:set,myflow2; sid:10;)");
1✔
1865
    FAIL_IF_NULL(s);
1✔
1866

1867
    s = s->next  = SigInit(de_ctx,"alert ip any any -> any any (msg:\"Flowbit unset\"; flowbits:toggle,myflow2; sid:11;)");
1✔
1868
    FAIL_IF_NULL(s);
1✔
1869

1870
    idx = VarNameStoreRegister("myflow", VAR_TYPE_FLOW_BIT);
1✔
1871
    FAIL_IF_NOT(idx);
1✔
1872
    SigGroupBuild(de_ctx);
1✔
1873
    DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
1✔
1874

1875
    SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
1✔
1876

1877
    gv = p->flow->flowvar;
1✔
1878
    FAIL_IF_NULL(gv);
1✔
1879

1880
    for ( ; gv != NULL; gv = gv->next) {
3✔
1881
        if (gv->type == DETECT_FLOWBITS && gv->idx == idx) {
2✔
1882
                result = 1;
1883
        }
1884
    }
2✔
1885
    FAIL_IF(result);
1✔
1886

1887
    PacketFree(p);
1✔
1888
    FLOW_DESTROY(&f);
1✔
1889
    DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
1✔
1890
    DetectEngineCtxFree(de_ctx);
1✔
1891
    StatsThreadCleanup(&th_v.stats);
1✔
1892
    PASS;
1✔
1893
}
1✔
1894

1895
/**
1896
 * \brief this function registers unit tests for FlowBits
1897
 */
1898
void FlowBitsRegisterTests(void)
1899
{
1✔
1900
    UtRegisterTest("FlowBitsTestParse01", FlowBitsTestParse01);
1✔
1901
    UtRegisterTest("FlowBitsTestSig01", FlowBitsTestSig01);
1✔
1902
    UtRegisterTest("FlowBitsTestSig02", FlowBitsTestSig02);
1✔
1903
    UtRegisterTest("FlowBitsTestSig03", FlowBitsTestSig03);
1✔
1904
    UtRegisterTest("FlowBitsTestSig04", FlowBitsTestSig04);
1✔
1905
    UtRegisterTest("FlowBitsTestSig05", FlowBitsTestSig05);
1✔
1906
    UtRegisterTest("FlowBitsTestSig06", FlowBitsTestSig06);
1✔
1907
    UtRegisterTest("FlowBitsTestSig07", FlowBitsTestSig07);
1✔
1908
    UtRegisterTest("FlowBitsTestSig08", FlowBitsTestSig08);
1✔
1909
}
1✔
1910
#endif /* UNITTESTS */
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