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

OISF / suricata / 22522913668

28 Feb 2026 02:48PM UTC coverage: 73.705% (+0.02%) from 73.687%
22522913668

Pull #14916

github

web-flow
Merge d18a4d509 into 90823fa90
Pull Request #14916: output/ipv6: Add per-output configuration option to compress IPv6 IP addresses

38322 of 77382 branches covered (49.52%)

Branch coverage included in aggregate %.

203 of 216 new or added lines in 15 files covered. (93.98%)

48 existing lines in 16 files now uncovered.

265573 of 334933 relevant lines covered (79.29%)

4563575.4 hits per line

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

80.61
/src/detect-engine-threshold.c
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

60
static SC_ATOMIC_DECLARE(uint64_t, threshold_bitmap_alloc_fail);
61
static SC_ATOMIC_DECLARE(uint64_t, threshold_bitmap_memuse);
62

63
/* UNITTESTS-only test seam to force allocation failure and query counters */
64
#ifdef UNITTESTS
65
void ThresholdForceAllocFail(int v);
66
uint64_t ThresholdGetBitmapMemuse(void);
67
uint64_t ThresholdGetBitmapAllocFail(void);
68

69
static int g_threshold_force_alloc_fail = 0;
70

71
void ThresholdForceAllocFail(int v)
72
{
4✔
73
    g_threshold_force_alloc_fail = v;
4✔
74
}
4✔
75

76
uint64_t ThresholdGetBitmapMemuse(void)
77
{
10✔
78
    return SC_ATOMIC_GET(threshold_bitmap_memuse);
10✔
79
}
10✔
80

81
uint64_t ThresholdGetBitmapAllocFail(void)
82
{
2✔
83
    return SC_ATOMIC_GET(threshold_bitmap_alloc_fail);
2✔
84
}
2✔
85
#endif
86

87
/* bitmap settings for exact distinct counting of 16-bit ports */
88
#define DF_PORT_BITMAP_SIZE (65536u / 8u)
27✔
89
#define DF_PORT_BYTE_IDX(p) ((uint32_t)((p) >> 3))
23✔
90
#define DF_PORT_BIT_MASK(p) ((uint8_t)(1u << ((p)&7u)))
23✔
91

92
struct Thresholds {
93
    THashTableContext *thash;
94
} ctx;
95

96
static int ThresholdsInit(struct Thresholds *t);
97
static void ThresholdsDestroy(struct Thresholds *t);
98

99
static uint64_t ThresholdBitmapAllocFailCounter(void)
100
{
3,011✔
101
    return SC_ATOMIC_GET(threshold_bitmap_alloc_fail);
3,011✔
102
}
3,011✔
103

104
static uint64_t ThresholdBitmapMemuseCounter(void)
105
{
3,011✔
106
    return SC_ATOMIC_GET(threshold_bitmap_memuse);
3,011✔
107
}
3,011✔
108

109
static uint64_t ThresholdMemuseCounter(void)
110
{
3,011✔
111
    if (ctx.thash == NULL)
3,011!
112
        return 0;
×
113
    return SC_ATOMIC_GET(ctx.thash->memuse);
3,011✔
114
}
3,011✔
115

116
static uint64_t ThresholdMemcapCounter(void)
117
{
3,011✔
118
    if (ctx.thash == NULL)
3,011!
119
        return 0;
×
120
    return SC_ATOMIC_GET(ctx.thash->config.memcap);
3,011✔
121
}
3,011✔
122

123
void ThresholdInit(void)
124
{
2,230✔
125
    SC_ATOMIC_INIT(threshold_bitmap_alloc_fail);
2,230✔
126
    SC_ATOMIC_INIT(threshold_bitmap_memuse);
2,230✔
127
    ThresholdsInit(&ctx);
2,230✔
128
}
2,230✔
129

130
void ThresholdRegisterGlobalCounters(void)
131
{
3,565✔
132
    StatsRegisterGlobalCounter("detect.thresholds.memuse", ThresholdMemuseCounter);
3,565✔
133
    StatsRegisterGlobalCounter("detect.thresholds.memcap", ThresholdMemcapCounter);
3,565✔
134
    StatsRegisterGlobalCounter("detect.thresholds.bitmap_memuse", ThresholdBitmapMemuseCounter);
3,565✔
135
    StatsRegisterGlobalCounter(
3,565✔
136
            "detect.thresholds.bitmap_alloc_fail", ThresholdBitmapAllocFailCounter);
3,565✔
137
}
3,565✔
138

139
void ThresholdDestroy(void)
140
{
2,151✔
141
    ThresholdsDestroy(&ctx);
2,151✔
142
}
2,151✔
143

144
#define SID    0
51,548✔
145
#define GID    1
26,231✔
146
#define REV    2
26,232✔
147
#define TRACK  3
2,756✔
148
#define TENANT 4
26,232✔
149

150
typedef struct ThresholdEntry_ {
151
    uint32_t key[5];
152

153
    SCTime_t tv_timeout;    /**< Timeout for new_action (for rate_filter)
154
                                 its not "seconds", that define the time interval */
155
    uint32_t seconds;       /**< Event seconds */
156
    uint32_t current_count; /**< Var for count control */
157

158
    union {
159
        struct {
160
            uint32_t next_value;
161
        } backoff;
162
        struct {
163
            SCTime_t tv1;  /**< Var for time control */
164
            Address addr;  /* used for src/dst/either tracking */
165
            Address addr2; /* used for both tracking */
166
            /* distinct counting state (for detection_filter unique_on ports) */
167
            uint8_t *distinct_bitmap_union; /* 8192 bytes (65536 bits) */
168
        };
169
    };
170

171
} ThresholdEntry;
172

173
static int ThresholdEntrySet(void *dst, void *src)
174
{
158✔
175
    const ThresholdEntry *esrc = src;
158✔
176
    ThresholdEntry *edst = dst;
158✔
177
    memset(edst, 0, sizeof(*edst));
158✔
178
    *edst = *esrc;
158✔
179
    return 0;
158✔
180
}
158✔
181

