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

OISF / suricata / 23374838686

21 Mar 2026 07:29AM UTC coverage: 59.341% (-20.0%) from 79.315%
23374838686

Pull #15075

github

web-flow
Merge 90b4e834f into 6587e363a
Pull Request #15075: Stack 8001 v16.4

38 of 70 new or added lines in 10 files covered. (54.29%)

34165 existing lines in 563 files now uncovered.

119621 of 201584 relevant lines covered (59.34%)

650666.92 hits per line

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

6.1
/src/util-threshold-config.c
1
/* Copyright (C) 2007-2023 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
 * \ingroup threshold
20
 * @{
21
 */
22

23
/**
24
 * \file
25
 *
26
 * \author Breno Silva Pinto <breno.silva@gmail.com>
27
 *
28
 * Implements Threshold support
29
 */
30

31
#include "suricata-common.h"
32

33
#include "action-globals.h"
34
#include "host.h"
35
#include "ippair.h"
36

37
#include "detect.h"
38
#include "detect-engine.h"
39
#include "detect-engine-address.h"
40
#include "detect-engine-threshold.h"
41
#include "detect-threshold.h"
42
#include "detect-parse.h"
43
#include "detect-engine-build.h"
44

45
#include "conf.h"
46
#include "util-threshold-config.h"
47
#include "util-unittest.h"
48
#include "util-unittest-helper.h"
49
#include "util-byte.h"
50
#include "util-time.h"
51
#include "util-debug.h"
52
#include "util-fmemopen.h"
53

54
typedef enum ThresholdRuleType {
55
    THRESHOLD_TYPE_EVENT_FILTER,
56
    THRESHOLD_TYPE_THRESHOLD,
57
    THRESHOLD_TYPE_RATE,
58
    THRESHOLD_TYPE_SUPPRESS,
59
} ThresholdRuleType;
60

61
#ifdef UNITTESTS
62
/* File descriptor for unittests */
63
static FILE *g_ut_threshold_fp = NULL;
64
#endif
65

66
/* common base for all options */
67
#define DETECT_BASE_REGEX "^\\s*(event_filter|threshold|rate_filter|suppress)\\s*gen_id\\s*(\\d+)\\s*,\\s*sig_id\\s*(\\d+)\\s*(.*)\\s*$"
2✔
68

69
#define DETECT_THRESHOLD_REGEX                                                                     \
70
    "^,\\s*type\\s*(limit|both|threshold)\\s*,\\s*track\\s*(by_dst|by_src|by_both|by_rule|by_"     \
2✔
71
    "flow)\\s*,"                                                                                   \
2✔
72
    "\\s*count\\s*(\\d+)\\s*,\\s*seconds\\s*(\\d+)\\s*$"
2✔
73

74
/* TODO: "apply_to" */
75
#define DETECT_RATE_REGEX                                                                          \
76
    "^,\\s*track\\s*(by_dst|by_src|by_both|by_rule|by_flow)\\s*,\\s*count\\s*(\\d+)\\s*,\\s*"      \
2✔
77
    "seconds\\s*(\\d+)\\s*,\\s*new_action\\s*(alert|drop|pass|log|sdrop|reject)\\s*,\\s*"          \
2✔
78
    "timeout\\s*(\\d+)\\s*$"
2✔
79

80
/*
81
 * suppress has two form:
82
 *  suppress gen_id 0, sig_id 0, track by_dst, ip 10.88.0.14
83
 *  suppress gen_id 1, sig_id 2000328
84
 *  suppress gen_id 1, sig_id 2000328, track by_src, ip fe80::/10
85
*/
86
#define DETECT_SUPPRESS_REGEX "^,\\s*track\\s*(by_dst|by_src|by_either)\\s*,\\s*ip\\s*([\\[\\],\\$\\s\\da-zA-Z.:/_]+)*\\s*$"
2✔
87

88
/* Default path for the threshold.config file */
89
#if defined OS_WIN32 || defined __CYGWIN__
90
#define THRESHOLD_CONF_DEF_CONF_FILEPATH CONFIG_DIR "\\\\threshold.config"
91
#else
92
#define THRESHOLD_CONF_DEF_CONF_FILEPATH CONFIG_DIR "/threshold.config"
35,438✔
93
#endif
94

95
static DetectParseRegex *regex_base = NULL;
96
static DetectParseRegex *regex_threshold = NULL;
97
static DetectParseRegex *regex_rate = NULL;
98
static DetectParseRegex *regex_suppress = NULL;
99

100
static void SCThresholdConfDeInitContext(DetectEngineCtx *de_ctx, FILE *fd);
101

102
void SCThresholdConfGlobalInit(void)
103
{
2✔
104
    regex_base = DetectSetupPCRE2(DETECT_BASE_REGEX, 0);
2✔
105
    if (regex_base == NULL) {
2✔
106
        FatalError("classification base regex setup failed");
×
107
    }
×
108
    regex_threshold = DetectSetupPCRE2(DETECT_THRESHOLD_REGEX, 0);
2✔
109
    if (regex_threshold == NULL) {
2✔
110
        FatalError("classification threshold regex setup failed");
×
111
    }
×
112
    regex_rate = DetectSetupPCRE2(DETECT_RATE_REGEX, 0);
2✔
113
    if (regex_rate == NULL) {
2✔
114
        FatalError("classification rate_filter regex setup failed");
×
115
    }
×
116
    regex_suppress = DetectSetupPCRE2(DETECT_SUPPRESS_REGEX, 0);
2✔
117
    if (regex_suppress == NULL) {
2✔
118
        FatalError("classification suppress regex setup failed");
×
119
    }
×
120
}
2✔
121

122
/**
123
 * \brief Returns the path for the Threshold Config file.  We check if we
124
 *        can retrieve the path from the yaml conf file.  If it is not present,
125
 *        return the default path for the threshold file which is
126
 *        "./threshold.config".
127
 *
128
 * \retval log_filename Pointer to a string containing the path for the
129
 *                      Threshold Config file.
130
 */
131
static const char *SCThresholdConfGetConfFilename(const DetectEngineCtx *de_ctx)
132
{
35,438✔
133
    const char *log_filename = NULL;
35,438✔
134

135
    if (de_ctx != NULL && strlen(de_ctx->config_prefix) > 0) {
35,438✔
UNCOV
136
        char config_value[256];
×
UNCOV
137
        snprintf(config_value, sizeof(config_value),
×
UNCOV
138
                 "%s.threshold-file", de_ctx->config_prefix);
×
139

140
        /* try loading prefix setting, fall back to global if that
141
         * fails. */
UNCOV
142
        if (SCConfGet(config_value, &log_filename) != 1) {
×
UNCOV
143
            if (SCConfGet("threshold-file", &log_filename) != 1) {
×
UNCOV
144
                log_filename = (char *)THRESHOLD_CONF_DEF_CONF_FILEPATH;
×
UNCOV
145
            }
×
UNCOV
146
        }
×
147
    } else {
35,438✔
148
        if (SCConfGet("threshold-file", &log_filename) != 1) {
35,438✔
149
            log_filename = (char *)THRESHOLD_CONF_DEF_CONF_FILEPATH;
35,438✔
150
        }
35,438✔
151
    }
35,438✔
152
    return log_filename;
35,438✔
153
}
35,438✔
154

155
/**
156
 * \brief Inits the context to be used by the Threshold Config parsing API.
157
 *
158
 *        This function initializes the hash table to be used by the Detection
159
 *        Engine Context to hold the data from the threshold.config file,
160
 *        obtains the file desc to parse the threshold.config file, and
161
 *        inits the regex used to parse the lines from threshold.config
162
 *        file.
163
 *
164
 * \param de_ctx Pointer to the Detection Engine Context.
165
 *
166
 * \retval  0 On success.
167
 * \retval -1 On failure.
168
 */
169
int SCThresholdConfInitContext(DetectEngineCtx *de_ctx)
170
{
35,438✔
171
    const char *filename = NULL;
35,438✔
172
    int ret = 0;
35,438✔
173
#ifndef UNITTESTS
35,438✔
174
    FILE *fd = NULL;
35,438✔
175
#else
176
    filename = "<ut>";
177
    FILE *fd = g_ut_threshold_fp;
178
    if (fd == NULL) {
179
#endif
180
        filename = SCThresholdConfGetConfFilename(de_ctx);
35,438✔
181
        if ( (fd = fopen(filename, "r")) == NULL) {
35,438✔
182
            SCLogWarning("Error opening file: \"%s\": %s", filename, strerror(errno));
35,438✔
183
            SCThresholdConfDeInitContext(de_ctx, fd);
35,438✔
184
            return 0;
35,438✔
185
        }
35,438✔
186
#ifdef UNITTESTS
187
    }
188
#endif
189

UNCOV
190
    if (SCThresholdConfParseFile(de_ctx, fd) < 0) {
×
UNCOV
191
        SCLogWarning("Error loading threshold configuration from %s", filename);
×
UNCOV
192
        SCThresholdConfDeInitContext(de_ctx, fd);
×
193
        /* maintain legacy behavior so no errors unless config testing */
UNCOV
194
        if (SCRunmodeGet() == RUNMODE_CONF_TEST) {
×
UNCOV
195
            ret = -1;
×
UNCOV
196
        }
×
UNCOV
197
        return ret;
×
UNCOV
198
    }
×
UNCOV
199
    SCThresholdConfDeInitContext(de_ctx, fd);
×
200

201
#ifdef UNITTESTS
202
    g_ut_threshold_fp = NULL;
203
#endif
UNCOV
204
    SCLogDebug("Global thresholding options defined");
×
UNCOV
205
    return 0;
×
UNCOV
206
}
×
207

208
/**
209
 * \brief Releases resources used by the Threshold Config API.
210
 *
211
 * \param de_ctx Pointer to the Detection Engine Context.
212
 * \param fd Pointer to file descriptor.
213
 */
214
static void SCThresholdConfDeInitContext(DetectEngineCtx *de_ctx, FILE *fd)
215
{
35,438✔
216
    if (fd != NULL)
35,438✔
UNCOV
217
        fclose(fd);
×
218
}
35,438✔
219

220
/** \internal
221
 *  \brief setup suppress rules
222
 *  \retval 0 ok
223
 *  \retval -1 error
224
 */
225
static int SetupSuppressRule(DetectEngineCtx *de_ctx, uint32_t id, uint32_t gid,
226
        uint8_t parsed_type, uint8_t parsed_track, uint32_t parsed_count,
227
        uint32_t parsed_seconds, uint32_t parsed_timeout, uint8_t parsed_new_action,
228
        const char *th_ip)
UNCOV
229
{
×
UNCOV
230
    Signature *s = NULL;
×
UNCOV
231
    DetectThresholdData *de = NULL;
×
232

UNCOV
233
    BUG_ON(parsed_type != TYPE_SUPPRESS);
×
234

UNCOV
235
    DetectThresholdData *orig_de = NULL;
×
UNCOV
236
    if (parsed_track != TRACK_RULE) {
×
UNCOV
237
        orig_de = SCCalloc(1, sizeof(DetectThresholdData));
×
UNCOV
238
        if (unlikely(orig_de == NULL))
×
239
            goto error;
×
240

UNCOV
241
        orig_de->type = TYPE_SUPPRESS;
×
UNCOV
242
        orig_de->track = parsed_track;
×
UNCOV
243
        orig_de->count = parsed_count;
×
UNCOV
244
        orig_de->seconds = parsed_seconds;
×
UNCOV
245
        orig_de->new_action = parsed_new_action;
×
UNCOV
246
        orig_de->timeout = parsed_timeout;
×
UNCOV
247
        if (DetectAddressParse((const DetectEngineCtx *)de_ctx, &orig_de->addrs, (char *)th_ip) <
×
UNCOV
248
                0) {
×
249
            SCLogError("failed to parse %s", th_ip);
×
250
            goto error;
×
251
        }
×
UNCOV
252
    }
×
253

254
    /* Install it */
UNCOV
255
    if (id == 0 && gid == 0) {
×
UNCOV
256
        if (parsed_track == TRACK_RULE) {
×
257
            SCLogWarning("suppressing all rules");
×
258
        }
×
259

260
        /* update each sig with our suppress info */
UNCOV
261
        for (s = de_ctx->sig_list; s != NULL; s = s->next) {
×
262
            /* tag the rule as noalert */
UNCOV
263
            if (parsed_track == TRACK_RULE) {
×
264
                s->action &= ~ACTION_ALERT;
×
265
                continue;
×
266
            }
×
267

UNCOV
268
            de = DetectThresholdDataCopy(orig_de);
×
UNCOV
269
            if (unlikely(de == NULL))
×
270
                goto error;
×
271

UNCOV
272
            if (SCSigMatchAppendSMToList(de_ctx, s, DETECT_THRESHOLD, (SigMatchCtx *)de,
×
UNCOV
273
                        DETECT_SM_LIST_SUPPRESS) == NULL) {
×
274
                goto error;
×
275
            }
×
UNCOV
276
        }
×
UNCOV
277
    } else if (id == 0 && gid > 0)    {
×
UNCOV
278
        if (parsed_track == TRACK_RULE) {
×
279
            SCLogWarning("suppressing all rules with gid %" PRIu32, gid);
×
280
        }
×
281
        /* set up suppression for each signature with a matching gid */
UNCOV
282
        for (s = de_ctx->sig_list; s != NULL; s = s->next) {
×
UNCOV
283
            if (s->gid != gid)
×
284
                continue;
×
285

286
            /* tag the rule as noalert */
UNCOV
287
            if (parsed_track == TRACK_RULE) {
×
288
                s->action &= ~ACTION_ALERT;
×
289
                continue;
×
290
            }
×
291

UNCOV
292
            de = DetectThresholdDataCopy(orig_de);
×
UNCOV
293
            if (unlikely(de == NULL))
×
294
                goto error;
×
295

UNCOV
296
            if (SCSigMatchAppendSMToList(de_ctx, s, DETECT_THRESHOLD, (SigMatchCtx *)de,
×
UNCOV
297
                        DETECT_SM_LIST_SUPPRESS) == NULL) {
×
298
                goto error;
×
299
            }
×
UNCOV
300
        }
×
UNCOV
301
    } else if (id > 0 && gid == 0) {
×
302
        SCLogError("Can't use a event config that has "
×
303
                   "sid > 0 and gid == 0. Please fix this "
×
304
                   "in your threshold.config file");
×
305
        goto error;
×
UNCOV
306
    } else {
×
UNCOV
307
        s = SigFindSignatureBySidGid(de_ctx, id, gid);
×
UNCOV
308
        if (s == NULL) {
×
UNCOV
309
            SCLogWarning("can't suppress sid "
×
UNCOV
310
                         "%" PRIu32 ", gid %" PRIu32 ": unknown rule",
×
UNCOV
311
                    id, gid);
×
UNCOV
312
        } else {
×
UNCOV
313
            if (parsed_track == TRACK_RULE) {
×
UNCOV
314
                s->action &= ~ACTION_ALERT;
×
UNCOV
315
                goto end;
×
UNCOV
316
            }
×
317

UNCOV
318
            de = DetectThresholdDataCopy(orig_de);
×
UNCOV
319
            if (unlikely(de == NULL))
×
320
                goto error;
×
321

UNCOV
322
            if (SCSigMatchAppendSMToList(de_ctx, s, DETECT_THRESHOLD, (SigMatchCtx *)de,
×
UNCOV
323
                        DETECT_SM_LIST_SUPPRESS) == NULL) {
×
324
                goto error;
×
325
            }
×
UNCOV
326
        }
×
UNCOV
327
    }
×
328

UNCOV
329
end:
×
UNCOV
330
    if (orig_de != NULL) {
×
UNCOV
331
        DetectAddressHeadCleanup(&orig_de->addrs);
×
UNCOV
332
        SCFree(orig_de);
×
UNCOV
333
    }
×
UNCOV
334
    return 0;
×
335
error:
×
336
    if (orig_de != NULL) {
×
337
        DetectAddressHeadCleanup(&orig_de->addrs);
×
338
        SCFree(orig_de);
×
339
    }
×
340
    if (de != NULL) {
×
341
        DetectAddressHeadCleanup(&de->addrs);
×
342
        SCFree(de);
×
343
    }
×
344
    return -1;
×
UNCOV
345
}
×
346

