Coverage Report

Created: 2026-09-28 07:39

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/suricata8/src/util-threshold-config.c
Line
Count
Source
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
77
#define DETECT_BASE_REGEX "^\\s*(event_filter|threshold|rate_filter|suppress)\\s*gen_id\\s*(\\d+)\\s*,\\s*sig_id\\s*(\\d+)\\s*(.*)\\s*$"
68
69
#define DETECT_THRESHOLD_REGEX                                                                     \
70
77
    "^,\\s*type\\s*(limit|both|threshold)\\s*,\\s*track\\s*(by_dst|by_src|by_both|by_rule|by_"     \
71
77
    "flow)\\s*,"                                                                                   \
72
77
    "\\s*count\\s*(\\d+)\\s*,\\s*seconds\\s*(\\d+)\\s*$"
73
74
/* TODO: "apply_to" */
75
#define DETECT_RATE_REGEX                                                                          \
76
77
    "^,\\s*track\\s*(by_dst|by_src|by_both|by_rule|by_flow)\\s*,\\s*count\\s*(\\d+)\\s*,\\s*"      \
77
77
    "seconds\\s*(\\d+)\\s*,\\s*new_action\\s*(alert|drop|pass|log|sdrop|reject)\\s*,\\s*"          \
78
77
    "timeout\\s*(\\d+)\\s*$"
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
77
#define DETECT_SUPPRESS_REGEX "^,\\s*track\\s*(by_dst|by_src|by_either)\\s*,\\s*ip\\s*([\\[\\],\\$\\s\\da-zA-Z.:/_]+)*\\s*$"
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
98.1k
#define THRESHOLD_CONF_DEF_CONF_FILEPATH CONFIG_DIR "/threshold.config"
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
77
{
104
77
    regex_base = DetectSetupPCRE2(DETECT_BASE_REGEX, 0);
105
77
    if (regex_base == NULL) {
106
0
        FatalError("classification base regex setup failed");
107
0
    }
108
77
    regex_threshold = DetectSetupPCRE2(DETECT_THRESHOLD_REGEX, 0);
109
77
    if (regex_threshold == NULL) {
110
0
        FatalError("classification threshold regex setup failed");
111
0
    }
112
77
    regex_rate = DetectSetupPCRE2(DETECT_RATE_REGEX, 0);
113
77
    if (regex_rate == NULL) {
114
0
        FatalError("classification rate_filter regex setup failed");
115
0
    }
116
77
    regex_suppress = DetectSetupPCRE2(DETECT_SUPPRESS_REGEX, 0);
117
77
    if (regex_suppress == NULL) {
118
0
        FatalError("classification suppress regex setup failed");
119
0
    }
120
77
}
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
98.1k
{
133
98.1k
    const char *log_filename = NULL;
134
135
98.1k
    if (de_ctx != NULL && strlen(de_ctx->config_prefix) > 0) {
136
0
        char config_value[256];
137
0
        snprintf(config_value, sizeof(config_value),
138
0
                 "%s.threshold-file", de_ctx->config_prefix);
139
140
        /* try loading prefix setting, fall back to global if that
141
         * fails. */
142
0
        if (SCConfGetNonNull(config_value, &log_filename) != 1) {
143
0
            if (SCConfGetNonNull("threshold-file", &log_filename) != 1) {
144
0
                log_filename = (char *)THRESHOLD_CONF_DEF_CONF_FILEPATH;
145
0
            }
146
0
        }
147
98.1k
    } else {
148
98.1k
        if (SCConfGetNonNull("threshold-file", &log_filename) != 1) {
149
98.1k
            log_filename = (char *)THRESHOLD_CONF_DEF_CONF_FILEPATH;
150
98.1k
        }
151
98.1k
    }
152
98.1k
    return log_filename;
153
98.1k
}
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
98.1k
{
171
98.1k
    const char *filename = NULL;
172
98.1k
    int ret = 0;
173
98.1k
#ifndef UNITTESTS
174
98.1k
    FILE *fd = NULL;
175
#else
176
    filename = "<ut>";
177
    FILE *fd = g_ut_threshold_fp;
178
    if (fd == NULL) {
179
#endif
180
98.1k
        filename = SCThresholdConfGetConfFilename(de_ctx);
181
98.1k
        if ( (fd = fopen(filename, "r")) == NULL) {
182
98.1k
            SCLogWarning("Error opening file: \"%s\": %s", filename, strerror(errno));
183
98.1k
            SCThresholdConfDeInitContext(de_ctx, fd);
184
98.1k
            return 0;
185
98.1k
        }
186
#ifdef UNITTESTS
187
    }
188
#endif
189
190
0
    if (SCThresholdConfParseFile(de_ctx, fd) < 0) {
191
0
        SCLogWarning("Error loading threshold configuration from %s", filename);
192
0
        SCThresholdConfDeInitContext(de_ctx, fd);
193
        /* maintain legacy behavior so no errors unless config testing */
194
0
        if (SCRunmodeGet() == RUNMODE_CONF_TEST) {
195
0
            ret = -1;
196
0
        }
197
0
        return ret;
198
0
    }
199
0
    SCThresholdConfDeInitContext(de_ctx, fd);
200
201
#ifdef UNITTESTS
202
    g_ut_threshold_fp = NULL;
203
#endif
204
0
    SCLogDebug("Global thresholding options defined");
205
0
    return 0;
206
0
}
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
98.1k
{
216
98.1k
    if (fd != NULL)
217
0
        fclose(fd);
218
98.1k
}
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)
229
0
{
230
0
    Signature *s = NULL;
231
0
    DetectThresholdData *de = NULL;
232
233
0
    BUG_ON(parsed_type != TYPE_SUPPRESS);
234
235
0
    DetectThresholdData *orig_de = NULL;
236
0
    if (parsed_track != TRACK_RULE) {
237
0
        orig_de = SCCalloc(1, sizeof(DetectThresholdData));
238
0
        if (unlikely(orig_de == NULL))
239
0
            goto error;
240
241
0
        orig_de->type = TYPE_SUPPRESS;
242
0
        orig_de->track = parsed_track;
243
0
        orig_de->count = parsed_count;
244
0
        orig_de->seconds = parsed_seconds;
245
0
        orig_de->new_action = parsed_new_action;
246
0
        orig_de->timeout = parsed_timeout;
247
0
        if (DetectAddressParse(
248
0
                    (const DetectEngineCtx *)de_ctx, &orig_de->addrs, (char *)th_ip, NULL) < 0) {
249
0
            SCLogError("failed to parse %s", th_ip);
250
0
            goto error;
251
0
        }
252
0
    }
253
254
    /* Install it */
255
0
    if (id == 0 && gid == 0) {
256
0
        if (parsed_track == TRACK_RULE) {
257
0
            SCLogWarning("suppressing all rules");
258
0
        }
259
260
        /* update each sig with our suppress info */
261
0
        for (s = de_ctx->sig_list; s != NULL; s = s->next) {
262
            /* tag the rule as noalert */
263
0
            if (parsed_track == TRACK_RULE) {
264
0
                s->action &= ~ACTION_ALERT;
265
0
                continue;
266
0
            }
267
268
0
            de = DetectThresholdDataCopy(orig_de);
269
0
            if (unlikely(de == NULL))
270
0
                goto error;
271
272
0
            if (SCSigMatchAppendSMToList(de_ctx, s, DETECT_THRESHOLD, (SigMatchCtx *)de,
273
0
                        DETECT_SM_LIST_SUPPRESS) == NULL) {
274
0
                goto error;
275
0
            }
276
0
        }
277
0
    } else if (id == 0 && gid > 0)    {
278
0
        if (parsed_track == TRACK_RULE) {
279
0
            SCLogWarning("suppressing all rules with gid %" PRIu32, gid);
280
0
        }
281
        /* set up suppression for each signature with a matching gid */
282
0
        for (s = de_ctx->sig_list; s != NULL; s = s->next) {
283
0
            if (s->gid != gid)
284
0
                continue;
285
286
            /* tag the rule as noalert */
287
0
            if (parsed_track == TRACK_RULE) {
288
0
                s->action &= ~ACTION_ALERT;
289
0
                continue;
290
0
            }
291
292
0
            de = DetectThresholdDataCopy(orig_de);
293
0
            if (unlikely(de == NULL))
294
0
                goto error;
295
296
0
            if (SCSigMatchAppendSMToList(de_ctx, s, DETECT_THRESHOLD, (SigMatchCtx *)de,
297
0
                        DETECT_SM_LIST_SUPPRESS) == NULL) {
298
0
                goto error;
299
0
            }
300
0
        }
301
0
    } else if (id > 0 && gid == 0) {
302
0
        SCLogError("Can't use a event config that has "
303
0
                   "sid > 0 and gid == 0. Please fix this "
304
0
                   "in your threshold.config file");
305
0
        goto error;
306
0
    } else {
307
0
        s = SigFindSignatureBySidGid(de_ctx, id, gid);
308
0
        if (s == NULL) {
309
0
            SCLogWarning("can't suppress sid "
310
0
                         "%" PRIu32 ", gid %" PRIu32 ": unknown rule",
311
0
                    id, gid);
312
0
        } else {
313
0
            if (parsed_track == TRACK_RULE) {
314
0
                s->action &= ~ACTION_ALERT;
315
0
                goto end;
316
0
            }
317
318
0
            de = DetectThresholdDataCopy(orig_de);
319
0
            if (unlikely(de == NULL))
320
0
                goto error;
321
322
0
            if (SCSigMatchAppendSMToList(de_ctx, s, DETECT_THRESHOLD, (SigMatchCtx *)de,
323
0
                        DETECT_SM_LIST_SUPPRESS) == NULL) {
324
0
                goto error;
325
0
            }
326
0
        }
327
0
    }
328
329
0
end:
330
0
    if (orig_de != NULL) {
331
0
        DetectAddressHeadCleanup(&orig_de->addrs);
332
0
        SCFree(orig_de);
333
0
    }
334
0
    return 0;
335
0
error:
336
0
    if (orig_de != NULL) {
337
0
        DetectAddressHeadCleanup(&orig_de->addrs);
338
0
        SCFree(orig_de);
339
0
    }
340
0
    if (de != NULL) {
341
0
        DetectAddressHeadCleanup(&de->addrs);
342
0
        SCFree(de);
343
0
    }
344
0
    return -1;
345
0
}
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)
355
0
{
356
0
    Signature *s = NULL;
357
0
    SigMatch *sm = NULL;
358
0
    DetectThresholdData *de = NULL;
359
360
0
    BUG_ON(parsed_type == TYPE_SUPPRESS);
361
362
    /* Install it */
363
0
    if (id == 0 && gid == 0) {
364
0
        for (s = de_ctx->sig_list; s != NULL; s = s->next) {
365
0
            sm = DetectGetLastSMByListId(s, DETECT_SM_LIST_THRESHOLD, DETECT_THRESHOLD, -1);
366
0
            if (sm != NULL) {
367
0
                SCLogWarning("signature sid:%" PRIu32 " has "
368
0
                             "an event var set.  The signature event var is "
369
0
                             "given precedence over the threshold.conf one.  "
370
0
                             "We'll change this in the future though.",
371
0
                        s->id);
372
0
                continue;
373
0
            }
374
375
0
            sm = DetectGetLastSMByListId(s,
376
0
                    DETECT_SM_LIST_THRESHOLD, DETECT_DETECTION_FILTER, -1);
377
0
            if (sm != NULL) {
378
0
                SCLogWarning("signature sid:%" PRIu32 " has "
379
0
                             "an event var set.  The signature event var is "
380
0
                             "given precedence over the threshold.conf one.  "
381
0
                             "We'll change this in the future though.",
382
0
                        s->id);
383
0
                continue;
384
0
            }
385
386
0
            de = SCCalloc(1, sizeof(DetectThresholdData));
387
0
            if (unlikely(de == NULL))
388
0
                goto error;
389
390
0
            de->type = parsed_type;
391
0
            de->track = parsed_track;
392
0
            de->count = parsed_count;
393
0
            de->seconds = parsed_seconds;
394
0
            de->new_action = parsed_new_action;
395
0
            de->timeout = parsed_timeout;
396
397
0
            uint16_t smtype = DETECT_THRESHOLD;
398
0
            if (parsed_type == TYPE_RATE)
399
0
                smtype = DETECT_DETECTION_FILTER;
400
401
0
            if (SCSigMatchAppendSMToList(
402
0
                        de_ctx, s, smtype, (SigMatchCtx *)de, DETECT_SM_LIST_THRESHOLD) == NULL) {
403
0
                goto error;
404
0
            }
405
0
        }
406
407
0
    } else if (id == 0 && gid > 0) {
408
0
        for (s = de_ctx->sig_list; s != NULL; s = s->next) {
409
0
            if (s->gid == gid) {
410
0
                sm = DetectGetLastSMByListId(s, DETECT_SM_LIST_THRESHOLD,
411
0
                        DETECT_THRESHOLD, DETECT_DETECTION_FILTER, -1);
412
0
                if (sm != NULL) {
413
0
                    SCLogWarning("signature sid:%" PRIu32 " has "
414
0
                                 "an event var set.  The signature event var is "
415
0
                                 "given precedence over the threshold.conf one.  "
416
0
                                 "We'll change this in the future though.",
417
0
                            id);
418
0
                    continue;
419
0
                }
420
421
0
                de = SCCalloc(1, sizeof(DetectThresholdData));
422
0
                if (unlikely(de == NULL))
423
0
                    goto error;
424
425
0
                de->type = parsed_type;
426
0
                de->track = parsed_track;
427
0
                de->count = parsed_count;
428
0
                de->seconds = parsed_seconds;
429
0
                de->new_action = parsed_new_action;
430
0
                de->timeout = parsed_timeout;
431
432
0
                uint16_t smtype = DETECT_THRESHOLD;
433
0
                if (parsed_type == TYPE_RATE)
434
0
                    smtype = DETECT_DETECTION_FILTER;
435
436
0
                if (SCSigMatchAppendSMToList(de_ctx, s, smtype, (SigMatchCtx *)de,
437
0
                            DETECT_SM_LIST_THRESHOLD) == NULL) {
438
0
                    goto error;
439
0
                }
440
0
            }
441
0
        }
442
0
    } else if (id > 0 && gid == 0) {
443
0
        SCLogError("Can't use a event config that has "
444
0
                   "sid > 0 and gid == 0. Please fix this "
445
0
                   "in your threshold.conf file");
446
0
    } else {
447
0
        s = SigFindSignatureBySidGid(de_ctx, id, gid);
448
0
        if (s == NULL) {
449
0
            SCLogWarning("can't suppress sid "
450
0
                         "%" PRIu32 ", gid %" PRIu32 ": unknown rule",
451
0
                    id, gid);
452
0
        } else {
453
0
            if (parsed_type != TYPE_SUPPRESS && parsed_type != TYPE_THRESHOLD &&
454
0
                parsed_type != TYPE_BOTH && parsed_type != TYPE_LIMIT)
455
0
            {
456
0
                sm = DetectGetLastSMByListId(s,
457
0
                        DETECT_SM_LIST_THRESHOLD, DETECT_THRESHOLD, -1);
458
0
                if (sm != NULL) {
459
0
                    SCLogWarning("signature sid:%" PRIu32 " has "
460
0
                                 "a threshold set. The signature event var is "
461
0
                                 "given precedence over the threshold.conf one. "
462
0
                                 "Bug #425.",
463
0
                            s->id);
464
0
                    goto end;
465
0
                }
466
467
0
                sm = DetectGetLastSMByListId(s, DETECT_SM_LIST_THRESHOLD,
468
0
                        DETECT_DETECTION_FILTER, -1);
469
0
                if (sm != NULL) {
470
0
                    SCLogWarning("signature sid:%" PRIu32 " has "
471
0
                                 "a detection_filter set. The signature event var is "
472
0
                                 "given precedence over the threshold.conf one. "
473
0
                                 "Bug #425.",
474
0
                            s->id);
475
0
                    goto end;
476
0
                }
477
478
            /* replace threshold on sig if we have a global override for it */
479
0
            } else if (parsed_type == TYPE_THRESHOLD || parsed_type == TYPE_BOTH || parsed_type == TYPE_LIMIT) {
480
0
                sm = DetectGetLastSMByListId(s, DETECT_SM_LIST_THRESHOLD,
481
0
                        DETECT_THRESHOLD, DETECT_DETECTION_FILTER, -1);
482
0
                if (sm != NULL) {
483
0
                    SigMatchRemoveSMFromList(s, sm, DETECT_SM_LIST_THRESHOLD);
484
0
                    SigMatchFree(de_ctx, sm);
485
0
                }
486
0
            }
487
488
0
            de = SCCalloc(1, sizeof(DetectThresholdData));
489
0
            if (unlikely(de == NULL))
490
0
                goto error;
491
492
0
            de->type = parsed_type;
493
0
            de->track = parsed_track;
494
0
            de->count = parsed_count;
495
0
            de->seconds = parsed_seconds;
496
0
            de->new_action = parsed_new_action;
497
0
            de->timeout = parsed_timeout;
498
499
0
            uint16_t smtype = DETECT_THRESHOLD;
500
0
            if (parsed_type == TYPE_RATE)
501
0
                smtype = DETECT_DETECTION_FILTER;
502
503
0
            if (SCSigMatchAppendSMToList(
504
0
                        de_ctx, s, smtype, (SigMatchCtx *)de, DETECT_SM_LIST_THRESHOLD) == NULL) {
505
0
                goto error;
506
0
            }
507
0
        }
508
0
    }
509
0
end:
510
0
    return 0;
511
0
error:
512
0
    if (de != NULL) {
513
0
        DetectAddressHeadCleanup(&de->addrs);
514
0
        SCFree(de);
515
0
    }
516
0
    return -1;
517
0
}
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)
523
0
{
524
0
    char th_rule_type[32];
525
0
    char th_gid[16];
526
0
    char th_sid[16];
527
0
    const char *rule_extend = NULL;
528
0
    char th_type[16] = "";
529
0
    char th_track[16] = "";
530
0
    char th_count[16] = "";
531
0
    char th_seconds[16] = "";
532
0
    char th_new_action[16] = "";
533
0
    char th_timeout[16] = "";
534
0
    const char *th_ip = NULL;
535
536
0
    uint8_t parsed_type = 0;
537
0
    uint8_t parsed_track = 0;
538
0
    uint8_t parsed_new_action = 0;
539
0
    uint32_t parsed_count = 0;
540
0
    uint32_t parsed_seconds = 0;
541
0
    uint32_t parsed_timeout = 0;
542
543
0
    int ret = 0;
544
0
    uint32_t id = 0, gid = 0;
545
0
    ThresholdRuleType rule_type;
546
547
0
    if (de_ctx == NULL)
548
0
        return -1;
549
550
0
    pcre2_match_data *regex_base_match = NULL;
551
0
    ret = DetectParsePcreExec(regex_base, &regex_base_match, rawstr, 0, 0);
552
0
    if (ret < 4) {
553
0
        SCLogError("pcre2_match parse error, ret %" PRId32 ", string %s", ret, rawstr);
554
0
        pcre2_match_data_free(regex_base_match);
555
0
        goto error;
556
0
    }
557
558
    /* retrieve the classtype name */
559
0
    size_t copylen = sizeof(th_rule_type);
560
0
    ret = pcre2_substring_copy_bynumber(
561
0
            regex_base_match, 1, (PCRE2_UCHAR8 *)th_rule_type, &copylen);
562
0
    if (ret < 0) {
563
0
        SCLogError("pcre2_substring_copy_bynumber failed");
564
0
        pcre2_match_data_free(regex_base_match);
565
0
        goto error;
566
0
    }
567
568
    /* retrieve the classtype name */
569
0
    copylen = sizeof(th_gid);
570
0
    ret = pcre2_substring_copy_bynumber(regex_base_match, 2, (PCRE2_UCHAR8 *)th_gid, &copylen);
571
0
    if (ret < 0) {
572
0
        SCLogError("pcre2_substring_copy_bynumber failed");
573
0
        pcre2_match_data_free(regex_base_match);
574
0
        goto error;
575
0
    }
576
577
0
    copylen = sizeof(th_sid);
578
0
    ret = pcre2_substring_copy_bynumber(regex_base_match, 3, (PCRE2_UCHAR8 *)th_sid, &copylen);
579
0
    if (ret < 0) {
580
0
        SCLogError("pcre2_substring_copy_bynumber failed");
581
0
        pcre2_match_data_free(regex_base_match);
582
0
        goto error;
583
0
    }
584
585
    /* Use "get" for heap allocation */
586
0
    ret = pcre2_substring_get_bynumber(
587
0
            regex_base_match, 4, (PCRE2_UCHAR8 **)&rule_extend, &copylen);
588
0
    if (ret < 0) {
589
0
        SCLogError("pcre2_substring_get_bynumber failed");
590
0
        pcre2_match_data_free(regex_base_match);
591
0
        goto error;
592
0
    }
593
0
    pcre2_match_data_free(regex_base_match);
594
0
    regex_base_match = NULL;
595
596
    /* get type of rule */
597
0
    if (strcasecmp(th_rule_type,"event_filter") == 0) {
598
0
        rule_type = THRESHOLD_TYPE_EVENT_FILTER;
599
0
    } else if (strcasecmp(th_rule_type,"threshold") == 0) {
600
0
        rule_type = THRESHOLD_TYPE_THRESHOLD;
601
0
    } else if (strcasecmp(th_rule_type,"rate_filter") == 0) {
602
0
        rule_type = THRESHOLD_TYPE_RATE;
603
0
    } else if (strcasecmp(th_rule_type,"suppress") == 0) {
604
0
        rule_type = THRESHOLD_TYPE_SUPPRESS;
605
0
    } else {
606
0
        SCLogError("rule type %s is unknown", th_rule_type);
607
0
        goto error;
608
0
    }
609
610
    /* get end of rule */
611
0
    switch(rule_type) {
612
0
        case THRESHOLD_TYPE_EVENT_FILTER:
613
0
        case THRESHOLD_TYPE_THRESHOLD:
614
0
            if (strlen(rule_extend) > 0) {
615
0
                pcre2_match_data *match = NULL;
616
617
0
                ret = DetectParsePcreExec(regex_threshold, &match, rule_extend, 0, 0);
618
0
                if (ret < 4) {
619
0
                    SCLogError("pcre2_match parse error, ret %" PRId32 ", string %s", ret,
620
0
                            rule_extend);
621
0
                    pcre2_match_data_free(match);
622
0
                    goto error;
623
0
                }
624
625
0
                copylen = sizeof(th_type);
626
0
                ret = pcre2_substring_copy_bynumber(match, 1, (PCRE2_UCHAR8 *)th_type, &copylen);
627
0
                if (ret < 0) {
628
0
                    SCLogError("pcre2_substring_copy_bynumber failed");
629
0
                    pcre2_match_data_free(match);
630
0
                    goto error;
631
0
                }
632
633
0
                copylen = sizeof(th_track);
634
0
                ret = pcre2_substring_copy_bynumber(match, 2, (PCRE2_UCHAR8 *)th_track, &copylen);
635
0
                if (ret < 0) {
636
0
                    SCLogError("pcre2_substring_copy_bynumber failed");
637
0
                    pcre2_match_data_free(match);
638
0
                    goto error;
639
0
                }
640
641
0
                copylen = sizeof(th_count);
642
0
                ret = pcre2_substring_copy_bynumber(match, 3, (PCRE2_UCHAR8 *)th_count, &copylen);
643
0
                if (ret < 0) {
644
0
                    SCLogError("pcre2_substring_copy_bynumber failed");
645
0
                    pcre2_match_data_free(match);
646
0
                    goto error;
647
0
                }
648
649
0
                copylen = sizeof(th_seconds);
650
0
                ret = pcre2_substring_copy_bynumber(match, 4, (PCRE2_UCHAR8 *)th_seconds, &copylen);
651
0
                if (ret < 0) {
652
0
                    SCLogError("pcre2_substring_copy_bynumber failed");
653
0
                    pcre2_match_data_free(match);
654
0
                    goto error;
655
0
                }
656
0
                pcre2_match_data_free(match);
657
658
0
                if (strcasecmp(th_type,"limit") == 0)
659
0
                    parsed_type = TYPE_LIMIT;
660
0
                else if (strcasecmp(th_type,"both") == 0)
661
0
                    parsed_type = TYPE_BOTH;
662
0
                else if (strcasecmp(th_type,"threshold") == 0)
663
0
                    parsed_type = TYPE_THRESHOLD;
664
0
                else {
665
0
                    SCLogError("limit type not supported: %s", th_type);
666
0
                    goto error;
667
0
                }
668
0
            } else {
669
0
                SCLogError("rule invalid: %s", rawstr);
670
0
                goto error;
671
0
            }
672
0
            break;
673
0
        case THRESHOLD_TYPE_SUPPRESS:
674
0
            if (strlen(rule_extend) > 0) {
675
0
                pcre2_match_data *match = NULL;
676
0
                ret = DetectParsePcreExec(regex_suppress, &match, rule_extend, 0, 0);
677
0
                if (ret < 2) {
678
0
                    SCLogError("pcre2_match parse error, ret %" PRId32 ", string %s", ret,
679
0
                            rule_extend);
680
0
                    pcre2_match_data_free(match);
681
0
                    goto error;
682
0
                }
683
                /* retrieve the track mode */
684
0
                copylen = sizeof(th_seconds);
685
0
                ret = pcre2_substring_copy_bynumber(match, 1, (PCRE2_UCHAR8 *)th_track, &copylen);
686
0
                if (ret < 0) {
687
0
                    SCLogError("pcre2_substring_copy_bynumber failed");
688
0
                    pcre2_match_data_free(match);
689
0
                    goto error;
690
0
                }
691
                /* retrieve the IP; use "get" for heap allocation */
692
0
                ret = pcre2_substring_get_bynumber(match, 2, (PCRE2_UCHAR8 **)&th_ip, &copylen);
693
0
                if (ret < 0) {
694
0
                    SCLogError("pcre2_substring_get_bynumber failed");
695
0
                    pcre2_match_data_free(match);
696
0
                    goto error;
697
0
                }
698
0
                pcre2_match_data_free(match);
699
0
            } else {
700
0
                parsed_track = TRACK_RULE;
701
0
            }
702
0
            parsed_type = TYPE_SUPPRESS;
703
0
            break;
704
0
        case THRESHOLD_TYPE_RATE:
705
0
            if (strlen(rule_extend) > 0) {
706
0
                pcre2_match_data *match = NULL;
707
0
                ret = DetectParsePcreExec(regex_rate, &match, rule_extend, 0, 0);
708
0
                if (ret < 5) {
709
0
                    SCLogError("pcre2_match parse error, ret %" PRId32 ", string %s", ret,
710
0
                            rule_extend);
711
0
                    pcre2_match_data_free(match);
712
0
                    goto error;
713
0
                }
714
715
0
                copylen = sizeof(th_track);
716
0
                ret = pcre2_substring_copy_bynumber(match, 1, (PCRE2_UCHAR8 *)th_track, &copylen);
717
0
                if (ret < 0) {
718
0
                    SCLogError("pcre2_substring_copy_bynumber failed");
719
0
                    pcre2_match_data_free(match);
720
0
                    goto error;
721
0
                }
722
723
0
                copylen = sizeof(th_count);
724
0
                ret = pcre2_substring_copy_bynumber(match, 2, (PCRE2_UCHAR8 *)th_count, &copylen);
725
0
                if (ret < 0) {
726
0
                    SCLogError("pcre2_substring_copy_bynumber failed");
727
0
                    pcre2_match_data_free(match);
728
0
                    goto error;
729
0
                }
730
731
0
                copylen = sizeof(th_seconds);
732
0
                ret = pcre2_substring_copy_bynumber(match, 3, (PCRE2_UCHAR8 *)th_seconds, &copylen);
733
0
                if (ret < 0) {
734
0
                    SCLogError("pcre2_substring_copy_bynumber failed");
735
0
                    pcre2_match_data_free(match);
736
0
                    goto error;
737
0
                }
738
739
0
                copylen = sizeof(th_new_action);
740
0
                ret = pcre2_substring_copy_bynumber(
741
0
                        match, 4, (PCRE2_UCHAR8 *)th_new_action, &copylen);
742
0
                if (ret < 0) {
743
0
                    SCLogError("pcre2_substring_copy_bynumber failed");
744
0
                    pcre2_match_data_free(match);
745
0
                    goto error;
746
0
                }
747
748
0
                copylen = sizeof(th_timeout);
749
0
                ret = pcre2_substring_copy_bynumber(match, 5, (PCRE2_UCHAR8 *)th_timeout, &copylen);
750
0
                if (ret < 0) {
751
0
                    SCLogError("pcre2_substring_copy_bynumber failed");
752
0
                    pcre2_match_data_free(match);
753
0
                    goto error;
754
0
                }
755
0
                pcre2_match_data_free(match);
756
757
                /* TODO: implement option "apply_to" */
758
759
0
                if (StringParseUint32(&parsed_timeout, 10, sizeof(th_timeout), th_timeout) <= 0) {
760
0
                    goto error;
761
0
                }
762
763
                /* Get the new action to take */
764
0
                if (strcasecmp(th_new_action, "alert") == 0)
765
0
                    parsed_new_action = TH_ACTION_ALERT;
766
0
                if (strcasecmp(th_new_action, "drop") == 0)
767
0
                    parsed_new_action = TH_ACTION_DROP;
768
0
                if (strcasecmp(th_new_action, "pass") == 0)
769
0
                    parsed_new_action = TH_ACTION_PASS;
770
0
                if (strcasecmp(th_new_action, "reject") == 0)
771
0
                    parsed_new_action = TH_ACTION_REJECT;
772
0
                if (strcasecmp(th_new_action, "log") == 0) {
773
0
                    SCLogInfo("log action for rate_filter not supported yet");
774
0
                    parsed_new_action = TH_ACTION_LOG;
775
0
                }
776
0
                if (strcasecmp(th_new_action, "sdrop") == 0) {
777
0
                    SCLogInfo("sdrop action for rate_filter not supported yet");
778
0
                    parsed_new_action = TH_ACTION_SDROP;
779
0
                }
780
0
                parsed_type = TYPE_RATE;
781
0
            } else {
782
0
                SCLogError("rule invalid: %s", rawstr);
783
0
                goto error;
784
0
            }
785
0
            break;
786
0
    }
787
788
0
    switch (rule_type) {
789
        /* This part is common to threshold/event_filter/rate_filter */
790
0
        case THRESHOLD_TYPE_EVENT_FILTER:
791
0
        case THRESHOLD_TYPE_THRESHOLD:
792
0
        case THRESHOLD_TYPE_RATE:
793
0
            if (strcasecmp(th_track,"by_dst") == 0)
794
0
                parsed_track = TRACK_DST;
795
0
            else if (strcasecmp(th_track,"by_src") == 0)
796
0
                parsed_track = TRACK_SRC;
797
0
            else if (strcasecmp(th_track, "by_both") == 0) {
798
0
                parsed_track = TRACK_BOTH;
799
0
            }
800
0
            else if (strcasecmp(th_track,"by_rule") == 0)
801
0
                parsed_track = TRACK_RULE;
802
0
            else if (strcasecmp(th_track, "by_flow") == 0)
803
0
                parsed_track = TRACK_FLOW;
804
0
            else {
805
0
                SCLogError("Invalid track parameter %s in %s", th_track, rawstr);
806
0
                goto error;
807
0
            }
808
809
0
            if (StringParseUint32(&parsed_count, 10, sizeof(th_count), th_count) <= 0) {
810
0
                goto error;
811
0
            }
812
0
            if (parsed_count == 0) {
813
0
                SCLogError("rate filter count should be > 0");
814
0
                goto error;
815
0
            }
816
817
0
            if (StringParseUint32(&parsed_seconds, 10, sizeof(th_seconds), th_seconds) <= 0) {
818
0
                goto error;
819
0
            }
820
821
0
           break;
822
0
        case THRESHOLD_TYPE_SUPPRESS:
823
            /* need to get IP if extension is provided */
824
0
            if (strcmp("", th_track) != 0) {
825
0
                if (strcasecmp(th_track,"by_dst") == 0)
826
0
                    parsed_track = TRACK_DST;
827
0
                else if (strcasecmp(th_track,"by_src") == 0)
828
0
                    parsed_track = TRACK_SRC;
829
0
                else if (strcasecmp(th_track,"by_either") == 0) {
830
0
                    parsed_track = TRACK_EITHER;
831
0
                }
832
0
                else {
833
0
                    SCLogError("Invalid track parameter %s in %s", th_track, rule_extend);
834
0
                    goto error;
835
0
                }
836
0
            }
837
0
            break;
838
0
    }
839
840
0
    if (StringParseUint32(&id, 10, sizeof(th_sid), th_sid) <= 0) {
841
0
        goto error;
842
0
    }
843
844
0
    if (StringParseUint32(&gid, 10, sizeof(th_gid), th_gid) <= 0) {
845
0
        goto error;
846
0
    }
847
848
0
    *ret_id = id;
849
0
    *ret_gid = gid;
850
0
    *ret_parsed_type = parsed_type;
851
0
    *ret_parsed_track = parsed_track;
852
0
    *ret_parsed_new_action = parsed_new_action;
853
0
    *ret_parsed_count = parsed_count;
854
0
    *ret_parsed_seconds = parsed_seconds;
855
0
    *ret_parsed_timeout = parsed_timeout;
856
0
    *ret_th_ip = NULL;
857
0
    if (th_ip != NULL) {
858
0
        *ret_th_ip = (char *)th_ip;
859
0
    }
860
0
    pcre2_substring_free((PCRE2_UCHAR8 *)rule_extend);
861
0
    return 0;
862
863
0
error:
864
0
    if (rule_extend != NULL) {
865
0
        pcre2_substring_free((PCRE2_UCHAR8 *)rule_extend);
866
0
    }
867
0
    if (th_ip != NULL) {
868
0
        pcre2_substring_free((PCRE2_UCHAR8 *)th_ip);
869
0
    }
870
0
    return -1;
871
0
}
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)
884
0
{
885
0
    uint8_t parsed_type = 0;
886
0
    uint8_t parsed_track = 0;
887
0
    uint8_t parsed_new_action = 0;
888
0
    uint32_t parsed_count = 0;
889
0
    uint32_t parsed_seconds = 0;
890
0
    uint32_t parsed_timeout = 0;
891
0
    char *th_ip = NULL;
892
0
    uint32_t id = 0, gid = 0;
893
894
0
    int r = ParseThresholdRule(de_ctx, rawstr, &id, &gid, &parsed_type, &parsed_track,
895
0
            &parsed_count, &parsed_seconds, &parsed_timeout, &parsed_new_action, &th_ip);
896
0
    if (r < 0)
897
0
        goto error;
898
899
0
    if (parsed_type == TYPE_SUPPRESS) {
900
0
        r = SetupSuppressRule(de_ctx, id, gid, parsed_type, parsed_track,
901
0
                    parsed_count, parsed_seconds, parsed_timeout, parsed_new_action,
902
0
                    th_ip);
903
0
    } else {
904
0
        r = SetupThresholdRule(de_ctx, id, gid, parsed_type, parsed_track, parsed_count,
905
0
                parsed_seconds, parsed_timeout, parsed_new_action);
906
0
    }
907
0
    if (r < 0) {
908
0
        goto error;
909
0
    }
910
911
0
    pcre2_substring_free((PCRE2_UCHAR8 *)th_ip);
912
0
    return 0;
913
0
error:
914
0
    if (th_ip != NULL)
915
0
        pcre2_substring_free((PCRE2_UCHAR8 *)th_ip);
916
0
    return -1;
917
0
}
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)
932
0
{
933
0
    while (*line != '\0') {
934
        /* we have a comment */
935
0
        if (*line == '#')
936
0
            return 1;
937
938
        /* this line is neither a comment line, nor a blank line */
939
0
        if (!isspace((unsigned char)*line))
940
0
            return 0;
941
942
0
        line++;
943
0
    }
944
945
    /* we have a blank line */
946
0
    return 1;
947
0
}
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)
958
0
{
959
0
    int flag = 0;
960
0
    char *rline = line;
961
0
    size_t len = strlen(line);
962
963
0
    while (line < rline + len && *line != '\n') {
964
        /* we have a comment */
965
0
        if (*line == '\\')
966
0
            flag = (int)(line - rline);
967
0
        else
968
0
            if (!isspace((unsigned char)*line))
969
0
                flag = 0;
970
971
0
        line++;
972
0
    }
973
974
    /* we have a blank line */
975
0
    return flag;
976
0
}
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)
985
0
{
986
0
    char line[8192] = "";
987
0
    int rule_num = 0;
988
989
    /* position of "\", on multiline rules */
990
0
    int esc_pos = 0;
991
992
0
    if (fp == NULL)
993
0
        return -1;
994
995
0
    while (fgets(line + esc_pos, (int)sizeof(line) - esc_pos, fp) != NULL) {
996
0
        if (SCThresholdConfIsLineBlankOrComment(line)) {
997
0
            continue;
998
0
        }
999
1000
0
        esc_pos = SCThresholdConfLineIsMultiline(line);
1001
0
        if (esc_pos == 0) {
1002
0
            if (SCThresholdConfAddThresholdtype(line, de_ctx) < 0) {
1003
0
                if (SCRunmodeGet() == RUNMODE_CONF_TEST)
1004
0
                    return -1;
1005
0
            } else {
1006
0
                SCLogDebug("Adding threshold.config rule num %" PRIu32 "( %s )", rule_num, line);
1007
0
                rule_num++;
1008
0
            }
1009
0
        }
1010
0
    }
1011
1012
0
    if (de_ctx != NULL && strlen(de_ctx->config_prefix) > 0)
1013
0
        SCLogInfo("tenant id %d: Threshold config parsed: %d rule(s) found", de_ctx->tenant_id,
1014
0
                rule_num);
1015
0
    else
1016
0
        SCLogInfo("Threshold config parsed: %d rule(s) found", rule_num);
1017
0
    return 0;
1018
0
}
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
    PASS;