182
static void ThresholdDistinctInit(ThresholdEntry *te, const DetectThresholdData *td)
183
{
173✔
184
    if (td->type != TYPE_DETECTION || td->unique_on == DF_UNIQUE_NONE) {
173✔
185
        return;
160✔
186
    }
160✔
187
    DEBUG_VALIDATE_BUG_ON(td->seconds == 0);
13✔
188

189
    const uint32_t bitmap_size = DF_PORT_BITMAP_SIZE;
13✔
190
    te->current_count = 0;
13✔
191
#ifdef UNITTESTS
9✔
192
    if (g_threshold_force_alloc_fail) {
9✔
193
        SC_ATOMIC_ADD(threshold_bitmap_alloc_fail, 1);
2✔
194
        te->distinct_bitmap_union = NULL;
2✔
195
        return;
2✔
196
    }
2✔
197
#endif
7✔
198
    /* Check memcap before allocating bitmap.
199
     * Bitmap memory is bounded by detect.thresholds.memcap via thash.
200
     * Note: if ctx.thash is NULL (e.g. init failed or unittests), we bypass
201
     * the memcap check but still attempt allocation unless forced to fail. */
202
    if (ctx.thash != NULL && !THASH_CHECK_MEMCAP(ctx.thash, bitmap_size)) {
11!
203
        SC_ATOMIC_ADD(threshold_bitmap_alloc_fail, 1);
×
204
        te->distinct_bitmap_union = NULL;
×
205
        return;
×
206
    }
×
207

208
    te->distinct_bitmap_union = SCCalloc(1, bitmap_size);
11✔
209
    if (te->distinct_bitmap_union == NULL) {
11!
210
        SC_ATOMIC_ADD(threshold_bitmap_alloc_fail, 1);
×
211
    } else {
11✔
212
        /* Track bitmap memory in thash memuse for proper accounting */
213
        if (ctx.thash != NULL) {
11!
214
            (void)SC_ATOMIC_ADD(ctx.thash->memuse, bitmap_size);
11✔
215
        }
11✔
216
        SC_ATOMIC_ADD(threshold_bitmap_memuse, bitmap_size);
11✔
217
    }
11✔
218
}
11✔
219

220
static void ThresholdDistinctReset(ThresholdEntry *te)
221
{
3✔
222
    const uint32_t bitmap_size = DF_PORT_BITMAP_SIZE;
3✔
223
    if (te->distinct_bitmap_union) {
3!
224
        memset(te->distinct_bitmap_union, 0x00, bitmap_size);
1✔
225
    }
1✔
226
    te->current_count = 0;
3✔
227
}
3✔
228

229
static inline void ThresholdDistinctAddPort(ThresholdEntry *te, uint16_t port)
230
{
23✔
231
    const uint32_t byte_index = DF_PORT_BYTE_IDX(port);
23✔
232
    const uint8_t bit_mask = DF_PORT_BIT_MASK(port);
23✔
233
    if (te->distinct_bitmap_union) {
23!
234
        bool already = (te->distinct_bitmap_union[byte_index] & bit_mask);
23✔
235
        if (!already) {
23✔
236
            te->distinct_bitmap_union[byte_index] =
21✔
237
                    (uint8_t)(te->distinct_bitmap_union[byte_index] | bit_mask);
21✔
238
            te->current_count++;
21✔
239
        }
21✔
240
    }
23✔
241
}
23✔
242

243
static void ThresholdEntryFree(void *ptr)
244
{
2,151,079✔
245
    if (ptr == NULL)
2,151,079!
246
        return;
×
247

248
    ThresholdEntry *e = ptr;
2,151,079✔
249
    if (e->distinct_bitmap_union) {
2,151,079✔
250
        const uint32_t bitmap_size = DF_PORT_BITMAP_SIZE;
11✔
251
        /* Decrement bitmap memory from thash memuse */
252
        if (ctx.thash != NULL) {
11!
253
            (void)SC_ATOMIC_SUB(ctx.thash->memuse, bitmap_size);
11✔
254
        }
11✔
255
        SC_ATOMIC_SUB(threshold_bitmap_memuse, bitmap_size);
11✔
256
        SCFree(e->distinct_bitmap_union);
11✔
257
        e->distinct_bitmap_union = NULL;
11✔
258
    }
11✔
259
}
2,151,079✔
260

261
static inline uint32_t HashAddress(const Address *a)
262
{
648✔
263
    uint32_t key;
648✔
264

265
    if (a->family == AF_INET) {
648!
266
        key = a->addr_data32[0];
647✔
267
    } else if (a->family == AF_INET6) {
648!
268
        key = hashword(a->addr_data32, 4, 0);
1✔
269
    } else
1✔
270
        key = 0;
×
271

272
    return key;
648✔
273
}
648✔
274

275
static inline int CompareAddress(const Address *a, const Address *b)
276
{
506✔
277
    if (a->family == b->family) {
506!
278
        switch (a->family) {
506!
279
            case AF_INET:
506!
280
                return (a->addr_data32[0] == b->addr_data32[0]);
506✔
281
            case AF_INET6:
×
282
                return CMP_ADDR(a, b);
×
283
        }
506✔
284
    }
506✔
285
    return 0;
×
286
}
506✔
287

288
static uint32_t ThresholdEntryHash(uint32_t seed, void *ptr)
289
{
690✔
290
    const ThresholdEntry *e = ptr;
690✔
291
    uint32_t hash = hashword(e->key, sizeof(e->key) / sizeof(uint32_t), seed);
690✔
292
    switch (e->key[TRACK]) {
690✔
293
        case TRACK_BOTH:
47✔
294
            hash += HashAddress(&e->addr2);
47✔
295
            /* fallthrough */
296
        case TRACK_SRC:
449✔
297
        case TRACK_DST:
601✔
298
            hash += HashAddress(&e->addr);
601✔
299
            break;
601✔
300
    }
690✔
301
    return hash;
690✔
302
}
690✔
303

304
static bool ThresholdEntryCompare(void *a, void *b)
305
{
549✔
306
    const ThresholdEntry *e1 = a;
549✔
307
    const ThresholdEntry *e2 = b;
549✔
308
    SCLogDebug("sid1: %u sid2: %u", e1->key[SID], e2->key[SID]);
549!
309

310
    if (memcmp(e1->key, e2->key, sizeof(e1->key)) != 0)
549!
311
        return false;
×
312
    switch (e1->key[TRACK]) {
549✔
313
        case TRACK_BOTH:
49✔
314
            if (!(CompareAddress(&e1->addr2, &e2->addr2)))
49!
315
                return false;
12✔
316
            /* fallthrough */
317
        case TRACK_SRC:
339✔
318
        case TRACK_DST:
457✔
319
            if (!(CompareAddress(&e1->addr, &e2->addr)))
457!
320
                return false;
5✔
321
            break;
452✔
322
    }
549✔
323
    return true;
532✔
324
}
549✔
325

326
static bool ThresholdEntryExpire(void *data, const SCTime_t ts)
327
{
3,043✔
328
    const ThresholdEntry *e = data;
3,043✔
329
    const SCTime_t entry = SCTIME_ADD_SECS(e->tv1, e->seconds);
3,043✔
330
    if (SCTIME_CMP_GT(ts, entry)) {
3,043!
331
        return true;
79✔
332
    }
79✔
333
    return false;
2,964✔
334
}
3,043✔
335