347
/** \internal
348
 *  \brief setup suppress rules
349
 *  \retval 0 ok
350
 *  \retval -1 error
351
 */
352
static int SetupThresholdRule(DetectEngineCtx *de_ctx, uint32_t id, uint32_t gid,
353
        uint8_t parsed_type, uint8_t parsed_track, uint32_t parsed_count, uint32_t parsed_seconds,
354
        uint32_t parsed_timeout, uint8_t parsed_new_action)
UNCOV
355
{
×
UNCOV
356
    Signature *s = NULL;
×
UNCOV
357
    SigMatch *sm = NULL;
×
UNCOV
358
    DetectThresholdData *de = NULL;
×
359

UNCOV
360
    BUG_ON(parsed_type == TYPE_SUPPRESS);
×
361

362
    /* Install it */
UNCOV
363
    if (id == 0 && gid == 0) {
×
UNCOV
364
        for (s = de_ctx->sig_list; s != NULL; s = s->next) {
×
UNCOV
365
            sm = DetectGetLastSMByListId(s, DETECT_SM_LIST_THRESHOLD, DETECT_THRESHOLD, -1);
×
UNCOV
366
            if (sm != NULL) {
×
367
                SCLogWarning("signature sid:%" PRIu32 " has "
×
368
                             "an event var set.  The signature event var is "
×
369
                             "given precedence over the threshold.conf one.  "
×
370
                             "We'll change this in the future though.",
×
371
                        s->id);
×
372
                continue;
×
373
            }
×
374

UNCOV
375
            sm = DetectGetLastSMByListId(s,
×
UNCOV
376
                    DETECT_SM_LIST_THRESHOLD, DETECT_DETECTION_FILTER, -1);
×
UNCOV
377
            if (sm != NULL) {
×
378
                SCLogWarning("signature sid:%" PRIu32 " has "
×
379
                             "an event var set.  The signature event var is "
×
380
                             "given precedence over the threshold.conf one.  "
×
381
                             "We'll change this in the future though.",
×
382
                        s->id);
×
383
                continue;
×
384
            }
×
385

UNCOV
386
            de = SCCalloc(1, sizeof(DetectThresholdData));
×
UNCOV
387
            if (unlikely(de == NULL))
×
388
                goto error;
×
389

UNCOV
390
            de->type = parsed_type;
×
UNCOV
391
            de->track = parsed_track;
×
UNCOV
392
            de->count = parsed_count;
×
UNCOV
393
            de->seconds = parsed_seconds;
×
UNCOV
394
            de->new_action = parsed_new_action;
×
UNCOV
395
            de->timeout = parsed_timeout;
×
396

UNCOV
397
            uint16_t smtype = DETECT_THRESHOLD;
×
UNCOV
398
            if (parsed_type == TYPE_RATE)
×
399
                smtype = DETECT_DETECTION_FILTER;
×
400

UNCOV
401
            if (SCSigMatchAppendSMToList(
×
UNCOV
402
                        de_ctx, s, smtype, (SigMatchCtx *)de, DETECT_SM_LIST_THRESHOLD) == NULL) {
×
403
                goto error;
×
404
            }
×
UNCOV
405
        }
×
406

UNCOV
407
    } else if (id == 0 && gid > 0) {
×
408
        for (s = de_ctx->sig_list; s != NULL; s = s->next) {
×
409
            if (s->gid == gid) {
×
410
                sm = DetectGetLastSMByListId(s, DETECT_SM_LIST_THRESHOLD,
×
411
                        DETECT_THRESHOLD, DETECT_DETECTION_FILTER, -1);
×
412
                if (sm != NULL) {
×
413
                    SCLogWarning("signature sid:%" PRIu32 " has "
×
414
                                 "an event var set.  The signature event var is "
×
415
                                 "given precedence over the threshold.conf one.  "
×
416
                                 "We'll change this in the future though.",
×
417
                            id);
×
418
                    continue;
×
419
                }
×
420

421
                de = SCCalloc(1, sizeof(DetectThresholdData));
×
422
                if (unlikely(de == NULL))
×
423
                    goto error;
×
424

425
                de->type = parsed_type;
×
426
                de->track = parsed_track;
×
427
                de->count = parsed_count;
×
428
                de->seconds = parsed_seconds;
×
429
                de->new_action = parsed_new_action;
×
430
                de->timeout = parsed_timeout;
×
431

432
                uint16_t smtype = DETECT_THRESHOLD;
×
433
                if (parsed_type == TYPE_RATE)
×
434
                    smtype = DETECT_DETECTION_FILTER;
×
435

436
                if (SCSigMatchAppendSMToList(de_ctx, s, smtype, (SigMatchCtx *)de,
×
437
                            DETECT_SM_LIST_THRESHOLD) == NULL) {
×
438
                    goto error;
×
439
                }
×
440
            }
×
441
        }
×
UNCOV
442
    } else if (id > 0 && gid == 0) {
×
443
        SCLogError("Can't use a event config that has "
×
444
                   "sid > 0 and gid == 0. Please fix this "
×
445
                   "in your threshold.conf file");
×
UNCOV
446
    } else {
×
UNCOV
447
        s = SigFindSignatureBySidGid(de_ctx, id, gid);
×
UNCOV
448
        if (s == NULL) {
×
UNCOV
449
            SCLogWarning("can't suppress sid "
×
UNCOV
450
                         "%" PRIu32 ", gid %" PRIu32 ": unknown rule",
×
UNCOV
451
                    id, gid);
×
UNCOV
452
        } else {
×
UNCOV
453
            if (parsed_type != TYPE_SUPPRESS && parsed_type != TYPE_THRESHOLD &&
×
UNCOV
454
                parsed_type != TYPE_BOTH && parsed_type != TYPE_LIMIT)
×
UNCOV
455
            {
×
UNCOV
456
                sm = DetectGetLastSMByListId(s,
×
UNCOV
457
                        DETECT_SM_LIST_THRESHOLD, DETECT_THRESHOLD, -1);
×
UNCOV
458
                if (sm != NULL) {
×
459
                    SCLogWarning("signature sid:%" PRIu32 " has "
×
460
                                 "a threshold set. The signature event var is "
×
461
                                 "given precedence over the threshold.conf one. "
×
462
                                 "Bug #425.",
×
463
                            s->id);
×
464
                    goto end;
×
465
                }
×
466

UNCOV
467
                sm = DetectGetLastSMByListId(s, DETECT_SM_LIST_THRESHOLD,
×
UNCOV
468
                        DETECT_DETECTION_FILTER, -1);
×
UNCOV
469
                if (sm != NULL) {
×
470
                    SCLogWarning("signature sid:%" PRIu32 " has "
×
471
                                 "a detection_filter set. The signature event var is "
×
472
                                 "given precedence over the threshold.conf one. "
×
473
                                 "Bug #425.",
×
474
                            s->id);
×
475
                    goto end;
×
476
                }
×
477

478
            /* replace threshold on sig if we have a global override for it */
UNCOV
479
            } else if (parsed_type == TYPE_THRESHOLD || parsed_type == TYPE_BOTH || parsed_type == TYPE_LIMIT) {
×
UNCOV
480
                sm = DetectGetLastSMByListId(s, DETECT_SM_LIST_THRESHOLD,
×
UNCOV
481
                        DETECT_THRESHOLD, DETECT_DETECTION_FILTER, -1);
×
UNCOV
482
                if (sm != NULL) {
×
483
                    SigMatchRemoveSMFromList(s, sm, DETECT_SM_LIST_THRESHOLD);
×
484
                    SigMatchFree(de_ctx, sm);
×
485
                }
×
UNCOV
486
            }
×
487

UNCOV
488
            de = SCCalloc(1, sizeof(DetectThresholdData));
×
UNCOV
489
            if (unlikely(de == NULL))
×
490
                goto error;
×
491

UNCOV
492
            de->type = parsed_type;
×
UNCOV
493
            de->track = parsed_track;
×
UNCOV
494
            de->count = parsed_count;
×
UNCOV
495
            de->seconds = parsed_seconds;
×
UNCOV
496
            de->new_action = parsed_new_action;
×
UNCOV
497
            de->timeout = parsed_timeout;
×
498

UNCOV
499
            uint16_t smtype = DETECT_THRESHOLD;
×
UNCOV
500
            if (parsed_type == TYPE_RATE)
×
UNCOV
501
                smtype = DETECT_DETECTION_FILTER;
×
502

UNCOV
503
            if (SCSigMatchAppendSMToList(
×
UNCOV
504
                        de_ctx, s, smtype, (SigMatchCtx *)de, DETECT_SM_LIST_THRESHOLD) == NULL) {
×
505
                goto error;
×
506
            }
×
UNCOV
507
        }
×
UNCOV
508
    }
×
UNCOV
509
end:
×
UNCOV
510
    return 0;
×
511
error:
×
512
    if (de != NULL) {
×
513
        DetectAddressHeadCleanup(&de->addrs);
×
514
        SCFree(de);
×
515
    }
×
516
    return -1;
×
UNCOV
517
}
×
518

519
static int ParseThresholdRule(const DetectEngineCtx *de_ctx, char *rawstr, uint32_t *ret_id,
520
        uint32_t *ret_gid, uint8_t *ret_parsed_type, uint8_t *ret_parsed_track,
521
        uint32_t *ret_parsed_count, uint32_t *ret_parsed_seconds, uint32_t *ret_parsed_timeout,
522
        uint8_t *ret_parsed_new_action, char **ret_th_ip)