1426
}
1427
1428
/**
1429
 * \test Check if the threshold file is loaded and well parsed
1430
 *
1431
 *  \retval 1 on success
1432
 *  \retval 0 on failure
1433
 */
1434
static int SCThresholdConfTest06(void)
1435
{
1436
    DetectEngineCtx *de_ctx = DetectEngineCtxInit();
1437
    FAIL_IF_NULL(de_ctx);
1438
    de_ctx->flags |= DE_QUIET;
1439
1440
    Signature *sig = DetectEngineAppendSig(de_ctx,
1441
            "alert tcp any any -> any any (msg:\"Threshold limit\"; gid:1; sid:10;)");
1442
    FAIL_IF_NULL(sig);
1443
1444
    FAIL_IF_NOT_NULL(g_ut_threshold_fp);
1445
    g_ut_threshold_fp = SCThresholdConfGenerateValidDummyFD04();
1446
    FAIL_IF_NULL(g_ut_threshold_fp);
1447
    FAIL_IF(-1 == SCThresholdConfInitContext(de_ctx));
1448
1449
    SigMatch *m = DetectGetLastSMByListId(sig, DETECT_SM_LIST_THRESHOLD,
1450
            DETECT_THRESHOLD, -1);
1451
    FAIL_IF_NULL(m);
1452
1453
    DetectThresholdData *de = (DetectThresholdData *)m->ctx;
1454
    FAIL_IF_NULL(de);
1455
    FAIL_IF_NOT(de->type == TYPE_LIMIT && de->track == TRACK_SRC && de->count == 1 && de->seconds == 60);
1456
1457
    DetectEngineCtxFree(de_ctx);
1458
    PASS;
1459
}
1460
1461
/**
1462
 * \test Check if the rate_filter rules are loaded and well parsed
1463
 *
1464
 *  \retval 1 on success
1465
 *  \retval 0 on failure
1466
 */