336
static int ThresholdsInit(struct Thresholds *t)
337
{
2,230✔
338
    uint32_t hashsize = 16384;
2,230✔
339
    uint64_t memcap = 16 * 1024 * 1024;
2,230✔
340

341
    const char *str;
2,230✔
342
    if (SCConfGet("detect.thresholds.memcap", &str) == 1) {
2,230✔
343
        if (ParseSizeStringU64(str, &memcap) < 0) {
1,466!
344
            SCLogError("Error parsing detect.thresholds.memcap from conf file - %s", str);
×
345
            return -1;
×
346
        }
×
347
    }
1,466✔
348

349
    intmax_t value = 0;
2,230✔
350
    if ((SCConfGetInt("detect.thresholds.hash-size", &value)) == 1) {
2,230✔
351
        if (value < 256 || value > INT_MAX) {
1,466!
352
            SCLogError("'detect.thresholds.hash-size' value %" PRIiMAX
×
353
                       " out of range. Valid range 256-2147483647.",
×
354
                    value);
×
355
            return -1;
×
356
        }
×
357
        hashsize = (uint32_t)value;
1,466✔
358
    }
1,466✔
359

360
    t->thash = THashInit("thresholds", sizeof(ThresholdEntry), ThresholdEntrySet,
2,230✔
361
            ThresholdEntryFree, ThresholdEntryHash, ThresholdEntryCompare, ThresholdEntryExpire,
2,230✔
362
            NULL, 0, memcap, hashsize);
2,230✔
363
    if (t->thash == NULL) {
2,230!
364
        SCLogError("failed to initialize thresholds hash table");
×
365
        return -1;
×
366
    }
×
367
    return 0;
2,230✔
368
}
2,230✔
369

370
static void ThresholdsDestroy(struct Thresholds *t)
371
{
2,151✔
372
    if (t->thash) {
2,151!
373
        THashShutdown(t->thash);
2,151✔
374
    }
2,151✔
375
}
2,151✔
376

377
uint32_t ThresholdsExpire(const SCTime_t ts)
378
{
5,258✔
379
    return THashExpire(ctx.thash, ts);
5,258✔
380
}
5,258✔
381

382
#define TC_ADDRESS 0
32✔
383
#define TC_SID     1
32✔
384
#define TC_GID     2
32✔
385
#define TC_REV     3
32✔
386
#define TC_TENANT  4
32✔
387

388
typedef struct ThresholdCacheItem {
389
    int8_t track; // by_src/by_dst
390
    int8_t ipv;
391
    int8_t retval;
392
    uint32_t key[5];
393
    SCTime_t expires_at;
394
    RB_ENTRY(ThresholdCacheItem) rb;
395
} ThresholdCacheItem;
396

397
static thread_local HashTable *threshold_cache_ht = NULL;
398

399
thread_local uint64_t cache_lookup_cnt = 0;
400
thread_local uint64_t cache_lookup_notinit = 0;
401
thread_local uint64_t cache_lookup_nosupport = 0;
402
thread_local uint64_t cache_lookup_miss_expired = 0;
403
thread_local uint64_t cache_lookup_miss = 0;
404
thread_local uint64_t cache_lookup_hit = 0;
405
thread_local uint64_t cache_housekeeping_check = 0;
406
thread_local uint64_t cache_housekeeping_expired = 0;
407

408
static void DumpCacheStats(void)
409
{
48,520✔
410
    SCLogPerf("threshold thread cache stats: cnt:%" PRIu64 " notinit:%" PRIu64 " nosupport:%" PRIu64
48,520✔
411
              " miss_expired:%" PRIu64 " miss:%" PRIu64 " hit:%" PRIu64
48,520✔
412
              ", housekeeping: checks:%" PRIu64 ", expired:%" PRIu64,
48,520✔
413
            cache_lookup_cnt, cache_lookup_notinit, cache_lookup_nosupport,
48,520✔
414
            cache_lookup_miss_expired, cache_lookup_miss, cache_lookup_hit,
48,520✔
415
            cache_housekeeping_check, cache_housekeeping_expired);
48,520✔
416
}
48,520✔
417

418
/* rbtree for expiry handling */
419

420
static int ThresholdCacheTreeCompareFunc(ThresholdCacheItem *a, ThresholdCacheItem *b)
421
{
16✔
422
    if (SCTIME_CMP_GTE(a->expires_at, b->expires_at)) {
16!
423
        return 1;
16✔
424
    } else {
16✔
425
        return -1;
×
426
    }
×
427
}
16✔
428

429
RB_HEAD(THRESHOLD_CACHE, ThresholdCacheItem);
430
RB_PROTOTYPE(THRESHOLD_CACHE, ThresholdCacheItem, rb, ThresholdCacheTreeCompareFunc);
431
RB_GENERATE(THRESHOLD_CACHE, ThresholdCacheItem, rb, ThresholdCacheTreeCompareFunc);
286✔
432
thread_local struct THRESHOLD_CACHE threshold_cache_tree;
286✔
433
thread_local uint64_t threshold_cache_housekeeping_ts = 0;
286✔
434

286✔
435
static void ThresholdCacheExpire(SCTime_t now)
286✔
436
{
286✔
437
    ThresholdCacheItem *iter, *safe = NULL;
48✔
438
    int cnt = 0;
48✔
439
    threshold_cache_housekeeping_ts = SCTIME_SECS(now);
48✔
440

441
    RB_FOREACH_SAFE (iter, THRESHOLD_CACHE, &threshold_cache_tree, safe) {
60!
442
        cache_housekeeping_check++;
60✔
443

444
        if (SCTIME_CMP_LT(iter->expires_at, now)) {
60!
445
            THRESHOLD_CACHE_RB_REMOVE(&threshold_cache_tree, iter);
10✔
446
            HashTableRemove(threshold_cache_ht, iter, 0);
10✔
447
            SCLogDebug("iter %p expired", iter);
10!
448
            cache_housekeeping_expired++;
10✔
449
        }
10✔
450

451
        if (++cnt > 1)
60!
452
            break;
15✔
453
    }
60✔
454
}
48✔
455

456
/* hash table for threshold look ups */
457

458
static uint32_t ThresholdCacheHashFunc(HashTable *ht, void *data, uint16_t datalen)
459
{
944✔
460
    ThresholdCacheItem *e = data;
944✔
461
    uint32_t hash = hashword(e->key, sizeof(e->key) / sizeof(uint32_t), 0) * (e->ipv + e->track);
944✔
462
    hash = hash % ht->array_size;
944✔
463
    return hash;
944✔
464
}
944✔
465

466
static char ThresholdCacheHashCompareFunc(
467
        void *data1, uint16_t datalen1, void *data2, uint16_t datalen2)