UNCOV
523
{
×
UNCOV
524
    char th_rule_type[32];
×
UNCOV
525
    char th_gid[16];
×
UNCOV
526
    char th_sid[16];
×
UNCOV
527
    const char *rule_extend = NULL;
×
UNCOV
528
    char th_type[16] = "";
×
UNCOV
529
    char th_track[16] = "";
×
UNCOV
530
    char th_count[16] = "";
×
UNCOV
531
    char th_seconds[16] = "";
×
UNCOV
532
    char th_new_action[16] = "";
×
UNCOV
533
    char th_timeout[16] = "";
×
UNCOV
534
    const char *th_ip = NULL;
×
535

UNCOV
536
    uint8_t parsed_type = 0;
×
UNCOV
537
    uint8_t parsed_track = 0;
×
UNCOV
538
    uint8_t parsed_new_action = 0;
×
UNCOV
539
    uint32_t parsed_count = 0;
×
UNCOV
540
    uint32_t parsed_seconds = 0;
×
UNCOV
541
    uint32_t parsed_timeout = 0;
×
542

UNCOV
543
    int ret = 0;
×
UNCOV
544
    uint32_t id = 0, gid = 0;
×
UNCOV
545
    ThresholdRuleType rule_type;
×
546

UNCOV
547
    if (de_ctx == NULL)
×
548
        return -1;
×
549

UNCOV
550
    pcre2_match_data *regex_base_match = NULL;
×
UNCOV
551
    ret = DetectParsePcreExec(regex_base, &regex_base_match, rawstr, 0, 0);
×
UNCOV
552
    if (ret < 4) {
×
UNCOV
553
        SCLogError("pcre2_match parse error, ret %" PRId32 ", string %s", ret, rawstr);
×
UNCOV
554
        pcre2_match_data_free(regex_base_match);
×
UNCOV
555
        goto error;
×
UNCOV
556
    }
×
557

558
    /* retrieve the classtype name */
UNCOV
559
    size_t copylen = sizeof(th_rule_type);
×
UNCOV
560
    ret = pcre2_substring_copy_bynumber(
×
UNCOV
561
            regex_base_match, 1, (PCRE2_UCHAR8 *)th_rule_type, &copylen);
×
UNCOV
562
    if (ret < 0) {
×
563
        SCLogError("pcre2_substring_copy_bynumber failed");
×
564
        pcre2_match_data_free(regex_base_match);
×
565
        goto error;
×
566
    }
×
567

568
    /* retrieve the classtype name */
UNCOV
569
    copylen = sizeof(th_gid);
×
UNCOV
570
    ret = pcre2_substring_copy_bynumber(regex_base_match, 2, (PCRE2_UCHAR8 *)th_gid, &copylen);
×
UNCOV
571
    if (ret < 0) {
×
572
        SCLogError("pcre2_substring_copy_bynumber failed");
×
573
        pcre2_match_data_free(regex_base_match);
×
574
        goto error;
×
575
    }
×
576

UNCOV
577
    copylen = sizeof(th_sid);
×
UNCOV
578
    ret = pcre2_substring_copy_bynumber(regex_base_match, 3, (PCRE2_UCHAR8 *)th_sid, &copylen);
×
UNCOV
579
    if (ret < 0) {
×
580
        SCLogError("pcre2_substring_copy_bynumber failed");
×
581
        pcre2_match_data_free(regex_base_match);
×
582
        goto error;
×
583
    }
×
584

585
    /* Use "get" for heap allocation */
UNCOV
586
    ret = pcre2_substring_get_bynumber(
×
UNCOV
587
            regex_base_match, 4, (PCRE2_UCHAR8 **)&rule_extend, &copylen);
×
UNCOV
588
    if (ret < 0) {
×
589
        SCLogError("pcre2_substring_get_bynumber failed");
×
590
        pcre2_match_data_free(regex_base_match);
×
591
        goto error;
×
592
    }
×
UNCOV
593
    pcre2_match_data_free(regex_base_match);
×
UNCOV
594
    regex_base_match = NULL;
×
595

596
    /* get type of rule */
UNCOV
597
    if (strcasecmp(th_rule_type,"event_filter") == 0) {
×
UNCOV
598
        rule_type = THRESHOLD_TYPE_EVENT_FILTER;
×
UNCOV
599
    } else if (strcasecmp(th_rule_type,"threshold") == 0) {
×
UNCOV
600
        rule_type = THRESHOLD_TYPE_THRESHOLD;
×
UNCOV
601
    } else if (strcasecmp(th_rule_type,"rate_filter") == 0) {
×
UNCOV
602
        rule_type = THRESHOLD_TYPE_RATE;
×
UNCOV
603
    } else if (strcasecmp(th_rule_type,"suppress") == 0) {
×
UNCOV
604
        rule_type = THRESHOLD_TYPE_SUPPRESS;
×
UNCOV
605
    } else {
×
606
        SCLogError("rule type %s is unknown", th_rule_type);
×
607
        goto error;
×
608
    }
×
609

610
    /* get end of rule */
UNCOV
611
    switch(rule_type) {
×
UNCOV
612
        case THRESHOLD_TYPE_EVENT_FILTER:
×
UNCOV
613
        case THRESHOLD_TYPE_THRESHOLD:
×
UNCOV
614
            if (strlen(rule_extend) > 0) {
×
UNCOV
615
                pcre2_match_data *match = NULL;
×
616

UNCOV
617
                ret = DetectParsePcreExec(regex_threshold, &match, rule_extend, 0, 0);
×
UNCOV
618
                if (ret < 4) {
×
UNCOV
619
                    SCLogError("pcre2_match parse error, ret %" PRId32 ", string %s", ret,
×
UNCOV
620
                            rule_extend);
×
UNCOV
621
                    pcre2_match_data_free(match);
×
UNCOV
622
                    goto error;
×
UNCOV
623
                }
×
624

UNCOV
625
                copylen = sizeof(th_type);
×
UNCOV
626
                ret = pcre2_substring_copy_bynumber(match, 1, (PCRE2_UCHAR8 *)th_type, &copylen);
×
UNCOV
627
                if (ret < 0) {
×
628
                    SCLogError("pcre2_substring_copy_bynumber failed");
×
629
                    pcre2_match_data_free(match);
×
630
                    goto error;
×
631
                }
×
632

UNCOV
633
                copylen = sizeof(th_track);
×
UNCOV
634
                ret = pcre2_substring_copy_bynumber(match, 2, (PCRE2_UCHAR8 *)th_track, &copylen);
×
UNCOV
635
                if (ret < 0) {
×
636
                    SCLogError("pcre2_substring_copy_bynumber failed");
×
637
                    pcre2_match_data_free(match);
×
638
                    goto error;
×
639
                }
×
640

UNCOV
641
                copylen = sizeof(th_count);
×
UNCOV
642
                ret = pcre2_substring_copy_bynumber(match, 3, (PCRE2_UCHAR8 *)th_count, &copylen);
×
UNCOV
643
                if (ret < 0) {
×
644
                    SCLogError("pcre2_substring_copy_bynumber failed");
×
645
                    pcre2_match_data_free(match);
×
646
                    goto error;
×
647
                }
×
648

UNCOV
649
                copylen = sizeof(th_seconds);
×
UNCOV
650
                ret = pcre2_substring_copy_bynumber(match, 4, (PCRE2_UCHAR8 *)th_seconds, &copylen);
×
UNCOV
651
                if (ret < 0) {
×
652
                    SCLogError("pcre2_substring_copy_bynumber failed");
×
653
                    pcre2_match_data_free(match);
×
654
                    goto error;
×
655
                }
×
UNCOV
656
                pcre2_match_data_free(match);
×
657

UNCOV
658
                if (strcasecmp(th_type,"limit") == 0)
×
UNCOV
659
                    parsed_type = TYPE_LIMIT;
×
UNCOV
660
                else if (strcasecmp(th_type,"both") == 0)
×
UNCOV
661
                    parsed_type = TYPE_BOTH;
×
UNCOV
662
                else if (strcasecmp(th_type,"threshold") == 0)
×
UNCOV
663
                    parsed_type = TYPE_THRESHOLD;
×
664
                else {
×
665
                    SCLogError("limit type not supported: %s", th_type);
×
666
                    goto error;
×
667
                }
×
UNCOV
668
            } else {
×
669
                SCLogError("rule invalid: %s", rawstr);
×
670
                goto error;
×
671
            }
×
UNCOV
672
            break;
×
UNCOV
673
        case THRESHOLD_TYPE_SUPPRESS:
×
UNCOV
674
            if (strlen(rule_extend) > 0) {
×
UNCOV
675
                pcre2_match_data *match = NULL;
×
UNCOV
676
                ret = DetectParsePcreExec(regex_suppress, &match, rule_extend, 0, 0);
×
UNCOV
677
                if (ret < 2) {
×
UNCOV
678
                    SCLogError("pcre2_match parse error, ret %" PRId32 ", string %s", ret,
×
UNCOV
679
                            rule_extend);
×
UNCOV
680
                    pcre2_match_data_free(match);
×
UNCOV
681
                    goto error;
×
UNCOV
682
                }
×
683
                /* retrieve the track mode */
UNCOV
684
                copylen = sizeof(th_seconds);
×
UNCOV
685
                ret = pcre2_substring_copy_bynumber(match, 1, (PCRE2_UCHAR8 *)th_track, &copylen);
×
UNCOV
686
                if (ret < 0) {
×
687
                    SCLogError("pcre2_substring_copy_bynumber failed");
×
688
                    pcre2_match_data_free(match);
×
689
                    goto error;
×
690
                }
×
691
                /* retrieve the IP; use "get" for heap allocation */
UNCOV
692
                ret = pcre2_substring_get_bynumber(match, 2, (PCRE2_UCHAR8 **)&th_ip, &copylen);
×
UNCOV
693
                if (ret < 0) {
×
694
                    SCLogError("pcre2_substring_get_bynumber failed");
×
695
                    pcre2_match_data_free(match);
×
696
                    goto error;
×
697
                }
×
UNCOV
698
                pcre2_match_data_free(match);
×
UNCOV
699
            } else {
×
UNCOV
700
                parsed_track = TRACK_RULE;
×
UNCOV
701
            }
×
UNCOV
702
            parsed_type = TYPE_SUPPRESS;
×
UNCOV
703
            break;
×
UNCOV
704
        case THRESHOLD_TYPE_RATE:
×
UNCOV
705
            if (strlen(rule_extend) > 0) {
×
UNCOV
706
                pcre2_match_data *match = NULL;
×
UNCOV
707
                ret = DetectParsePcreExec(regex_rate, &match, rule_extend, 0, 0);
×
UNCOV
708
                if (ret < 5) {
×
709
                    SCLogError("pcre2_match parse error, ret %" PRId32 ", string %s", ret,
×
710
                            rule_extend);
×
711
                    pcre2_match_data_free(match);
×
712
                    goto error;
×
713
                }
×
714

UNCOV
715
                copylen = sizeof(th_track);
×
UNCOV
716
                ret = pcre2_substring_copy_bynumber(match, 1, (PCRE2_UCHAR8 *)th_track, &copylen);
×
UNCOV
717
                if (ret < 0) {
×
718
                    SCLogError("pcre2_substring_copy_bynumber failed");
×
719
                    pcre2_match_data_free(match);
×
720
                    goto error;
×
721
                }
×
722

UNCOV
723
                copylen = sizeof(th_count);
×
UNCOV
724
                ret = pcre2_substring_copy_bynumber(match, 2, (PCRE2_UCHAR8 *)th_count, &copylen);
×
UNCOV
725
                if (ret < 0) {
×
726
                    SCLogError("pcre2_substring_copy_bynumber failed");
×
727
                    pcre2_match_data_free(match);
×
728
                    goto error;
×
729
                }
×
730

UNCOV
731
                copylen = sizeof(th_seconds);
×
UNCOV
732
                ret = pcre2_substring_copy_bynumber(match, 3, (PCRE2_UCHAR8 *)th_seconds, &copylen);
×
UNCOV
733
                if (ret < 0) {
×
734
                    SCLogError("pcre2_substring_copy_bynumber failed");
×
735
                    pcre2_match_data_free(match);
×
736
                    goto error;
×
737
                }
×
738

UNCOV
739
                copylen = sizeof(th_new_action);
×
UNCOV
740
                ret = pcre2_substring_copy_bynumber(
×
UNCOV
741
                        match, 4, (PCRE2_UCHAR8 *)th_new_action, &copylen);
×
UNCOV
742
                if (ret < 0) {
×
743
                    SCLogError("pcre2_substring_copy_bynumber failed");
×
744
                    pcre2_match_data_free(match);
×
745
                    goto error;
×
746
                }
×
747

UNCOV
748
                copylen = sizeof(th_timeout);
×
UNCOV
749
                ret = pcre2_substring_copy_bynumber(match, 5, (PCRE2_UCHAR8 *)th_timeout, &copylen);
×
UNCOV
750
                if (ret < 0) {
×
751
                    SCLogError("pcre2_substring_copy_bynumber failed");
×
752
                    pcre2_match_data_free(match);
×
753
                    goto error;
×
754
                }
×
UNCOV
755
                pcre2_match_data_free(match);
×
756

757
                /* TODO: implement option "apply_to" */
758

UNCOV
759
                if (StringParseUint32(&parsed_timeout, 10, sizeof(th_timeout), th_timeout) <= 0) {
×
760
                    goto error;
×
761
                }
×
762

763
                /* Get the new action to take */
UNCOV
764
                if (strcasecmp(th_new_action, "alert") == 0)
×
UNCOV
765
                    parsed_new_action = TH_ACTION_ALERT;
×
UNCOV
766
                if (strcasecmp(th_new_action, "drop") == 0)
×
UNCOV
767
                    parsed_new_action = TH_ACTION_DROP;
×
UNCOV
768
                if (strcasecmp(th_new_action, "pass") == 0)
×
UNCOV
769
                    parsed_new_action = TH_ACTION_PASS;
×
UNCOV
770
                if (strcasecmp(th_new_action, "reject") == 0)
×
UNCOV
771
                    parsed_new_action = TH_ACTION_REJECT;
×
UNCOV
772
                if (strcasecmp(th_new_action, "log") == 0) {
×
773
                    SCLogInfo("log action for rate_filter not supported yet");
×
774
                    parsed_new_action = TH_ACTION_LOG;
×
775
                }
×
UNCOV
776
                if (strcasecmp(th_new_action, "sdrop") == 0) {
×
777
                    SCLogInfo("sdrop action for rate_filter not supported yet");
×
778
                    parsed_new_action = TH_ACTION_SDROP;
×
779
                }
×
UNCOV
780
                parsed_type = TYPE_RATE;
×
UNCOV
781
            } else {
×
782
                SCLogError("rule invalid: %s", rawstr);
×
783
                goto error;
×
784
            }
×
UNCOV
785
            break;
×
UNCOV
786
    }
×
787

UNCOV
788
    switch (rule_type) {
×
789
        /* This part is common to threshold/event_filter/rate_filter */
UNCOV
790
        case THRESHOLD_TYPE_EVENT_FILTER:
×
UNCOV
791
        case THRESHOLD_TYPE_THRESHOLD:
×
UNCOV
792
        case THRESHOLD_TYPE_RATE:
×
UNCOV
793
            if (strcasecmp(th_track,"by_dst") == 0)
×
UNCOV
794
                parsed_track = TRACK_DST;
×
UNCOV
795
            else if (strcasecmp(th_track,"by_src") == 0)
×
UNCOV
796
                parsed_track = TRACK_SRC;
×
UNCOV
797
            else if (strcasecmp(th_track, "by_both") == 0) {
×
UNCOV
798
                parsed_track = TRACK_BOTH;
×
UNCOV
799
            }
×
UNCOV
800
            else if (strcasecmp(th_track,"by_rule") == 0)
×
UNCOV
801
                parsed_track = TRACK_RULE;
×
UNCOV
802
            else if (strcasecmp(th_track, "by_flow") == 0)
×
UNCOV
803
                parsed_track = TRACK_FLOW;
×
804
            else {
×
805
                SCLogError("Invalid track parameter %s in %s", th_track, rawstr);
×
806
                goto error;
×
807
            }
×
808

UNCOV
809
            if (StringParseUint32(&parsed_count, 10, sizeof(th_count), th_count) <= 0) {
×
810
                goto error;
×
811
            }
×
UNCOV
812
            if (parsed_count == 0) {
×
813
                SCLogError("rate filter count should be > 0");
×
814
                goto error;
×
815
            }
×
816

UNCOV
817
            if (StringParseUint32(&parsed_seconds, 10, sizeof(th_seconds), th_seconds) <= 0) {
×
818
                goto error;
×
819
            }
×
820

UNCOV
821
           break;
×
UNCOV
822
        case THRESHOLD_TYPE_SUPPRESS:
×
823
            /* need to get IP if extension is provided */
UNCOV
824
            if (strcmp("", th_track) != 0) {
×
UNCOV
825
                if (strcasecmp(th_track,"by_dst") == 0)
×
UNCOV
826
                    parsed_track = TRACK_DST;
×
UNCOV
827
                else if (strcasecmp(th_track,"by_src") == 0)
×
UNCOV
828
                    parsed_track = TRACK_SRC;
×
UNCOV
829
                else if (strcasecmp(th_track,"by_either") == 0) {
×
UNCOV
830
                    parsed_track = TRACK_EITHER;
×
UNCOV
831
                }
×
832
                else {
×
833
                    SCLogError("Invalid track parameter %s in %s", th_track, rule_extend);
×
834
                    goto error;
×
835
                }
×
UNCOV
836
            }
×
UNCOV
837
            break;
×
UNCOV
838
    }
×
839

UNCOV
840
    if (StringParseUint32(&id, 10, sizeof(th_sid), th_sid) <= 0) {
×
841
        goto error;
×
842
    }
×
843

UNCOV
844
    if (StringParseUint32(&gid, 10, sizeof(th_gid), th_gid) <= 0) {
×
845
        goto error;
×
846
    }
×
847

UNCOV
848
    *ret_id = id;
×
UNCOV
849
    *ret_gid = gid;
×
UNCOV
850
    *ret_parsed_type = parsed_type;
×
UNCOV
851
    *ret_parsed_track = parsed_track;
×
UNCOV
852
    *ret_parsed_new_action = parsed_new_action;
×
UNCOV
853
    *ret_parsed_count = parsed_count;
×
UNCOV
854
    *ret_parsed_seconds = parsed_seconds;
×
UNCOV
855
    *ret_parsed_timeout = parsed_timeout;
×
UNCOV
856
    *ret_th_ip = NULL;
×
UNCOV
857
    if (th_ip != NULL) {
×
UNCOV
858
        *ret_th_ip = (char *)th_ip;
×
UNCOV
859
    }
×
UNCOV
860
    pcre2_substring_free((PCRE2_UCHAR8 *)rule_extend);
×
UNCOV
861
    return 0;
×
862

UNCOV
863
error:
×
UNCOV
864
    if (rule_extend != NULL) {
×
UNCOV
865
        pcre2_substring_free((PCRE2_UCHAR8 *)rule_extend);
×
UNCOV
866
    }
×
UNCOV
867
    if (th_ip != NULL) {
×
868
        pcre2_substring_free((PCRE2_UCHAR8 *)th_ip);
×
869
    }
×
UNCOV
870
    return -1;
×
UNCOV
871
}
×
872

873
/**
874
 * \brief Parses a line from the threshold file and applies it to the
875
 *        detection engine
876
 *
877
 * \param rawstr Pointer to the string to be parsed.
878
 * \param de_ctx Pointer to the Detection Engine Context.
879
 *
880
 * \retval  0 On success.
881
 * \retval -1 On failure.
882
 */
883
static int SCThresholdConfAddThresholdtype(char *rawstr, DetectEngineCtx *de_ctx)
UNCOV
884
{
×
UNCOV
885
    uint8_t parsed_type = 0;
×
UNCOV
886
    uint8_t parsed_track = 0;
×
UNCOV
887
    uint8_t parsed_new_action = 0;
×
UNCOV
888
    uint32_t parsed_count = 0;
×
UNCOV
889
    uint32_t parsed_seconds = 0;
×
UNCOV
890
    uint32_t parsed_timeout = 0;
×
UNCOV
891
    char *th_ip = NULL;
×
UNCOV
892
    uint32_t id = 0, gid = 0;
×
893

UNCOV
894
    int r = ParseThresholdRule(de_ctx, rawstr, &id, &gid, &parsed_type, &parsed_track,
×
UNCOV
895
            &parsed_count, &parsed_seconds, &parsed_timeout, &parsed_new_action, &th_ip);
×
UNCOV
896
    if (r < 0)
×
UNCOV
897
        goto error;
×
898

UNCOV
899
    if (parsed_type == TYPE_SUPPRESS) {
×
UNCOV
900
        r = SetupSuppressRule(de_ctx, id, gid, parsed_type, parsed_track,
×
UNCOV
901
                    parsed_count, parsed_seconds, parsed_timeout, parsed_new_action,
×
UNCOV
902
                    th_ip);
×
UNCOV
903
    } else {
×
UNCOV
904
        r = SetupThresholdRule(de_ctx, id, gid, parsed_type, parsed_track, parsed_count,
×
UNCOV
905
                parsed_seconds, parsed_timeout, parsed_new_action);
×
UNCOV
906
    }
×
UNCOV
907
    if (r < 0) {
×
908
        goto error;
×
909
    }
×
910

UNCOV
911
    pcre2_substring_free((PCRE2_UCHAR8 *)th_ip);
×
UNCOV
912
    return 0;
×
UNCOV
913
error:
×
UNCOV
914
    if (th_ip != NULL)
×
UNCOV
915
        pcre2_substring_free((PCRE2_UCHAR8 *)th_ip);
×
UNCOV
916
    return -1;
×
UNCOV
917
}
×
918

919
/**
920
 * \brief Checks if a string is a comment or a blank line.
921
 *
922
 *        Comments lines are lines of the following format -
923
 *        "# This is a comment string" or
924
 *        "   # This is a comment string".
925
 *
926
 * \param line String that has to be checked
927
 *
928
 * \retval 1 On the argument string being a comment or blank line
929
 * \retval 0 Otherwise
930
 */
931
static int SCThresholdConfIsLineBlankOrComment(char *line)
UNCOV
932
{
×
UNCOV
933
    while (*line != '\0') {
×
934
        /* we have a comment */
UNCOV
935
        if (*line == '#')
×
936
            return 1;
×
937

938
        /* this line is neither a comment line, nor a blank line */
UNCOV
939
        if (!isspace((unsigned char)*line))
×
UNCOV
940
            return 0;
×
941

942
        line++;
×
943
    }
×
944

945
    /* we have a blank line */
946
    return 1;
×
UNCOV
947
}
×
948