1467
static int SCThresholdConfTest07(void)
1468
{
1469
    DetectEngineCtx *de_ctx = DetectEngineCtxInit();
1470
    FAIL_IF_NULL(de_ctx);
1471
    de_ctx->flags |= DE_QUIET;
1472
1473
    Signature *sig = DetectEngineAppendSig(de_ctx,
1474
            "alert tcp any any -> any any (msg:\"Threshold limit\"; gid:1; sid:10;)");
1475
    FAIL_IF_NULL(sig);
1476
1477
    FAIL_IF_NOT_NULL(g_ut_threshold_fp);
1478
    g_ut_threshold_fp = SCThresholdConfGenerateValidDummyFD05();
1479
    FAIL_IF_NULL(g_ut_threshold_fp);
1480
    FAIL_IF(-1 == SCThresholdConfInitContext(de_ctx));
1481
1482
    SigMatch *m = DetectGetLastSMByListId(sig, DETECT_SM_LIST_THRESHOLD,
1483
            DETECT_DETECTION_FILTER, -1);
1484
    FAIL_IF_NULL(m);
1485
1486
    DetectThresholdData *de = (DetectThresholdData *)m->ctx;
1487
    FAIL_IF_NULL(de);
1488
    FAIL_IF_NOT(de->type == TYPE_RATE && de->track == TRACK_SRC && de->count == 1 && de->seconds == 60);
1489
1490
    DetectEngineCtxFree(de_ctx);
1491
    PASS;
1492
}
1493
1494
/**
1495
 * \test Check if the rate_filter rules are loaded and well parsed
1496
 *       with multilines
1497
 *
1498
 *  \retval 1 on success
1499
 *  \retval 0 on failure
1500
 */