468
{
825✔
469
    ThresholdCacheItem *tci1 = data1;
825✔
470
    ThresholdCacheItem *tci2 = data2;
825✔
471
    return tci1->ipv == tci2->ipv && tci1->track == tci2->track &&
825!
472
           memcmp(tci1->key, tci2->key, sizeof(tci1->key)) == 0;
825!
473
}
825✔
474

475
static void ThresholdCacheHashFreeFunc(void *data)
476
{
32✔
477
    SCFree(data);
32✔
478
}
32✔
479

480
/// \brief Thread local cache
481
static int SetupCache(const Packet *p, const int8_t track, const int8_t retval, const uint32_t sid,
482
        const uint32_t gid, const uint32_t rev, SCTime_t expires)
483
{
148✔
484
    if (!threshold_cache_ht) {
148✔
485
        threshold_cache_ht = HashTableInit(256, ThresholdCacheHashFunc,
31✔
486
                ThresholdCacheHashCompareFunc, ThresholdCacheHashFreeFunc);
31✔
487
    }
31✔
488

489
    uint32_t addr;
148✔
490
    if (track == TRACK_SRC) {
148!
491
        addr = p->src.addr_data32[0];
25✔
492
    } else if (track == TRACK_DST) {
140!
493
        addr = p->dst.addr_data32[0];
7✔
494
    } else {
116✔
495
        return -1;
116✔
496
    }
116✔
497

498
    ThresholdCacheItem lookup = {
32✔
499
        .track = track,
32✔
500
        .ipv = 4,
32✔
501
        .retval = retval,
32✔
502
        .key[TC_ADDRESS] = addr,
32✔
503
        .key[TC_SID] = sid,
32✔
504
        .key[TC_GID] = gid,
32✔
505
        .key[TC_REV] = rev,
32✔
506
        .key[TC_TENANT] = p->tenant_id,
32✔
507
        .expires_at = expires,
32✔
508
    };
32✔
509
    ThresholdCacheItem *found = HashTableLookup(threshold_cache_ht, &lookup, 0);
32✔
510
    if (!found) {
32!
511
        ThresholdCacheItem *n = SCCalloc(1, sizeof(*n));
32✔
512
        if (n) {
32!
513
            n->track = track;
32✔
514
            n->ipv = 4;
32✔
515
            n->retval = retval;
32✔
516
            n->key[TC_ADDRESS] = addr;
32✔
517
            n->key[TC_SID] = sid;
32✔
518
            n->key[TC_GID] = gid;
32✔
519
            n->key[TC_REV] = rev;
32✔
520
            n->key[TC_TENANT] = p->tenant_id;
32✔
521
            n->expires_at = expires;
32✔
522

523
            if (HashTableAdd(threshold_cache_ht, n, 0) == 0) {
32!
524
                ThresholdCacheItem *r = THRESHOLD_CACHE_RB_INSERT(&threshold_cache_tree, n);
32✔
525
                DEBUG_VALIDATE_BUG_ON(r != NULL); // duplicate; should be impossible
32✔
526
                (void)r;                          // only used by DEBUG_VALIDATE_BUG_ON
32✔
527
                return 1;
32✔
528
            }
32✔
529
            SCFree(n);
×
530
        }
×
531
        return -1;
×
532
    } else {
32✔
533
        found->expires_at = expires;
×
534
        found->retval = retval;
×
535

536
        THRESHOLD_CACHE_RB_REMOVE(&threshold_cache_tree, found);
×
537
        THRESHOLD_CACHE_RB_INSERT(&threshold_cache_tree, found);
×
538
        return 1;
×
539
    }
×
540
}
32✔
541

542
/** \brief Check Thread local thresholding cache
543
 *  \note only supports IPv4
544
 *  \retval -1 cache miss - not found
545
 *  \retval -2 cache miss - found but expired
546
 *  \retval -3 error - cache not initialized
547
 *  \retval -4 error - unsupported tracker
548
 *  \retval ret cached return code
549
 */
550
static int CheckCache(const Packet *p, const int8_t track, const uint32_t sid, const uint32_t gid,
551
        const uint32_t rev)
552
{
1,147✔
553
    cache_lookup_cnt++;
1,147✔
554

555
    if (!threshold_cache_ht) {
1,147!
556
        cache_lookup_notinit++;
279✔
557
        return -3; // error cache initialized
279✔
558
    }
279✔
559

560
    uint32_t addr;
868✔
561
    if (track == TRACK_SRC) {
868!
562
        addr = p->src.addr_data32[0];
853✔
563
    } else if (track == TRACK_DST) {
853!
564
        addr = p->dst.addr_data32[0];
15✔
565
    } else {
15✔
566
        cache_lookup_nosupport++;
×
567
        return -4; // error tracker not unsupported
×
568
    }
×
569

570
    if (SCTIME_SECS(p->ts) > threshold_cache_housekeeping_ts) {
868!
571
        ThresholdCacheExpire(p->ts);
48✔
572
    }
48✔
573

574
    ThresholdCacheItem lookup = {
868✔
575
        .track = track,
868✔
576
        .ipv = 4,
868✔
577
        .key[TC_ADDRESS] = addr,
868✔
578
        .key[TC_SID] = sid,
868✔
579
        .key[TC_GID] = gid,
868✔
580
        .key[TC_REV] = rev,
868✔
581
        .key[TC_TENANT] = p->tenant_id,
868✔
582
    };
868✔
583
    ThresholdCacheItem *found = HashTableLookup(threshold_cache_ht, &lookup, 0);
868✔
584
    if (found) {
868!
585
        if (SCTIME_CMP_GT(p->ts, found->expires_at)) {
813!
586
            THRESHOLD_CACHE_RB_REMOVE(&threshold_cache_tree, found);
2✔
587
            HashTableRemove(threshold_cache_ht, found, 0);
2✔
588
            cache_lookup_miss_expired++;
2✔
589
            return -2; // cache miss - found but expired
2✔
590
        }
2✔
591
        cache_lookup_hit++;
811✔
592
        return found->retval;
811✔
593
    }
813✔
594
    cache_lookup_miss++;
55✔
595
    return -1; // cache miss - not found
55✔
596
}
868✔
597

598
void ThresholdCacheThreadFree(void)
599
{
48,520✔
600
    if (threshold_cache_ht) {
48,520✔
601
        HashTableFree(threshold_cache_ht);
31✔
602
        threshold_cache_ht = NULL;
31✔
603
    }
31✔
604
    RB_INIT(&threshold_cache_tree);
48,520!
605
    DumpCacheStats();
48,520✔
606
}
48,520✔
607

608
/**
609
 * \brief Return next DetectThresholdData for signature
610
 *
611
 * \param sig  Signature pointer
612
 * \param psm  Pointer to a Signature Match pointer
613
 * \param list List to return data from
614
 *
615
 * \retval tsh Return the threshold data from signature or NULL if not found
616
 */