949
/**
950
 * \brief Checks if the rule is multiline, by searching an ending slash
951
 *
952
 * \param line String that has to be checked
953
 *
954
 * \retval the position of the slash making it multiline
955
 * \retval 0 Otherwise
956
 */
957
static int SCThresholdConfLineIsMultiline(char *line)
UNCOV
958
{
×
UNCOV
959
    int flag = 0;
×
UNCOV
960
    char *rline = line;
×
UNCOV
961
    size_t len = strlen(line);
×
962

UNCOV
963
    while (line < rline + len && *line != '\n') {
×
964
        /* we have a comment */
UNCOV
965
        if (*line == '\\')
×
UNCOV
966
            flag = (int)(line - rline);
×
UNCOV
967
        else
×
UNCOV
968
            if (!isspace((unsigned char)*line))
×
UNCOV
969
                flag = 0;
×
970

UNCOV
971
        line++;
×
UNCOV
972
    }
×
973

974
    /* we have a blank line */
UNCOV
975
    return flag;
×
UNCOV
976
}
×
977

978
/**
979
 * \brief Parses the Threshold Config file
980
 *
981
 * \param de_ctx Pointer to the Detection Engine Context.
982
 * \param fd Pointer to file descriptor.
983
 */
984
int SCThresholdConfParseFile(DetectEngineCtx *de_ctx, FILE *fp)
UNCOV
985
{
×
UNCOV
986
    char line[8192] = "";
×
UNCOV
987
    int rule_num = 0;
×
988

989
    /* position of "\", on multiline rules */
UNCOV
990
    int esc_pos = 0;
×
991

UNCOV
992
    if (fp == NULL)
×
993
        return -1;
×
994

UNCOV
995
    while (fgets(line + esc_pos, (int)sizeof(line) - esc_pos, fp) != NULL) {
×
UNCOV
996
        if (SCThresholdConfIsLineBlankOrComment(line)) {
×
997
            continue;
×
998
        }
×
999

UNCOV
1000
        esc_pos = SCThresholdConfLineIsMultiline(line);
×
UNCOV
1001
        if (esc_pos == 0) {
×
UNCOV
1002
            if (SCThresholdConfAddThresholdtype(line, de_ctx) < 0) {
×
UNCOV
1003
                if (SCRunmodeGet() == RUNMODE_CONF_TEST)
×
UNCOV
1004
                    return -1;
×
UNCOV
1005
            } else {
×
UNCOV
1006
                SCLogDebug("Adding threshold.config rule num %" PRIu32 "( %s )", rule_num, line);
×
UNCOV
1007
                rule_num++;
×
UNCOV
1008
            }
×
UNCOV
1009
        }
×
UNCOV
1010
    }
×
1011

UNCOV
1012
    if (de_ctx != NULL && strlen(de_ctx->config_prefix) > 0)
×
1013
        SCLogInfo("tenant id %d: Threshold config parsed: %d rule(s) found", de_ctx->tenant_id,
×
UNCOV
1014
                rule_num);
×
UNCOV
1015
    else
×
UNCOV
1016
        SCLogInfo("Threshold config parsed: %d rule(s) found", rule_num);
×
UNCOV
1017
    return 0;
×
UNCOV
1018
}
×
1019

1020
#ifdef UNITTESTS
1021
#include "detect-engine-alert.h"
1022
#include "packet.h"
1023
#include "action-globals.h"
1024

1025
/**
1026
 * \brief Creates a dummy threshold file, with all valid options, for testing purposes.
1027
 *
1028
 * \retval fd Pointer to file descriptor.
1029
 */
1030
static FILE *SCThresholdConfGenerateValidDummyFD01(void)
1031
{
1032
    FILE *fd = NULL;
1033
    const char *buffer =
1034
        "event_filter gen_id 1, sig_id 10, type limit, track by_src, count 1, seconds 60\n"
1035
        "threshold gen_id 1, sig_id 100, type both, track by_dst, count 10, seconds 60\n"
1036
        "event_filter gen_id 1, sig_id 1000, type threshold, track by_src, count 100, seconds 60\n";
1037

1038
    fd = SCFmemopen((void *)buffer, strlen(buffer), "r");
1039
    if (fd == NULL)
1040
        SCLogDebug("Error with SCFmemopen() called by Threshold Config test code");
1041

1042
    return fd;
1043
}
1044

1045
/**
1046
 * \brief Creates a dummy threshold file, with some valid options and a couple of invalid options.
1047
 *        For testing purposes.
1048
 *
1049
 * \retval fd Pointer to file descriptor.
1050
 */
1051
static FILE *SCThresholdConfGenerateInvalidDummyFD02(void)
1052
{
1053
    FILE *fd;
1054
    const char *buffer =
1055
        "event_filter gen_id 1, sig_id 1000, type invalid, track by_src, count 100, seconds 60\n";
1056

1057
    fd = SCFmemopen((void *)buffer, strlen(buffer), "r");
1058
    if (fd == NULL)
1059
        SCLogDebug("Error with SCFmemopen() called by Threshold Config test code");
1060

1061
    return fd;
1062
}
1063

1064
/**
1065
 * \brief Creates a dummy threshold file, with all valid options, for testing purposes.
1066
 *
1067
 * \retval fd Pointer to file descriptor.
1068
 */
1069
static FILE *SCThresholdConfGenerateValidDummyFD03(void)
1070
{
1071
    FILE *fd;
1072
    const char *buffer =
1073
        "event_filter gen_id 0, sig_id 0, type threshold, track by_src, count 100, seconds 60\n";
1074

1075
    fd = SCFmemopen((void *)buffer, strlen(buffer), "r");
1076
    if (fd == NULL)
1077
        SCLogDebug("Error with SCFmemopen() called by Threshold Config test code");
1078

1079
    return fd;
1080
}
1081

1082
/**
1083
 * \brief Creates a dummy threshold file, with all valid options, but
1084
 *        with split rules (multiline), for testing purposes.
1085
 *
1086
 * \retval fd Pointer to file descriptor.
1087
 */
1088
static FILE *SCThresholdConfGenerateValidDummyFD04(void)
1089
{
1090
    FILE *fd = NULL;
1091
    const char *buffer =
1092
        "event_filter gen_id 1 \\\n, sig_id 10, type limit, track by_src, \\\ncount 1, seconds 60\n"
1093
        "threshold gen_id 1, \\\nsig_id 100, type both\\\n, track by_dst, count 10, \\\n seconds 60\n"
1094
        "event_filter gen_id 1, sig_id 1000, \\\ntype threshold, track \\\nby_src, count 100, seconds 60\n";
1095

1096
    fd = SCFmemopen((void *)buffer, strlen(buffer), "r");
1097
    if (fd == NULL)
1098
        SCLogDebug("Error with SCFmemopen() called by Threshold Config test code");
1099

1100
    return fd;
1101
}
1102

1103
/**
1104
 * \brief Creates a dummy threshold file, with all valid options, for testing purposes.
1105
 *
1106
 * \retval fd Pointer to file descriptor.
1107
 */
1108
static FILE *SCThresholdConfGenerateValidDummyFD05(void)
1109
{
1110
    FILE *fd = NULL;
1111
    const char *buffer =
1112
        "rate_filter gen_id 1, sig_id 10, track by_src, count 1, seconds 60, new_action drop, timeout 10\n"
1113
        "rate_filter gen_id 1, sig_id 100, track by_dst, count 10, seconds 60, new_action pass, timeout 5\n"
1114
        "rate_filter gen_id 1, sig_id 1000, track by_rule, count 100, seconds 60, new_action alert, timeout 30\n"
1115
        "rate_filter gen_id 1, sig_id 10000, track by_both, count 1000, seconds 60, new_action reject, timeout 21\n";
1116

1117
    fd = SCFmemopen((void *)buffer, strlen(buffer), "r");
1118
    if (fd == NULL)
1119
        SCLogDebug("Error with SCFmemopen() called by Threshold Config test code");
1120

1121
    return fd;
1122
}
1123

1124
/**
1125
 * \brief Creates a dummy threshold file, with all valid options, but
1126
 *        with split rules (multiline), for testing purposes.
1127
 *
1128
 * \retval fd Pointer to file descriptor.
1129
 */
1130
static FILE *SCThresholdConfGenerateValidDummyFD06(void)
1131
{
1132
    FILE *fd = NULL;
1133
    const char *buffer =
1134
        "rate_filter \\\ngen_id 1, sig_id 10, track by_src, count 1, seconds 60\\\n, new_action drop, timeout 10\n"
1135
        "rate_filter gen_id 1, \\\nsig_id 100, track by_dst, \\\ncount 10, seconds 60, new_action pass, timeout 5\n"
1136
        "rate_filter gen_id 1, sig_id 1000, \\\ntrack by_rule, count 100, seconds 60, new_action alert, timeout 30\n"
1137
        "rate_filter gen_id 1, sig_id 10000, track by_both, count 1000, \\\nseconds 60, new_action reject, timeout 21\n";
1138

1139
    fd = SCFmemopen((void *)buffer, strlen(buffer), "r");
1140
    if (fd == NULL)
1141
        SCLogDebug("Error with SCFmemopen() called by Threshold Config test code");
1142

1143
    return fd;
1144
}
1145

1146
/**
1147
 * \brief Creates a dummy threshold file, with all valid options, but
1148
 *        with split rules (multiline), for testing purposes.
1149
 *
1150
 * \retval fd Pointer to file descriptor.
1151
 */
1152
static FILE *SCThresholdConfGenerateValidDummyFD07(void)
1153
{
1154
    FILE *fd = NULL;
1155
    const char *buffer =
1156
        "rate_filter gen_id 1, sig_id 10, track by_src, count 3, seconds 3, new_action drop, timeout 10\n"
1157
        "rate_filter gen_id 1, sig_id 11, track by_src, count 3, seconds 1, new_action drop, timeout 5\n";
1158

1159
    fd = SCFmemopen((void *)buffer, strlen(buffer), "r");
1160
    if (fd == NULL)
1161
        SCLogDebug("Error with SCFmemopen() called by Threshold Config test code");
1162

1163
    return fd;
1164
}
1165

1166
/**
1167
 * \brief Creates a dummy threshold file, for testing rate_filter, track by_rule
1168
 *
1169
 * \retval fd Pointer to file descriptor.
1170
 */
1171
static FILE *SCThresholdConfGenerateValidDummyFD08(void)
1172
{
1173
    FILE *fd = NULL;
1174
    const char *buffer =
1175
        "rate_filter gen_id 1, sig_id 10, track by_rule, count 3, seconds 3, new_action drop, timeout 10\n";
1176

1177
    fd = SCFmemopen((void *)buffer, strlen(buffer), "r");
1178
    if (fd == NULL)
1179
        SCLogDebug("Error with SCFmemopen() called by Threshold Config test code");
1180

1181
    return fd;
1182
}
1183

1184
/**
1185
 * \brief Creates a dummy threshold file, with all valid options, but
1186
 *        with split rules (multiline), for testing purposes.
1187
 *
1188
 * \retval fd Pointer to file descriptor.
1189
 */
1190
static FILE *SCThresholdConfGenerateValidDummyFD09(void)
1191
{
1192
    FILE *fd = NULL;
1193
    const char *buffer =
1194
        "event_filter gen_id 1 \\\n, sig_id 10, type limit, track by_src, \\\ncount 2, seconds 60\n"
1195
        "threshold gen_id 1, \\\nsig_id 11, type threshold\\\n, track by_dst, count 3, \\\n seconds 60\n"
1196
        "event_filter gen_id 1, sig_id 12, \\\ntype both, track \\\nby_src, count 2, seconds 60\n";
1197

1198
    fd = SCFmemopen((void *)buffer, strlen(buffer), "r");
1199
    if (fd == NULL)
1200
        SCLogDebug("Error with SCFmemopen() called by Threshold Config test code");
1201

1202
    return fd;
1203
}
1204

1205
/**
1206
 * \brief Creates a dummy threshold file, with all valid options, but
1207
 *        with split rules (multiline), for testing purposes.
1208
 *
1209
 * \retval fd Pointer to file descriptor.
1210
 */
1211
static FILE *SCThresholdConfGenerateValidDummyFD10(void)
1212
{
1213
    FILE *fd = NULL;
1214
    const char *buffer =
1215
        "event_filter gen_id 1 \\\n, sig_id 10, type limit, track by_src, \\\ncount 5, seconds 2\n"
1216
        "threshold gen_id 1, \\\nsig_id 11, type threshold\\\n, track by_dst, count 5, \\\n seconds 2\n"
1217
        "event_filter gen_id 1, sig_id 12, \\\ntype both, track \\\nby_src, count 5, seconds 2\n";
1218

1219
    fd = SCFmemopen((void *)buffer, strlen(buffer), "r");
1220
    if (fd == NULL)
1221
        SCLogDebug("Error with SCFmemopen() called by Threshold Config test code");
1222

1223
    return fd;
1224
}
1225

1226
/**
1227
 * \brief Creates a dummy threshold file, with all valid options, for testing purposes.
1228
 *
1229
 * \retval fd Pointer to file descriptor.
1230
 */
1231
static FILE *SCThresholdConfGenerateValidDummyFD11(void)
1232
{
1233
    FILE *fd = NULL;
1234
    const char *buffer =
1235
        "suppress gen_id 1, sig_id 10000\n"
1236
        "suppress gen_id 1, sig_id 1000, track by_src, ip 192.168.1.1\n";
1237

1238
    fd = SCFmemopen((void *)buffer, strlen(buffer), "r");
1239
    if (fd == NULL)
1240
        SCLogDebug("Error with SCFmemopen() called by Threshold Config test code");
1241

1242
    return fd;
1243
}
1244

1245
/**
1246
 * \test Check if the threshold file is loaded and well parsed
1247
 *
1248
 *  \retval 1 on success
1249
 *  \retval 0 on failure
1250
 */
1251
static int SCThresholdConfTest01(void)
1252
{
1253
    DetectEngineCtx *de_ctx = DetectEngineCtxInit();
1254
    FAIL_IF_NULL(de_ctx);
1255
    de_ctx->flags |= DE_QUIET;
1256

1257
    Signature *sig = DetectEngineAppendSig(de_ctx,
1258
            "alert tcp any any -> any any (msg:\"Threshold limit\"; gid:1; sid:10;)");
1259
    FAIL_IF_NULL(sig);
1260

1261
    FAIL_IF_NOT_NULL(g_ut_threshold_fp);
1262
    g_ut_threshold_fp = SCThresholdConfGenerateValidDummyFD01();
1263
    FAIL_IF_NULL(g_ut_threshold_fp);
1264
    FAIL_IF(-1 == SCThresholdConfInitContext(de_ctx));
1265

1266
    SigMatch *m = DetectGetLastSMByListId(sig, DETECT_SM_LIST_THRESHOLD,
1267
            DETECT_THRESHOLD, -1);
1268
    FAIL_IF_NULL(m);
1269

1270
    DetectThresholdData *de = (DetectThresholdData *)m->ctx;
1271
    FAIL_IF_NULL(de);
1272

1273
    FAIL_IF_NOT(de->type == TYPE_LIMIT && de->track == TRACK_SRC && de->count == 1 && de->seconds == 60);
1274
    DetectEngineCtxFree(de_ctx);
1275
    PASS;
1276
}
1277

1278
/**
1279
 * \test Check if the threshold file is loaded and well parsed
1280
 *
1281
 *  \retval 1 on success
1282
 *  \retval 0 on failure
1283
 */
