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

jasonish / suricata / 23218895894

17 Mar 2026 06:04AM UTC coverage: 79.296% (-0.02%) from 79.313%
23218895894

push

github

victorjulien
rust/Makefile: add RUSTC and RUSTDOC to CARGO_ENV

And consistently use the CARGO_ENV for all cargo calls.

This allows a user to point to rust binaries that are not on their
path just during ./configure, for example:

  PATH=/usr/lib/rust-1.89/bin:$PATH ./configure ...

The full path to rustc and rustdoc will now be used, as discovered in
the PATH without the user have to carry the path over to the "make"
command. This was already done for cargo.

Related to ticket: #8381

265884 of 335307 relevant lines covered (79.3%)

4508559.17 hits per line

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

89.09
/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,028✔
101
    return SC_ATOMIC_GET(threshold_bitmap_alloc_fail);
3,028✔
102
}
3,028✔
103

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

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

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

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

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

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

144
#define SID    0
51,563✔
145
#define GID    1
26,235✔
146
#define REV    2
26,235✔
147
#define TRACK  3
2,755✔
148
#define TENANT 4
26,235✔
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,177,080✔
245
    if (ptr == NULL)
2,177,080✔
246
        return;
×
247

248
    ThresholdEntry *e = ptr;
2,177,080✔
249
    if (e->distinct_bitmap_union) {
2,177,080✔
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,177,080✔
260

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

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

272
    return key;
649✔
273
}
649✔
274

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

288
static uint32_t ThresholdEntryHash(uint32_t seed, void *ptr)
289
{
689✔
290
    const ThresholdEntry *e = ptr;
689✔
291
    uint32_t hash = hashword(e->key, sizeof(e->key) / sizeof(uint32_t), seed);
689✔
292
    switch (e->key[TRACK]) {
689✔
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
    }
689✔
301
    return hash;
690✔
302
}
689✔
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:
340✔
318
        case TRACK_DST:
458✔
319
            if (!(CompareAddress(&e1->addr, &e2->addr)))
458✔
320
                return false;
5✔
321
            break;
453✔
322
    }
549✔
323
    return true;
532✔
324
}
549✔
325

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

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

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

349
    intmax_t value = 0;
2,259✔
350
    if ((SCConfGetInt("detect.thresholds.hash-size", &value)) == 1) {
2,259✔
351
        if (value < 256 || value > INT_MAX) {
1,483✔
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,483✔
358
    }
1,483✔
359

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

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

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

382
#define TC_ADDRESS 0
33✔
383
#define TC_SID     1
33✔
384
#define TC_GID     2
33✔
385
#define TC_REV     3
33✔
386
#define TC_TENANT  4
33✔
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,504✔
410
    SCLogPerf("threshold thread cache stats: cnt:%" PRIu64 " notinit:%" PRIu64 " nosupport:%" PRIu64
48,504✔
411
              " miss_expired:%" PRIu64 " miss:%" PRIu64 " hit:%" PRIu64
48,504✔
412
              ", housekeeping: checks:%" PRIu64 ", expired:%" PRIu64,
48,504✔
413
            cache_lookup_cnt, cache_lookup_notinit, cache_lookup_nosupport,
48,504✔
414
            cache_lookup_miss_expired, cache_lookup_miss, cache_lookup_hit,
48,504✔
415
            cache_housekeeping_check, cache_housekeeping_expired);
48,504✔
416
}
48,504✔
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);
296✔
432
thread_local struct THRESHOLD_CACHE threshold_cache_tree;
296✔
433
thread_local uint64_t threshold_cache_housekeeping_ts = 0;
296✔
434

296✔
435
static void ThresholdCacheExpire(SCTime_t now)
296✔
436
{
296✔
437
    ThresholdCacheItem *iter, *safe = NULL;
51✔
438
    int cnt = 0;
51✔
439
    threshold_cache_housekeeping_ts = SCTIME_SECS(now);
51✔
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);
11✔
446
            HashTableRemove(threshold_cache_ht, iter, 0);
11✔
447
            SCLogDebug("iter %p expired", iter);
11✔
448
            cache_housekeeping_expired++;
11✔
449
        }
11✔
450

451
        if (++cnt > 1)
60✔
452
            break;
14✔
453
    }
60✔
454
}
51✔
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
{
33✔
477
    SCFree(data);
33✔
478
}
33✔
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,
32✔
486
                ThresholdCacheHashCompareFunc, ThresholdCacheHashFreeFunc);
32✔
487
    }
32✔
488

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

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

523
            if (HashTableAdd(threshold_cache_ht, n, 0) == 0) {
33✔
524
                ThresholdCacheItem *r = THRESHOLD_CACHE_RB_INSERT(&threshold_cache_tree, n);
33✔
525
                DEBUG_VALIDATE_BUG_ON(r != NULL); // duplicate; should be impossible
33✔
526
                (void)r;                          // only used by DEBUG_VALIDATE_BUG_ON
33✔
527
                return 1;
33✔
528
            }
33✔
529
            SCFree(n);
×
530
        }
×
531
        return -1;
×
532
    } else {
33✔
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
}
33✔
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++;
282✔
557
        return -3; // error cache initialized
282✔
558
    }
282✔
559

560
    uint32_t addr;
865✔
561
    if (track == TRACK_SRC) {
865✔
562
        addr = p->src.addr_data32[0];
850✔
563
    } else if (track == TRACK_DST) {
850✔
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) {
865✔
571
        ThresholdCacheExpire(p->ts);
51✔
572
    }
51✔
573