617
const DetectThresholdData *SigGetThresholdTypeIter(
618
        const Signature *sig, const SigMatchData **psm, int list)
619
{
27,057✔
620
    const SigMatchData *smd = NULL;
27,057✔
621
    const DetectThresholdData *tsh = NULL;
27,057✔
622

623
    if (sig == NULL)
27,057!
624
        return NULL;
×
625

626
    if (*psm == NULL) {
27,058✔
627
        smd = sig->sm_arrays[list];
27,058✔
628
    } else {
2,147,484,989✔
629
        /* Iteration in progress, using provided value */
630
        smd = *psm;
2,147,483,647✔
631
    }
2,147,483,647✔
632

633
    while (1) {
27,059!
634
        if (smd->type == DETECT_THRESHOLD || smd->type == DETECT_DETECTION_FILTER) {
27,059!
635
            tsh = (DetectThresholdData *)smd->ctx;
27,057✔
636

637
            if (smd->is_last) {
27,057✔
638
                *psm = NULL;
27,055✔
639
            } else {
27,055✔
640
                *psm = smd + 1;
2✔
641
            }
2✔
642
            return tsh;
27,057✔
643
        }
27,057✔
644

645
        if (smd->is_last) {
2!
646
            break;
×
647
        }
×
648
        smd++;
2✔
649
    }
2✔
UNCOV
650
    *psm = NULL;
×
UNCOV
651
    return NULL;
×
652
}
27,057✔
653

654
typedef struct FlowThresholdEntryList_ {
655
    struct FlowThresholdEntryList_ *next;
656
    ThresholdEntry threshold;
657
} FlowThresholdEntryList;
658

659
static void FlowThresholdEntryListFree(FlowThresholdEntryList *list)
660
{
227✔
661
    for (FlowThresholdEntryList *i = list; i != NULL;) {
896✔
662
        FlowThresholdEntryList *next = i->next;
669✔
663
        SCFree(i);
669✔
664
        i = next;
669✔
665
    }
669✔
666
}
227✔
667

668
/** struct for storing per flow thresholds. This will be stored in the Flow::flowvar list, so it
669
 * needs to follow the GenericVar header format. */
670
typedef struct FlowVarThreshold_ {
671
    uint16_t type;
672
    uint8_t pad[6];
673
    struct GenericVar_ *next;
674
    FlowThresholdEntryList *thresholds;
675
} FlowVarThreshold;
676

677
void FlowThresholdVarFree(void *ptr)
678
{
227✔
679
    FlowVarThreshold *t = ptr;
227✔
680
    FlowThresholdEntryListFree(t->thresholds);
227✔
681
    SCFree(t);
227✔
682
}
227✔
683

684
static FlowVarThreshold *FlowThresholdVarGet(Flow *f)
685
{
26,051✔
686
    if (f == NULL)
26,051!
687
        return NULL;
×
688

689
    for (GenericVar *gv = f->flowvar; gv != NULL; gv = gv->next) {
26,052✔
690
        if (gv->type == DETECT_THRESHOLD)
25,595✔
691
            return (FlowVarThreshold *)gv;
25,594✔
692
    }
25,595✔
693

694
    return NULL;
457✔
695
}
26,051✔
696

697
static ThresholdEntry *ThresholdFlowLookupEntry(
698
        Flow *f, uint32_t sid, uint32_t gid, uint32_t rev, uint32_t tenant_id)
699
{
25,383✔
700
    FlowVarThreshold *t = FlowThresholdVarGet(f);
25,383✔
701
    if (t == NULL)
25,383✔
702
        return NULL;
227✔
703

704
    for (FlowThresholdEntryList *e = t->thresholds; e != NULL; e = e->next) {
50,472✔
705
        if (e->threshold.key[SID] == sid && e->threshold.key[GID] == gid &&
50,031✔
706
                e->threshold.key[REV] == rev && e->threshold.key[TENANT] == tenant_id) {
50,031!
707
            return &e->threshold;
24,715✔
708
        }
24,715✔
709
    }
50,031✔
710
    return NULL;
441✔
711
}
25,156✔
712

713
static int AddEntryToFlow(Flow *f, FlowThresholdEntryList *e, SCTime_t packet_time)
714
{
669✔
715
    DEBUG_VALIDATE_BUG_ON(e == NULL);
669✔
716

717
    FlowVarThreshold *t = FlowThresholdVarGet(f);
669✔
718
    if (t == NULL) {
669✔
719
        t = SCCalloc(1, sizeof(*t));
227✔
720
        if (t == NULL) {
227!
721
            return -1;
×
722
        }
×
723
        t->type = DETECT_THRESHOLD;
227✔
724
        GenericVarAppend(&f->flowvar, (GenericVar *)t);
227✔
725
    }
227✔
726

727
    e->next = t->thresholds;
669✔
728
    t->thresholds = e;
669✔
729
    return 0;
669✔
730
}
669✔
731

732
static int ThresholdHandlePacketSuppress(
733
        Packet *p, const DetectThresholdData *td, uint32_t sid, uint32_t gid)
734
{
174✔
735
    int ret = 0;
174✔
736
    DetectAddress *m = NULL;
174✔
737
    switch (td->track) {
174✔
738
        case TRACK_DST:
57✔
739
            m = DetectAddressLookupInHead(&td->addrs, &p->dst);
57✔
740
            SCLogDebug("TRACK_DST");
57!
741
            break;
57✔
742
        case TRACK_SRC:
60✔
743
            m = DetectAddressLookupInHead(&td->addrs, &p->src);
60✔
744
            SCLogDebug("TRACK_SRC");
60!
745
            break;
60✔
746
        /* suppress if either src or dst is a match on the suppress
747
         * address list */
748
        case TRACK_EITHER:
57✔
749
            m = DetectAddressLookupInHead(&td->addrs, &p->src);
57✔
750
            if (m == NULL) {
57✔
751
                m = DetectAddressLookupInHead(&td->addrs, &p->dst);
12✔
752
            }
12✔
753
            break;
57✔
754
        case TRACK_RULE:
×
755
        case TRACK_FLOW:
×
756
        default:
×
757
            SCLogError("track mode %d is not supported", td->track);
×
758
            break;
×
759
    }
174✔
760
    if (m == NULL)
174✔
761
        ret = 1;
58✔
762
    else
116✔
763
        ret = 2; /* suppressed but still need actions */
116✔
764

765
    return ret;
174✔
766
}
174✔
767