1284
static int SCThresholdConfTest02(void)
1285
{
1286
    DetectEngineCtx *de_ctx = DetectEngineCtxInit();
1287
    FAIL_IF_NULL(de_ctx);
1288
    de_ctx->flags |= DE_QUIET;
1289

1290
    Signature *sig = DetectEngineAppendSig(de_ctx,
1291
            "alert tcp any any -> any any (msg:\"Threshold limit\"; gid:1; sid:100;)");
1292
    FAIL_IF_NULL(sig);
1293

1294
    FAIL_IF_NOT_NULL(g_ut_threshold_fp);
1295
    g_ut_threshold_fp = SCThresholdConfGenerateValidDummyFD01();
1296
    FAIL_IF_NULL(g_ut_threshold_fp);
1297
    FAIL_IF(-1 == SCThresholdConfInitContext(de_ctx));
1298

1299
    SigMatch *m = DetectGetLastSMByListId(sig, DETECT_SM_LIST_THRESHOLD,
1300
            DETECT_THRESHOLD, -1);
1301
    FAIL_IF_NULL(m);
1302

1303
    DetectThresholdData *de = (DetectThresholdData *)m->ctx;
1304
    FAIL_IF_NULL(de);
1305

1306
    FAIL_IF_NOT(de->type == TYPE_BOTH && de->track == TRACK_DST && de->count == 10 && de->seconds == 60);
1307
    DetectEngineCtxFree(de_ctx);
1308
    PASS;
1309
}
1310

1311
/**
1312
 * \test Check if the threshold file is loaded and well parsed
1313
 *
1314
 *  \retval 1 on success
1315
 *  \retval 0 on failure
1316
 */
1317
static int SCThresholdConfTest03(void)
1318
{
1319
    DetectEngineCtx *de_ctx = DetectEngineCtxInit();
1320
    FAIL_IF_NULL(de_ctx);
1321
    de_ctx->flags |= DE_QUIET;
1322

1323
    Signature *sig = DetectEngineAppendSig(de_ctx,
1324
            "alert tcp any any -> any any (msg:\"Threshold limit\"; gid:1; sid:1000;)");
1325
    FAIL_IF_NULL(sig);
1326

1327
    FAIL_IF_NOT_NULL(g_ut_threshold_fp);
1328
    g_ut_threshold_fp = SCThresholdConfGenerateValidDummyFD01();
1329
    FAIL_IF_NULL(g_ut_threshold_fp);
1330
    FAIL_IF(-1 == SCThresholdConfInitContext(de_ctx));
1331

1332
    SigMatch *m = DetectGetLastSMByListId(sig, DETECT_SM_LIST_THRESHOLD,
1333
            DETECT_THRESHOLD, -1);
1334
    FAIL_IF_NULL(m);
1335

1336
    DetectThresholdData *de = (DetectThresholdData *)m->ctx;
1337
    FAIL_IF_NULL(de);
1338

1339
    FAIL_IF_NOT(de->type == TYPE_THRESHOLD && de->track == TRACK_SRC && de->count == 100 && de->seconds == 60);
1340
    DetectEngineCtxFree(de_ctx);
1341
    PASS;
1342
}
1343

1344
/**
1345
 * \test Check if the threshold file is loaded and well parsed
1346
 *
1347
 *  \retval 1 on success
1348
 *  \retval 0 on failure
1349
 */
1350
static int SCThresholdConfTest04(void)
1351
{
1352
    DetectEngineCtx *de_ctx = DetectEngineCtxInit();
1353
    FAIL_IF_NULL(de_ctx);
1354
    de_ctx->flags |= DE_QUIET;
1355

1356
    Signature *sig = DetectEngineAppendSig(de_ctx,
1357
            "alert tcp any any -> any any (msg:\"Threshold limit\"; gid:1; sid:1000;)");
1358
    FAIL_IF_NULL(sig);
1359

1360
    FAIL_IF_NOT_NULL(g_ut_threshold_fp);
1361
    g_ut_threshold_fp = SCThresholdConfGenerateInvalidDummyFD02();
1362
    FAIL_IF_NULL(g_ut_threshold_fp);
1363
    FAIL_IF(-1 == SCThresholdConfInitContext(de_ctx));
1364

1365
    SigMatch *m = DetectGetLastSMByListId(sig, DETECT_SM_LIST_THRESHOLD,
1366
            DETECT_THRESHOLD, -1);
1367
    FAIL_IF_NOT_NULL(m);
1368

1369
    DetectEngineCtxFree(de_ctx);
1370
    PASS;
1371
}
1372

1373
/**
1374
 * \test Check if the threshold file is loaded and well parsed
1375
 *
1376
 *  \retval 1 on success
1377
 *  \retval 0 on failure
1378
 */
1379
static int SCThresholdConfTest05(void)
1380
{
1381
    DetectEngineCtx *de_ctx = DetectEngineCtxInit();
1382
    FAIL_IF_NULL(de_ctx);
1383
    de_ctx->flags |= DE_QUIET;
1384

1385
    Signature *sig = DetectEngineAppendSig(de_ctx,
1386
            "alert tcp any any -> any any (msg:\"Threshold limit\"; gid:1; sid:1;)");
1387
    FAIL_IF_NULL(sig);
1388
    sig = DetectEngineAppendSig(de_ctx,
1389
            "alert tcp any any -> any 80 (msg:\"Threshold limit\"; gid:1; sid:10;)");
1390
    FAIL_IF_NULL(sig);
1391

1392
    sig = DetectEngineAppendSig(de_ctx,
1393
            "alert tcp any any -> any 80 (msg:\"Threshold limit\"; gid:1; sid:100;)");
1394
    FAIL_IF_NULL(sig);
1395

1396
    FAIL_IF_NOT_NULL(g_ut_threshold_fp);
1397
    g_ut_threshold_fp = SCThresholdConfGenerateValidDummyFD03();
1398
    FAIL_IF_NULL(g_ut_threshold_fp);
1399
    FAIL_IF(-1 == SCThresholdConfInitContext(de_ctx));
1400

1401
    Signature *s = de_ctx->sig_list;
1402
    SigMatch *m = DetectGetLastSMByListId(s, DETECT_SM_LIST_THRESHOLD,
1403
            DETECT_THRESHOLD, -1);
1404
    FAIL_IF_NULL(m);
1405
    FAIL_IF_NULL(m->ctx);
1406
    DetectThresholdData *de = (DetectThresholdData *)m->ctx;
1407
    FAIL_IF_NOT(de->type == TYPE_THRESHOLD && de->track == TRACK_SRC && de->count == 100 && de->seconds == 60);
1408

1409
    s = de_ctx->sig_list->next;
1410
    m = DetectGetLastSMByListId(s, DETECT_SM_LIST_THRESHOLD,
1411
            DETECT_THRESHOLD, -1);
1412
    FAIL_IF_NULL(m);
1413
    FAIL_IF_NULL(m->ctx);
1414
    de = (DetectThresholdData *)m->ctx;
1415
    FAIL_IF_NOT(de->type == TYPE_THRESHOLD && de->track == TRACK_SRC && de->count == 100 && de->seconds == 60);
1416

1417
    s = de_ctx->sig_list->next->next;
1418
    m = DetectGetLastSMByListId(s, DETECT_SM_LIST_THRESHOLD,
1419
            DETECT_THRESHOLD, -1);
1420
    FAIL_IF_NULL(m);
1421
    FAIL_IF_NULL(m->ctx);
1422
    de = (DetectThresholdData *)m->ctx;
1423
    FAIL_IF_NOT(de->type == TYPE_THRESHOLD && de->track == TRACK_SRC && de->count == 100 && de->seconds == 60);
1424

1425
    DetectEngineCtxFree(de_ctx);
1426
    PASS;
1427
}
1428

1429
/**
1430
 * \test Check if the threshold file is loaded and well parsed
1431
 *
1432
 *  \retval 1 on success
1433
 *  \retval 0 on failure
1434
 */
1435
static int SCThresholdConfTest06(void)
1436
{
1437
    DetectEngineCtx *de_ctx = DetectEngineCtxInit();
1438
    FAIL_IF_NULL(de_ctx);
1439
    de_ctx->flags |= DE_QUIET;
1440

1441
    Signature *sig = DetectEngineAppendSig(de_ctx,
1442
            "alert tcp any any -> any any (msg:\"Threshold limit\"; gid:1; sid:10;)");
1443
    FAIL_IF_NULL(sig);
1444

1445
    FAIL_IF_NOT_NULL(g_ut_threshold_fp);
1446
    g_ut_threshold_fp = SCThresholdConfGenerateValidDummyFD04();
1447
    FAIL_IF_NULL(g_ut_threshold_fp);
1448
    FAIL_IF(-1 == SCThresholdConfInitContext(de_ctx));
1449

1450
    SigMatch *m = DetectGetLastSMByListId(sig, DETECT_SM_LIST_THRESHOLD,
1451
            DETECT_THRESHOLD, -1);
1452
    FAIL_IF_NULL(m);
1453

1454
    DetectThresholdData *de = (DetectThresholdData *)m->ctx;
1455
    FAIL_IF_NULL(de);
1456
    FAIL_IF_NOT(de->type == TYPE_LIMIT && de->track == TRACK_SRC && de->count == 1 && de->seconds == 60);
1457

1458
    DetectEngineCtxFree(de_ctx);
1459
    PASS;
1460
}
1461

1462
/**
1463
 * \test Check if the rate_filter rules are loaded and well parsed
1464
 *
1465
 *  \retval 1 on success
1466
 *  \retval 0 on failure
1467
 */
1468
static int SCThresholdConfTest07(void)
1469
{
1470
    DetectEngineCtx *de_ctx = DetectEngineCtxInit();
1471
    FAIL_IF_NULL(de_ctx);
1472
    de_ctx->flags |= DE_QUIET;
1473

1474
    Signature *sig = DetectEngineAppendSig(de_ctx,
1475
            "alert tcp any any -> any any (msg:\"Threshold limit\"; gid:1; sid:10;)");
1476
    FAIL_IF_NULL(sig);
1477

1478
    FAIL_IF_NOT_NULL(g_ut_threshold_fp);
1479
    g_ut_threshold_fp = SCThresholdConfGenerateValidDummyFD05();
1480
    FAIL_IF_NULL(g_ut_threshold_fp);
1481
    FAIL_IF(-1 == SCThresholdConfInitContext(de_ctx));
1482

1483
    SigMatch *m = DetectGetLastSMByListId(sig, DETECT_SM_LIST_THRESHOLD,
1484
            DETECT_DETECTION_FILTER, -1);
1485
    FAIL_IF_NULL(m);
1486

1487
    DetectThresholdData *de = (DetectThresholdData *)m->ctx;
1488
    FAIL_IF_NULL(de);
1489
    FAIL_IF_NOT(de->type == TYPE_RATE && de->track == TRACK_SRC && de->count == 1 && de->seconds == 60);
1490

1491
    DetectEngineCtxFree(de_ctx);
1492
    PASS;
1493
}
1494

1495
/**
1496
 * \test Check if the rate_filter rules are loaded and well parsed
1497
 *       with multilines
1498
 *
1499
 *  \retval 1 on success
1500
 *  \retval 0 on failure
1501
 */
1502
static int SCThresholdConfTest08(void)
1503
{
1504
    DetectEngineCtx *de_ctx = DetectEngineCtxInit();
1505
    FAIL_IF_NULL(de_ctx);
1506
    de_ctx->flags |= DE_QUIET;
1507

1508
    Signature *sig = DetectEngineAppendSig(de_ctx,
1509
            "alert tcp any any -> any any (msg:\"Threshold limit\"; gid:1; sid:10;)");
1510
    FAIL_IF_NULL(sig);
1511

1512
    FAIL_IF_NOT_NULL(g_ut_threshold_fp);
1513
    g_ut_threshold_fp = SCThresholdConfGenerateValidDummyFD06();
1514
    FAIL_IF_NULL(g_ut_threshold_fp);
1515
    FAIL_IF(-1 == SCThresholdConfInitContext(de_ctx));
1516

1517
    SigMatch *m = DetectGetLastSMByListId(sig, DETECT_SM_LIST_THRESHOLD,
1518
            DETECT_DETECTION_FILTER, -1);
1519
    FAIL_IF_NULL(m);
1520

1521
    DetectThresholdData *de = (DetectThresholdData *)m->ctx;
1522
    FAIL_IF_NULL(de);
1523
    FAIL_IF_NOT(de->type == TYPE_RATE && de->track == TRACK_SRC && de->count == 1 && de->seconds == 60);
1524

1525
    DetectEngineCtxFree(de_ctx);
1526
    PASS;
1527
}
1528

1529
/**
1530
 * \test Check if the rate_filter rules work
1531
 *
1532
 *  \retval 1 on success
1533
 *  \retval 0 on failure
1534
 */
1535
static int SCThresholdConfTest09(void)
1536
{
1537
    ThreadVars th_v;
1538
    memset(&th_v, 0, sizeof(th_v));
1539
    StatsThreadInit(&th_v.stats);
1540

1541
    ThresholdInit();
1542

1543
    Packet *p = UTHBuildPacket((uint8_t*)"lalala", 6, IPPROTO_TCP);
1544
    FAIL_IF_NULL(p);
1545

1546
    DetectEngineThreadCtx *det_ctx = NULL;
1547

1548
    DetectEngineCtx *de_ctx = DetectEngineCtxInit();
1549
    FAIL_IF_NULL(de_ctx);
1550
    de_ctx->flags |= DE_QUIET;
1551

1552
    Signature *s = DetectEngineAppendSig(de_ctx,
1553
            "alert tcp any any -> any any (msg:\"ratefilter test\"; gid:1; sid:10;)");
1554
    FAIL_IF_NULL(s);
1555

1556
    FAIL_IF_NOT_NULL(g_ut_threshold_fp);
1557
    g_ut_threshold_fp = SCThresholdConfGenerateValidDummyFD07();
1558
    FAIL_IF_NULL(g_ut_threshold_fp);
1559
    FAIL_IF(-1 == SCThresholdConfInitContext(de_ctx));
1560

1561
    SigGroupBuild(de_ctx);
1562
    DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
1563

1564
    p->ts = TimeGet();
1565
    p->alerts.cnt = 0;
1566
    p->action = 0;
1567
    SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
1568
    FAIL_IF(p->alerts.cnt != 1 || PacketTestAction(p, ACTION_DROP));
1569
    p->alerts.cnt = 0;
1570
    p->action = 0;
1571
    SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
1572
    FAIL_IF(p->alerts.cnt != 1 || PacketTestAction(p, ACTION_DROP));
1573
    p->alerts.cnt = 0;
1574
    p->action = 0;
1575
    SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
1576
    FAIL_IF(p->alerts.cnt != 1 || PacketTestAction(p, ACTION_DROP));
1577

1578
    TimeSetIncrementTime(2);
1579
    p->ts = TimeGet();
1580

1581
    p->alerts.cnt = 0;
1582
    p->action = 0;
1583
    SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
1584
    FAIL_IF(p->alerts.cnt != 1 || !(PacketTestAction(p, ACTION_DROP)));
1585

1586
    TimeSetIncrementTime(3);
1587
    p->ts = TimeGet();
1588

1589
    p->alerts.cnt = 0;
1590
    p->action = 0;
1591
    SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
1592
    FAIL_IF(p->alerts.cnt != 1 || !(PacketTestAction(p, ACTION_DROP)));
1593

1594
    TimeSetIncrementTime(10);
1595
    p->ts = TimeGet();
1596

1597
    p->alerts.cnt = 0;
1598
    p->action = 0;
1599
    SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
1600
    FAIL_IF(p->alerts.cnt != 1 || PacketTestAction(p, ACTION_DROP));
1601

1602
    p->alerts.cnt = 0;
1603
    p->action = 0;
1604
    SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
1605
    FAIL_IF(p->alerts.cnt != 1 || PacketTestAction(p, ACTION_DROP));
1606

1607
    UTHFreePacket(p);
1608
    DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
1609
    DetectEngineCtxFree(de_ctx);
1610
    ThresholdDestroy();
1611
    StatsThreadCleanup(&th_v.stats);
1612
    PASS;
1613
}
1614

1615
/**
1616
 * \test Check if the rate_filter rules work with track by_rule
1617
 *
1618
 *  \retval 1 on success
1619
 *  \retval 0 on failure
1620
 */
