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