Coverage Report

Created: 2026-06-07 07:05

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