1621
static int SCThresholdConfTest10(void)
1622
{
1623
    ThresholdInit();
1624

1625
    /* Create two different packets falling to the same rule, and
1626
    *  because count:3, we should drop on match #4.
1627
    */
1628
    Packet *p1 = UTHBuildPacketSrcDst((uint8_t*)"lalala", 6, IPPROTO_TCP,
1629
            "172.26.0.2", "172.26.0.11");
1630
    FAIL_IF_NULL(p1);
1631
    Packet *p2 = UTHBuildPacketSrcDst((uint8_t*)"lalala", 6, IPPROTO_TCP,
1632
            "172.26.0.1", "172.26.0.10");
1633
    FAIL_IF_NULL(p2);
1634

1635
    ThreadVars th_v;
1636
    memset(&th_v, 0, sizeof(th_v));
1637
    StatsThreadInit(&th_v.stats);
1638

1639
    DetectEngineCtx *de_ctx = DetectEngineCtxInit();
1640
    FAIL_IF_NULL(de_ctx);
1641
    de_ctx->flags |= DE_QUIET;
1642
    DetectEngineThreadCtx *det_ctx = NULL;
1643

1644
    Signature *s = DetectEngineAppendSig(de_ctx,
1645
            "alert tcp any any -> any any (msg:\"ratefilter test\"; gid:1; sid:10;)");
1646
    FAIL_IF_NULL(s);
1647

1648
    FAIL_IF_NOT_NULL(g_ut_threshold_fp);
1649
    g_ut_threshold_fp = SCThresholdConfGenerateValidDummyFD08();
1650
    FAIL_IF_NULL(g_ut_threshold_fp);
1651
    FAIL_IF(-1 == SCThresholdConfInitContext(de_ctx));
1652

1653
    SigGroupBuild(de_ctx);
1654
    DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
1655
    p1->ts = TimeGet();
1656
    p2->ts = p1->ts;
1657

1658
    /* All should be alerted, none dropped */
1659
    SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
1660
    FAIL_IF(PacketTestAction(p1, ACTION_DROP));
1661
    FAIL_IF(PacketAlertCheck(p1, 10) != 1);
1662
    p1->action = 0;
1663
    SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
1664
    FAIL_IF(PacketTestAction(p2, ACTION_DROP));
1665
    FAIL_IF(PacketAlertCheck(p2, 10) != 1);
1666
    p2->action = 0;
1667
    SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
1668
    FAIL_IF(PacketTestAction(p1, ACTION_DROP));
1669
    FAIL_IF(PacketAlertCheck(p1, 10) != 1);
1670
    p1->action = 0;
1671

1672
    /* Match #4 should be dropped*/
1673
    SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
1674
    FAIL_IF_NOT(PacketTestAction(p2, ACTION_DROP));
1675
    FAIL_IF(PacketAlertCheck(p2, 10) != 1);
1676
    p2->action = 0;
1677

1678
    TimeSetIncrementTime(2);
1679
    p1->ts = TimeGet();
1680

1681
    /* Still dropped because timeout not expired */
1682
    SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
1683
    FAIL_IF_NOT(PacketTestAction(p1, ACTION_DROP));
1684
    FAIL_IF(PacketAlertCheck(p1, 10) != 1);
1685
    p1->action = 0;
1686

1687
    TimeSetIncrementTime(10);
1688
    p1->ts = TimeGet();
1689

1690
    /* Not dropped because timeout expired */
1691
    SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
1692
    FAIL_IF(PacketTestAction(p1, ACTION_DROP));
1693
    FAIL_IF(PacketAlertCheck(p1, 10) != 1);
1694
#if 0
1695
    /* Ensure that a Threshold entry was installed at the sig */
1696
    FAIL_IF_NULL(de_ctx->ths_ctx.th_entry[s->iid]);
1697
#endif
1698
    UTHFreePacket(p1);
1699
    UTHFreePacket(p2);
1700
    DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
1701
    DetectEngineCtxFree(de_ctx);
1702
    ThresholdDestroy();
1703
    StatsThreadCleanup(&th_v.stats);
1704
    PASS;
1705
}
1706

1707
/**
1708
 * \test Check if the rate_filter rules work
1709
 *
1710
 *  \retval 1 on success
1711
 *  \retval 0 on failure
1712
 */
1713
static int SCThresholdConfTest11(void)
1714
{
1715
    ThresholdInit();
1716

1717
    Packet *p = UTHBuildPacket((uint8_t*)"lalala", 6, IPPROTO_TCP);
1718
    FAIL_IF_NULL(p);
1719

1720
    ThreadVars th_v;
1721
    memset(&th_v, 0, sizeof(th_v));
1722
    StatsThreadInit(&th_v.stats);
1723

1724
    DetectEngineCtx *de_ctx = DetectEngineCtxInit();
1725
    FAIL_IF_NULL(de_ctx);
1726
    de_ctx->flags |= DE_QUIET;
1727
    DetectEngineThreadCtx *det_ctx = NULL;
1728

1729
    Signature *s = DetectEngineAppendSig(de_ctx,
1730
            "alert tcp any any -> any any (msg:\"event_filter test limit\"; gid:1; sid:10;)");
1731
    FAIL_IF_NULL(s);
1732
    s = DetectEngineAppendSig(de_ctx,
1733
            "alert tcp any any -> any any (msg:\"event_filter test threshold\"; gid:1; sid:11;)");
1734
    FAIL_IF_NULL(s);
1735
    s = DetectEngineAppendSig(de_ctx,
1736
            "alert tcp any any -> any any (msg:\"event_filter test both\"; gid:1; sid:12;)");
1737
    FAIL_IF_NULL(s);
1738

1739
    FAIL_IF_NOT_NULL(g_ut_threshold_fp);
1740
    g_ut_threshold_fp = SCThresholdConfGenerateValidDummyFD09();
1741
    FAIL_IF_NULL(g_ut_threshold_fp);
1742
    FAIL_IF(-1 == SCThresholdConfInitContext(de_ctx));
1743

1744
    SigGroupBuild(de_ctx);
1745
    DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
1746

1747
    p->ts = TimeGet();
1748

1749
    int alerts10 = 0;
1750
    int alerts11 = 0;
1751
    int alerts12 = 0;
1752

1753
    SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
1754
    alerts10 += PacketAlertCheck(p, 10);
1755
    alerts11 += PacketAlertCheck(p, 11);
1756
    alerts12 += PacketAlertCheck(p, 12);
1757
    SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
1758
    alerts10 += PacketAlertCheck(p, 10);
1759
    alerts11 += PacketAlertCheck(p, 11);
1760
    alerts12 += PacketAlertCheck(p, 12);
1761
    SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
1762
    alerts10 += PacketAlertCheck(p, 10);
1763
    alerts11 += PacketAlertCheck(p, 11);
1764
    alerts12 += PacketAlertCheck(p, 12);
1765
    SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
1766
    alerts10 += PacketAlertCheck(p, 10);
1767
    alerts11 += PacketAlertCheck(p, 11);
1768
    alerts12 += PacketAlertCheck(p, 12);
1769
    SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
1770
    alerts10 += PacketAlertCheck(p, 10);
1771
    alerts11 += PacketAlertCheck(p, 11);
1772
    alerts12 += PacketAlertCheck(p, 12);
1773

1774
    TimeSetIncrementTime(100);
1775
    p->ts = TimeGet();
1776

1777
    SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
1778
    alerts10 += PacketAlertCheck(p, 10);
1779
    alerts11 += PacketAlertCheck(p, 11);
1780

1781
    TimeSetIncrementTime(10);
1782
    p->ts = TimeGet();
1783

1784
    SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
1785
    alerts10 += PacketAlertCheck(p, 10);
1786
    alerts11 += PacketAlertCheck(p, 11);
1787
    alerts12 += PacketAlertCheck(p, 12);
1788
    SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
1789
    alerts10 += PacketAlertCheck(p, 10);
1790
    alerts11 += PacketAlertCheck(p, 11);
1791
    alerts12 += PacketAlertCheck(p, 12);
1792
    SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
1793
    alerts10 += PacketAlertCheck(p, 10);
1794
    alerts11 += PacketAlertCheck(p, 11);
1795
    alerts12 += PacketAlertCheck(p, 12);
1796
    SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
1797
    alerts10 += PacketAlertCheck(p, 10);
1798
    alerts11 += PacketAlertCheck(p, 11);
1799
    alerts12 += PacketAlertCheck(p, 12);
1800

1801
    FAIL_IF_NOT(alerts10 == 4);
1802
    /* One on the first interval, another on the second */
1803
    FAIL_IF_NOT(alerts11 == 2);
1804
    FAIL_IF_NOT(alerts12 == 2);
1805

1806
    UTHFreePacket(p);
1807
    DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
1808
    DetectEngineCtxFree(de_ctx);
1809
    ThresholdDestroy();
1810
    StatsThreadCleanup(&th_v.stats);
1811
    PASS;
1812
}
1813

1814
/**
1815
 * \test Check if the rate_filter rules work
1816
 *
1817
 *  \retval 1 on success
1818
 *  \retval 0 on failure
1819
 */
1820
static int SCThresholdConfTest12(void)
1821
{
1822
    ThresholdInit();
1823

1824
    Packet *p = UTHBuildPacket((uint8_t*)"lalala", 6, IPPROTO_TCP);
1825
    FAIL_IF_NULL(p);
1826

1827
    ThreadVars th_v;
1828
    memset(&th_v, 0, sizeof(th_v));
1829
    StatsThreadInit(&th_v.stats);
1830

1831
    DetectEngineCtx *de_ctx = DetectEngineCtxInit();
1832
    FAIL_IF_NULL(de_ctx);
1833
    de_ctx->flags |= DE_QUIET;
1834
    DetectEngineThreadCtx *det_ctx = NULL;
1835

1836
    Signature *s = DetectEngineAppendSig(de_ctx,
1837
            "alert tcp any any -> any any (msg:\"event_filter test limit\"; gid:1; sid:10;)");
1838
    FAIL_IF_NULL(s);
1839
    s = DetectEngineAppendSig(de_ctx,
1840
            "alert tcp any any -> any any (msg:\"event_filter test threshold\"; gid:1; sid:11;)");
1841
    FAIL_IF_NULL(s);
1842
    s = DetectEngineAppendSig(de_ctx,
1843
            "alert tcp any any -> any any (msg:\"event_filter test both\"; gid:1; sid:12;)");
1844
    FAIL_IF_NULL(s);
1845

1846
    FAIL_IF_NOT_NULL(g_ut_threshold_fp);
1847
    g_ut_threshold_fp = SCThresholdConfGenerateValidDummyFD10();
1848
    FAIL_IF_NULL(g_ut_threshold_fp);
1849
    FAIL_IF(-1 == SCThresholdConfInitContext(de_ctx));
1850

1851
    SigGroupBuild(de_ctx);
1852
    DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
1853

1854
    p->ts = TimeGet();
1855

1856
    int alerts10 = 0;
1857
    int alerts11 = 0;
1858
    int alerts12 = 0;
1859

1860
    SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
1861
    alerts10 += PacketAlertCheck(p, 10);
1862
    alerts11 += PacketAlertCheck(p, 11);
1863
    alerts12 += PacketAlertCheck(p, 12);
1864
    SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
1865
    alerts10 += PacketAlertCheck(p, 10);
1866
    alerts11 += PacketAlertCheck(p, 11);
1867
    alerts12 += PacketAlertCheck(p, 12);
1868
    SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
1869
    alerts10 += PacketAlertCheck(p, 10);
1870
    alerts11 += PacketAlertCheck(p, 11);
1871
    alerts12 += PacketAlertCheck(p, 12);
1872
    SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
1873
    alerts10 += PacketAlertCheck(p, 10);
1874
    alerts11 += PacketAlertCheck(p, 11);
1875
    alerts12 += PacketAlertCheck(p, 12);
1876
    SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
1877
    alerts10 += PacketAlertCheck(p, 10);
1878
    alerts11 += PacketAlertCheck(p, 11);
1879
    alerts12 += PacketAlertCheck(p, 12);
1880

1881
    TimeSetIncrementTime(100);
1882
    p->ts = TimeGet();
1883

1884
    SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
1885
    alerts10 += PacketAlertCheck(p, 10);
1886
    alerts11 += PacketAlertCheck(p, 11);
1887

1888
    TimeSetIncrementTime(10);
1889
    p->ts = TimeGet();
1890

1891
    SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
1892
    alerts10 += PacketAlertCheck(p, 10);
1893
    alerts11 += PacketAlertCheck(p, 11);
1894
    alerts12 += PacketAlertCheck(p, 12);
1895
    SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
1896
    alerts10 += PacketAlertCheck(p, 10);
1897
    alerts11 += PacketAlertCheck(p, 11);
1898
    alerts12 += PacketAlertCheck(p, 12);
1899
    SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
1900
    alerts10 += PacketAlertCheck(p, 10);
1901
    alerts11 += PacketAlertCheck(p, 11);
1902
    alerts12 += PacketAlertCheck(p, 12);
1903
    SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
1904
    alerts10 += PacketAlertCheck(p, 10);
1905
    alerts11 += PacketAlertCheck(p, 11);
1906
    alerts12 += PacketAlertCheck(p, 12);
1907

1908
    FAIL_IF_NOT(alerts10 == 10);
1909
    /* One on the first interval, another on the second */
1910
    FAIL_IF_NOT(alerts11 == 1);
1911
    FAIL_IF_NOT(alerts12 == 1);
1912

1913
    UTHFreePacket(p);
1914
    DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
1915
    DetectEngineCtxFree(de_ctx);
1916
    ThresholdDestroy();
1917
    StatsThreadCleanup(&th_v.stats);
1918
    PASS;
1919
}
1920

1921
/**
1922
 * \test Check if the threshold file is loaded and well parsed
1923
 *
1924
 *  \retval 1 on success
1925
 *  \retval 0 on failure
1926
 */
1927
static int SCThresholdConfTest13(void)
1928
{
1929
    DetectEngineCtx *de_ctx = DetectEngineCtxInit();
1930
    FAIL_IF_NULL(de_ctx);
1931
    de_ctx->flags |= DE_QUIET;
1932

1933
    Signature *sig = DetectEngineAppendSig(de_ctx,
1934
            "alert tcp any any -> any any (msg:\"Threshold limit\"; gid:1; sid:1000;)");
1935
    FAIL_IF_NULL(sig);
1936

1937
    FAIL_IF_NOT_NULL(g_ut_threshold_fp);
1938
    g_ut_threshold_fp = SCThresholdConfGenerateValidDummyFD11();
1939
    FAIL_IF_NULL(g_ut_threshold_fp);
1940
    FAIL_IF(-1 == SCThresholdConfInitContext(de_ctx));
1941

1942
    SigMatch *m = DetectGetLastSMByListId(sig,
1943
            DETECT_SM_LIST_SUPPRESS, DETECT_THRESHOLD, -1);
1944
    FAIL_IF_NULL(m);
1945

1946
    DetectThresholdData *de = (DetectThresholdData *)m->ctx;
1947
    FAIL_IF_NULL(de);
1948
    FAIL_IF_NOT(de->type == TYPE_SUPPRESS && de->track == TRACK_SRC);
1949

1950
    DetectEngineCtxFree(de_ctx);
1951
    PASS;
1952
}
1953

1954
/**
1955
 * \test Check if the suppress rules work
1956
 *
1957
 *  \retval 1 on success
1958
 *  \retval 0 on failure
1959
 */