1501
static int SCThresholdConfTest08(void)
1502
{
1503
    DetectEngineCtx *de_ctx = DetectEngineCtxInit();
1504
    FAIL_IF_NULL(de_ctx);
1505
    de_ctx->flags |= DE_QUIET;
1506
1507
    Signature *sig = DetectEngineAppendSig(de_ctx,
1508
            "alert tcp any any -> any any (msg:\"Threshold limit\"; gid:1; sid:10;)");
1509
    FAIL_IF_NULL(sig);
1510
1511
    FAIL_IF_NOT_NULL(g_ut_threshold_fp);
1512
    g_ut_threshold_fp = SCThresholdConfGenerateValidDummyFD06();
1513
    FAIL_IF_NULL(g_ut_threshold_fp);
1514
    FAIL_IF(-1 == SCThresholdConfInitContext(de_ctx));
1515
1516
    SigMatch *m = DetectGetLastSMByListId(sig, DETECT_SM_LIST_THRESHOLD,
1517
            DETECT_DETECTION_FILTER, -1);
1518
    FAIL_IF_NULL(m);
1519
1520
    DetectThresholdData *de = (DetectThresholdData *)m->ctx;
1521
    FAIL_IF_NULL(de);
1522
    FAIL_IF_NOT(de->type == TYPE_RATE && de->track == TRACK_SRC && de->count == 1 && de->seconds == 60);
1523
1524
    DetectEngineCtxFree(de_ctx);
1525
    PASS;
1526
}
1527
1528
/**
1529
 * \test Check if the rate_filter rules work
1530
 *
1531
 *  \retval 1 on success
1532
 *  \retval 0 on failure
1533
 */
1534
static int SCThresholdConfTest09(void)
1535
{
1536
    ThreadVars th_v;
1537
    memset(&th_v, 0, sizeof(th_v));
1538
1539
    ThresholdInit();
1540
1541
    Packet *p = UTHBuildPacket((uint8_t*)"lalala", 6, IPPROTO_TCP);
1542
    FAIL_IF_NULL(p);
1543
1544
    DetectEngineThreadCtx *det_ctx = NULL;
1545
1546
    DetectEngineCtx *de_ctx = DetectEngineCtxInit();
1547
    FAIL_IF_NULL(de_ctx);
1548
    de_ctx->flags |= DE_QUIET;
1549
1550
    Signature *s = DetectEngineAppendSig(de_ctx,
1551
            "alert tcp any any -> any any (msg:\"ratefilter test\"; gid:1; sid:10;)");
1552
    FAIL_IF_NULL(s);
1553
1554
    FAIL_IF_NOT_NULL(g_ut_threshold_fp);
1555
    g_ut_threshold_fp = SCThresholdConfGenerateValidDummyFD07();
1556
    FAIL_IF_NULL(g_ut_threshold_fp);
1557
    FAIL_IF(-1 == SCThresholdConfInitContext(de_ctx));
1558
1559
    SigGroupBuild(de_ctx);
1560
    DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
1561
1562
    p->ts = TimeGet();
1563
    p->alerts.cnt = 0;
1564
    p->action = 0;
1565
    SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
1566
    FAIL_IF(p->alerts.cnt != 1 || PacketTestAction(p, ACTION_DROP));
1567
    p->alerts.cnt = 0;
1568
    p->action = 0;
1569
    SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
1570
    FAIL_IF(p->alerts.cnt != 1 || PacketTestAction(p, ACTION_DROP));
1571
    p->alerts.cnt = 0;
1572
    p->action = 0;
1573
    SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
1574
    FAIL_IF(p->alerts.cnt != 1 || PacketTestAction(p, ACTION_DROP));
1575
1576
    TimeSetIncrementTime(2);
1577
    p->ts = TimeGet();
1578
1579
    p->alerts.cnt = 0;
1580
    p->action = 0;
1581
    SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
1582
    FAIL_IF(p->alerts.cnt != 1 || !(PacketTestAction(p, ACTION_DROP)));
1583
1584
    TimeSetIncrementTime(3);
1585
    p->ts = TimeGet();
1586
1587
    p->alerts.cnt = 0;
1588
    p->action = 0;
1589
    SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
1590
    FAIL_IF(p->alerts.cnt != 1 || !(PacketTestAction(p, ACTION_DROP)));
1591
1592
    TimeSetIncrementTime(10);
1593
    p->ts = TimeGet();
1594
1595
    p->alerts.cnt = 0;
1596
    p->action = 0;
1597
    SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
1598
    FAIL_IF(p->alerts.cnt != 1 || PacketTestAction(p, ACTION_DROP));
1599
1600
    p->alerts.cnt = 0;
1601
    p->action = 0;
1602
    SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
1603
    FAIL_IF(p->alerts.cnt != 1 || PacketTestAction(p, ACTION_DROP));
1604
1605
    UTHFreePacket(p);
1606
    DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
1607
    DetectEngineCtxFree(de_ctx);
1608
    ThresholdDestroy();
1609
    PASS;
1610
}
1611
1612
/**
1613
 * \test Check if the rate_filter rules work with track by_rule
1614
 *
1615
 *  \retval 1 on success
1616
 *  \retval 0 on failure
1617
 */