768
static inline void RateFilterSetAction(PacketAlert *pa, uint8_t new_action)
769
{
104✔
770
    switch (new_action) {
104✔
771
        case TH_ACTION_ALERT:
86✔
772
            pa->flags |= PACKET_ALERT_FLAG_RATE_FILTER_MODIFIED;
86✔
773
            pa->action = ACTION_ALERT;
86✔
774
            break;
86✔
775
        case TH_ACTION_DROP:
10✔
776
            pa->flags |= PACKET_ALERT_FLAG_RATE_FILTER_MODIFIED;
10✔
777
            pa->action = (ACTION_DROP | ACTION_ALERT);
10✔
778
            break;
10✔
779
        case TH_ACTION_REJECT:
4✔
780
            pa->flags |= PACKET_ALERT_FLAG_RATE_FILTER_MODIFIED;
4✔
781
            pa->action = (ACTION_REJECT | ACTION_DROP | ACTION_ALERT);
4✔
782
            break;
4✔
783
        case TH_ACTION_PASS:
4✔
784
            pa->flags |= PACKET_ALERT_FLAG_RATE_FILTER_MODIFIED;
4✔
785
            pa->action = ACTION_PASS;
4✔
786
            break;
4✔
787
        default:
×
788
            /* Weird, leave the default action */
789
            break;
×
790
    }
104✔
791
}
104✔
792

793
/** \internal
794
 *  \brief Apply the multiplier and return the new value.
795
 *  If it would overflow the uint32_t we return UINT32_MAX.
796
 */
797
static uint32_t BackoffCalcNextValue(const uint32_t cur, const uint32_t m)
798
{
1,335✔
799
    /* goal is to see if cur * m would overflow uint32_t */
800
    if (unlikely(UINT32_MAX / m < cur)) {
1,335!
801
        return UINT32_MAX;
×
802
    }
×
803
    return cur * m;
1,335✔
804
}
1,335✔
805

806
/**
807
 *  \retval 2 silent match (no alert but apply actions)
808
 *  \retval 1 normal match
809
 *  \retval 0 no match
810
 */
811
static int ThresholdSetup(const DetectThresholdData *td, ThresholdEntry *te, const Packet *p,
812
        const uint32_t sid, const uint32_t gid, const uint32_t rev)
813
{
827✔
814
    te->key[SID] = sid;
827✔
815
    te->key[GID] = gid;
827✔
816
    te->key[REV] = rev;
827✔
817
    te->key[TRACK] = td->track;
827✔
818
    te->key[TENANT] = p->tenant_id;
827✔
819

820
    te->seconds = td->seconds;
827✔
821
    te->current_count = 1;
827✔
822

823
    switch (td->type) {
827✔
824
        case TYPE_BACKOFF:
654✔
825
            te->backoff.next_value = td->count;
654✔
826
            break;
654✔
827
        default:
173✔
828
            te->tv1 = p->ts;
173✔
829
            te->tv_timeout = SCTIME_INITIALIZER;
173✔
830
            ThresholdDistinctInit(te, td);
173✔
831
            /* If unique_on is enabled, we must add the current packet's port to the bitmap.
832
             * ThresholdDistinctInit resets current_count to 0, so we must add the port
833
             * or restore the count if allocation failed. */
834
            if (td->type == TYPE_DETECTION && td->unique_on != DF_UNIQUE_NONE) {
173✔
835
                if (te->distinct_bitmap_union) {
13!
836
                    uint16_t port = (td->unique_on == DF_UNIQUE_SRC_PORT) ? p->sp : p->dp;
11✔
837
                    ThresholdDistinctAddPort(te, port);
11✔
838
                } else {
11✔
839
                    /* Allocation failed (or test mode), fallback to classic counting.
840
                     * We must set current_count to 1 for this first packet. */
841
                    te->current_count = 1;
2✔
842
                }
2✔
843
            }
13✔
844
            break;
173✔
845
    }
827✔
846

847
    switch (td->type) {
827!
848
        case TYPE_LIMIT:
33✔
849
        case TYPE_RATE:
64✔
850
            return 1;
64✔
851
        case TYPE_THRESHOLD:
20✔
852
        case TYPE_BOTH:
83✔
853
            if (td->count == 1)
83!
854
                return 1;
×
855
            return 0;
83✔
856
        case TYPE_BACKOFF:
654✔
857
            if (td->count == 1) {
654✔
858
                te->backoff.next_value =
434✔
859
                        BackoffCalcNextValue(te->backoff.next_value, td->multiplier);
434✔
860
                return 1;
434✔
861
            }
434✔
862
            return 0;
220✔
863
        case TYPE_DETECTION:
26✔
864
            return 0;
26✔
865
    }
827✔
866
    return 0;
×
867
}
827✔
868

869
static int ThresholdCheckUpdate(const DetectEngineCtx *de_ctx, const DetectThresholdData *td,
870
        ThresholdEntry *te,
871
        const Packet *p, // ts only? - cache too
872
        const uint32_t sid, const uint32_t gid, const uint32_t rev, PacketAlert *pa)