1960
static int SCThresholdConfTest14(void)
1961
{
1962
    ThresholdInit();
1963

1964
    Packet *p1 = UTHBuildPacketReal((uint8_t*)"lalala", 6, IPPROTO_TCP, "192.168.0.10",
1965
                                    "192.168.0.100", 1234, 24);
1966
    FAIL_IF_NULL(p1);
1967
    Packet *p2 = UTHBuildPacketReal((uint8_t*)"lalala", 6, IPPROTO_TCP, "192.168.1.1",
1968
                                    "192.168.0.100", 1234, 24);
1969
    FAIL_IF_NULL(p2);
1970

1971
    DetectEngineThreadCtx *det_ctx = NULL;
1972
    DetectEngineCtx *de_ctx = DetectEngineCtxInit();
1973
    FAIL_IF_NULL(de_ctx);
1974
    de_ctx->flags |= DE_QUIET;
1975

1976
    Signature *sig = DetectEngineAppendSig(de_ctx,
1977
        "alert tcp any any -> any any (msg:\"suppress test\"; gid:1; sid:10000;)");
1978
    FAIL_IF_NULL(sig);
1979
    sig = DetectEngineAppendSig(de_ctx,
1980
            "alert tcp any any -> any any (msg:\"suppress test 2\"; gid:1; sid:10;)");
1981
    FAIL_IF_NULL(sig);
1982
    sig = DetectEngineAppendSig(de_ctx,
1983
            "alert tcp any any -> any any (msg:\"suppress test 3\"; gid:1; sid:1000;)");
1984
    FAIL_IF_NULL(sig);
1985

1986
    ThreadVars th_v;
1987
    memset(&th_v, 0, sizeof(th_v));
1988
    StatsThreadInit(&th_v.stats);
1989

1990
    FAIL_IF_NOT_NULL(g_ut_threshold_fp);
1991
    g_ut_threshold_fp = SCThresholdConfGenerateValidDummyFD11();
1992
    FAIL_IF_NULL(g_ut_threshold_fp);
1993
    FAIL_IF(-1 == SCThresholdConfInitContext(de_ctx));
1994

1995
    SigGroupBuild(de_ctx);
1996
    DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
1997

1998
    SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
1999
    SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
2000

2001
    FAIL_IF_NOT(PacketAlertCheck(p1, 10000) == 0);
2002
    FAIL_IF_NOT(PacketAlertCheck(p1, 10) == 1);
2003
    FAIL_IF_NOT(PacketAlertCheck(p1, 1000) == 1);
2004
    FAIL_IF_NOT(PacketAlertCheck(p2, 1000) == 0);
2005

2006
    UTHFreePacket(p1);
2007
    UTHFreePacket(p2);
2008

2009
    DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
2010
    DetectEngineCtxFree(de_ctx);
2011

2012
    ThresholdDestroy();
2013
    StatsThreadCleanup(&th_v.stats);
2014
    PASS;
2015
}
2016

2017
/**
2018
 * \test Check if the suppress rules work
2019
 *
2020
 *  \retval 1 on success
2021
 *  \retval 0 on failure
2022
 */
2023
static int SCThresholdConfTest15(void)
2024
{
2025
    ThresholdInit();
2026

2027
    Packet *p = UTHBuildPacketReal((uint8_t*)"lalala", 6, IPPROTO_TCP, "192.168.0.10",
2028
                                    "192.168.0.100", 1234, 24);
2029
    FAIL_IF_NULL(p);
2030

2031
    ThreadVars th_v;
2032
    memset(&th_v, 0, sizeof(th_v));
2033
    StatsThreadInit(&th_v.stats);
2034

2035
    DetectEngineThreadCtx *det_ctx = NULL;
2036
    DetectEngineCtx *de_ctx = DetectEngineCtxInit();
2037
    FAIL_IF_NULL(de_ctx);
2038
    de_ctx->flags |= DE_QUIET;
2039

2040
    Signature *sig = DetectEngineAppendSig(de_ctx,
2041
            "drop tcp any any -> any any (msg:\"suppress test\"; content:\"lalala\"; gid:1; sid:10000;)");
2042
    FAIL_IF_NULL(sig);
2043

2044
    FAIL_IF_NOT_NULL(g_ut_threshold_fp);
2045
    g_ut_threshold_fp = SCThresholdConfGenerateValidDummyFD11();
2046
    FAIL_IF_NULL(g_ut_threshold_fp);
2047
    FAIL_IF(-1 == SCThresholdConfInitContext(de_ctx));
2048

2049
    SigGroupBuild(de_ctx);
2050
    DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
2051

2052
    SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
2053

2054
    /* 10000 shouldn't match */
2055
    FAIL_IF(PacketAlertCheck(p, 10000) != 0);
2056
    /* however, it should have set the drop flag */
2057
    FAIL_IF(!(PacketTestAction(p, ACTION_DROP)));
2058

2059
    UTHFreePacket(p);
2060
    DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
2061
    DetectEngineCtxFree(de_ctx);
2062
    ThresholdDestroy();
2063
    StatsThreadCleanup(&th_v.stats);
2064
    PASS;
2065
}
2066

2067
/**
2068
 * \test Check if the suppress rules work
2069
 *
2070
 *  \retval 1 on success
2071
 *  \retval 0 on failure
2072
 */
2073
static int SCThresholdConfTest16(void)
2074
{
2075
    ThresholdInit();
2076

2077
    Packet *p = UTHBuildPacketReal((uint8_t*)"lalala", 6, IPPROTO_TCP, "192.168.1.1",
2078
                                    "192.168.0.100", 1234, 24);
2079
    FAIL_IF_NULL(p);
2080

2081
    ThreadVars th_v;
2082
    memset(&th_v, 0, sizeof(th_v));
2083
    StatsThreadInit(&th_v.stats);
2084

2085
    DetectEngineThreadCtx *det_ctx = NULL;
2086
    DetectEngineCtx *de_ctx = DetectEngineCtxInit();
2087
    FAIL_IF_NULL(de_ctx);
2088
    de_ctx->flags |= DE_QUIET;
2089

2090
    Signature *sig = DetectEngineAppendSig(de_ctx,
2091
            "drop tcp any any -> any any (msg:\"suppress test\"; gid:1; sid:1000;)");
2092
    FAIL_IF_NULL(sig);
2093

2094
    FAIL_IF_NOT_NULL(g_ut_threshold_fp);
2095
    g_ut_threshold_fp = SCThresholdConfGenerateValidDummyFD11();
2096
    FAIL_IF_NULL(g_ut_threshold_fp);
2097
    FAIL_IF(-1 == SCThresholdConfInitContext(de_ctx));
2098

2099
    SigGroupBuild(de_ctx);
2100
    DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
2101

2102
    SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
2103

2104
    FAIL_IF(PacketAlertCheck(p, 1000) != 0);
2105
    /* however, it should have set the drop flag */
2106
    FAIL_IF(!(PacketTestAction(p, ACTION_DROP)));
2107

2108
    UTHFreePacket(p);
2109
    DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
2110
    DetectEngineCtxFree(de_ctx);
2111
    ThresholdDestroy();
2112
    StatsThreadCleanup(&th_v.stats);
2113
    PASS;
2114
}
2115

2116
/**
2117
 * \test Check if the suppress rules work - ip only rule
2118
 *
2119
 *  \retval 1 on success
2120
 *  \retval 0 on failure
2121
 */
2122
static int SCThresholdConfTest17(void)
2123
{
2124
    ThresholdInit();
2125

2126
    Packet *p = UTHBuildPacketReal((uint8_t*)"lalala", 6, IPPROTO_TCP, "192.168.0.10",
2127
                                    "192.168.0.100", 1234, 24);
2128
    FAIL_IF_NULL(p);
2129

2130
    ThreadVars th_v;
2131
    memset(&th_v, 0, sizeof(th_v));
2132
    StatsThreadInit(&th_v.stats);
2133

2134
    DetectEngineThreadCtx *det_ctx = NULL;
2135
    DetectEngineCtx *de_ctx = DetectEngineCtxInit();
2136
    FAIL_IF_NULL(de_ctx);
2137
    de_ctx->flags |= DE_QUIET;
2138

2139
    Signature *sig = DetectEngineAppendSig(de_ctx,
2140
            "drop tcp 192.168.0.10 any -> 192.168.0.100 any (msg:\"suppress test\"; gid:1; sid:10000;)");
2141
    FAIL_IF_NULL(sig);
2142

2143
    FAIL_IF_NOT_NULL(g_ut_threshold_fp);
2144
    g_ut_threshold_fp = SCThresholdConfGenerateValidDummyFD11();
2145
    FAIL_IF_NULL(g_ut_threshold_fp);
2146
    FAIL_IF(-1 == SCThresholdConfInitContext(de_ctx));
2147

2148
    SigGroupBuild(de_ctx);
2149
    DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
2150

2151
    SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
2152

2153
    /* 10000 shouldn't match */
2154
    FAIL_IF(PacketAlertCheck(p, 10000) != 0);
2155
    /* however, it should have set the drop flag */
2156
    FAIL_IF(!(PacketTestAction(p, ACTION_DROP)));
2157

2158
    UTHFreePacket(p);
2159
    DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
2160
    DetectEngineCtxFree(de_ctx);
2161
    ThresholdDestroy();
2162
    StatsThreadCleanup(&th_v.stats);
2163
    PASS;
2164
}
2165

2166
/**
2167
 * \brief Creates a dummy threshold file, with all valid options, for testing purposes.
2168
 *
2169
 * \retval fd Pointer to file descriptor.
2170
 */
2171
static FILE *SCThresholdConfGenerateInvalidDummyFD12(void)
2172
{
2173
    FILE *fd = NULL;
2174
    const char *buffer =
2175
        "suppress gen_id 1, sig_id 2200029, track by_dst, ip fe80::/16\n"
2176
        "suppress gen_id 1, sig_id 2200029, track by_stc, ip fe80::/16\n";
2177

2178
    fd = SCFmemopen((void *)buffer, strlen(buffer), "r");
2179
    if (fd == NULL)
2180
        SCLogDebug("Error with SCFmemopen() called by Threshold Config test code");
2181

2182
    return fd;
2183
}
2184

2185
/**
2186
 * \test Check if the suppress rule parsing handles errors correctly
2187
 *
2188
 *  \retval 1 on success
2189
 *  \retval 0 on failure
2190
 */
2191
static int SCThresholdConfTest18(void)
2192
{
2193
    ThresholdInit();
2194
    DetectEngineCtx *de_ctx = DetectEngineCtxInit();
2195
    FAIL_IF_NULL(de_ctx);
2196
    de_ctx->flags |= DE_QUIET;
2197

2198
    Signature *s = DetectEngineAppendSig(de_ctx,
2199
            "alert tcp 192.168.0.10 any -> 192.168.0.100 any (msg:\"suppress test\"; gid:1; sid:2200029;)");
2200
    FAIL_IF_NULL(s);
2201
    FAIL_IF_NOT_NULL(g_ut_threshold_fp);
2202
    g_ut_threshold_fp = SCThresholdConfGenerateInvalidDummyFD12();
2203
    FAIL_IF_NULL(g_ut_threshold_fp);
2204
    FAIL_IF(-1 == SCThresholdConfInitContext(de_ctx));
2205
    SigGroupBuild(de_ctx);
2206

2207
    FAIL_IF_NULL(s->sm_arrays[DETECT_SM_LIST_SUPPRESS]);
2208
    SigMatchData *smd = s->sm_arrays[DETECT_SM_LIST_SUPPRESS];
2209
    DetectThresholdData *de = (DetectThresholdData *)smd->ctx;
2210
    FAIL_IF_NULL(de);
2211
    FAIL_IF_NOT(de->type == TYPE_SUPPRESS && de->track == TRACK_DST);
2212

2213
    DetectEngineCtxFree(de_ctx);
2214
    ThresholdDestroy();
2215
    PASS;
2216
}
2217

2218
/**
2219
 * \brief Creates a dummy threshold file, with all valid options, for testing purposes.
2220
 *
2221
 * \retval fd Pointer to file descriptor.
2222
 */
2223
static FILE *SCThresholdConfGenerateInvalidDummyFD13(void)
2224
{
2225
    FILE *fd = NULL;
2226
    const char *buffer =
2227
        "suppress gen_id 1, sig_id 2200029, track by_stc, ip fe80::/16\n"
2228
        "suppress gen_id 1, sig_id 2200029, track by_dst, ip fe80::/16\n";
2229

2230
    fd = SCFmemopen((void *)buffer, strlen(buffer), "r");
2231
    if (fd == NULL)
2232
        SCLogDebug("Error with SCFmemopen() called by Threshold Config test code");
2233

2234
    return fd;
2235
}
2236

2237
/**
2238
 * \test Check if the suppress rule parsing handles errors correctly
2239
 *
2240
 *  \retval 1 on success
2241
 *  \retval 0 on failure
2242
 */
2243
static int SCThresholdConfTest19(void)
2244
{
2245
    ThresholdInit();
2246
    DetectEngineCtx *de_ctx = DetectEngineCtxInit();
2247
    FAIL_IF_NULL(de_ctx);
2248
    de_ctx->flags |= DE_QUIET;
2249
    Signature *s = DetectEngineAppendSig(de_ctx,
2250
            "alert tcp 192.168.0.10 any -> 192.168.0.100 any (msg:\"suppress test\"; gid:1; sid:2200029;)");
2251
    FAIL_IF_NULL(s);
2252
    FAIL_IF_NOT_NULL(g_ut_threshold_fp);
2253
    g_ut_threshold_fp = SCThresholdConfGenerateInvalidDummyFD13();
2254
    FAIL_IF_NULL(g_ut_threshold_fp);
2255
    FAIL_IF(-1 == SCThresholdConfInitContext(de_ctx));
2256
    SigGroupBuild(de_ctx);
2257
    FAIL_IF_NULL(s->sm_arrays[DETECT_SM_LIST_SUPPRESS]);
2258
    SigMatchData *smd = s->sm_arrays[DETECT_SM_LIST_SUPPRESS];
2259
    DetectThresholdData *de = (DetectThresholdData *)smd->ctx;
2260
    FAIL_IF_NULL(de);
2261
    FAIL_IF_NOT(de->type == TYPE_SUPPRESS && de->track == TRACK_DST);
2262
    DetectEngineCtxFree(de_ctx);
2263
    ThresholdDestroy();
2264
    PASS;
2265
}
2266

2267
/**
2268
 * \brief Creates a dummy threshold file, with all valid options, for testing purposes.
2269
 *
2270
 * \retval fd Pointer to file descriptor.
2271
 */
2272
static FILE *SCThresholdConfGenerateValidDummyFD20(void)
2273
{
2274
    FILE *fd = NULL;
2275
    const char *buffer =
2276
        "suppress gen_id 1, sig_id 1000, track by_src, ip 2.2.3.4\n"
2277
        "suppress gen_id 1, sig_id 1000, track by_src, ip 1.2.3.4\n"
2278
        "suppress gen_id 1, sig_id 1000, track by_src, ip 192.168.1.1\n";
2279

2280
    fd = SCFmemopen((void *)buffer, strlen(buffer), "r");
2281
    if (fd == NULL)
2282
        SCLogDebug("Error with SCFmemopen() called by Threshold Config test code");
2283

2284
    return fd;
2285
}
2286

2287
/**
2288
 * \test Check if the threshold file is loaded and well parsed
2289
 *
2290
 *  \retval 1 on success
2291
 *  \retval 0 on failure
2292
 */
2293
static int SCThresholdConfTest20(void)
2294
{
2295
    ThresholdInit();
2296
    DetectEngineCtx *de_ctx = DetectEngineCtxInit();
2297
    FAIL_IF_NULL(de_ctx);
2298
    de_ctx->flags |= DE_QUIET;
2299
    Signature *s = DetectEngineAppendSig(de_ctx,
2300
            "alert tcp any any -> any any (msg:\"Threshold limit\"; content:\"abc\"; sid:1000;)");
2301
    FAIL_IF_NULL(s);
2302
    FAIL_IF_NOT_NULL(g_ut_threshold_fp);
2303
    g_ut_threshold_fp = SCThresholdConfGenerateValidDummyFD20();
2304
    FAIL_IF_NULL(g_ut_threshold_fp);
2305
    FAIL_IF(-1 == SCThresholdConfInitContext(de_ctx));
2306
    SigGroupBuild(de_ctx);
2307
    FAIL_IF_NULL(s->sm_arrays[DETECT_SM_LIST_SUPPRESS]);
2308

2309
    SigMatchData *smd = s->sm_arrays[DETECT_SM_LIST_SUPPRESS];
2310
    DetectThresholdData *de = (DetectThresholdData *)smd->ctx;
2311
    FAIL_IF_NULL(de);
2312
    FAIL_IF_NOT(de->type == TYPE_SUPPRESS && de->track == TRACK_SRC);
2313
    FAIL_IF(smd->is_last);
2314

2315
    smd++;
2316
    de = (DetectThresholdData *)smd->ctx;
2317
    FAIL_IF_NULL(de);
2318
    FAIL_IF_NOT(de->type == TYPE_SUPPRESS && de->track == TRACK_SRC);
2319
    FAIL_IF(smd->is_last);
2320

2321
    smd++;
2322
    de = (DetectThresholdData *)smd->ctx;
2323
    FAIL_IF_NULL(de);
2324
    FAIL_IF_NOT(de->type == TYPE_SUPPRESS && de->track == TRACK_SRC);
2325
    FAIL_IF_NOT(smd->is_last);
2326

2327
    DetectEngineCtxFree(de_ctx);
2328
    ThresholdDestroy();
2329
    PASS;
2330
}
2331