1618
static int SCThresholdConfTest10(void)
1619
{
1620
    ThresholdInit();
1621
1622
    /* Create two different packets falling to the same rule, and
1623
    *  because count:3, we should drop on match #4.
1624
    */
1625
    Packet *p1 = UTHBuildPacketSrcDst((uint8_t*)"lalala", 6, IPPROTO_TCP,
1626
            "172.26.0.2", "172.26.0.11");
1627
    FAIL_IF_NULL(p1);
1628
    Packet *p2 = UTHBuildPacketSrcDst((uint8_t*)"lalala", 6, IPPROTO_TCP,
1629
            "172.26.0.1", "172.26.0.10");
1630
    FAIL_IF_NULL(p2);
1631
1632
    ThreadVars th_v;
1633
    memset(&th_v, 0, sizeof(th_v));
1634
1635
    DetectEngineCtx *de_ctx = DetectEngineCtxInit();
1636
    FAIL_IF_NULL(de_ctx);
1637
    de_ctx->flags |= DE_QUIET;
1638
    DetectEngineThreadCtx *det_ctx = NULL;
1639
1640
    Signature *s = DetectEngineAppendSig(de_ctx,
1641
            "alert tcp any any -> any any (msg:\"ratefilter test\"; gid:1; sid:10;)");
1642
    FAIL_IF_NULL(s);
1643
1644
    FAIL_IF_NOT_NULL(g_ut_threshold_fp);
1645
    g_ut_threshold_fp = SCThresholdConfGenerateValidDummyFD08();
1646
    FAIL_IF_NULL(g_ut_threshold_fp);
1647
    FAIL_IF(-1 == SCThresholdConfInitContext(de_ctx));
1648
1649
    SigGroupBuild(de_ctx);
1650
    DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
1651
    p1->ts = TimeGet();
1652
    p2->ts = p1->ts;
1653
1654
    /* All should be alerted, none dropped */
1655
    SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
1656
    FAIL_IF(PacketTestAction(p1, ACTION_DROP));
1657
    FAIL_IF(PacketAlertCheck(p1, 10) != 1);
1658
    p1->action = 0;
1659
    SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
1660
    FAIL_IF(PacketTestAction(p2, ACTION_DROP));
1661
    FAIL_IF(PacketAlertCheck(p2, 10) != 1);
1662
    p2->action = 0;
1663
    SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
1664
    FAIL_IF(PacketTestAction(p1, ACTION_DROP));
1665
    FAIL_IF(PacketAlertCheck(p1, 10) != 1);
1666
    p1->action = 0;
1667
1668
    /* Match #4 should be dropped*/
1669
    SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
1670
    FAIL_IF_NOT(PacketTestAction(p2, ACTION_DROP));
1671
    FAIL_IF(PacketAlertCheck(p2, 10) != 1);
1672
    p2->action = 0;
1673
1674
    TimeSetIncrementTime(2);
1675
    p1->ts = TimeGet();
1676
1677
    /* Still dropped because timeout not expired */
1678
    SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
1679
    FAIL_IF_NOT(PacketTestAction(p1, ACTION_DROP));
1680
    FAIL_IF(PacketAlertCheck(p1, 10) != 1);
1681
    p1->action = 0;
1682
1683
    TimeSetIncrementTime(10);
1684
    p1->ts = TimeGet();
1685
1686
    /* Not dropped because timeout expired */
1687
    SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
1688
    FAIL_IF(PacketTestAction(p1, ACTION_DROP));
1689
    FAIL_IF(PacketAlertCheck(p1, 10) != 1);
1690
#if 0
1691
    /* Ensure that a Threshold entry was installed at the sig */
1692
    FAIL_IF_NULL(de_ctx->ths_ctx.th_entry[s->iid]);
1693
#endif
1694
    UTHFreePacket(p1);
1695
    UTHFreePacket(p2);
1696
    DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
1697
    DetectEngineCtxFree(de_ctx);
1698
    ThresholdDestroy();
1699
    PASS;
1700
}
1701
1702
/**
1703
 * \test Check if the rate_filter rules work
1704
 *
1705
 *  \retval 1 on success
1706
 *  \retval 0 on failure
1707
 */
1708
static int SCThresholdConfTest11(void)
1709
{
1710
    ThresholdInit();
1711
1712
    Packet *p = UTHBuildPacket((uint8_t*)"lalala", 6, IPPROTO_TCP);
1713
    FAIL_IF_NULL(p);
1714
1715
    ThreadVars th_v;
1716
    memset(&th_v, 0, sizeof(th_v));
1717
1718
    DetectEngineCtx *de_ctx = DetectEngineCtxInit();
1719
    FAIL_IF_NULL(de_ctx);
1720
    de_ctx->flags |= DE_QUIET;
1721
    DetectEngineThreadCtx *det_ctx = NULL;
1722
1723
    Signature *s = DetectEngineAppendSig(de_ctx,
1724
            "alert tcp any any -> any any (msg:\"event_filter test limit\"; gid:1; sid:10;)");
1725
    FAIL_IF_NULL(s);
1726
    s = DetectEngineAppendSig(de_ctx,
1727
            "alert tcp any any -> any any (msg:\"event_filter test threshold\"; gid:1; sid:11;)");
1728
    FAIL_IF_NULL(s);
1729
    s = DetectEngineAppendSig(de_ctx,
1730
            "alert tcp any any -> any any (msg:\"event_filter test both\"; gid:1; sid:12;)");
1731
    FAIL_IF_NULL(s);
1732
1733
    FAIL_IF_NOT_NULL(g_ut_threshold_fp);
1734
    g_ut_threshold_fp = SCThresholdConfGenerateValidDummyFD09();
1735
    FAIL_IF_NULL(g_ut_threshold_fp);
1736
    FAIL_IF(-1 == SCThresholdConfInitContext(de_ctx));
1737
1738
    SigGroupBuild(de_ctx);
1739
    DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
1740
1741
    p->ts = TimeGet();
1742
1743
    int alerts10 = 0;
1744
    int alerts11 = 0;
1745
    int alerts12 = 0;
1746
1747
    SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
1748
    alerts10 += PacketAlertCheck(p, 10);
1749
    alerts11 += PacketAlertCheck(p, 11);
1750
    alerts12 += PacketAlertCheck(p, 12);
1751
    SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
1752
    alerts10 += PacketAlertCheck(p, 10);
1753
    alerts11 += PacketAlertCheck(p, 11);
1754
    alerts12 += PacketAlertCheck(p, 12);
1755
    SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
1756
    alerts10 += PacketAlertCheck(p, 10);
1757
    alerts11 += PacketAlertCheck(p, 11);
1758
    alerts12 += PacketAlertCheck(p, 12);
1759
    SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
1760
    alerts10 += PacketAlertCheck(p, 10);
1761
    alerts11 += PacketAlertCheck(p, 11);
1762
    alerts12 += PacketAlertCheck(p, 12);
1763
    SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
1764
    alerts10 += PacketAlertCheck(p, 10);
1765
    alerts11 += PacketAlertCheck(p, 11);
1766
    alerts12 += PacketAlertCheck(p, 12);
1767
1768
    TimeSetIncrementTime(100);
1769
    p->ts = TimeGet();
1770
1771
    SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
1772
    alerts10 += PacketAlertCheck(p, 10);
1773
    alerts11 += PacketAlertCheck(p, 11);
1774
1775
    TimeSetIncrementTime(10);
1776
    p->ts = TimeGet();
1777
1778
    SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
1779
    alerts10 += PacketAlertCheck(p, 10);
1780
    alerts11 += PacketAlertCheck(p, 11);
1781
    alerts12 += PacketAlertCheck(p, 12);
1782
    SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
1783
    alerts10 += PacketAlertCheck(p, 10);
1784
    alerts11 += PacketAlertCheck(p, 11);
1785
    alerts12 += PacketAlertCheck(p, 12);
1786
    SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
1787
    alerts10 += PacketAlertCheck(p, 10);
1788
    alerts11 += PacketAlertCheck(p, 11);
1789
    alerts12 += PacketAlertCheck(p, 12);
1790
    SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
1791
    alerts10 += PacketAlertCheck(p, 10);
1792
    alerts11 += PacketAlertCheck(p, 11);
1793
    alerts12 += PacketAlertCheck(p, 12);
1794
1795
    FAIL_IF_NOT(alerts10 == 4);
1796
    /* One on the first interval, another on the second */
1797
    FAIL_IF_NOT(alerts11 == 2);
1798
    FAIL_IF_NOT(alerts12 == 2);
1799
1800
    UTHFreePacket(p);
1801
    DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
1802
    DetectEngineCtxFree(de_ctx);
1803
    ThresholdDestroy();
1804
    PASS;
1805
}
1806
1807
/**
1808
 * \test Check if the rate_filter rules work
1809
 *
1810
 *  \retval 1 on success
1811
 *  \retval 0 on failure
1812
 */
1813
static int SCThresholdConfTest12(void)
1814
{
1815
    ThresholdInit();
1816
1817
    Packet *p = UTHBuildPacket((uint8_t*)"lalala", 6, IPPROTO_TCP);
1818
    FAIL_IF_NULL(p);
1819
1820
    ThreadVars th_v;
1821
    memset(&th_v, 0, sizeof(th_v));
1822
1823
    DetectEngineCtx *de_ctx = DetectEngineCtxInit();
1824
    FAIL_IF_NULL(de_ctx);
1825
    de_ctx->flags |= DE_QUIET;
1826
    DetectEngineThreadCtx *det_ctx = NULL;
1827
1828
    Signature *s = DetectEngineAppendSig(de_ctx,
1829
            "alert tcp any any -> any any (msg:\"event_filter test limit\"; gid:1; sid:10;)");
1830
    FAIL_IF_NULL(s);
1831
    s = DetectEngineAppendSig(de_ctx,
1832
            "alert tcp any any -> any any (msg:\"event_filter test threshold\"; gid:1; sid:11;)");
1833
    FAIL_IF_NULL(s);
1834
    s = DetectEngineAppendSig(de_ctx,
1835
            "alert tcp any any -> any any (msg:\"event_filter test both\"; gid:1; sid:12;)");
1836
    FAIL_IF_NULL(s);
1837
1838
    FAIL_IF_NOT_NULL(g_ut_threshold_fp);
1839
    g_ut_threshold_fp = SCThresholdConfGenerateValidDummyFD10();
1840
    FAIL_IF_NULL(g_ut_threshold_fp);
1841
    FAIL_IF(-1 == SCThresholdConfInitContext(de_ctx));
1842
1843
    SigGroupBuild(de_ctx);
1844
    DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
1845
1846
    p->ts = TimeGet();
1847
1848
    int alerts10 = 0;
1849
    int alerts11 = 0;
1850
    int alerts12 = 0;
1851
1852
    SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
1853
    alerts10 += PacketAlertCheck(p, 10);
1854
    alerts11 += PacketAlertCheck(p, 11);
1855
    alerts12 += PacketAlertCheck(p, 12);
1856
    SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
1857
    alerts10 += PacketAlertCheck(p, 10);
1858
    alerts11 += PacketAlertCheck(p, 11);
1859
    alerts12 += PacketAlertCheck(p, 12);
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
1873
    TimeSetIncrementTime(100);
1874
    p->ts = TimeGet();
1875
1876
    SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
1877
    alerts10 += PacketAlertCheck(p, 10);
1878
    alerts11 += PacketAlertCheck(p, 11);
1879
1880
    TimeSetIncrementTime(10);
1881
    p->ts = TimeGet();
1882
1883
    SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
1884
    alerts10 += PacketAlertCheck(p, 10);
1885
    alerts11 += PacketAlertCheck(p, 11);
1886
    alerts12 += PacketAlertCheck(p, 12);
1887
    SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
1888
    alerts10 += PacketAlertCheck(p, 10);
1889
    alerts11 += PacketAlertCheck(p, 11);
1890
    alerts12 += PacketAlertCheck(p, 12);
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
1900
    FAIL_IF_NOT(alerts10 == 10);
1901
    /* One on the first interval, another on the second */
1902
    FAIL_IF_NOT(alerts11 == 1);
1903
    FAIL_IF_NOT(alerts12 == 1);
1904
1905
    UTHFreePacket(p);
1906
    DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
1907
    DetectEngineCtxFree(de_ctx);
1908
    ThresholdDestroy();
1909
    PASS;
1910
}
1911
1912
/**
1913
 * \test Check if the threshold file is loaded and well parsed
1914
 *
1915
 *  \retval 1 on success
1916
 *  \retval 0 on failure
1917
 */