873
{
25,248✔
874
    int ret = 0;
25,248✔
875
    const SCTime_t packet_time = p->ts;
25,248✔
876
    const SCTime_t entry = SCTIME_ADD_SECS(te->tv1, td->seconds);
25,248✔
877
    switch (td->type) {
25,248✔
878
        case TYPE_LIMIT:
171✔
879
            SCLogDebug("limit");
171!
880

881
            if (SCTIME_CMP_LTE(p->ts, entry)) {
171✔
882
                te->current_count++;
163✔
883

884
                if (te->current_count <= td->count) {
163✔
885
                    ret = 1;
50✔
886
                } else {
134✔
887
                    ret = 2;
113✔
888

889
                    if (PacketIsIPv4(p)) {
113!
890
                        SetupCache(p, td->track, (int8_t)ret, sid, gid, rev, entry);
113✔
891
                    }
113✔
892
                }
113✔
893
            } else {
163✔
894
                /* entry expired, reset */
895
                te->tv1 = p->ts;
8✔
896
                te->current_count = 1;
8✔
897
                ret = 1;
8✔
898
            }
8✔
899
            break;
171✔
900
        case TYPE_THRESHOLD:
66✔
901
            if (SCTIME_CMP_LTE(p->ts, entry)) {
66✔
902
                te->current_count++;
62✔
903

904
                if (te->current_count >= td->count) {
62✔
905
                    ret = 1;
12✔
906
                    te->current_count = 0;
12✔
907
                }
12✔
908
            } else {
62✔
909
                te->tv1 = p->ts;
4✔
910
                te->current_count = 1;
4✔
911
            }
4✔
912
            break;
66✔
913
        case TYPE_BOTH:
223✔
914
            if (SCTIME_CMP_LTE(p->ts, entry)) {
223✔
915
                /* within time limit */
916

917
                te->current_count++;
195✔
918
                if (te->current_count == td->count) {
195✔
919
                    ret = 1;
12✔
920
                } else if (te->current_count > td->count) {
183✔
921
                    /* silent match */
922
                    ret = 2;
35✔
923

924
                    if (PacketIsIPv4(p)) {
35!
925
                        SetupCache(p, td->track, (int8_t)ret, sid, gid, rev, entry);
35✔
926
                    }
35✔
927
                }
35✔
928
            } else {
195✔
929
                /* expired, so reset */
930
                te->tv1 = p->ts;
28✔
931
                te->current_count = 1;
28✔
932

933
                /* if we have a limit of 1, this is a match */
934
                if (te->current_count == td->count) {
28!
935
                    ret = 1;
×
936
                }
×
937
            }
28✔
938
            break;
223✔
939
        case TYPE_DETECTION: {
102✔
940
            SCLogDebug("detection_filter");
102!
941

942
            if (SCTIME_CMP_LTE(p->ts, entry)) {
102✔
943
                /* within timeout */
944
                if (td->unique_on != DF_UNIQUE_NONE && te->distinct_bitmap_union) {
99!
945
                    uint16_t port = (td->unique_on == DF_UNIQUE_SRC_PORT) ? p->sp : p->dp;
11✔
946
                    ThresholdDistinctAddPort(te, port);
11✔
947
                    if (te->current_count > td->count) {
11✔
948
                        ret = 1;
4✔
949
                    }
4✔
950
                } else {
88✔
951
                    te->current_count++;
88✔
952
                    if (te->current_count > td->count) {
88✔
953
                        ret = 1;
43✔
954
                    }
43✔
955
                }
88✔
956
            } else {
99✔
957
                /* expired, reset to new window starting now */
958
                te->tv1 = p->ts;
3✔
959
                ThresholdDistinctReset(te);
3✔
960

961
                /* record current packet's distinct port as the first in the new window */
962
                if (td->unique_on != DF_UNIQUE_NONE && te->distinct_bitmap_union) {
3!
963
                    uint16_t port = (td->unique_on == DF_UNIQUE_SRC_PORT) ? p->sp : p->dp;
1!
964
                    ThresholdDistinctAddPort(te, port);
1✔
965
                } else {
2✔
966
                    te->current_count = 1;
2✔
967
                }
2✔
968
            }
3✔
969
            break;
102✔
970
        }
×
971
        case TYPE_RATE: {
119✔
972
            SCLogDebug("rate_filter");
119!
973
            const uint8_t original_action = pa->action;
119✔
974
            ret = 1;
119✔
975
            /* Check if we have a timeout enabled, if so,
976
             * we still matching (and enabling the new_action) */
977
            if (SCTIME_CMP_NEQ(te->tv_timeout, SCTIME_INITIALIZER)) {
119✔
978
                if ((SCTIME_SECS(packet_time) - SCTIME_SECS(te->tv_timeout)) > td->timeout) {
84!
979
                    /* Ok, we are done, timeout reached */
980
                    te->tv_timeout = SCTIME_INITIALIZER;
3✔
981
                } else {
82✔
982
                    /* Already matching */
983
                    RateFilterSetAction(pa, td->new_action);
81✔
984
                }
81✔
985
            } else {
93✔
986
                /* Update the matching state with the timeout interval */
987
                if (SCTIME_CMP_LTE(packet_time, entry)) {
35✔
988
                    te->current_count++;
32✔
989
                    if (te->current_count > td->count) {
32✔
990
                        /* Then we must enable the new action by setting a
991
                         * timeout */
992
                        te->tv_timeout = packet_time;
23✔
993
                        RateFilterSetAction(pa, td->new_action);
23✔
994
                    }
23✔
995
                } else {
32✔
996
                    te->tv1 = packet_time;
3✔
997
                    te->current_count = 1;
3✔
998
                }
3✔
999
            }
35✔
1000
            if (de_ctx->RateFilterCallback && original_action != pa->action) {
119!
1001
                pa->action = de_ctx->RateFilterCallback(p, sid, gid, rev, original_action,
×
1002
                        pa->action, de_ctx->rate_filter_callback_arg);
×
1003
                if (pa->action == original_action) {
×
1004
                    /* Reset back to original action, clear modified flag. */
1005
                    pa->flags &= ~PACKET_ALERT_FLAG_RATE_FILTER_MODIFIED;
×
1006
                }
×
1007
            }
×
1008
            break;
119✔
1009
        }
×
1010
        case TYPE_BACKOFF:
24,568✔
1011
            SCLogDebug("backoff");
24,568!
1012

1013
            if (te->current_count < UINT32_MAX) {
24,568!
1014
                te->current_count++;
24,568✔
1015
                if (te->backoff.next_value == te->current_count) {
24,568✔
1016
                    te->backoff.next_value =
901✔
1017
                            BackoffCalcNextValue(te->backoff.next_value, td->multiplier);
901✔
1018
                    SCLogDebug("te->backoff.next_value %u", te->backoff.next_value);
901!
1019
                    ret = 1;
901✔
1020
                } else {
23,667✔
1021
                    ret = 2;
23,667✔
1022
                }
23,667✔
1023
            } else {
24,568✔
1024
                /* if count reaches UINT32_MAX, we just silent match on the rest of the flow */
1025
                ret = 2;
×
1026
            }
×
1027
            break;
24,568✔
1028
    }
25,248✔
1029
    return ret;
25,249✔
1030
}
25,248✔
1031

1032
static int ThresholdGetFromHash(const DetectEngineCtx *de_ctx, struct Thresholds *tctx,
1033
        const Packet *p, const Signature *s, const DetectThresholdData *td, PacketAlert *pa)