2332
/**
2333
 * \test Check if the threshold file is loaded and well parsed, and applied
2334
 *       correctly to a rule with thresholding
2335
 *
2336
 *  \retval 1 on success
2337
 *  \retval 0 on failure
2338
 */
2339
static int SCThresholdConfTest21(void)
2340
{
2341
    ThresholdInit();
2342
    DetectEngineCtx *de_ctx = DetectEngineCtxInit();
2343
    FAIL_IF_NULL(de_ctx);
2344
    de_ctx->flags |= DE_QUIET;
2345
    Signature *s = DetectEngineAppendSig(de_ctx,
2346
            "alert tcp any any -> any any (msg:\"Threshold limit\"; content:\"abc\"; threshold: type limit, track by_dst, count 5, seconds 60; sid:1000;)");
2347
    FAIL_IF_NULL(s);
2348
    g_ut_threshold_fp = SCThresholdConfGenerateValidDummyFD20();
2349
    FAIL_IF_NULL(g_ut_threshold_fp);
2350
    FAIL_IF(-1 == SCThresholdConfInitContext(de_ctx));
2351
    SigGroupBuild(de_ctx);
2352
    FAIL_IF_NULL(s->sm_arrays[DETECT_SM_LIST_SUPPRESS]);
2353

2354
    SigMatchData *smd = s->sm_arrays[DETECT_SM_LIST_SUPPRESS];
2355
    DetectThresholdData *de = (DetectThresholdData *)smd->ctx;
2356
    FAIL_IF_NULL(de);
2357
    FAIL_IF_NOT(de->type == TYPE_SUPPRESS && de->track == TRACK_SRC);
2358
    FAIL_IF(smd->is_last);
2359

2360
    smd++;
2361
    de = (DetectThresholdData *)smd->ctx;
2362
    FAIL_IF_NULL(de);
2363
    FAIL_IF_NOT(de->type == TYPE_SUPPRESS && de->track == TRACK_SRC);
2364
    FAIL_IF(smd->is_last);
2365

2366
    smd++;
2367
    de = (DetectThresholdData *)smd->ctx;
2368
    FAIL_IF_NULL(de);
2369
    FAIL_IF_NOT(de->type == TYPE_SUPPRESS && de->track == TRACK_SRC);
2370
    FAIL_IF_NOT(smd->is_last);
2371

2372
    DetectEngineCtxFree(de_ctx);
2373
    ThresholdDestroy();
2374
    PASS;
2375
}
2376

2377
/**
2378
* \brief Creates a dummy rate_filter file, for testing rate filtering by_both source and destination
2379
*
2380
* \retval fd Pointer to file descriptor.
2381
*/
2382
static FILE *SCThresholdConfGenerateValidDummyFD22(void)
2383
{
2384
    FILE *fd = NULL;
2385
    const char *buffer =
2386
        "rate_filter gen_id 1, sig_id 10, track by_both, count 2, seconds 5, new_action drop, timeout 6\n";
2387

2388
    fd = SCFmemopen((void *)buffer, strlen(buffer), "r");
2389
    if (fd == NULL)
2390
        SCLogDebug("Error with SCFmemopen() called by Threshold Config test code");
2391

2392
    return fd;
2393
}
2394

2395
/**
2396
 * \test Check if the rate_filter rules work with track by_both
2397
 *
2398
 *  \retval 1 on success
2399
 *  \retval 0 on failure
2400
 */
2401
static int SCThresholdConfTest22(void)
2402
{
2403
    ThreadVars th_v;
2404
    memset(&th_v, 0, sizeof(th_v));
2405
    StatsThreadInit(&th_v.stats);
2406

2407
    ThresholdInit();
2408

2409
    /* This packet will cause rate_filter */
2410
    Packet *p1 = UTHBuildPacketSrcDst((uint8_t*)"lalala", 6, IPPROTO_TCP, "172.26.0.1", "172.26.0.10");
2411
    FAIL_IF_NULL(p1);
2412

2413
    /* Should not be filtered for different destination */
2414
    Packet *p2 = UTHBuildPacketSrcDst((uint8_t*)"lalala", 6, IPPROTO_TCP, "172.26.0.1", "172.26.0.2");
2415
    FAIL_IF_NULL(p2);
2416

2417
    /* Should not be filtered when both src and dst the same */
2418
    Packet *p3 = UTHBuildPacketSrcDst((uint8_t*)"lalala", 6, IPPROTO_TCP, "172.26.0.1", "172.26.0.1");
2419
    FAIL_IF_NULL(p3);
2420

2421
    DetectEngineThreadCtx *det_ctx = NULL;
2422

2423
    DetectEngineCtx *de_ctx = DetectEngineCtxInit();
2424
    FAIL_IF_NULL(de_ctx);
2425
    de_ctx->flags |= DE_QUIET;
2426

2427
    Signature *sig = DetectEngineAppendSig(de_ctx,
2428
            "alert tcp any any -> any any (msg:\"ratefilter by_both test\"; gid:1; sid:10;)");
2429
    FAIL_IF_NULL(sig);
2430

2431
    FAIL_IF_NOT_NULL(g_ut_threshold_fp);
2432
    g_ut_threshold_fp = SCThresholdConfGenerateValidDummyFD22();
2433
    FAIL_IF_NULL(g_ut_threshold_fp);
2434
    FAIL_IF(-1 == SCThresholdConfInitContext(de_ctx));
2435

2436
    SigGroupBuild(de_ctx);
2437
    DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
2438

2439
    p1->ts = TimeGet();
2440
    p2->ts = p3->ts = p1->ts;
2441

2442
    /* All should be alerted, none dropped */
2443
    SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
2444
    FAIL_IF(PacketTestAction(p1, ACTION_DROP));
2445
    FAIL_IF(PacketAlertCheck(p1, 10) != 1);
2446

2447
    SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
2448
    FAIL_IF(PacketTestAction(p2, ACTION_DROP));
2449
    FAIL_IF(PacketAlertCheck(p2, 10) != 1);
2450

2451
    SigMatchSignatures(&th_v, de_ctx, det_ctx, p3);
2452
    FAIL_IF(PacketTestAction(p3, ACTION_DROP));
2453
    FAIL_IF(PacketAlertCheck(p3, 10) != 1);
2454

2455
    p1->action = p2->action = p3->action = 0;
2456

2457
    TimeSetIncrementTime(2);
2458
    p1->ts = TimeGet();
2459
    p2->ts = p3->ts = p1->ts;
2460

2461
    /* p1 still shouldn't be dropped after 2nd alert */
2462
    SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
2463
    FAIL_IF(PacketTestAction(p1, ACTION_DROP));
2464
    FAIL_IF(PacketAlertCheck(p1, 10) != 1);
2465

2466
    p1->action = 0;
2467

2468
    TimeSetIncrementTime(2);
2469
    p1->ts = TimeGet();
2470
    p2->ts = p3->ts = p1->ts;
2471

2472
    /* All should be alerted, only p1 must be dropped due to rate_filter*/
2473
    SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
2474
    FAIL_IF_NOT(PacketTestAction(p1, ACTION_DROP));
2475
    FAIL_IF(PacketAlertCheck(p1, 10) != 1);
2476

2477
    SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
2478
    FAIL_IF(PacketTestAction(p2, ACTION_DROP));
2479
    FAIL_IF(PacketAlertCheck(p2, 10) != 1);
2480

2481
    SigMatchSignatures(&th_v, de_ctx, det_ctx, p3);
2482
    FAIL_IF(PacketTestAction(p3, ACTION_DROP));
2483
    FAIL_IF(PacketAlertCheck(p3, 10) != 1);
2484

2485
    p1->action = p2->action = p3->action = 0;
2486

2487
    TimeSetIncrementTime(7);
2488
    p1->ts = TimeGet();
2489
    p2->ts = p3->ts = p1->ts;
2490

2491
    /* All should be alerted, none dropped (because timeout expired) */
2492
    SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
2493
    FAIL_IF(PacketTestAction(p1, ACTION_DROP));
2494
    FAIL_IF(PacketAlertCheck(p1, 10) != 1);
2495

2496
    SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
2497
    FAIL_IF(PacketTestAction(p2, ACTION_DROP));
2498
    FAIL_IF(PacketAlertCheck(p2, 10) != 1);
2499

2500
    SigMatchSignatures(&th_v, de_ctx, det_ctx, p3);
2501
    FAIL_IF(PacketTestAction(p3, ACTION_DROP));
2502
    FAIL_IF(PacketAlertCheck(p3, 10) != 1);
2503

2504
    UTHFreePacket(p3);
2505
    UTHFreePacket(p2);
2506
    UTHFreePacket(p1);
2507

2508
    DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
2509
    DetectEngineCtxFree(de_ctx);
2510
    ThresholdDestroy();
2511
    StatsThreadCleanup(&th_v.stats);
2512
    PASS;
2513
}
2514

2515
/**
2516
* \brief Creates a dummy rate_filter file, for testing rate filtering by_both source and destination
2517
*
2518
* \retval fd Pointer to file descriptor.
2519
*/
2520
static FILE *SCThresholdConfGenerateValidDummyFD23(void)
2521
{
2522
    FILE *fd = NULL;
2523
    const char *buffer =
2524
        "rate_filter gen_id 1, sig_id 10, track by_both, count 1, seconds 5, new_action drop, timeout 6\n";
2525

2526
    fd = SCFmemopen((void *)buffer, strlen(buffer), "r");
2527
    if (fd == NULL)
2528
        SCLogDebug("Error with SCFmemopen() called by Threshold Config test code");
2529

2530
    return fd;
2531
}
2532

2533
/**
2534
 * \test Check if the rate_filter by_both work when similar packets
2535
 *       going in opposite direction
2536
 *
2537
 *  \retval 1 on success
2538
 *  \retval 0 on failure
2539
 */
2540
static int SCThresholdConfTest23(void)
2541
{
2542
    ThreadVars th_v;
2543
    memset(&th_v, 0, sizeof(th_v));
2544
    StatsThreadInit(&th_v.stats);
2545

2546
    ThresholdInit();
2547

2548
    /* Create two packets between same addresses in opposite direction */
2549
    Packet *p1 = UTHBuildPacketSrcDst((uint8_t*)"lalala", 6, IPPROTO_TCP, "172.26.0.1", "172.26.0.10");
2550
    FAIL_IF_NULL(p1);
2551

2552
    Packet *p2 = UTHBuildPacketSrcDst((uint8_t*)"lalala", 6, IPPROTO_TCP, "172.26.0.10", "172.26.0.1");
2553
    FAIL_IF_NULL(p2);
2554

2555
    DetectEngineThreadCtx *det_ctx = NULL;
2556

2557
    DetectEngineCtx *de_ctx = DetectEngineCtxInit();
2558
    FAIL_IF_NULL(de_ctx);
2559
    de_ctx->flags |= DE_QUIET;
2560

2561
    Signature *sig = DetectEngineAppendSig(de_ctx,
2562
        "alert tcp any any -> any any (msg:\"ratefilter by_both test\"; gid:1; sid:10;)");
2563
    FAIL_IF_NULL(sig);
2564

2565
    FAIL_IF_NOT_NULL(g_ut_threshold_fp);
2566
    g_ut_threshold_fp = SCThresholdConfGenerateValidDummyFD23();
2567
    FAIL_IF_NULL(g_ut_threshold_fp);
2568
    FAIL_IF(-1 == SCThresholdConfInitContext(de_ctx));
2569

2570
    SigGroupBuild(de_ctx);
2571
    DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
2572

2573
    p1->ts = TimeGet();
2574
    SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
2575
    /* First packet should be alerted, not dropped */
2576
    FAIL_IF(PacketTestAction(p1, ACTION_DROP));
2577
    FAIL_IF(PacketAlertCheck(p1, 10) != 1);
2578

2579
    TimeSetIncrementTime(2);
2580
    p2->ts = TimeGet();
2581
    SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
2582

2583
    /* Second packet should be dropped because it considered as "the same pair"
2584
       and rate_filter count reached*/
2585
    FAIL_IF_NOT(PacketTestAction(p2, ACTION_DROP));
2586
    FAIL_IF(PacketAlertCheck(p2, 10) != 1);
2587

2588
    UTHFreePacket(p2);
2589
    UTHFreePacket(p1);
2590

2591
    DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
2592
    DetectEngineCtxFree(de_ctx);
2593
    ThresholdDestroy();
2594
    StatsThreadCleanup(&th_v.stats);
2595
    PASS;
2596
}
2597
#endif /* UNITTESTS */
2598

2599
/**
2600
 * \brief This function registers unit tests for Classification Config API.
2601
 */
2602
void SCThresholdConfRegisterTests(void)
UNCOV
2603
{
×
2604
#ifdef UNITTESTS
2605
    UtRegisterTest("SCThresholdConfTest01", SCThresholdConfTest01);
2606
    UtRegisterTest("SCThresholdConfTest02", SCThresholdConfTest02);
2607
    UtRegisterTest("SCThresholdConfTest03", SCThresholdConfTest03);
2608
    UtRegisterTest("SCThresholdConfTest04", SCThresholdConfTest04);
2609
    UtRegisterTest("SCThresholdConfTest05", SCThresholdConfTest05);
2610
    UtRegisterTest("SCThresholdConfTest06", SCThresholdConfTest06);
2611
    UtRegisterTest("SCThresholdConfTest07", SCThresholdConfTest07);
2612
    UtRegisterTest("SCThresholdConfTest08", SCThresholdConfTest08);
2613
    UtRegisterTest("SCThresholdConfTest09 - rate_filter",
2614
                   SCThresholdConfTest09);
2615
    UtRegisterTest("SCThresholdConfTest10 - rate_filter",
2616
                   SCThresholdConfTest10);
2617
    UtRegisterTest("SCThresholdConfTest11 - event_filter",
2618
                   SCThresholdConfTest11);
2619
    UtRegisterTest("SCThresholdConfTest12 - event_filter",
2620
                   SCThresholdConfTest12);
2621
    UtRegisterTest("SCThresholdConfTest13", SCThresholdConfTest13);
2622
    UtRegisterTest("SCThresholdConfTest14 - suppress", SCThresholdConfTest14);
2623
    UtRegisterTest("SCThresholdConfTest15 - suppress drop",
2624
                   SCThresholdConfTest15);
2625
    UtRegisterTest("SCThresholdConfTest16 - suppress drop",
2626
                   SCThresholdConfTest16);
2627
    UtRegisterTest("SCThresholdConfTest17 - suppress drop",
2628
                   SCThresholdConfTest17);
2629

2630
    UtRegisterTest("SCThresholdConfTest18 - suppress parsing",
2631
                   SCThresholdConfTest18);
2632
    UtRegisterTest("SCThresholdConfTest19 - suppress parsing",
2633
                   SCThresholdConfTest19);
2634
    UtRegisterTest("SCThresholdConfTest20 - suppress parsing",
2635
                   SCThresholdConfTest20);
2636
    UtRegisterTest("SCThresholdConfTest21 - suppress parsing",
2637
                   SCThresholdConfTest21);
2638
    UtRegisterTest("SCThresholdConfTest22 - rate_filter by_both",
2639
                   SCThresholdConfTest22);
2640
    UtRegisterTest("SCThresholdConfTest23 - rate_filter by_both opposite",
2641
        SCThresholdConfTest23);
2642

2643
#endif /* UNITTESTS */
UNCOV
2644
}
×
2645

2646
/**
2647
 * @}
2648
 */
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