1918
static int SCThresholdConfTest13(void)
1919
{
1920
    DetectEngineCtx *de_ctx = DetectEngineCtxInit();
1921
    FAIL_IF_NULL(de_ctx);
1922
    de_ctx->flags |= DE_QUIET;
1923
1924
    Signature *sig = DetectEngineAppendSig(de_ctx,
1925
            "alert tcp any any -> any any (msg:\"Threshold limit\"; gid:1; sid:1000;)");
1926
    FAIL_IF_NULL(sig);
1927
1928
    FAIL_IF_NOT_NULL(g_ut_threshold_fp);
1929
    g_ut_threshold_fp = SCThresholdConfGenerateValidDummyFD11();
1930
    FAIL_IF_NULL(g_ut_threshold_fp);
1931
    FAIL_IF(-1 == SCThresholdConfInitContext(de_ctx));
1932
1933
    SigMatch *m = DetectGetLastSMByListId(sig,
1934
            DETECT_SM_LIST_SUPPRESS, DETECT_THRESHOLD, -1);
1935
    FAIL_IF_NULL(m);
1936
1937
    DetectThresholdData *de = (DetectThresholdData *)m->ctx;
1938
    FAIL_IF_NULL(de);
1939
    FAIL_IF_NOT(de->type == TYPE_SUPPRESS && de->track == TRACK_SRC);
1940
1941
    DetectEngineCtxFree(de_ctx);
1942
    PASS;
1943
}
1944
1945
/**
1946
 * \test Check if the suppress rules work
1947
 *
1948
 *  \retval 1 on success
1949
 *  \retval 0 on failure
1950
 */
1951
static int SCThresholdConfTest14(void)
1952
{
1953
    ThresholdInit();
1954
1955
    Packet *p1 = UTHBuildPacketReal((uint8_t*)"lalala", 6, IPPROTO_TCP, "192.168.0.10",
1956
                                    "192.168.0.100", 1234, 24);
1957
    FAIL_IF_NULL(p1);
1958
    Packet *p2 = UTHBuildPacketReal((uint8_t*)"lalala", 6, IPPROTO_TCP, "192.168.1.1",
1959
                                    "192.168.0.100", 1234, 24);
1960
    FAIL_IF_NULL(p2);
1961
1962
    DetectEngineThreadCtx *det_ctx = NULL;
1963
    DetectEngineCtx *de_ctx = DetectEngineCtxInit();
1964
    FAIL_IF_NULL(de_ctx);
1965
    de_ctx->flags |= DE_QUIET;
1966
1967
    Signature *sig = DetectEngineAppendSig(de_ctx,
1968
        "alert tcp any any -> any any (msg:\"suppress test\"; gid:1; sid:10000;)");
1969
    FAIL_IF_NULL(sig);
1970
    sig = DetectEngineAppendSig(de_ctx,
1971
            "alert tcp any any -> any any (msg:\"suppress test 2\"; gid:1; sid:10;)");
1972
    FAIL_IF_NULL(sig);
1973
    sig = DetectEngineAppendSig(de_ctx,
1974
            "alert tcp any any -> any any (msg:\"suppress test 3\"; gid:1; sid:1000;)");
1975
    FAIL_IF_NULL(sig);
1976
1977
    ThreadVars th_v;
1978
    memset(&th_v, 0, sizeof(th_v));
1979
1980
    FAIL_IF_NOT_NULL(g_ut_threshold_fp);
1981
    g_ut_threshold_fp = SCThresholdConfGenerateValidDummyFD11();
1982
    FAIL_IF_NULL(g_ut_threshold_fp);
1983
    FAIL_IF(-1 == SCThresholdConfInitContext(de_ctx));
1984
1985
    SigGroupBuild(de_ctx);
1986
    DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
1987
1988
    SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
1989
    SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
1990
1991
    FAIL_IF_NOT(PacketAlertCheck(p1, 10000) == 0);
1992
    FAIL_IF_NOT(PacketAlertCheck(p1, 10) == 1);
1993
    FAIL_IF_NOT(PacketAlertCheck(p1, 1000) == 1);
1994
    FAIL_IF_NOT(PacketAlertCheck(p2, 1000) == 0);
1995
1996
    UTHFreePacket(p1);
1997
    UTHFreePacket(p2);
1998
1999
    DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
2000
    DetectEngineCtxFree(de_ctx);
2001
2002
    ThresholdDestroy();
2003
    PASS;
2004
}
2005
2006
/**
2007
 * \test Check if the suppress rules work
2008
 *
2009
 *  \retval 1 on success
2010
 *  \retval 0 on failure
2011
 */
2012
static int SCThresholdConfTest15(void)
2013
{
2014
    ThresholdInit();
2015
2016
    Packet *p = UTHBuildPacketReal((uint8_t*)"lalala", 6, IPPROTO_TCP, "192.168.0.10",
2017
                                    "192.168.0.100", 1234, 24);
2018
    FAIL_IF_NULL(p);
2019
2020
    ThreadVars th_v;
2021
    memset(&th_v, 0, sizeof(th_v));
2022
2023
    DetectEngineThreadCtx *det_ctx = NULL;
2024
    DetectEngineCtx *de_ctx = DetectEngineCtxInit();
2025
    FAIL_IF_NULL(de_ctx);
2026
    de_ctx->flags |= DE_QUIET;
2027
2028
    Signature *sig = DetectEngineAppendSig(de_ctx,
2029
            "drop tcp any any -> any any (msg:\"suppress test\"; content:\"lalala\"; gid:1; sid:10000;)");
2030
    FAIL_IF_NULL(sig);
2031
2032
    FAIL_IF_NOT_NULL(g_ut_threshold_fp);
2033
    g_ut_threshold_fp = SCThresholdConfGenerateValidDummyFD11();
2034
    FAIL_IF_NULL(g_ut_threshold_fp);
2035
    FAIL_IF(-1 == SCThresholdConfInitContext(de_ctx));
2036
2037
    SigGroupBuild(de_ctx);
2038
    DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
2039
2040
    SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
2041
2042
    /* 10000 shouldn't match */
2043
    FAIL_IF(PacketAlertCheck(p, 10000) != 0);
2044
    /* however, it should have set the drop flag */
2045
    FAIL_IF(!(PacketTestAction(p, ACTION_DROP)));
2046
2047
    UTHFreePacket(p);
2048
    DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
2049
    DetectEngineCtxFree(de_ctx);
2050
    ThresholdDestroy();
2051
    PASS;
2052
}
2053
2054
/**
2055
 * \test Check if the suppress rules work
2056
 *
2057
 *  \retval 1 on success
2058
 *  \retval 0 on failure
2059
 */
2060
static int SCThresholdConfTest16(void)
2061
{
2062
    ThresholdInit();
2063
2064
    Packet *p = UTHBuildPacketReal((uint8_t*)"lalala", 6, IPPROTO_TCP, "192.168.1.1",
2065
                                    "192.168.0.100", 1234, 24);
2066
    FAIL_IF_NULL(p);
2067
2068
    ThreadVars th_v;
2069
    memset(&th_v, 0, sizeof(th_v));
2070
2071
    DetectEngineThreadCtx *det_ctx = NULL;
2072
    DetectEngineCtx *de_ctx = DetectEngineCtxInit();
2073
    FAIL_IF_NULL(de_ctx);
2074
    de_ctx->flags |= DE_QUIET;
2075
2076
    Signature *sig = DetectEngineAppendSig(de_ctx,
2077
            "drop tcp any any -> any any (msg:\"suppress test\"; gid:1; sid:1000;)");
2078
    FAIL_IF_NULL(sig);
2079
2080
    FAIL_IF_NOT_NULL(g_ut_threshold_fp);
2081
    g_ut_threshold_fp = SCThresholdConfGenerateValidDummyFD11();
2082
    FAIL_IF_NULL(g_ut_threshold_fp);
2083
    FAIL_IF(-1 == SCThresholdConfInitContext(de_ctx));
2084
2085
    SigGroupBuild(de_ctx);
2086
    DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
2087
2088
    SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
2089
2090
    FAIL_IF(PacketAlertCheck(p, 1000) != 0);
2091
    /* however, it should have set the drop flag */
2092
    FAIL_IF(!(PacketTestAction(p, ACTION_DROP)));
2093
2094
    UTHFreePacket(p);
2095
    DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
2096
    DetectEngineCtxFree(de_ctx);
2097
    ThresholdDestroy();
2098
    PASS;
2099
}
2100
2101
/**
2102
 * \test Check if the suppress rules work - ip only rule
2103
 *
2104
 *  \retval 1 on success
2105
 *  \retval 0 on failure
2106
 */
2107
static int SCThresholdConfTest17(void)
2108
{
2109
    ThresholdInit();
2110
2111
    Packet *p = UTHBuildPacketReal((uint8_t*)"lalala", 6, IPPROTO_TCP, "192.168.0.10",
2112
                                    "192.168.0.100", 1234, 24);
2113
    FAIL_IF_NULL(p);
2114
2115
    ThreadVars th_v;
2116
    memset(&th_v, 0, sizeof(th_v));
2117
2118
    DetectEngineThreadCtx *det_ctx = NULL;
2119
    DetectEngineCtx *de_ctx = DetectEngineCtxInit();
2120
    FAIL_IF_NULL(de_ctx);
2121
    de_ctx->flags |= DE_QUIET;
2122
2123
    Signature *sig = DetectEngineAppendSig(de_ctx,
2124
            "drop tcp 192.168.0.10 any -> 192.168.0.100 any (msg:\"suppress test\"; gid:1; sid:10000;)");
2125
    FAIL_IF_NULL(sig);
2126
2127
    FAIL_IF_NOT_NULL(g_ut_threshold_fp);
2128
    g_ut_threshold_fp = SCThresholdConfGenerateValidDummyFD11();
2129
    FAIL_IF_NULL(g_ut_threshold_fp);
2130
    FAIL_IF(-1 == SCThresholdConfInitContext(de_ctx));
2131
2132
    SigGroupBuild(de_ctx);
2133
    DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
2134
2135
    SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
2136
2137
    /* 10000 shouldn't match */
2138
    FAIL_IF(PacketAlertCheck(p, 10000) != 0);
2139
    /* however, it should have set the drop flag */
2140
    FAIL_IF(!(PacketTestAction(p, ACTION_DROP)));
2141
2142
    UTHFreePacket(p);
2143
    DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
2144
    DetectEngineCtxFree(de_ctx);
2145
    ThresholdDestroy();
2146
    PASS;
2147
}
2148
2149
/**
2150
 * \brief Creates a dummy threshold file, with all valid options, for testing purposes.
2151
 *
2152
 * \retval fd Pointer to file descriptor.
2153
 */
2154
static FILE *SCThresholdConfGenerateInvalidDummyFD12(void)
2155
{
2156
    FILE *fd = NULL;
2157
    const char *buffer =
2158
        "suppress gen_id 1, sig_id 2200029, track by_dst, ip fe80::/16\n"
2159
        "suppress gen_id 1, sig_id 2200029, track by_stc, ip fe80::/16\n";
2160
2161
    fd = SCFmemopen((void *)buffer, strlen(buffer), "r");
2162
    if (fd == NULL)
2163
        SCLogDebug("Error with SCFmemopen() called by Threshold Config test code");
2164
2165
    return fd;
2166
}
2167
2168
/**
2169
 * \test Check if the suppress rule parsing handles errors correctly
2170
 *
2171
 *  \retval 1 on success
2172
 *  \retval 0 on failure
2173
 */
2174
static int SCThresholdConfTest18(void)
2175
{
2176
    ThresholdInit();
2177
    DetectEngineCtx *de_ctx = DetectEngineCtxInit();
2178
    FAIL_IF_NULL(de_ctx);
2179
    de_ctx->flags |= DE_QUIET;
2180
2181
    Signature *s = DetectEngineAppendSig(de_ctx,
2182
            "alert tcp 192.168.0.10 any -> 192.168.0.100 any (msg:\"suppress test\"; gid:1; sid:2200029;)");
2183
    FAIL_IF_NULL(s);
2184
    FAIL_IF_NOT_NULL(g_ut_threshold_fp);
2185
    g_ut_threshold_fp = SCThresholdConfGenerateInvalidDummyFD12();
2186
    FAIL_IF_NULL(g_ut_threshold_fp);
2187
    FAIL_IF(-1 == SCThresholdConfInitContext(de_ctx));
2188
    SigGroupBuild(de_ctx);
2189
2190
    FAIL_IF_NULL(s->sm_arrays[DETECT_SM_LIST_SUPPRESS]);
2191
    SigMatchData *smd = s->sm_arrays[DETECT_SM_LIST_SUPPRESS];
2192
    DetectThresholdData *de = (DetectThresholdData *)smd->ctx;
2193
    FAIL_IF_NULL(de);
2194
    FAIL_IF_NOT(de->type == TYPE_SUPPRESS && de->track == TRACK_DST);
2195
2196
    DetectEngineCtxFree(de_ctx);
2197
    ThresholdDestroy();
2198
    PASS;
2199
}
2200
2201
/**
2202
 * \brief Creates a dummy threshold file, with all valid options, for testing purposes.
2203
 *
2204
 * \retval fd Pointer to file descriptor.
2205
 */
2206
static FILE *SCThresholdConfGenerateInvalidDummyFD13(void)
2207
{
2208
    FILE *fd = NULL;
2209
    const char *buffer =
2210
        "suppress gen_id 1, sig_id 2200029, track by_stc, ip fe80::/16\n"
2211
        "suppress gen_id 1, sig_id 2200029, track by_dst, ip fe80::/16\n";
2212
2213
    fd = SCFmemopen((void *)buffer, strlen(buffer), "r");
2214
    if (fd == NULL)
2215
        SCLogDebug("Error with SCFmemopen() called by Threshold Config test code");
2216
2217
    return fd;
2218
}
2219
2220
/**
2221
 * \test Check if the suppress rule parsing handles errors correctly
2222
 *
2223
 *  \retval 1 on success
2224
 *  \retval 0 on failure
2225
 */
2226
static int SCThresholdConfTest19(void)
2227
{
2228
    ThresholdInit();
2229
    DetectEngineCtx *de_ctx = DetectEngineCtxInit();
2230
    FAIL_IF_NULL(de_ctx);
2231
    de_ctx->flags |= DE_QUIET;
2232
    Signature *s = DetectEngineAppendSig(de_ctx,
2233
            "alert tcp 192.168.0.10 any -> 192.168.0.100 any (msg:\"suppress test\"; gid:1; sid:2200029;)");
2234
    FAIL_IF_NULL(s);
2235
    FAIL_IF_NOT_NULL(g_ut_threshold_fp);
2236
    g_ut_threshold_fp = SCThresholdConfGenerateInvalidDummyFD13();
2237
    FAIL_IF_NULL(g_ut_threshold_fp);
2238
    FAIL_IF(-1 == SCThresholdConfInitContext(de_ctx));
2239
    SigGroupBuild(de_ctx);
2240
    FAIL_IF_NULL(s->sm_arrays[DETECT_SM_LIST_SUPPRESS]);
2241
    SigMatchData *smd = s->sm_arrays[DETECT_SM_LIST_SUPPRESS];
2242
    DetectThresholdData *de = (DetectThresholdData *)smd->ctx;
2243
    FAIL_IF_NULL(de);
2244
    FAIL_IF_NOT(de->type == TYPE_SUPPRESS && de->track == TRACK_DST);
2245
    DetectEngineCtxFree(de_ctx);
2246
    ThresholdDestroy();
2247
    PASS;
2248
}
2249
2250
/**
2251
 * \brief Creates a dummy threshold file, with all valid options, for testing purposes.
2252
 *
2253
 * \retval fd Pointer to file descriptor.
2254
 */
2255
static FILE *SCThresholdConfGenerateValidDummyFD20(void)
2256
{
2257
    FILE *fd = NULL;
2258
    const char *buffer =
2259
        "suppress gen_id 1, sig_id 1000, track by_src, ip 2.2.3.4\n"
2260
        "suppress gen_id 1, sig_id 1000, track by_src, ip 1.2.3.4\n"
2261
        "suppress gen_id 1, sig_id 1000, track by_src, ip 192.168.1.1\n";
2262
2263
    fd = SCFmemopen((void *)buffer, strlen(buffer), "r");
2264
    if (fd == NULL)
2265
        SCLogDebug("Error with SCFmemopen() called by Threshold Config test code");
2266
2267
    return fd;
2268
}
2269
2270
/**
2271
 * \test Check if the threshold file is loaded and well parsed
2272
 *
2273
 *  \retval 1 on success
2274
 *  \retval 0 on failure
2275
 */
2276
static int SCThresholdConfTest20(void)
2277
{
2278
    ThresholdInit();
2279
    DetectEngineCtx *de_ctx = DetectEngineCtxInit();
2280
    FAIL_IF_NULL(de_ctx);
2281
    de_ctx->flags |= DE_QUIET;
2282
    Signature *s = DetectEngineAppendSig(de_ctx,
2283
            "alert tcp any any -> any any (msg:\"Threshold limit\"; content:\"abc\"; sid:1000;)");
2284
    FAIL_IF_NULL(s);
2285
    FAIL_IF_NOT_NULL(g_ut_threshold_fp);
2286
    g_ut_threshold_fp = SCThresholdConfGenerateValidDummyFD20();
2287
    FAIL_IF_NULL(g_ut_threshold_fp);
2288
    FAIL_IF(-1 == SCThresholdConfInitContext(de_ctx));
2289
    SigGroupBuild(de_ctx);
2290
    FAIL_IF_NULL(s->sm_arrays[DETECT_SM_LIST_SUPPRESS]);
2291
2292
    SigMatchData *smd = s->sm_arrays[DETECT_SM_LIST_SUPPRESS];
2293
    DetectThresholdData *de = (DetectThresholdData *)smd->ctx;
2294
    FAIL_IF_NULL(de);
2295
    FAIL_IF_NOT(de->type == TYPE_SUPPRESS && de->track == TRACK_SRC);
2296
    FAIL_IF(smd->is_last);
2297
2298
    smd++;
2299
    de = (DetectThresholdData *)smd->ctx;
2300
    FAIL_IF_NULL(de);
2301
    FAIL_IF_NOT(de->type == TYPE_SUPPRESS && de->track == TRACK_SRC);
2302
    FAIL_IF(smd->is_last);
2303
2304
    smd++;
2305
    de = (DetectThresholdData *)smd->ctx;
2306
    FAIL_IF_NULL(de);
2307
    FAIL_IF_NOT(de->type == TYPE_SUPPRESS && de->track == TRACK_SRC);
2308
    FAIL_IF_NOT(smd->is_last);
2309
2310
    DetectEngineCtxFree(de_ctx);
2311
    ThresholdDestroy();
2312
    PASS;
2313
}
2314
2315
/**
2316
 * \test Check if the threshold file is loaded and well parsed, and applied
2317
 *       correctly to a rule with thresholding
2318
 *
2319
 *  \retval 1 on success
2320
 *  \retval 0 on failure
2321
 */
2322
static int SCThresholdConfTest21(void)
2323
{
2324
    ThresholdInit();
2325
    DetectEngineCtx *de_ctx = DetectEngineCtxInit();
2326
    FAIL_IF_NULL(de_ctx);
2327
    de_ctx->flags |= DE_QUIET;
2328
    Signature *s = DetectEngineAppendSig(de_ctx,
2329
            "alert tcp any any -> any any (msg:\"Threshold limit\"; content:\"abc\"; threshold: type limit, track by_dst, count 5, seconds 60; sid:1000;)");
2330
    FAIL_IF_NULL(s);
2331
    g_ut_threshold_fp = SCThresholdConfGenerateValidDummyFD20();
2332
    FAIL_IF_NULL(g_ut_threshold_fp);
2333
    FAIL_IF(-1 == SCThresholdConfInitContext(de_ctx));
2334
    SigGroupBuild(de_ctx);
2335
    FAIL_IF_NULL(s->sm_arrays[DETECT_SM_LIST_SUPPRESS]);
2336
2337
    SigMatchData *smd = s->sm_arrays[DETECT_SM_LIST_SUPPRESS];
2338
    DetectThresholdData *de = (DetectThresholdData *)smd->ctx;
2339
    FAIL_IF_NULL(de);
2340
    FAIL_IF_NOT(de->type == TYPE_SUPPRESS && de->track == TRACK_SRC);
2341
    FAIL_IF(smd->is_last);
2342
2343
    smd++;
2344
    de = (DetectThresholdData *)smd->ctx;
2345
    FAIL_IF_NULL(de);
2346
    FAIL_IF_NOT(de->type == TYPE_SUPPRESS && de->track == TRACK_SRC);
2347
    FAIL_IF(smd->is_last);
2348
2349
    smd++;
2350
    de = (DetectThresholdData *)smd->ctx;
2351
    FAIL_IF_NULL(de);
2352
    FAIL_IF_NOT(de->type == TYPE_SUPPRESS && de->track == TRACK_SRC);
2353
    FAIL_IF_NOT(smd->is_last);
2354
2355
    DetectEngineCtxFree(de_ctx);
2356
    ThresholdDestroy();
2357
    PASS;
2358
}
2359
2360
/**
2361
* \brief Creates a dummy rate_filter file, for testing rate filtering by_both source and destination
2362
*
2363
* \retval fd Pointer to file descriptor.
2364
*/
2365
static FILE *SCThresholdConfGenerateValidDummyFD22(void)
2366
{
2367
    FILE *fd = NULL;
2368
    const char *buffer =
2369
        "rate_filter gen_id 1, sig_id 10, track by_both, count 2, seconds 5, new_action drop, timeout 6\n";
2370
2371
    fd = SCFmemopen((void *)buffer, strlen(buffer), "r");
2372
    if (fd == NULL)
2373
        SCLogDebug("Error with SCFmemopen() called by Threshold Config test code");
2374
2375
    return fd;
2376
}
2377
2378
/**
2379
 * \test Check if the rate_filter rules work with track by_both
2380
 *
2381
 *  \retval 1 on success
2382
 *  \retval 0 on failure
2383
 */
2384
static int SCThresholdConfTest22(void)
2385
{
2386
    ThreadVars th_v;
2387
    memset(&th_v, 0, sizeof(th_v));
2388
2389
    ThresholdInit();
2390
2391
    /* This packet will cause rate_filter */
2392
    Packet *p1 = UTHBuildPacketSrcDst((uint8_t*)"lalala", 6, IPPROTO_TCP, "172.26.0.1", "172.26.0.10");
2393
    FAIL_IF_NULL(p1);
2394
2395
    /* Should not be filtered for different destination */
2396
    Packet *p2 = UTHBuildPacketSrcDst((uint8_t*)"lalala", 6, IPPROTO_TCP, "172.26.0.1", "172.26.0.2");
2397
    FAIL_IF_NULL(p2);
2398
2399
    /* Should not be filtered when both src and dst the same */
2400
    Packet *p3 = UTHBuildPacketSrcDst((uint8_t*)"lalala", 6, IPPROTO_TCP, "172.26.0.1", "172.26.0.1");
2401
    FAIL_IF_NULL(p3);
2402
2403
    DetectEngineThreadCtx *det_ctx = NULL;
2404
2405
    DetectEngineCtx *de_ctx = DetectEngineCtxInit();
2406
    FAIL_IF_NULL(de_ctx);
2407
    de_ctx->flags |= DE_QUIET;
2408
2409
    Signature *sig = DetectEngineAppendSig(de_ctx,
2410
            "alert tcp any any -> any any (msg:\"ratefilter by_both test\"; gid:1; sid:10;)");
2411
    FAIL_IF_NULL(sig);
2412
2413
    FAIL_IF_NOT_NULL(g_ut_threshold_fp);
2414
    g_ut_threshold_fp = SCThresholdConfGenerateValidDummyFD22();
2415
    FAIL_IF_NULL(g_ut_threshold_fp);
2416
    FAIL_IF(-1 == SCThresholdConfInitContext(de_ctx));
2417
2418
    SigGroupBuild(de_ctx);
2419
    DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
2420
2421
    p1->ts = TimeGet();
2422
    p2->ts = p3->ts = p1->ts;
2423
2424
    /* All should be alerted, none dropped */
2425
    SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
2426
    FAIL_IF(PacketTestAction(p1, ACTION_DROP));
2427
    FAIL_IF(PacketAlertCheck(p1, 10) != 1);
2428
2429
    SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
2430
    FAIL_IF(PacketTestAction(p2, ACTION_DROP));
2431
    FAIL_IF(PacketAlertCheck(p2, 10) != 1);
2432
2433
    SigMatchSignatures(&th_v, de_ctx, det_ctx, p3);
2434
    FAIL_IF(PacketTestAction(p3, ACTION_DROP));
2435
    FAIL_IF(PacketAlertCheck(p3, 10) != 1);
2436
2437
    p1->action = p2->action = p3->action = 0;
2438
2439
    TimeSetIncrementTime(2);
2440
    p1->ts = TimeGet();
2441
    p2->ts = p3->ts = p1->ts;
2442
2443
    /* p1 still shouldn't be dropped after 2nd alert */
2444
    SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
2445
    FAIL_IF(PacketTestAction(p1, ACTION_DROP));
2446
    FAIL_IF(PacketAlertCheck(p1, 10) != 1);
2447
2448
    p1->action = 0;
2449
2450
    TimeSetIncrementTime(2);
2451
    p1->ts = TimeGet();
2452
    p2->ts = p3->ts = p1->ts;
2453
2454
    /* All should be alerted, only p1 must be dropped  due to rate_filter*/
2455
    SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
2456
    FAIL_IF_NOT(PacketTestAction(p1, ACTION_DROP));
2457
    FAIL_IF(PacketAlertCheck(p1, 10) != 1);
2458
2459
    SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
2460
    FAIL_IF(PacketTestAction(p2, ACTION_DROP));
2461
    FAIL_IF(PacketAlertCheck(p2, 10) != 1);
2462
2463
    SigMatchSignatures(&th_v, de_ctx, det_ctx, p3);
2464
    FAIL_IF(PacketTestAction(p3, ACTION_DROP));
2465
    FAIL_IF(PacketAlertCheck(p3, 10) != 1);
2466
2467
    p1->action = p2->action = p3->action = 0;
2468
2469
    TimeSetIncrementTime(7);
2470
    p1->ts = TimeGet();
2471
    p2->ts = p3->ts = p1->ts;
2472
2473
    /* All should be alerted, none dropped (because timeout expired) */
2474
    SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
2475
    FAIL_IF(PacketTestAction(p1, ACTION_DROP));
2476
    FAIL_IF(PacketAlertCheck(p1, 10) != 1);
2477
2478
    SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
2479
    FAIL_IF(PacketTestAction(p2, ACTION_DROP));
2480
    FAIL_IF(PacketAlertCheck(p2, 10) != 1);
2481
2482
    SigMatchSignatures(&th_v, de_ctx, det_ctx, p3);
2483
    FAIL_IF(PacketTestAction(p3, ACTION_DROP));
2484
    FAIL_IF(PacketAlertCheck(p3, 10) != 1);
2485
2486
    UTHFreePacket(p3);
2487
    UTHFreePacket(p2);
2488
    UTHFreePacket(p1);
2489
2490
    DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
2491
    DetectEngineCtxFree(de_ctx);
2492
    ThresholdDestroy();
2493
    PASS;
2494
}
2495
2496
/**
2497
* \brief Creates a dummy rate_filter file, for testing rate filtering by_both source and destination
2498
*
2499
* \retval fd Pointer to file descriptor.
2500
*/
2501
static FILE *SCThresholdConfGenerateValidDummyFD23(void)
2502
{
2503
    FILE *fd = NULL;
2504
    const char *buffer =
2505
        "rate_filter gen_id 1, sig_id 10, track by_both, count 1, seconds 5, new_action drop, timeout 6\n";
2506
2507
    fd = SCFmemopen((void *)buffer, strlen(buffer), "r");
2508
    if (fd == NULL)
2509
        SCLogDebug("Error with SCFmemopen() called by Threshold Config test code");
2510
2511
    return fd;
2512
}
2513
2514
/**
2515
 * \test Check if the rate_filter by_both work when similar packets
2516
 *       going in opposite direction
2517
 *
2518
 *  \retval 1 on success
2519
 *  \retval 0 on failure
2520
 */
2521
static int SCThresholdConfTest23(void)
2522
{
2523
    ThreadVars th_v;
2524
    memset(&th_v, 0, sizeof(th_v));
2525
2526
    ThresholdInit();
2527
2528
    /* Create two packets between same addresses in opposite direction */
2529
    Packet *p1 = UTHBuildPacketSrcDst((uint8_t*)"lalala", 6, IPPROTO_TCP, "172.26.0.1", "172.26.0.10");
2530
    FAIL_IF_NULL(p1);
2531
2532
    Packet *p2 = UTHBuildPacketSrcDst((uint8_t*)"lalala", 6, IPPROTO_TCP, "172.26.0.10", "172.26.0.1");
2533
    FAIL_IF_NULL(p2);
2534
2535
    DetectEngineThreadCtx *det_ctx = NULL;
2536
2537
    DetectEngineCtx *de_ctx = DetectEngineCtxInit();
2538
    FAIL_IF_NULL(de_ctx);
2539
    de_ctx->flags |= DE_QUIET;
2540
2541
    Signature *sig = DetectEngineAppendSig(de_ctx,
2542
        "alert tcp any any -> any any (msg:\"ratefilter by_both test\"; gid:1; sid:10;)");
2543
    FAIL_IF_NULL(sig);
2544
2545
    FAIL_IF_NOT_NULL(g_ut_threshold_fp);
2546
    g_ut_threshold_fp = SCThresholdConfGenerateValidDummyFD23();
2547
    FAIL_IF_NULL(g_ut_threshold_fp);
2548
    FAIL_IF(-1 == SCThresholdConfInitContext(de_ctx));
2549
2550
    SigGroupBuild(de_ctx);
2551
    DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
2552
2553
    p1->ts = TimeGet();
2554
    SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
2555
    /* First packet should be alerted, not dropped */
2556
    FAIL_IF(PacketTestAction(p1, ACTION_DROP));
2557
    FAIL_IF(PacketAlertCheck(p1, 10) != 1);
2558
2559
    TimeSetIncrementTime(2);
2560
    p2->ts = TimeGet();
2561
    SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
2562
2563
    /* Second packet should be dropped because it considered as "the same pair"
2564
       and rate_filter count reached*/
2565
    FAIL_IF_NOT(PacketTestAction(p2, ACTION_DROP));
2566
    FAIL_IF(PacketAlertCheck(p2, 10) != 1);
2567
2568
    UTHFreePacket(p2);
2569
    UTHFreePacket(p1);
2570
2571
    DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
2572
    DetectEngineCtxFree(de_ctx);
2573
    ThresholdDestroy();
2574
    PASS;
2575
}
2576
#endif /* UNITTESTS */
2577
2578
/**
2579
 * \brief This function registers unit tests for Classification Config API.
2580
 */
2581
void SCThresholdConfRegisterTests(void)
2582
0
{
2583
#ifdef UNITTESTS
2584
    UtRegisterTest("SCThresholdConfTest01", SCThresholdConfTest01);
2585
    UtRegisterTest("SCThresholdConfTest02", SCThresholdConfTest02);
2586
    UtRegisterTest("SCThresholdConfTest03", SCThresholdConfTest03);
2587
    UtRegisterTest("SCThresholdConfTest04", SCThresholdConfTest04);
2588
    UtRegisterTest("SCThresholdConfTest05", SCThresholdConfTest05);
2589
    UtRegisterTest("SCThresholdConfTest06", SCThresholdConfTest06);
2590
    UtRegisterTest("SCThresholdConfTest07", SCThresholdConfTest07);
2591
    UtRegisterTest("SCThresholdConfTest08", SCThresholdConfTest08);
2592
    UtRegisterTest("SCThresholdConfTest09 - rate_filter",
2593
                   SCThresholdConfTest09);
2594
    UtRegisterTest("SCThresholdConfTest10 - rate_filter",
2595
                   SCThresholdConfTest10);
2596
    UtRegisterTest("SCThresholdConfTest11 - event_filter",
2597
                   SCThresholdConfTest11);
2598
    UtRegisterTest("SCThresholdConfTest12 - event_filter",
2599
                   SCThresholdConfTest12);
2600
    UtRegisterTest("SCThresholdConfTest13", SCThresholdConfTest13);
2601
    UtRegisterTest("SCThresholdConfTest14 - suppress", SCThresholdConfTest14);
2602
    UtRegisterTest("SCThresholdConfTest15 - suppress drop",
2603
                   SCThresholdConfTest15);
2604
    UtRegisterTest("SCThresholdConfTest16 - suppress drop",
2605
                   SCThresholdConfTest16);
2606
    UtRegisterTest("SCThresholdConfTest17 - suppress drop",
2607
                   SCThresholdConfTest17);
2608
2609
    UtRegisterTest("SCThresholdConfTest18 - suppress parsing",
2610
                   SCThresholdConfTest18);
2611
    UtRegisterTest("SCThresholdConfTest19 - suppress parsing",
2612
                   SCThresholdConfTest19);
2613
    UtRegisterTest("SCThresholdConfTest20 - suppress parsing",
2614
                   SCThresholdConfTest20);
2615
    UtRegisterTest("SCThresholdConfTest21 - suppress parsing",
2616
                   SCThresholdConfTest21);
2617
    UtRegisterTest("SCThresholdConfTest22 - rate_filter by_both",
2618
                   SCThresholdConfTest22);
2619
    UtRegisterTest("SCThresholdConfTest23 - rate_filter by_both opposite",
2620
        SCThresholdConfTest23);
2621
2622
#endif /* UNITTESTS */
2623
0
}
2624
2625
/**
2626
 * @}
2627
 */