574
    ThresholdCacheItem lookup = {
865✔
575
        .track = track,
865✔
576
        .ipv = 4,
865✔
577
        .key[TC_ADDRESS] = addr,
865✔
578
        .key[TC_SID] = sid,
865✔
579
        .key[TC_GID] = gid,
865✔
580
        .key[TC_REV] = rev,
865✔
581
        .key[TC_TENANT] = p->tenant_id,
865✔
582
    };
865✔
583
    ThresholdCacheItem *found = HashTableLookup(threshold_cache_ht, &lookup, 0);
865✔
584
    if (found) {
865✔
585
        if (SCTIME_CMP_GT(p->ts, found->expires_at)) {
812✔
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++;
810✔
592
        return found->retval;
810✔
593
    }
812✔
594
    cache_lookup_miss++;
53✔
595
    return -1; // cache miss - not found
53✔
596
}
865✔
597

598
void ThresholdCacheThreadFree(void)
599
{
48,504✔
600
    if (threshold_cache_ht) {
48,504✔
601
        HashTableFree(threshold_cache_ht);
32✔
602
        threshold_cache_ht = NULL;
32✔
603
    }
32✔
604
    RB_INIT(&threshold_cache_tree);
48,504✔
605
    DumpCacheStats();
48,504✔
606
}
48,504✔
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,059✔
620
    const SigMatchData *smd = NULL;
27,059✔
621
    const DetectThresholdData *tsh = NULL;
27,059✔
622

623
    if (sig == NULL)
27,059✔
624
        return NULL;
×
625

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

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

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

645
        if (smd->is_last) {
2,147,483,648✔
646
            break;
×
647
        }
×
648
        smd++;
2,147,483,648✔
649
    }
2,147,483,648✔
650
    *psm = NULL;
2,147,483,648✔
651
    return NULL;
2,147,483,648✔
652
}
27,059✔
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,055✔
686
    if (f == NULL)
26,055✔
687
        return NULL;
×
688

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

694
    return NULL;
452✔
695
}
26,055✔
696

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

704
    for (FlowThresholdEntryList *e = t->thresholds; e != NULL; e = e->next) {
50,487✔
705
        if (e->threshold.key[SID] == sid && e->threshold.key[GID] == gid &&
50,046✔
706
                e->threshold.key[REV] == rev && e->threshold.key[TENANT] == tenant_id) {
50,046✔
707
            return &e->threshold;
24,718✔
708
        }
24,718✔
709
    }
50,046✔
710
    return NULL;
441✔
711
}
25,159✔
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,246✔
874
    int ret = 0;
25,246✔
875
    const SCTime_t packet_time = p->ts;
25,246✔
876
    const SCTime_t entry = SCTIME_ADD_SECS(te->tv1, td->seconds);
25,246✔
877
    switch (td->type) {
25,246✔
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,566✔
1011
            SCLogDebug("backoff");
24,566✔
1012

1013
            if (te->current_count < UINT32_MAX) {
24,566✔
1014
                te->current_count++;
24,566✔
1015
                if (te->backoff.next_value == te->current_count) {
24,566✔
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,665✔
1021
                    ret = 2;
23,665✔
1022
                }
23,665✔
1023
            } else {
24,566✔
1024
                /* if count reaches UINT32_MAX, we just silent match on the rest of the flow */
1025
                ret = 2;
×
1026
            }
×
1027
            break;
24,566✔
1028
    }
25,246✔
1029
    return ret;
25,247✔
1030
}
25,246✔
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) {
540✔
1050
        COPY_ADDRESS(&p->dst, &lookup.addr);
152✔
1051
    } else if (td->track == TRACK_BOTH) {
218✔
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,387✔
1100
    int ret = 0;
25,387✔
1101
    ThresholdEntry *found = ThresholdFlowLookupEntry(f, sid, gid, rev, p->tenant_id);
25,387✔
1102
    SCLogDebug("found %p sid %u gid %u rev %u", found, sid, gid, rev);
25,387✔
1103

1104
    if (found == NULL) {
25,387✔
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,718✔
1117
        // existing, check/update
1118
        ret = ThresholdCheckUpdate(de_ctx, td, found, p, sid, gid, rev, pa);
24,718✔
1119
    }
24,718✔
1120
    return ret;
25,387✔
1121
}
25,387✔
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,060✔
1138
    SCEnter();
27,060✔
1139

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

1145
    if (td->type == TYPE_SUPPRESS) {
27,060✔
1146
        ret = ThresholdHandlePacketSuppress(p, td, s->id, s->gid);
174✔
1147
    } else if (td->track == TRACK_SRC) {
26,886✔
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,085✔
1150
            if (cache_ret >= 0) {
1,085✔
1151
                SCReturnInt(cache_ret);
800✔
1152
            }
800✔
1153
        }
1,085✔
1154

1155
        ret = ThresholdGetFromHash(de_ctx, &ctx, p, s, td, pa);
403✔
1156
    } else if (td->track == TRACK_DST) {
25,939✔
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) {
2,147,509,244✔
1166
        ret = ThresholdGetFromHash(de_ctx, &ctx, p, s, td, pa);
47✔
1167
    } else if (td->track == TRACK_RULE) {
2,147,509,131✔
1168
        ret = ThresholdGetFromHash(de_ctx, &ctx, p, s, td, pa);
88✔
1169
    } else if (td->track == TRACK_FLOW) {
2,147,509,049✔
1170
        if (p->flow) {
25,388✔
1171
            ret = ThresholdHandlePacketFlow(de_ctx, p->flow, p, td, s->id, s->gid, s->rev, pa);
25,387✔
1172
        }
25,387✔
1173
    }
25,388✔
1174

1175
    SCReturnInt(ret);
26,250✔
1176
}
27,060✔
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