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