suricata
detect-engine-threshold.c
Go to the documentation of this file.
1 /* Copyright (C) 2007-2024 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  * \defgroup threshold Thresholding
20  *
21  * This feature is used to reduce the number of logged alerts for noisy rules.
22  * This can be tuned to significantly reduce false alarms, and it can also be
23  * used to write a newer breed of rules. Thresholding commands limit the number
24  * of times a particular event is logged during a specified time interval.
25  *
26  * @{
27  */
28 
29 /**
30  * \file
31  *
32  * \author Breno Silva <breno.silva@gmail.com>
33  * \author Victor Julien <victor@inliniac.net>
34  *
35  * Threshold part of the detection engine.
36  */
37 
38 #include "suricata-common.h"
39 #include "detect.h"
40 #include "flow.h"
41 
42 #include "detect-parse.h"
43 #include "detect-engine.h"
45 #include "detect-engine-address.h"
47 
48 #include "util-misc.h"
49 #include "util-error.h"
50 #include "util-debug.h"
51 #include "action-globals.h"
52 #include "util-validate.h"
53 
54 #include "util-hash.h"
55 #include "util-thash.h"
56 #include "util-hash-lookup3.h"
57 #include "counters.h"
58 #include "util-random.h"
59 
60 #include "thread-storage.h"
61 
62 static SC_ATOMIC_DECLARE(uint64_t, threshold_bitmap_alloc_fail);
63 static SC_ATOMIC_DECLARE(uint64_t, threshold_bitmap_memuse);
64 static SC_ATOMIC_DECLARE(uint64_t, threshold_cache_memuse);
65 
66 static void ThresholdCacheInit(void);
67 
68 /* UNITTESTS-only test seam to force allocation failure and query counters */
69 #ifdef UNITTESTS
70 void ThresholdForceAllocFail(int v);
71 uint64_t ThresholdGetBitmapMemuse(void);
72 uint64_t ThresholdGetBitmapAllocFail(void);
73 
74 static int g_threshold_force_alloc_fail = 0;
75 
77 {
78  g_threshold_force_alloc_fail = v;
79 }
80 
82 {
83  return SC_ATOMIC_GET(threshold_bitmap_memuse);
84 }
85 
87 {
88  return SC_ATOMIC_GET(threshold_bitmap_alloc_fail);
89 }
90 #endif
91 
92 /* bitmap settings for exact distinct counting of 16-bit ports */
93 #define DF_PORT_BITMAP_SIZE (65536u / 8u)
94 #define DF_PORT_BYTE_IDX(p) ((uint32_t)((p) >> 3))
95 #define DF_PORT_BIT_MASK(p) ((uint8_t)(1u << ((p)&7u)))
96 
97 struct Thresholds {
99 } ctx;
100 
101 static int ThresholdsInit(struct Thresholds *t);
102 static void ThresholdsDestroy(struct Thresholds *t);
103 
104 static uint64_t ThresholdBitmapAllocFailCounter(void)
105 {
106  return SC_ATOMIC_GET(threshold_bitmap_alloc_fail);
107 }
108 
109 static uint64_t ThresholdBitmapMemuseCounter(void)
110 {
111  return SC_ATOMIC_GET(threshold_bitmap_memuse);
112 }
113 
114 static uint64_t ThresholdCacheMemuseCounter(void)
115 {
116  return SC_ATOMIC_GET(threshold_cache_memuse);
117 }
118 
119 static uint64_t ThresholdMemuseCounter(void)
120 {
121  if (ctx.thash == NULL)
122  return 0;
123  return SC_ATOMIC_GET(ctx.thash->memuse);
124 }
125 
126 static uint64_t ThresholdMemcapCounter(void)
127 {
128  if (ctx.thash == NULL)
129  return 0;
130  return SC_ATOMIC_GET(ctx.thash->config.memcap);
131 }
132 
133 void ThresholdInit(void)
134 {
135  SC_ATOMIC_INIT(threshold_bitmap_alloc_fail);
136  SC_ATOMIC_INIT(threshold_bitmap_memuse);
137  SC_ATOMIC_INIT(threshold_cache_memuse);
138 
139  if (ThresholdsInit(&ctx) < 0) {
140  FatalError("Failed to initialize threshold table");
141  }
142  ThresholdCacheInit();
143 }
144 
146 {
147  StatsRegisterGlobalCounter("detect.thresholds.memuse", ThresholdMemuseCounter);
148  StatsRegisterGlobalCounter("detect.thresholds.memcap", ThresholdMemcapCounter);
149  StatsRegisterGlobalCounter("detect.thresholds.cache.memuse", ThresholdCacheMemuseCounter);
150  StatsRegisterGlobalCounter("detect.thresholds.bitmap_memuse", ThresholdBitmapMemuseCounter);
152  "detect.thresholds.bitmap_alloc_fail", ThresholdBitmapAllocFailCounter);
153 }
154 
156 {
157  ThresholdsDestroy(&ctx);
158 }
159 
160 #define SID 0
161 #define GID 1
162 #define REV 2
163 #define TRACK 3
164 #define TENANT 4
165 
166 typedef struct ThresholdEntry_ {
167  uint32_t key[5];
168 
169  SCTime_t tv_timeout; /**< Timeout for new_action (for rate_filter)
170  its not "seconds", that define the time interval */
171  uint32_t seconds; /**< Event seconds */
172  uint32_t current_count; /**< Var for count control */
173 
174  union {
175  struct {
176  uint32_t next_value;
178  struct {
179  SCTime_t tv1; /**< Var for time control */
180  Address addr; /* used for src/dst/either tracking */
181  Address addr2; /* used for both tracking */
182  /* distinct counting state (for detection_filter unique_on ports) */
183  uint8_t *distinct_bitmap_union; /* 8192 bytes (65536 bits) */
184  };
185  };
186 
188 
189 static int ThresholdEntrySet(void *dst, void *src)
190 {
191  const ThresholdEntry *esrc = src;
192  ThresholdEntry *edst = dst;
193  memset(edst, 0, sizeof(*edst));
194  *edst = *esrc;
195  return 0;
196 }
197 
198 static void ThresholdDistinctInit(ThresholdEntry *te, const DetectThresholdData *td)
199 {
200  if (td->type != TYPE_DETECTION || td->unique_on == DF_UNIQUE_NONE) {
201  return;
202  }
203  DEBUG_VALIDATE_BUG_ON(td->seconds == 0);
204 
205  const uint32_t bitmap_size = DF_PORT_BITMAP_SIZE;
206  te->current_count = 0;
207 #ifdef UNITTESTS
208  if (g_threshold_force_alloc_fail) {
209  SC_ATOMIC_ADD(threshold_bitmap_alloc_fail, 1);
210  te->distinct_bitmap_union = NULL;
211  return;
212  }
213 #endif
214  /* Check memcap before allocating bitmap.
215  * Bitmap memory is bounded by detect.thresholds.memcap via thash.
216  * Note: if ctx.thash is NULL (e.g. init failed or unittests), we bypass
217  * the memcap check but still attempt allocation unless forced to fail. */
218  if (ctx.thash != NULL && !THASH_CHECK_MEMCAP(ctx.thash, bitmap_size)) {
219  SC_ATOMIC_ADD(threshold_bitmap_alloc_fail, 1);
220  te->distinct_bitmap_union = NULL;
221  return;
222  }
223 
224  te->distinct_bitmap_union = SCCalloc(1, bitmap_size);
225  if (te->distinct_bitmap_union == NULL) {
226  SC_ATOMIC_ADD(threshold_bitmap_alloc_fail, 1);
227  } else {
228  /* Track bitmap memory in thash memuse for proper accounting */
229  if (ctx.thash != NULL) {
230  (void)SC_ATOMIC_ADD(ctx.thash->memuse, bitmap_size);
231  }
232  SC_ATOMIC_ADD(threshold_bitmap_memuse, bitmap_size);
233  }
234 }
235 
236 static void ThresholdDistinctReset(ThresholdEntry *te)
237 {
238  const uint32_t bitmap_size = DF_PORT_BITMAP_SIZE;
239  if (te->distinct_bitmap_union) {
240  memset(te->distinct_bitmap_union, 0x00, bitmap_size);
241  }
242  te->current_count = 0;
243 }
244 
245 static inline void ThresholdDistinctAddPort(ThresholdEntry *te, uint16_t port)
246 {
247  const uint32_t byte_index = DF_PORT_BYTE_IDX(port);
248  const uint8_t bit_mask = DF_PORT_BIT_MASK(port);
249  if (te->distinct_bitmap_union) {
250  bool already = (te->distinct_bitmap_union[byte_index] & bit_mask);
251  if (!already) {
252  te->distinct_bitmap_union[byte_index] =
253  (uint8_t)(te->distinct_bitmap_union[byte_index] | bit_mask);
254  te->current_count++;
255  }
256  }
257 }
258 
259 static void ThresholdEntryFree(void *ptr)
260 {
261  if (ptr == NULL)
262  return;
263 
264  ThresholdEntry *e = ptr;
265  if (e->distinct_bitmap_union) {
266  const uint32_t bitmap_size = DF_PORT_BITMAP_SIZE;
267  /* Decrement bitmap memory from thash memuse */
268  if (ctx.thash != NULL) {
269  (void)SC_ATOMIC_SUB(ctx.thash->memuse, bitmap_size);
270  }
271  SC_ATOMIC_SUB(threshold_bitmap_memuse, bitmap_size);
273  e->distinct_bitmap_union = NULL;
274  }
275 }
276 
277 static inline uint32_t HashAddress(const Address *a, const uint32_t seed)
278 {
279  uint32_t key;
280 
281  if (a->family == AF_INET) {
282  key = hashword(a->addr_data32, 1, seed);
283  } else if (a->family == AF_INET6) {
284  key = hashword(a->addr_data32, 4, seed);
285  } else
286  key = 0;
287 
288  return key;
289 }
290 
291 static inline int CompareAddress(const Address *a, const Address *b)
292 {
293  if (a->family == b->family) {
294  switch (a->family) {
295  case AF_INET:
296  return (a->addr_data32[0] == b->addr_data32[0]);
297  case AF_INET6:
298  return CMP_ADDR(a, b);
299  }
300  }
301  return 0;
302 }
303 
304 static uint32_t ThresholdEntryHash(const uint32_t seed, void *ptr)
305 {
306  const ThresholdEntry *e = ptr;
307  uint32_t hash = hashword(e->key, sizeof(e->key) / sizeof(uint32_t), seed);
308  switch (e->key[TRACK]) {
309  case TRACK_BOTH:
310  hash += HashAddress(&e->addr2, seed);
311  /* fallthrough */
312  case TRACK_SRC:
313  case TRACK_DST:
314  hash += HashAddress(&e->addr, seed);
315  break;
316  }
317  return hash;
318 }
319 
320 static bool ThresholdEntryCompare(void *a, void *b)
321 {
322  const ThresholdEntry *e1 = a;
323  const ThresholdEntry *e2 = b;
324  SCLogDebug("sid1: %u sid2: %u", e1->key[SID], e2->key[SID]);
325 
326  if (memcmp(e1->key, e2->key, sizeof(e1->key)) != 0)
327  return false;
328  switch (e1->key[TRACK]) {
329  case TRACK_BOTH:
330  if (!(CompareAddress(&e1->addr2, &e2->addr2)))
331  return false;
332  /* fallthrough */
333  case TRACK_SRC:
334  case TRACK_DST:
335  if (!(CompareAddress(&e1->addr, &e2->addr)))
336  return false;
337  break;
338  }
339  return true;
340 }
341 
342 static bool ThresholdEntryExpire(void *data, const SCTime_t ts)
343 {
344  const ThresholdEntry *e = data;
345  const SCTime_t entry = SCTIME_ADD_SECS(e->tv1, e->seconds);
346  return SCTIME_CMP_GT(ts, entry);
347 }
348 
349 static int ThresholdsInit(struct Thresholds *t)
350 {
351  uint32_t hashsize = 16384;
352  uint64_t memcap = 16 * 1024 * 1024;
353 
354  const char *str;
355  if (SCConfGetNonNull("detect.thresholds.memcap", &str) == 1) {
356  if (ParseSizeStringU64(str, &memcap) < 0) {
357  SCLogError("Error parsing detect.thresholds.memcap from conf file - %s", str);
358  return -1;
359  }
360  }
361 
362  intmax_t value = 0;
363  if ((SCConfGetInt("detect.thresholds.hash-size", &value)) == 1) {
364  if (value < 256 || value > INT_MAX) {
365  SCLogError("'detect.thresholds.hash-size' value %" PRIiMAX
366  " out of range. Valid range 256-2147483647.",
367  value);
368  return -1;
369  }
370  hashsize = (uint32_t)value;
371  }
372 
373  t->thash = THashInit("thresholds", sizeof(ThresholdEntry), ThresholdEntrySet,
374  ThresholdEntryFree, ThresholdEntryHash, ThresholdEntryCompare, ThresholdEntryExpire,
375  NULL, 0, memcap, hashsize);
376  if (t->thash == NULL) {
377  SCLogError("failed to initialize thresholds hash table");
378  return -1;
379  }
380  return 0;
381 }
382 
383 static void ThresholdsDestroy(struct Thresholds *t)
384 {
385  if (t->thash) {
386  THashShutdown(t->thash);
387  }
388 }
389 
390 uint32_t ThresholdsExpire(const SCTime_t ts)
391 {
392  return THashExpire(ctx.thash, ts);
393 }
394 
395 #define TC_ADDRESS 0
396 #define TC_SID 1
397 #define TC_GID 2
398 #define TC_REV 3
399 #define TC_TENANT 4
400 
401 typedef struct ThresholdCacheItem {
402  int8_t track; // by_src/by_dst
403  int8_t ipv;
404  int8_t retval;
405  uint32_t key[5];
409 
410 /* rbtree for expiry handling */
411 
412 static int ThresholdCacheTreeCompareFunc(ThresholdCacheItem *a, ThresholdCacheItem *b)
413 {
414  if (SCTIME_CMP_GTE(a->expires_at, b->expires_at)) {
415  return 1;
416  } else {
417  return -1;
418  }
419 }
420 
421 RB_HEAD(THRESHOLD_CACHE, ThresholdCacheItem);
422 RB_PROTOTYPE(THRESHOLD_CACHE, ThresholdCacheItem, rb, ThresholdCacheTreeCompareFunc);
423 RB_GENERATE(THRESHOLD_CACHE, ThresholdCacheItem, rb, ThresholdCacheTreeCompareFunc);
424 
427  struct THRESHOLD_CACHE tree;
428  uint64_t housekeeping_ts;
429  uint32_t entries; /* number of entries in ht/tree, <= cache_max_entries */
430  uint64_t init_mem; /* charged fixed per-thread memory (see ThresholdCacheThreadInit) */
431 
432  uint64_t lookup_cnt;
435  uint64_t lookup_miss;
436  uint64_t lookup_hit;
439 };
440 
441 /* per-thread cap on the number of decision cache entries, configured via
442  * detect.thresholds.cache.max-entries */
443 #define THRESHOLD_CACHE_MAX_ENTRIES_DEFAULT 256
444 #define THRESHOLD_CACHE_MAX_ENTRIES_MIN 256
445 #define THRESHOLD_CACHE_MAX_ENTRIES_MAX 1048576
446 static uint32_t cache_max_entries = THRESHOLD_CACHE_MAX_ENTRIES_DEFAULT;
447 
448 /* bytes charged to the cache memory counter per entry: the item plus the
449  * hash-table bucket allocated for it */
450 #define THRESHOLD_CACHE_ENTRY_MEM (sizeof(ThresholdCacheItem) + sizeof(HashTableBucket))
451 
452 static SCThreadStorageId thread_storage_id = { .id = -1 };
453 
454 static void DumpCacheStats(struct ThresholdCacheThreadCtx *tctx)
455 {
456  SCLogPerf("threshold thread cache stats: cnt:%" PRIu64 " nosupport:%" PRIu64
457  " miss_expired:%" PRIu64 " miss:%" PRIu64 " hit:%" PRIu64 ", entries:%" PRIu32
458  ", housekeeping: checks:%" PRIu64 ", expired:%" PRIu64,
459  tctx->lookup_cnt, tctx->lookup_nosupport, tctx->lookup_miss_expired, tctx->lookup_miss,
460  tctx->lookup_hit, tctx->entries, tctx->housekeeping_check, tctx->housekeeping_expired);
461 }
462 
463 static inline struct ThresholdCacheThreadCtx *GetThreadCtx(DetectEngineThreadCtx *det_ctx)
464 {
465  if (unlikely(det_ctx->tv == NULL || thread_storage_id.id < 0)) {
466  return NULL;
467  }
468  return SCThreadGetStorageById(det_ctx->tv, thread_storage_id);
469 }
470 
471 static void ThresholdCacheExpire(DetectEngineThreadCtx *det_ctx, SCTime_t now)
472 {
473  struct ThresholdCacheThreadCtx *tctx = GetThreadCtx(det_ctx);
474  if (tctx == NULL)
475  return;
476  tctx->housekeeping_ts = SCTIME_SECS(now);
477 
478  ThresholdCacheItem *iter, *safe = NULL;
479  int cnt = 0;
480  RB_FOREACH_SAFE (iter, THRESHOLD_CACHE, &tctx->tree, safe) {
481  tctx->housekeeping_check++;
482 
483  if (SCTIME_CMP_LT(iter->expires_at, now)) {
484  THRESHOLD_CACHE_RB_REMOVE(&tctx->tree, iter);
485  HashTableRemove(tctx->ht, iter, 0);
486  SCLogDebug("iter %p expired", iter);
487  tctx->housekeeping_expired++;
488  tctx->entries--;
489  (void)SC_ATOMIC_SUB(threshold_cache_memuse, THRESHOLD_CACHE_ENTRY_MEM);
490  }
491 
492  if (++cnt > 1)
493  break;
494  }
495 }
496 
497 /* hash table for threshold look ups */
498 
499 static uint32_t ThresholdCacheHashFunc(HashTable *ht, void *data, uint16_t datalen)
500 {
501  ThresholdCacheItem *e = data;
502  uint32_t hash =
503  hashword(e->key, sizeof(e->key) / sizeof(uint32_t), ht->seed) * (e->ipv + e->track);
504  hash = hash % ht->array_size;
505  return hash;
506 }
507 
508 static char ThresholdCacheHashCompareFunc(
509  void *data1, uint16_t datalen1, void *data2, uint16_t datalen2)
510 {
511  ThresholdCacheItem *tci1 = data1;
512  ThresholdCacheItem *tci2 = data2;
513  return tci1->ipv == tci2->ipv && tci1->track == tci2->track &&
514  memcmp(tci1->key, tci2->key, sizeof(tci1->key)) == 0;
515 }
516 
517 static void ThresholdCacheHashFreeFunc(void *data)
518 {
519  SCFree(data);
520 }
521 
522 /// \brief Thread local cache
523 static int SetupCache(DetectEngineThreadCtx *det_ctx, const Packet *p, const int8_t track,
524  const int8_t retval, const uint32_t sid, const uint32_t gid, const uint32_t rev,
525  SCTime_t expires)
526 {
527  struct ThresholdCacheThreadCtx *tctx = GetThreadCtx(det_ctx);
528  if (!tctx) {
529  return -1;
530  }
531 
532  uint32_t addr;
533  if (track == TRACK_SRC) {
534  addr = p->src.addr_data32[0];
535  } else if (track == TRACK_DST) {
536  addr = p->dst.addr_data32[0];
537  } else {
538  return -1;
539  }
540 
541  ThresholdCacheItem lookup = {
542  .track = track,
543  .ipv = 4,
544  .retval = retval,
545  .key[TC_ADDRESS] = addr,
546  .key[TC_SID] = sid,
547  .key[TC_GID] = gid,
548  .key[TC_REV] = rev,
549  .key[TC_TENANT] = p->tenant_id,
550  .expires_at = expires,
551  };
552  ThresholdCacheItem *found = HashTableLookup(tctx->ht, &lookup, 0);
553  if (!found) {
554  /* the cache is bounded by cache_max_entries entries: evict the
555  * entry with the earliest expiry (head of the tree) to make room
556  * for the new one. */
557  if (tctx->entries >= cache_max_entries) {
558  ThresholdCacheItem *victim = THRESHOLD_CACHE_RB_MINMAX(&tctx->tree, RB_NEGINF);
559  if (victim == NULL) {
560  /* defensive: cannot happen while entries > 0 */
562  return -1;
563  }
564  THRESHOLD_CACHE_RB_REMOVE(&tctx->tree, victim);
565  HashTableRemove(tctx->ht, victim, 0);
566  tctx->entries--;
567  (void)SC_ATOMIC_SUB(threshold_cache_memuse, THRESHOLD_CACHE_ENTRY_MEM);
568  }
569 
570  ThresholdCacheItem *n = SCCalloc(1, sizeof(*n));
571  if (n) {
572  n->track = track;
573  n->ipv = 4;
574  n->retval = retval;
575  n->key[TC_ADDRESS] = addr;
576  n->key[TC_SID] = sid;
577  n->key[TC_GID] = gid;
578  n->key[TC_REV] = rev;
579  n->key[TC_TENANT] = p->tenant_id;
580  n->expires_at = expires;
581 
582  if (HashTableAdd(tctx->ht, n, 0) == 0) {
583  ThresholdCacheItem *r = THRESHOLD_CACHE_RB_INSERT(&tctx->tree, n);
584  DEBUG_VALIDATE_BUG_ON(r != NULL); // duplicate; should be impossible
585  (void)r; // only used by DEBUG_VALIDATE_BUG_ON
586  tctx->entries++;
587  (void)SC_ATOMIC_ADD(threshold_cache_memuse, THRESHOLD_CACHE_ENTRY_MEM);
588  return 1;
589  }
590  SCFree(n);
591  }
592  return -1;
593  } else {
594  found->expires_at = expires;
595  found->retval = retval;
596 
597  THRESHOLD_CACHE_RB_REMOVE(&tctx->tree, found);
598  THRESHOLD_CACHE_RB_INSERT(&tctx->tree, found);
599  return 1;
600  }
601 }
602 
603 /** \brief Check Thread local thresholding cache
604  * \note only supports IPv4
605  * \retval -1 cache miss - not found
606  * \retval -2 cache miss - found but expired
607  * \retval -3 error - cache not initialized
608  * \retval -4 error - unsupported tracker
609  * \retval ret cached return code
610  */
611 static int CheckCache(DetectEngineThreadCtx *det_ctx, const Packet *p, const int8_t track,
612  const uint32_t sid, const uint32_t gid, const uint32_t rev)
613 {
614  struct ThresholdCacheThreadCtx *tctx = GetThreadCtx(det_ctx);
615  if (!tctx) {
616  return -3;
617  }
618 
619  tctx->lookup_cnt++;
620 
621  uint32_t addr;
622  if (track == TRACK_SRC) {
623  addr = p->src.addr_data32[0];
624  } else if (track == TRACK_DST) {
625  addr = p->dst.addr_data32[0];
626  } else {
627  tctx->lookup_nosupport++;
628  return -4; // error tracker not unsupported
629  }
630 
631  if (SCTIME_SECS(p->ts) > tctx->housekeeping_ts) {
632  ThresholdCacheExpire(det_ctx, p->ts);
633  }
634 
635  ThresholdCacheItem lookup = {
636  .track = track,
637  .ipv = 4,
638  .key[TC_ADDRESS] = addr,
639  .key[TC_SID] = sid,
640  .key[TC_GID] = gid,
641  .key[TC_REV] = rev,
642  .key[TC_TENANT] = p->tenant_id,
643  };
644  ThresholdCacheItem *found = HashTableLookup(tctx->ht, &lookup, 0);
645  if (found) {
646  if (SCTIME_CMP_GT(p->ts, found->expires_at)) {
647  THRESHOLD_CACHE_RB_REMOVE(&tctx->tree, found);
648  HashTableRemove(tctx->ht, found, 0);
649  tctx->lookup_miss_expired++;
650  tctx->entries--;
651  (void)SC_ATOMIC_SUB(threshold_cache_memuse, THRESHOLD_CACHE_ENTRY_MEM);
652  return -2; // cache miss - found but expired
653  }
654  tctx->lookup_hit++;
655  return found->retval;
656  }
657  tctx->lookup_miss++;
658  return -1; // cache miss - not found
659 }
660 
661 static void ThresholdCacheThreadFree(void *ptr)
662 {
663  if (ptr != NULL) {
664  struct ThresholdCacheThreadCtx *tctx = ptr;
665  DumpCacheStats(tctx);
666  (void)SC_ATOMIC_SUB(threshold_cache_memuse,
667  (uint64_t)tctx->entries * THRESHOLD_CACHE_ENTRY_MEM + tctx->init_mem);
668  HashTableFree(tctx->ht);
669  SCFree(tctx);
670  }
671 }
672 
673 static void ThresholdCacheInit(void)
674 {
675 #ifdef UNITTESTS
676  /* many tests don't manage the thread storage correctly, so skip the cache in unittests */
677  if (!(RunmodeIsUnittests())) {
678 #endif
679  intmax_t value = 0;
680  if (SCConfGetInt("detect.thresholds.cache.max-entries", &value) == 1) {
681  if (value < THRESHOLD_CACHE_MAX_ENTRIES_MIN ||
683  SCLogError("'detect.thresholds.cache.max-entries' value %" PRIdMAX
684  " out of range. Valid range %d-%d.",
686  FatalError("Invalid value for detect.thresholds.cache.max-entries");
687  }
688  cache_max_entries = (uint32_t)value;
689  }
690 
691  /* Register thread storage. */
692  thread_storage_id = SCThreadStorageRegister("threshold_cache", ThresholdCacheThreadFree);
693  if (thread_storage_id.id < 0) {
694  FatalError("Failed to register threshold_cache thread storage");
695  }
696 #ifdef UNITTESTS
697  }
698 #endif
699 }
700 
702 {
703  if (thread_storage_id.id < 0)
704  return 0;
705  /* we can get called more than once per thread for MT */
706  if (SCThreadGetStorageById(det_ctx->tv, thread_storage_id) != NULL)
707  return 0;
708 
709  struct ThresholdCacheThreadCtx *tctx = SCCalloc(1, sizeof(*tctx));
710  if (tctx == NULL)
711  return -1;
712 
713  uint32_t seed = (uint32_t)RandomGet();
714 
715  uint32_t hashsize = cache_max_entries;
717  uint32_t hashpow = 1;
718  while (hashpow < hashsize)
719  hashpow <<= 1;
720 
721  tctx->ht = HashTableInitWithSeed(hashpow, ThresholdCacheHashFunc, ThresholdCacheHashCompareFunc,
722  ThresholdCacheHashFreeFunc, seed);
723  if (tctx->ht == NULL) {
724  SCFree(tctx);
725  return -1;
726  }
727 
728  RB_INIT(&tctx->tree);
729  /* charge the fixed per-thread baseline (thread context, hash table
730  * struct and the eagerly allocated bucket pointer array) so the memuse
731  * gauge reflects all cache memory in use from the moment the cache is
732  * set up, not just the entries; released symmetrically in
733  * ThresholdCacheThreadFree */
734  tctx->init_mem =
735  sizeof(*tctx) + sizeof(*tctx->ht) + (uint64_t)hashpow * sizeof(HashTableBucket *);
736  (void)SC_ATOMIC_ADD(threshold_cache_memuse, tctx->init_mem);
737  SCThreadSetStorageById(det_ctx->tv, thread_storage_id, tctx);
738  return 0;
739 }
740 
741 /**
742  * \brief Return next DetectThresholdData for signature
743  *
744  * \param sig Signature pointer
745  * \param psm Pointer to a Signature Match pointer
746  * \param list List to return data from
747  *
748  * \retval tsh Return the threshold data from signature or NULL if not found
749  */
751  const Signature *sig, const SigMatchData **psm, int list)
752 {
753  const SigMatchData *smd = NULL;
754  const DetectThresholdData *tsh = NULL;
755 
756  if (sig == NULL)
757  return NULL;
758 
759  if (*psm == NULL) {
760  smd = sig->sm_arrays[list];
761  } else {
762  /* Iteration in progress, using provided value */
763  smd = *psm;
764  }
765 
766  while (1) {
767  if (smd->type == DETECT_THRESHOLD || smd->type == DETECT_DETECTION_FILTER) {
768  tsh = (DetectThresholdData *)smd->ctx;
769 
770  if (smd->is_last) {
771  *psm = NULL;
772  } else {
773  *psm = smd + 1;
774  }
775  return tsh;
776  }
777 
778  if (smd->is_last) {
779  break;
780  }
781  smd++;
782  }
783  *psm = NULL;
784  return NULL;
785 }
786 
787 typedef struct FlowThresholdEntryList_ {
791 
792 static void FlowThresholdEntryListFree(FlowThresholdEntryList *list)
793 {
794  for (FlowThresholdEntryList *i = list; i != NULL;) {
796  SCFree(i);
797  i = next;
798  }
799 }
800 
801 /** struct for storing per flow thresholds. This will be stored in the Flow::flowvar list, so it
802  * needs to follow the GenericVar header format. */
803 typedef struct FlowVarThreshold_ {
804  uint16_t type;
805  uint8_t pad[6];
806  struct GenericVar_ *next;
809 
810 void FlowThresholdVarFree(void *ptr)
811 {
812  FlowVarThreshold *t = ptr;
813  FlowThresholdEntryListFree(t->thresholds);
814  SCFree(t);
815 }
816 
817 static FlowVarThreshold *FlowThresholdVarGet(Flow *f)
818 {
819  if (f == NULL)
820  return NULL;
821 
822  for (GenericVar *gv = f->flowvar; gv != NULL; gv = gv->next) {
823  if (gv->type == DETECT_THRESHOLD)
824  return (FlowVarThreshold *)gv;
825  }
826 
827  return NULL;
828 }
829 
830 static ThresholdEntry *ThresholdFlowLookupEntry(
831  Flow *f, uint32_t sid, uint32_t gid, uint32_t rev, uint32_t tenant_id)
832 {
833  FlowVarThreshold *t = FlowThresholdVarGet(f);
834  if (t == NULL)
835  return NULL;
836 
837  for (FlowThresholdEntryList *e = t->thresholds; e != NULL; e = e->next) {
838  if (e->threshold.key[SID] == sid && e->threshold.key[GID] == gid &&
839  e->threshold.key[REV] == rev && e->threshold.key[TENANT] == tenant_id) {
840  return &e->threshold;
841  }
842  }
843  return NULL;
844 }
845 
846 static int AddEntryToFlow(Flow *f, FlowThresholdEntryList *e, SCTime_t packet_time)
847 {
848  DEBUG_VALIDATE_BUG_ON(e == NULL);
849 
850  FlowVarThreshold *t = FlowThresholdVarGet(f);
851  if (t == NULL) {
852  t = SCCalloc(1, sizeof(*t));
853  if (t == NULL) {
854  return -1;
855  }
856  t->type = DETECT_THRESHOLD;
858  }
859 
860  e->next = t->thresholds;
861  t->thresholds = e;
862  return 0;
863 }
864 
865 static int ThresholdHandlePacketSuppress(
866  Packet *p, const DetectThresholdData *td, uint32_t sid, uint32_t gid)
867 {
868  int ret = 0;
869  DetectAddress *m = NULL;
870  switch (td->track) {
871  case TRACK_DST:
873  SCLogDebug("TRACK_DST");
874  break;
875  case TRACK_SRC:
877  SCLogDebug("TRACK_SRC");
878  break;
879  /* suppress if either src or dst is a match on the suppress
880  * address list */
881  case TRACK_EITHER:
883  if (m == NULL) {
885  }
886  break;
887  case TRACK_RULE:
888  case TRACK_FLOW:
889  default:
890  SCLogError("track mode %d is not supported", td->track);
891  break;
892  }
893  if (m == NULL)
894  ret = 1;
895  else
896  ret = 2; /* suppressed but still need actions */
897 
898  return ret;
899 }
900 
901 static inline void RateFilterSetAction(PacketAlert *pa, uint8_t new_action)
902 {
903  switch (new_action) {
904  case TH_ACTION_ALERT:
906  pa->action = ACTION_ALERT;
907  break;
908  case TH_ACTION_DROP:
910  pa->action = (ACTION_DROP | ACTION_ALERT);
911  break;
912  case TH_ACTION_REJECT:
915  break;
916  case TH_ACTION_PASS:
918  pa->action = ACTION_PASS;
919  break;
920  default:
921  /* Weird, leave the default action */
922  break;
923  }
924 }
925 
926 /** \internal
927  * \brief Apply the multiplier and return the new value.
928  * If it would overflow the uint32_t we return UINT32_MAX.
929  */
930 static uint32_t BackoffCalcNextValue(const uint32_t cur, const uint32_t m)
931 {
932  /* goal is to see if cur * m would overflow uint32_t */
933  if (unlikely(UINT32_MAX / m < cur)) {
934  return UINT32_MAX;
935  }
936  return cur * m;
937 }
938 
939 /**
940  * \retval 2 silent match (no alert but apply actions)
941  * \retval 1 normal match
942  * \retval 0 no match
943  */
944 static int ThresholdSetup(const DetectThresholdData *td, ThresholdEntry *te, const Packet *p,
945  const uint32_t sid, const uint32_t gid, const uint32_t rev)
946 {
947  te->key[SID] = sid;
948  te->key[GID] = gid;
949  te->key[REV] = rev;
950  te->key[TRACK] = td->track;
951  te->key[TENANT] = p->tenant_id;
952 
953  te->seconds = td->seconds;
954  te->current_count = 1;
955 
956  switch (td->type) {
957  case TYPE_BACKOFF:
958  te->backoff.next_value = td->count;
959  break;
960  default:
961  te->tv1 = p->ts;
963  ThresholdDistinctInit(te, td);
964  /* If unique_on is enabled, we must add the current packet's port to the bitmap.
965  * ThresholdDistinctInit resets current_count to 0, so we must add the port
966  * or restore the count if allocation failed. */
967  if (td->type == TYPE_DETECTION && td->unique_on != DF_UNIQUE_NONE) {
968  if (te->distinct_bitmap_union) {
969  uint16_t port = (td->unique_on == DF_UNIQUE_SRC_PORT) ? p->sp : p->dp;
970  ThresholdDistinctAddPort(te, port);
971  } else {
972  /* Allocation failed (or test mode), fallback to classic counting.
973  * We must set current_count to 1 for this first packet. */
974  te->current_count = 1;
975  }
976  }
977  break;
978  }
979 
980  switch (td->type) {
981  case TYPE_LIMIT:
982  case TYPE_RATE:
983  return 1;
984  case TYPE_THRESHOLD:
985  case TYPE_BOTH:
986  if (td->count == 1)
987  return 1;
988  return 0;
989  case TYPE_BACKOFF:
990  if (td->count == 1) {
991  te->backoff.next_value =
992  BackoffCalcNextValue(te->backoff.next_value, td->multiplier);
993  return 1;
994  }
995  return 0;
996  case TYPE_DETECTION:
997  return 0;
998  }
999  return 0;
1000 }
1001 
1002 static int ThresholdCheckUpdate(const DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx,
1003  const DetectThresholdData *td, ThresholdEntry *te,
1004  const Packet *p, // ts only? - cache too
1005  const uint32_t sid, const uint32_t gid, const uint32_t rev, PacketAlert *pa)
1006 {
1007  int ret = 0;
1008  const SCTime_t packet_time = p->ts;
1009  const SCTime_t entry = SCTIME_ADD_SECS(te->tv1, td->seconds);
1010  switch (td->type) {
1011  case TYPE_LIMIT:
1012  SCLogDebug("limit");
1013 
1014  if (SCTIME_CMP_LTE(p->ts, entry)) {
1015  te->current_count++;
1016 
1017  if (te->current_count <= td->count) {
1018  ret = 1;
1019  } else {
1020  ret = 2;
1021 
1022  if (PacketIsIPv4(p)) {
1023  SetupCache(det_ctx, p, td->track, (int8_t)ret, sid, gid, rev, entry);
1024  }
1025  }
1026  } else {
1027  /* entry expired, reset */
1028  te->tv1 = p->ts;
1029  te->current_count = 1;
1030  ret = 1;
1031  }
1032  break;
1033  case TYPE_THRESHOLD:
1034  if (SCTIME_CMP_LTE(p->ts, entry)) {
1035  te->current_count++;
1036 
1037  if (te->current_count >= td->count) {
1038  ret = 1;
1039  te->current_count = 0;
1040  }
1041  } else {
1042  te->tv1 = p->ts;
1043  te->current_count = 1;
1044  }
1045  break;
1046  case TYPE_BOTH:
1047  if (SCTIME_CMP_LTE(p->ts, entry)) {
1048  /* within time limit */
1049 
1050  te->current_count++;
1051  if (te->current_count == td->count) {
1052  ret = 1;
1053  } else if (te->current_count > td->count) {
1054  /* silent match */
1055  ret = 2;
1056 
1057  if (PacketIsIPv4(p)) {
1058  SetupCache(det_ctx, p, td->track, (int8_t)ret, sid, gid, rev, entry);
1059  }
1060  }
1061  } else {
1062  /* expired, so reset */
1063  te->tv1 = p->ts;
1064  te->current_count = 1;
1065 
1066  /* if we have a limit of 1, this is a match */
1067  if (te->current_count == td->count) {
1068  ret = 1;
1069  }
1070  }
1071  break;
1072  case TYPE_DETECTION: {
1073  SCLogDebug("detection_filter");
1074 
1075  if (SCTIME_CMP_LTE(p->ts, entry)) {
1076  /* within timeout */
1077  if (td->unique_on != DF_UNIQUE_NONE && te->distinct_bitmap_union) {
1078  uint16_t port = (td->unique_on == DF_UNIQUE_SRC_PORT) ? p->sp : p->dp;
1079  ThresholdDistinctAddPort(te, port);
1080  if (te->current_count > td->count) {
1081  ret = 1;
1082  }
1083  } else {
1084  te->current_count++;
1085  if (te->current_count > td->count) {
1086  ret = 1;
1087  }
1088  }
1089  } else {
1090  /* expired, reset to new window starting now */
1091  te->tv1 = p->ts;
1092  ThresholdDistinctReset(te);
1093 
1094  /* record current packet's distinct port as the first in the new window */
1095  if (td->unique_on != DF_UNIQUE_NONE && te->distinct_bitmap_union) {
1096  uint16_t port = (td->unique_on == DF_UNIQUE_SRC_PORT) ? p->sp : p->dp;
1097  ThresholdDistinctAddPort(te, port);
1098  } else {
1099  te->current_count = 1;
1100  }
1101  }
1102  break;
1103  }
1104  case TYPE_RATE: {
1105  SCLogDebug("rate_filter");
1106  const uint8_t original_action = pa->action;
1107  ret = 1;
1108  /* Check if we have a timeout enabled, if so,
1109  * we still matching (and enabling the new_action) */
1111  if ((SCTIME_SECS(packet_time) - SCTIME_SECS(te->tv_timeout)) > td->timeout) {
1112  /* Ok, we are done, timeout reached */
1114  } else {
1115  /* Already matching */
1116  RateFilterSetAction(pa, td->new_action);
1117  }
1118  } else {
1119  /* Update the matching state with the timeout interval */
1120  if (SCTIME_CMP_LTE(packet_time, entry)) {
1121  te->current_count++;
1122  if (te->current_count > td->count) {
1123  /* Then we must enable the new action by setting a
1124  * timeout */
1125  te->tv_timeout = packet_time;
1126  RateFilterSetAction(pa, td->new_action);
1127  }
1128  } else {
1129  te->tv1 = packet_time;
1130  te->current_count = 1;
1131  }
1132  }
1133  if (de_ctx->RateFilterCallback && original_action != pa->action) {
1134  pa->action = de_ctx->RateFilterCallback(p, sid, gid, rev, original_action,
1136  if (pa->action == original_action) {
1137  /* Reset back to original action, clear modified flag. */
1139  }
1140  }
1141  break;
1142  }
1143  case TYPE_BACKOFF:
1144  SCLogDebug("backoff");
1145 
1146  if (te->current_count < UINT32_MAX) {
1147  te->current_count++;
1148  if (te->backoff.next_value == te->current_count) {
1149  te->backoff.next_value =
1150  BackoffCalcNextValue(te->backoff.next_value, td->multiplier);
1151  SCLogDebug("te->backoff.next_value %u", te->backoff.next_value);
1152  ret = 1;
1153  } else {
1154  ret = 2;
1155  }
1156  } else {
1157  /* if count reaches UINT32_MAX, we just silent match on the rest of the flow */
1158  ret = 2;
1159  }
1160  break;
1161  }
1162  return ret;
1163 }
1164 
1165 static int ThresholdGetFromHash(const DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx,
1166  struct Thresholds *tctx, const Packet *p, const Signature *s, const DetectThresholdData *td,
1167  PacketAlert *pa)
1168 {
1169  /* fast track for count 1 threshold */
1170  if (td->count == 1 && td->type == TYPE_THRESHOLD) {
1171  return 1;
1172  }
1173 
1174  ThresholdEntry lookup;
1175  memset(&lookup, 0, sizeof(lookup));
1176  lookup.key[SID] = s->id;
1177  lookup.key[GID] = s->gid;
1178  lookup.key[REV] = s->rev;
1179  lookup.key[TRACK] = td->track;
1180  lookup.key[TENANT] = p->tenant_id;
1181  if (td->track == TRACK_SRC) {
1182  COPY_ADDRESS(&p->src, &lookup.addr);
1183  } else if (td->track == TRACK_DST) {
1184  COPY_ADDRESS(&p->dst, &lookup.addr);
1185  } else if (td->track == TRACK_BOTH) {
1186  /* make sure lower ip address is first */
1187  if (PacketIsIPv4(p)) {
1188  if (SCNtohl(p->src.addr_data32[0]) < SCNtohl(p->dst.addr_data32[0])) {
1189  COPY_ADDRESS(&p->src, &lookup.addr);
1190  COPY_ADDRESS(&p->dst, &lookup.addr2);
1191  } else {
1192  COPY_ADDRESS(&p->dst, &lookup.addr);
1193  COPY_ADDRESS(&p->src, &lookup.addr2);
1194  }
1195  } else {
1196  if (AddressIPv6Lt(&p->src, &p->dst)) {
1197  COPY_ADDRESS(&p->src, &lookup.addr);
1198  COPY_ADDRESS(&p->dst, &lookup.addr2);
1199  } else {
1200  COPY_ADDRESS(&p->dst, &lookup.addr);
1201  COPY_ADDRESS(&p->src, &lookup.addr2);
1202  }
1203  }
1204  }
1205 
1206  struct THashDataGetResult res = THashGetFromHash(tctx->thash, &lookup);
1207  if (res.data) {
1208  SCLogDebug("found %p, is_new %s", res.data, BOOL2STR(res.is_new));
1209  int r;
1210  ThresholdEntry *te = res.data->data;
1211  if (res.is_new) {
1212  // new threshold, set up
1213  r = ThresholdSetup(td, te, p, s->id, s->gid, s->rev);
1214  } else {
1215  // existing, check/update
1216  r = ThresholdCheckUpdate(de_ctx, det_ctx, td, te, p, s->id, s->gid, s->rev, pa);
1217  }
1218 
1219  (void)THashDecrUsecnt(res.data);
1220  THashDataUnlock(res.data);
1221  return r;
1222  }
1223  return 0; // TODO error?
1224 }
1225 
1226 /**
1227  * \retval 2 silent match (no alert but apply actions)
1228  * \retval 1 normal match
1229  * \retval 0 no match
1230  */
1231 static int ThresholdHandlePacketFlow(const DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx,
1232  Flow *f, Packet *p, const DetectThresholdData *td, uint32_t sid, uint32_t gid, uint32_t rev,
1233  PacketAlert *pa)
1234 {
1235  int ret = 0;
1236  ThresholdEntry *found = ThresholdFlowLookupEntry(f, sid, gid, rev, p->tenant_id);
1237  SCLogDebug("found %p sid %u gid %u rev %u", found, sid, gid, rev);
1238 
1239  if (found == NULL) {
1240  FlowThresholdEntryList *new = SCCalloc(1, sizeof(*new));
1241  if (new == NULL)
1242  return 0;
1243 
1244  // new threshold, set up
1245  ret = ThresholdSetup(td, &new->threshold, p, sid, gid, rev);
1246 
1247  if (AddEntryToFlow(f, new, p->ts) == -1) {
1248  SCFree(new);
1249  return 0;
1250  }
1251  } else {
1252  // existing, check/update
1253  ret = ThresholdCheckUpdate(de_ctx, det_ctx, td, found, p, sid, gid, rev, pa);
1254  }
1255  return ret;
1256 }
1257 
1258 /**
1259  * \brief Make the threshold logic for signatures
1260  *
1261  * \param de_ctx Detection Context
1262  * \param tsh_ptr Threshold element
1263  * \param p Packet structure
1264  * \param s Signature structure
1265  *
1266  * \retval 2 silent match (no alert but apply actions)
1267  * \retval 1 alert on this event
1268  * \retval 0 do not alert on this event
1269  */
1271  const DetectThresholdData *td, Packet *p, const Signature *s, PacketAlert *pa)
1272 {
1273  SCEnter();
1274 
1275  int ret = 0;
1276  if (td == NULL) {
1277  SCReturnInt(0);
1278  }
1279 
1280  if (td->type == TYPE_SUPPRESS) {
1281  ret = ThresholdHandlePacketSuppress(p, td, s->id, s->gid);
1282  } else if (td->track == TRACK_SRC) {
1283  if (PacketIsIPv4(p) && (td->type == TYPE_LIMIT || td->type == TYPE_BOTH)) {
1284  int cache_ret = CheckCache(det_ctx, p, td->track, s->id, s->gid, s->rev);
1285  if (cache_ret >= 0) {
1286  SCReturnInt(cache_ret);
1287  }
1288  }
1289 
1290  ret = ThresholdGetFromHash(de_ctx, det_ctx, &ctx, p, s, td, pa);
1291  } else if (td->track == TRACK_DST) {
1292  if (PacketIsIPv4(p) && (td->type == TYPE_LIMIT || td->type == TYPE_BOTH)) {
1293  int cache_ret = CheckCache(det_ctx, p, td->track, s->id, s->gid, s->rev);
1294  if (cache_ret >= 0) {
1295  SCReturnInt(cache_ret);
1296  }
1297  }
1298 
1299  ret = ThresholdGetFromHash(de_ctx, det_ctx, &ctx, p, s, td, pa);
1300  } else if (td->track == TRACK_BOTH) {
1301  ret = ThresholdGetFromHash(de_ctx, det_ctx, &ctx, p, s, td, pa);
1302  } else if (td->track == TRACK_RULE) {
1303  ret = ThresholdGetFromHash(de_ctx, det_ctx, &ctx, p, s, td, pa);
1304  } else if (td->track == TRACK_FLOW) {
1305  if (p->flow) {
1306  ret = ThresholdHandlePacketFlow(
1307  de_ctx, det_ctx, p->flow, p, td, s->id, s->gid, s->rev, pa);
1308  }
1309  }
1310 
1311  SCReturnInt(ret);
1312 }
1313 
1314 /**
1315  * @}
1316  */
THRESHOLD_CACHE_MAX_ENTRIES_MAX
#define THRESHOLD_CACHE_MAX_ENTRIES_MAX
Definition: detect-engine-threshold.c:445
TRACK_BOTH
#define TRACK_BOTH
Definition: detect-threshold.h:39
SID
#define SID
Definition: detect-engine-threshold.c:160
DetectThresholdData_::timeout
uint32_t timeout
Definition: detect-threshold.h:68
ThresholdEntry_::tv_timeout
SCTime_t tv_timeout
Definition: detect-engine-threshold.c:169
StatsRegisterGlobalCounter
StatsCounterGlobalId StatsRegisterGlobalCounter(const char *name, uint64_t(*Func)(void))
Registers a counter, which represents a global value.
Definition: counters.c:1093
ts
uint64_t ts
Definition: source-erf-file.c:68
GenericVarAppend
void GenericVarAppend(GenericVar **list, GenericVar *gv)
Definition: util-var.c:98
detect-engine.h
FlowVarThreshold_
Definition: detect-engine-threshold.c:803
THashDataGetResult::data
THashData * data
Definition: util-thash.h:192
hashword
uint32_t hashword(const uint32_t *k, size_t length, uint32_t initval)
Definition: util-hash-lookup3.c:172
DF_PORT_BITMAP_SIZE
#define DF_PORT_BITMAP_SIZE
Definition: detect-engine-threshold.c:93
SCTIME_CMP_NEQ
#define SCTIME_CMP_NEQ(a, b)
Definition: util-time.h:107
ThresholdCacheThreadCtx::lookup_nosupport
uint64_t lookup_nosupport
Definition: detect-engine-threshold.c:433
FlowVarThreshold_::next
struct GenericVar_ * next
Definition: detect-engine-threshold.c:806
SC_ATOMIC_INIT
#define SC_ATOMIC_INIT(name)
wrapper for initializing an atomic variable.
Definition: util-atomic.h:314
Thresholds::thash
THashTableContext * thash
Definition: detect-engine-threshold.c:98
unlikely
#define unlikely(expr)
Definition: util-optimize.h:35
ThresholdEntry
struct ThresholdEntry_ ThresholdEntry
ACTION_PASS
#define ACTION_PASS
Definition: action-globals.h:34
ACTION_REJECT
#define ACTION_REJECT
Definition: action-globals.h:31
TYPE_BACKOFF
#define TYPE_BACKOFF
Definition: detect-threshold.h:33
TRACK
#define TRACK
Definition: detect-engine-threshold.c:163
ThresholdCacheThreadCtx::lookup_cnt
uint64_t lookup_cnt
Definition: detect-engine-threshold.c:432
DetectAddress_
address structure for use in the detection engine.
Definition: detect.h:170
GID
#define GID
Definition: detect-engine-threshold.c:161
SCLogDebug
#define SCLogDebug(...)
Definition: util-debug.h:282
ParseSizeStringU64
int ParseSizeStringU64(const char *size, uint64_t *res)
Definition: util-misc.c:190
ThresholdCacheItem
struct ThresholdCacheItem ThresholdCacheItem
next
struct HtpBodyChunk_ * next
Definition: app-layer-htp.h:0
SigMatchData_::is_last
bool is_last
Definition: detect.h:371
TC_GID
#define TC_GID
Definition: detect-engine-threshold.c:397
SCThreadStorageRegister
SCThreadStorageId SCThreadStorageRegister(const char *name, void(*Free)(void *))
Definition: thread-storage.c:50
RB_PROTOTYPE
RB_PROTOTYPE(THRESHOLD_CACHE, ThresholdCacheItem, rb, ThresholdCacheTreeCompareFunc)
DetectThresholdData_::count
uint32_t count
Definition: detect-threshold.h:63
SigMatchData_::ctx
SigMatchCtx * ctx
Definition: detect.h:372
Thresholds
Definition: detect-engine-threshold.c:97
action-globals.h
Flow_
Flow data structure.
Definition: flow.h:359
util-hash.h
ctx
struct Thresholds ctx
SC_ATOMIC_ADD
#define SC_ATOMIC_ADD(name, val)
add a value to our atomic variable
Definition: util-atomic.h:332
DetectEngineCtx_
main detection engine ctx
Definition: detect.h:995
TYPE_LIMIT
#define TYPE_LIMIT
Definition: detect-threshold.h:27
SCThreadSetStorageById
int SCThreadSetStorageById(ThreadVars *tv, SCThreadStorageId id, void *ptr)
Definition: thread-storage.c:34
TRACK_DST
#define TRACK_DST
Definition: detect-detection-filter.c:43
HashTable_
Definition: util-hash.h:35
DetectThresholdData_::new_action
uint8_t new_action
Definition: detect-threshold.h:67
FlowVarThreshold_::pad
uint8_t pad[6]
Definition: detect-engine-threshold.c:805
Address_
Definition: decode.h:113
DetectThresholdData_::multiplier
uint32_t multiplier
Definition: detect-threshold.h:70
ThresholdCacheThreadCtx::init_mem
uint64_t init_mem
Definition: detect-engine-threshold.c:430
p
Packet * p
Definition: fuzz_dataset.c:30
TH_ACTION_ALERT
#define TH_ACTION_ALERT
Definition: detect-threshold.h:43
Signature_::sm_arrays
SigMatchData * sm_arrays[DETECT_SM_LIST_MAX]
Definition: detect.h:759
RandomGet
long int RandomGet(void)
Definition: util-random.c:130
m
SCMutex m
Definition: flow-hash.h:6
ThresholdCacheItem
Definition: detect-engine-threshold.c:401
FlowThresholdEntryList_::threshold
ThresholdEntry threshold
Definition: detect-engine-threshold.c:789
SigMatchData_
Data needed for Match()
Definition: detect.h:369
SigMatchData_::type
uint16_t type
Definition: detect.h:370
ThresholdCacheThreadCtx::lookup_hit
uint64_t lookup_hit
Definition: detect-engine-threshold.c:436
TC_ADDRESS
#define TC_ADDRESS
Definition: detect-engine-threshold.c:395
ThresholdRegisterGlobalCounters
void ThresholdRegisterGlobalCounters(void)
Definition: detect-engine-threshold.c:145
DetectAddressLookupInHead
DetectAddress * DetectAddressLookupInHead(const DetectAddressHead *gh, Address *a)
Find the group matching address in a group head.
Definition: detect-engine-address.c:1812
HashTableFree
void HashTableFree(HashTable *ht)
Free a HashTable and all its contents.
Definition: util-hash.c:111
DetectThresholdData_::unique_on
enum DetectThresholdUniqueOn unique_on
Definition: detect-threshold.h:71
DetectThresholdData_::type
uint8_t type
Definition: detect-threshold.h:65
ThresholdCacheThreadCtx::lookup_miss_expired
uint64_t lookup_miss_expired
Definition: detect-engine-threshold.c:434
SCThreadStorageId::id
int id
Definition: thread-storage.h:30
HashTable_::array_size
uint32_t array_size
Definition: util-hash.h:37
PacketAlert_::action
uint8_t action
Definition: decode.h:251
Signature_::gid
uint32_t gid
Definition: detect.h:742
HashTableBucket_
Definition: util-hash.h:28
TC_TENANT
#define TC_TENANT
Definition: detect-engine-threshold.c:399
FlowThresholdEntryList_::next
struct FlowThresholdEntryList_ * next
Definition: detect-engine-threshold.c:788
FlowThresholdEntryList_
Definition: detect-engine-threshold.c:787
ThresholdForceAllocFail
void ThresholdForceAllocFail(int v)
Definition: detect-engine-threshold.c:76
ThresholdCacheThreadCtx::tree
struct THRESHOLD_CACHE tree
Definition: detect-engine-threshold.c:427
counters.h
ThresholdGetBitmapAllocFail
uint64_t ThresholdGetBitmapAllocFail(void)
Definition: detect-engine-threshold.c:86
TRACK_RULE
#define TRACK_RULE
Definition: detect-threshold.h:37
RB_INIT
#define RB_INIT(root)
Definition: tree.h:308
util-debug.h
TRACK_FLOW
#define TRACK_FLOW
Definition: detect-threshold.h:40
ThresholdEntry_::seconds
uint32_t seconds
Definition: detect-engine-threshold.c:171
GenericVar_::next
struct GenericVar_ * next
Definition: util-var.h:57
util-error.h
REV
#define REV
Definition: detect-engine-threshold.c:162
de_ctx
DetectEngineCtx * de_ctx
Definition: fuzz_siginit.c:22
HashTableInitWithSeed
HashTable * HashTableInitWithSeed(uint32_t size, uint32_t(*Hash)(struct HashTable_ *, void *, uint16_t), char(*Compare)(void *, uint16_t, void *, uint16_t), void(*Free)(void *), const uint32_t seed)
Definition: util-hash.c:77
TYPE_RATE
#define TYPE_RATE
Definition: detect-threshold.h:31
FlowThresholdEntryList
struct FlowThresholdEntryList_ FlowThresholdEntryList
DetectEngineThreadCtx_
Definition: detect.h:1316
Packet_::ts
SCTime_t ts
Definition: decode.h:570
DETECT_THRESHOLD
@ DETECT_THRESHOLD
Definition: detect-engine-register.h:67
THashTableContext_
Definition: util-thash.h:141
ThresholdEntry_::next_value
uint32_t next_value
Definition: detect-engine-threshold.c:176
SCThreadStorageId
Definition: thread-storage.h:29
TH_ACTION_PASS
#define TH_ACTION_PASS
Definition: detect-threshold.h:45
SCConfGetInt
int SCConfGetInt(const char *name, intmax_t *val)
Retrieve a configuration value as an integer.
Definition: conf.c:440
ThresholdEntry_::current_count
uint32_t current_count
Definition: detect-engine-threshold.c:172
BOOL2STR
#define BOOL2STR(b)
Definition: util-debug.h:542
RB_FOREACH_SAFE
#define RB_FOREACH_SAFE(x, name, head, y)
Definition: tree.h:791
SCEnter
#define SCEnter(...)
Definition: util-debug.h:284
HashTableLookup
void * HashTableLookup(HashTable *ht, void *data, uint16_t datalen)
Definition: util-hash.c:193
SCConfGetNonNull
int SCConfGetNonNull(const char *name, const char **vptr)
Retrieve the non-null value of a configuration node.
Definition: conf.c:380
detect.h
ThresholdCacheThreadCtx::housekeeping_ts
uint64_t housekeeping_ts
Definition: detect-engine-threshold.c:428
Packet_::sp
Port sp
Definition: decode.h:523
HashTableRemove
int HashTableRemove(HashTable *ht, void *data, uint16_t datalen)
Remove an item from the hash table.
Definition: util-hash.c:177
ThresholdDestroy
void ThresholdDestroy(void)
Definition: detect-engine-threshold.c:155
thread-storage.h
HashTableAdd
int HashTableAdd(HashTable *ht, void *data, uint16_t datalen)
Definition: util-hash.c:131
TYPE_BOTH
#define TYPE_BOTH
Definition: detect-threshold.h:28
SC_ATOMIC_SUB
#define SC_ATOMIC_SUB(name, val)
sub a value from our atomic variable
Definition: util-atomic.h:341
SC_ATOMIC_DECLARE
#define SC_ATOMIC_DECLARE(type, name)
wrapper for declaring atomic variables.
Definition: util-atomic.h:280
ThresholdCacheThreadCtx::housekeeping_expired
uint64_t housekeeping_expired
Definition: detect-engine-threshold.c:438
THashDataGetResult
Definition: util-thash.h:191
ACTION_ALERT
#define ACTION_ALERT
Definition: action-globals.h:29
Packet_
Definition: decode.h:516
TRACK_EITHER
#define TRACK_EITHER
Definition: detect-threshold.h:38
SCTime_t
Definition: util-time.h:40
ThresholdCacheThreadInit
int ThresholdCacheThreadInit(DetectEngineThreadCtx *det_ctx)
Definition: detect-engine-threshold.c:701
ThresholdCacheThreadCtx::entries
uint32_t entries
Definition: detect-engine-threshold.c:429
ThresholdCacheItem::ipv
int8_t ipv
Definition: detect-engine-threshold.c:403
DetectEngineCtx_::RateFilterCallback
SCDetectRateFilterFunc RateFilterCallback
Definition: detect.h:1217
RunmodeIsUnittests
int RunmodeIsUnittests(void)
Definition: suricata.c:293
FlowVarThreshold_::thresholds
FlowThresholdEntryList * thresholds
Definition: detect-engine-threshold.c:807
Flow_::flowvar
GenericVar * flowvar
Definition: flow.h:494
SCThreadGetStorageById
void * SCThreadGetStorageById(const ThreadVars *tv, SCThreadStorageId id)
Definition: thread-storage.c:29
DetectThresholdData_::track
uint8_t track
Definition: detect-threshold.h:66
ThresholdEntry_
Definition: detect-engine-threshold.c:166
RB_NEGINF
#define RB_NEGINF
Definition: tree.h:769
ThresholdEntry_::backoff
struct ThresholdEntry_::@61::@63 backoff
THRESHOLD_CACHE_MAX_ENTRIES_DEFAULT
#define THRESHOLD_CACHE_MAX_ENTRIES_DEFAULT
Definition: detect-engine-threshold.c:443
THashShutdown
void THashShutdown(THashTableContext *ctx)
shutdown the flow engine
Definition: util-thash.c:354
SCTIME_CMP_LT
#define SCTIME_CMP_LT(a, b)
Definition: util-time.h:105
PacketAlert_::flags
uint8_t flags
Definition: decode.h:252
ThresholdEntry_::tv1
SCTime_t tv1
Definition: detect-engine-threshold.c:179
ThresholdCacheThreadCtx::lookup_miss
uint64_t lookup_miss
Definition: detect-engine-threshold.c:435
THRESHOLD_CACHE_ENTRY_MEM
#define THRESHOLD_CACHE_ENTRY_MEM
Definition: detect-engine-threshold.c:450
ThresholdCacheThreadCtx
Definition: detect-engine-threshold.c:425
TH_ACTION_REJECT
#define TH_ACTION_REJECT
Definition: detect-threshold.h:48
THashData_::data
void * data
Definition: util-thash.h:92
cnt
uint32_t cnt
Definition: tmqh-packetpool.h:7
Packet_::flow
struct Flow_ * flow
Definition: decode.h:564
Packet_::tenant_id
uint32_t tenant_id
Definition: decode.h:678
CMP_ADDR
#define CMP_ADDR(a1, a2)
Definition: decode.h:223
suricata-common.h
DetectThresholdData_
Definition: detect-threshold.h:62
GenericVar_
Definition: util-var.h:53
DF_PORT_BIT_MASK
#define DF_PORT_BIT_MASK(p)
Definition: detect-engine-threshold.c:95
TYPE_SUPPRESS
#define TYPE_SUPPRESS
Definition: detect-threshold.h:32
ThresholdCacheThreadCtx::ht
HashTable * ht
Definition: detect-engine-threshold.c:426
TC_SID
#define TC_SID
Definition: detect-engine-threshold.c:396
ACTION_DROP
#define ACTION_DROP
Definition: action-globals.h:30
SCLogPerf
#define SCLogPerf(...)
Definition: util-debug.h:241
SCTIME_SECS
#define SCTIME_SECS(t)
Definition: util-time.h:57
DetectEngineCtx_::rate_filter_callback_arg
void * rate_filter_callback_arg
Definition: detect.h:1220
TH_ACTION_DROP
#define TH_ACTION_DROP
Definition: detect-threshold.h:44
Signature_::rev
uint32_t rev
Definition: detect.h:743
ThresholdEntry_::addr2
Address addr2
Definition: detect-engine-threshold.c:181
THashGetFromHash
struct THashDataGetResult THashGetFromHash(THashTableContext *ctx, void *data)
Definition: util-thash.c:637
FatalError
#define FatalError(...)
Definition: util-debug.h:517
hashsize
#define hashsize(n)
Definition: util-hash-lookup3.h:40
util-hash-lookup3.h
detect-engine-address-ipv6.h
ThresholdCacheItem::track
int8_t track
Definition: detect-engine-threshold.c:402
THashDecrUsecnt
#define THashDecrUsecnt(h)
Definition: util-thash.h:170
SigGetThresholdTypeIter
const DetectThresholdData * SigGetThresholdTypeIter(const Signature *sig, const SigMatchData **psm, int list)
Return next DetectThresholdData for signature.
Definition: detect-engine-threshold.c:750
util-validate.h
ThresholdGetBitmapMemuse
uint64_t ThresholdGetBitmapMemuse(void)
Definition: detect-engine-threshold.c:81
SCTIME_CMP_GT
#define SCTIME_CMP_GT(a, b)
Definition: util-time.h:104
THRESHOLD_CACHE_MAX_ENTRIES_MIN
#define THRESHOLD_CACHE_MAX_ENTRIES_MIN
Definition: detect-engine-threshold.c:444
TYPE_THRESHOLD
#define TYPE_THRESHOLD
Definition: detect-threshold.h:29
DETECT_DETECTION_FILTER
@ DETECT_DETECTION_FILTER
Definition: detect-engine-register.h:130
HtpBodyChunk_::next
struct HtpBodyChunk_ * next
Definition: app-layer-htp.h:124
PACKET_ALERT_FLAG_RATE_FILTER_MODIFIED
#define PACKET_ALERT_FLAG_RATE_FILTER_MODIFIED
Definition: decode.h:276
ThresholdCacheItem::expires_at
SCTime_t expires_at
Definition: detect-engine-threshold.c:406
str
#define str(s)
Definition: suricata-common.h:313
DetectEngineThreadCtx_::tv
ThreadVars * tv
Definition: detect.h:1324
SCLogError
#define SCLogError(...)
Macro used to log ERROR messages.
Definition: util-debug.h:274
ThresholdEntry_::distinct_bitmap_union
uint8_t * distinct_bitmap_union
Definition: detect-engine-threshold.c:183
SCFree
#define SCFree(p)
Definition: util-mem.h:61
SCNtohl
#define SCNtohl(x)
Definition: suricata-common.h:435
AddressIPv6Lt
int AddressIPv6Lt(const Address *a, const Address *b)
Compares 2 ipv6 addresses and returns if the first address(a) is less than the second address(b) or n...
Definition: detect-engine-address-ipv6.c:51
SCTIME_CMP_GTE
#define SCTIME_CMP_GTE(a, b)
Definition: util-time.h:103
Signature_::id
uint32_t id
Definition: detect.h:741
ThresholdEntry_::key
uint32_t key[5]
Definition: detect-engine-threshold.c:167
detect-parse.h
src
uint16_t src
Definition: app-layer-dnp3.h:5
Signature_
Signature container.
Definition: detect.h:692
FlowVarThreshold_::type
uint16_t type
Definition: detect-engine-threshold.c:804
ThresholdEntry_::addr
Address addr
Definition: detect-engine-threshold.c:180
DF_UNIQUE_NONE
@ DF_UNIQUE_NONE
Definition: detect-threshold.h:52
RB_GENERATE
RB_GENERATE(THRESHOLD_CACHE, ThresholdCacheItem, rb, ThresholdCacheTreeCompareFunc)
util-random.h
THashDataGetResult::is_new
bool is_new
Definition: util-thash.h:193
RB_ENTRY
#define RB_ENTRY(type)
Definition: tree.h:314
Address_::family
char family
Definition: decode.h:114
Packet_::dst
Address dst
Definition: decode.h:521
THashInit
THashTableContext * THashInit(const char *cnf_prefix, uint32_t data_size, int(*DataSet)(void *, void *), void(*DataFree)(void *), uint32_t(*DataHash)(uint32_t, void *), bool(*DataCompare)(void *, void *), bool(*DataExpired)(void *, SCTime_t), uint32_t(*DataSize)(void *), bool reset_memcap, uint64_t memcap, uint32_t hashsize)
Definition: util-thash.c:302
TRACK_SRC
#define TRACK_SRC
Definition: detect-detection-filter.c:44
PacketAlert_
Definition: decode.h:249
THashExpire
uint32_t THashExpire(THashTableContext *ctx, const SCTime_t ts)
expire data from the hash Walk the hash table and remove data that is exprired according to the DataE...
Definition: util-thash.c:442
DF_UNIQUE_SRC_PORT
@ DF_UNIQUE_SRC_PORT
Definition: detect-threshold.h:53
ThresholdCacheThreadCtx::housekeeping_check
uint64_t housekeeping_check
Definition: detect-engine-threshold.c:437
DetectThresholdData_::seconds
uint32_t seconds
Definition: detect-threshold.h:64
dst
uint16_t dst
Definition: app-layer-dnp3.h:4
SC_ATOMIC_GET
#define SC_ATOMIC_GET(name)
Get the value from the atomic variable.
Definition: util-atomic.h:375
PacketAlertThreshold
int PacketAlertThreshold(const DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx, const DetectThresholdData *td, Packet *p, const Signature *s, PacketAlert *pa)
Make the threshold logic for signatures.
Definition: detect-engine-threshold.c:1270
util-misc.h
COPY_ADDRESS
#define COPY_ADDRESS(a, b)
Definition: decode.h:128
flow.h
util-thash.h
SCTIME_INITIALIZER
#define SCTIME_INITIALIZER
Definition: util-time.h:51
SCTIME_ADD_SECS
#define SCTIME_ADD_SECS(ts, s)
Definition: util-time.h:64
Packet_::dp
Port dp
Definition: decode.h:531
TENANT
#define TENANT
Definition: detect-engine-threshold.c:164
SCCalloc
#define SCCalloc(nm, sz)
Definition: util-mem.h:53
SCReturnInt
#define SCReturnInt(x)
Definition: util-debug.h:288
ThresholdCacheItem::retval
int8_t retval
Definition: detect-engine-threshold.c:404
DF_PORT_BYTE_IDX
#define DF_PORT_BYTE_IDX(p)
Definition: detect-engine-threshold.c:94
FlowThresholdVarFree
void FlowThresholdVarFree(void *ptr)
Definition: detect-engine-threshold.c:810
ThresholdInit
void ThresholdInit(void)
Definition: detect-engine-threshold.c:133
THASH_CHECK_MEMCAP
#define THASH_CHECK_MEMCAP(ctx, size)
check if a memory alloc would fit in the memcap
Definition: util-thash.h:164
DEBUG_VALIDATE_BUG_ON
#define DEBUG_VALIDATE_BUG_ON(exp)
Definition: util-validate.h:109
SCTIME_CMP_LTE
#define SCTIME_CMP_LTE(a, b)
Definition: util-time.h:106
ThresholdCacheItem::key
uint32_t key[5]
Definition: detect-engine-threshold.c:405
detect-engine-address.h
Packet_::src
Address src
Definition: decode.h:520
detect-engine-threshold.h
ThresholdsExpire
uint32_t ThresholdsExpire(const SCTime_t ts)
Definition: detect-engine-threshold.c:390
HashTable_::seed
uint32_t seed
Definition: util-hash.h:38
TC_REV
#define TC_REV
Definition: detect-engine-threshold.c:398
TYPE_DETECTION
#define TYPE_DETECTION
Definition: detect-threshold.h:30
f
Flow f
Definition: fuzz_dataset.c:32
THashTableContext_::config
THashConfig config
Definition: util-thash.h:151
RB_HEAD
RB_HEAD(THRESHOLD_CACHE, ThresholdCacheItem)
FlowVarThreshold
struct FlowVarThreshold_ FlowVarThreshold
DetectThresholdData_::addrs
DetectAddressHead addrs
Definition: detect-threshold.h:73