1034
{
690✔
1035
    /* fast track for count 1 threshold */
1036
    if (td->count == 1 && td->type == TYPE_THRESHOLD) {
690!
1037
        return 1;
×
1038
    }
×
1039

1040
    ThresholdEntry lookup;
690✔
1041
    memset(&lookup, 0, sizeof(lookup));
690✔
1042
    lookup.key[SID] = s->id;
690✔
1043
    lookup.key[GID] = s->gid;
690✔
1044
    lookup.key[REV] = s->rev;
690✔
1045
    lookup.key[TRACK] = td->track;
690✔
1046
    lookup.key[TENANT] = p->tenant_id;
690✔
1047
    if (td->track == TRACK_SRC) {
690✔
1048
        COPY_ADDRESS(&p->src, &lookup.addr);
402!
1049
    } else if (td->track == TRACK_DST) {
541✔
1050
        COPY_ADDRESS(&p->dst, &lookup.addr);
152!
1051
    } else if (td->track == TRACK_BOTH) {
219✔
1052
        /* make sure lower ip address is first */
1053
        if (PacketIsIPv4(p)) {
47!
1054
            if (SCNtohl(p->src.addr_data32[0]) < SCNtohl(p->dst.addr_data32[0])) {
47✔
1055
                COPY_ADDRESS(&p->src, &lookup.addr);
25!
1056
                COPY_ADDRESS(&p->dst, &lookup.addr2);
25!
1057
            } else {
36✔
1058
                COPY_ADDRESS(&p->dst, &lookup.addr);
22!
1059
                COPY_ADDRESS(&p->src, &lookup.addr2);
22!
1060
            }
22✔
1061
        } else {
47✔
1062
            if (AddressIPv6Lt(&p->src, &p->dst)) {
×
1063
                COPY_ADDRESS(&p->src, &lookup.addr);
×
1064
                COPY_ADDRESS(&p->dst, &lookup.addr2);
×
1065
            } else {
×
1066
                COPY_ADDRESS(&p->dst, &lookup.addr);
×
1067
                COPY_ADDRESS(&p->src, &lookup.addr2);
×
1068
            }
×
1069
        }
×
1070
    }
47✔
1071

1072
    struct THashDataGetResult res = THashGetFromHash(tctx->thash, &lookup);
690✔
1073
    if (res.data) {
690!
1074
        SCLogDebug("found %p, is_new %s", res.data, BOOL2STR(res.is_new));
690!
1075
        int r;
690✔
1076
        ThresholdEntry *te = res.data->data;
690✔
1077
        if (res.is_new) {
690✔
1078
            // new threshold, set up
1079
            r = ThresholdSetup(td, te, p, s->id, s->gid, s->rev);
158✔
1080
        } else {
532✔
1081
            // existing, check/update
1082
            r = ThresholdCheckUpdate(de_ctx, td, te, p, s->id, s->gid, s->rev, pa);
532✔
1083
        }
532✔
1084

1085
        (void)THashDecrUsecnt(res.data);
690✔
1086
        THashDataUnlock(res.data);
690✔
1087
        return r;
690✔
1088
    }
690✔
1089
    return 0; // TODO error?
×
1090
}
690✔
1091

1092
/**
1093
 *  \retval 2 silent match (no alert but apply actions)
1094
 *  \retval 1 normal match
1095
 *  \retval 0 no match
1096
 */
1097
static int ThresholdHandlePacketFlow(const DetectEngineCtx *de_ctx, Flow *f, Packet *p,
1098
        const DetectThresholdData *td, uint32_t sid, uint32_t gid, uint32_t rev, PacketAlert *pa)
1099
{
25,383✔
1100
    int ret = 0;
25,383✔
1101
    ThresholdEntry *found = ThresholdFlowLookupEntry(f, sid, gid, rev, p->tenant_id);
25,383✔
1102
    SCLogDebug("found %p sid %u gid %u rev %u", found, sid, gid, rev);
25,383!
1103

1104
    if (found == NULL) {
25,383✔
1105
        FlowThresholdEntryList *new = SCCalloc(1, sizeof(*new));
669✔
1106
        if (new == NULL)
669!
1107
            return 0;
×
1108

1109
        // new threshold, set up
1110
        ret = ThresholdSetup(td, &new->threshold, p, sid, gid, rev);
669✔
1111

1112
        if (AddEntryToFlow(f, new, p->ts) == -1) {
669!
1113
            SCFree(new);
×
1114
            return 0;
×
1115
        }
×
1116
    } else {
24,714✔
1117
        // existing, check/update
1118
        ret = ThresholdCheckUpdate(de_ctx, td, found, p, sid, gid, rev, pa);
24,714✔
1119
    }
24,714✔
1120
    return ret;
25,383✔
1121
}
25,383✔
1122

1123
/**
1124
 * \brief Make the threshold logic for signatures
1125
 *
1126
 * \param de_ctx Detection Context
1127
 * \param tsh_ptr Threshold element
1128
 * \param p Packet structure
1129
 * \param s Signature structure
1130
 *
1131
 * \retval 2 silent match (no alert but apply actions)
1132
 * \retval 1 alert on this event
1133
 * \retval 0 do not alert on this event
1134
 */
1135
int PacketAlertThreshold(const DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx,
1136
        const DetectThresholdData *td, Packet *p, const Signature *s, PacketAlert *pa)
1137
{
27,054✔
1138
    SCEnter();
27,054✔
1139

1140
    int ret = 0;
27,054✔
1141
    if (td == NULL) {
27,054!
1142
        SCReturnInt(0);
×
1143
    }
×
1144

1145
    if (td->type == TYPE_SUPPRESS) {
27,054✔
1146
        ret = ThresholdHandlePacketSuppress(p, td, s->id, s->gid);
174✔
1147
    } else if (td->track == TRACK_SRC) {
26,880✔
1148
        if (PacketIsIPv4(p) && (td->type == TYPE_LIMIT || td->type == TYPE_BOTH)) {
1,203!
1149
            int cache_ret = CheckCache(p, td->track, s->id, s->gid, s->rev);
1,086✔
1150
            if (cache_ret >= 0) {
1,086!
1151
                SCReturnInt(cache_ret);
801✔
1152
            }
801✔
1153
        }
1,086✔
1154

1155
        ret = ThresholdGetFromHash(de_ctx, &ctx, p, s, td, pa);
402✔
1156
    } else if (td->track == TRACK_DST) {
25,930✔
1157
        if (PacketIsIPv4(p) && (td->type == TYPE_LIMIT || td->type == TYPE_BOTH)) {
162!
1158
            int cache_ret = CheckCache(p, td->track, s->id, s->gid, s->rev);
61✔
1159
            if (cache_ret >= 0) {
61!
1160
                SCReturnInt(cache_ret);
10✔
1161
            }
10✔
1162
        }
61✔
1163

1164
        ret = ThresholdGetFromHash(de_ctx, &ctx, p, s, td, pa);
152✔
1165
    } else if (td->track == TRACK_BOTH) {
25,598✔
1166
        ret = ThresholdGetFromHash(de_ctx, &ctx, p, s, td, pa);
47✔
1167
    } else if (td->track == TRACK_RULE) {
25,476✔
1168
        ret = ThresholdGetFromHash(de_ctx, &ctx, p, s, td, pa);
89✔
1169
    } else if (td->track == TRACK_FLOW) {
25,395✔
1170
        if (p->flow) {
25,382✔
1171
            ret = ThresholdHandlePacketFlow(de_ctx, p->flow, p, td, s->id, s->gid, s->rev, pa);
25,382✔
1172
        }
25,382✔
1173
    }
25,381✔
1174

1175
    SCReturnInt(ret);
26,243✔
1176
}
27,054✔
1177

1178
/**
1179
 * @}
1180
 */
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc