suricata
util-threshold-config.c
Go to the documentation of this file.
1 /* Copyright (C) 2007-2021 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  * \todo Need to support suppress
29  *
30  * Implements Threshold support
31  */
32 
33 #include "suricata-common.h"
34 
35 #include "host.h"
36 #include "ippair.h"
37 
38 #include "detect.h"
39 #include "detect-engine.h"
40 #include "detect-engine-address.h"
42 #include "detect-threshold.h"
43 #include "detect-parse.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-error.h"
52 #include "util-debug.h"
53 #include "util-fmemopen.h"
54 
55 typedef enum ThresholdRuleType {
61 
62 #ifdef UNITTESTS
63 /* File descriptor for unittests */
64 static FILE *g_ut_threshold_fp = NULL;
65 #endif
66 
67 /* common base for all options */
68 #define DETECT_BASE_REGEX "^\\s*(event_filter|threshold|rate_filter|suppress)\\s*gen_id\\s*(\\d+)\\s*,\\s*sig_id\\s*(\\d+)\\s*(.*)\\s*$"
69 
70 #define DETECT_THRESHOLD_REGEX "^,\\s*type\\s*(limit|both|threshold)\\s*,\\s*track\\s*(by_dst|by_src)\\s*,\\s*count\\s*(\\d+)\\s*,\\s*seconds\\s*(\\d+)\\s*$"
71 
72 /* TODO: "apply_to" */
73 #define DETECT_RATE_REGEX "^,\\s*track\\s*(by_dst|by_src|by_both|by_rule)\\s*,\\s*count\\s*(\\d+)\\s*,\\s*seconds\\s*(\\d+)\\s*,\\s*new_action\\s*(alert|drop|pass|log|sdrop|reject)\\s*,\\s*timeout\\s*(\\d+)\\s*$"
74 
75 /*
76  * suppress has two form:
77  * suppress gen_id 0, sig_id 0, track by_dst, ip 10.88.0.14
78  * suppress gen_id 1, sig_id 2000328
79  * suppress gen_id 1, sig_id 2000328, track by_src, ip fe80::/10
80 */
81 #define DETECT_SUPPRESS_REGEX "^,\\s*track\\s*(by_dst|by_src|by_either)\\s*,\\s*ip\\s*([\\[\\],\\$\\s\\da-zA-Z.:/_]+)*\\s*$"
82 
83 /* Default path for the threshold.config file */
84 #if defined OS_WIN32 || defined __CYGWIN__
85 #define THRESHOLD_CONF_DEF_CONF_FILEPATH CONFIG_DIR "\\\\threshold.config"
86 #else
87 #define THRESHOLD_CONF_DEF_CONF_FILEPATH CONFIG_DIR "/threshold.config"
88 #endif
89 
90 static pcre2_code *regex_base = NULL;
91 static pcre2_match_data *regex_base_match = NULL;
92 
93 static pcre2_code *regex_threshold = NULL;
94 static pcre2_match_data *regex_threshold_match = NULL;
95 
96 static pcre2_code *regex_rate = NULL;
97 static pcre2_match_data *regex_rate_match = NULL;
98 
99 static pcre2_code *regex_suppress = NULL;
100 static pcre2_match_data *regex_suppress_match = NULL;
101 
102 static void SCThresholdConfDeInitContext(DetectEngineCtx *de_ctx, FILE *fd);
103 
105 {
106  int en;
107  PCRE2_SIZE eo;
108  int opts = 0;
109  PCRE2_UCHAR errbuffer[256];
110 
111  regex_base = pcre2_compile(
112  (PCRE2_SPTR8)DETECT_BASE_REGEX, PCRE2_ZERO_TERMINATED, opts, &en, &eo, NULL);
113  if (regex_base == NULL) {
114  pcre2_get_error_message(en, errbuffer, sizeof(errbuffer));
116  "pcre2 compile of \"%s\" failed at "
117  "offset %d: %s",
118  DETECT_BASE_REGEX, (int)eo, errbuffer);
119  }
120  regex_base_match = pcre2_match_data_create_from_pattern(regex_base, NULL);
121 
122  regex_threshold = pcre2_compile(
123  (PCRE2_SPTR8)DETECT_THRESHOLD_REGEX, PCRE2_ZERO_TERMINATED, opts, &en, &eo, NULL);
124  if (regex_threshold == NULL) {
125  pcre2_get_error_message(en, errbuffer, sizeof(errbuffer));
127  "pcre2 compile of \"%s\" failed at "
128  "offset %d: %s",
129  DETECT_THRESHOLD_REGEX, (int)eo, errbuffer);
130  }
131  regex_threshold_match = pcre2_match_data_create_from_pattern(regex_threshold, NULL);
132 
133  regex_rate = pcre2_compile(
134  (PCRE2_SPTR8)DETECT_RATE_REGEX, PCRE2_ZERO_TERMINATED, opts, &en, &eo, NULL);
135  if (regex_rate == NULL) {
136  pcre2_get_error_message(en, errbuffer, sizeof(errbuffer));
138  "pcre2 compile of \"%s\" failed at "
139  "offset %d: %s",
140  DETECT_RATE_REGEX, (int)eo, errbuffer);
141  }
142  regex_rate_match = pcre2_match_data_create_from_pattern(regex_rate, NULL);
143 
144  regex_suppress = pcre2_compile(
145  (PCRE2_SPTR8)DETECT_SUPPRESS_REGEX, PCRE2_ZERO_TERMINATED, opts, &en, &eo, NULL);
146  if (regex_suppress == NULL) {
147  pcre2_get_error_message(en, errbuffer, sizeof(errbuffer));
149  "pcre2 compile of \"%s\" failed at "
150  "offset %d: %s",
151  DETECT_SUPPRESS_REGEX, (int)eo, errbuffer);
152  }
153  regex_suppress_match = pcre2_match_data_create_from_pattern(regex_suppress, NULL);
154 }
155 
157 {
158  if (regex_base != NULL) {
159  pcre2_code_free(regex_base);
160  regex_base = NULL;
161  }
162  if (regex_base_match != NULL) {
163  pcre2_match_data_free(regex_base_match);
164  regex_base_match = NULL;
165  }
166 
167  if (regex_threshold != NULL) {
168  pcre2_code_free(regex_threshold);
169  regex_threshold = NULL;
170  }
171  if (regex_threshold_match != NULL) {
172  pcre2_match_data_free(regex_threshold_match);
173  regex_threshold_match = NULL;
174  }
175 
176  if (regex_rate != NULL) {
177  pcre2_code_free(regex_rate);
178  regex_rate = NULL;
179  }
180  if (regex_rate_match != NULL) {
181  pcre2_match_data_free(regex_rate_match);
182  regex_rate_match = NULL;
183  }
184 
185  if (regex_suppress != NULL) {
186  pcre2_code_free(regex_suppress);
187  regex_suppress = NULL;
188  }
189  if (regex_suppress_match != NULL) {
190  pcre2_match_data_free(regex_suppress_match);
191  regex_suppress_match = NULL;
192  }
193 }
194 
195 /**
196  * \brief Returns the path for the Threshold Config file. We check if we
197  * can retrieve the path from the yaml conf file. If it is not present,
198  * return the default path for the threshold file which is
199  * "./threshold.config".
200  *
201  * \retval log_filename Pointer to a string containing the path for the
202  * Threshold Config file.
203  */
204 static const char *SCThresholdConfGetConfFilename(const DetectEngineCtx *de_ctx)
205 {
206  const char *log_filename = NULL;
207 
208  if (de_ctx != NULL && strlen(de_ctx->config_prefix) > 0) {
209  char config_value[256];
210  snprintf(config_value, sizeof(config_value),
211  "%s.threshold-file", de_ctx->config_prefix);
212 
213  /* try loading prefix setting, fall back to global if that
214  * fails. */
215  if (ConfGet(config_value, &log_filename) != 1) {
216  if (ConfGet("threshold-file", &log_filename) != 1) {
217  log_filename = (char *)THRESHOLD_CONF_DEF_CONF_FILEPATH;
218  }
219  }
220  } else {
221  if (ConfGet("threshold-file", &log_filename) != 1) {
222  log_filename = (char *)THRESHOLD_CONF_DEF_CONF_FILEPATH;
223  }
224  }
225  return log_filename;
226 }
227 
228 /**
229  * \brief Inits the context to be used by the Threshold Config parsing API.
230  *
231  * This function initializes the hash table to be used by the Detection
232  * Engine Context to hold the data from the threshold.config file,
233  * obtains the file desc to parse the threshold.config file, and
234  * inits the regex used to parse the lines from threshold.config
235  * file.
236  *
237  * \param de_ctx Pointer to the Detection Engine Context.
238  *
239  * \retval 0 On success.
240  * \retval -1 On failure.
241  */
243 {
244  const char *filename = NULL;
245  int ret = 0;
246 #ifndef UNITTESTS
247  FILE *fd = NULL;
248 #else
249  FILE *fd = g_ut_threshold_fp;
250  if (fd == NULL) {
251 #endif
252  filename = SCThresholdConfGetConfFilename(de_ctx);
253  if ( (fd = fopen(filename, "r")) == NULL) {
254  SCLogWarning(SC_ERR_FOPEN, "Error opening file: \"%s\": %s", filename, strerror(errno));
255  goto error;
256  }
257 #ifdef UNITTESTS
258  }
259 #endif
260 
261  if (SCThresholdConfParseFile(de_ctx, fd) < 0) {
262  SCLogWarning(
263  SC_WARN_THRESH_CONFIG, "Error loading threshold configuration from %s", filename);
264  /* maintain legacy behavior so no errors unless config testing */
266  ret = -1;
267  }
268  goto error;
269  }
270  SCThresholdConfDeInitContext(de_ctx, fd);
271 
272 #ifdef UNITTESTS
273  g_ut_threshold_fp = NULL;
274 #endif
275  SCLogDebug("Global thresholding options defined");
276  return 0;
277 
278 error:
279  SCThresholdConfDeInitContext(de_ctx, fd);
280 
281 return ret;
282 }
283 
284 /**
285  * \brief Releases resources used by the Threshold Config API.
286  *
287  * \param de_ctx Pointer to the Detection Engine Context.
288  * \param fd Pointer to file descriptor.
289  */
290 static void SCThresholdConfDeInitContext(DetectEngineCtx *de_ctx, FILE *fd)
291 {
292  if (fd != NULL)
293  fclose(fd);
294  return;
295 }
296 
297 /** \internal
298  * \brief setup suppress rules
299  * \retval 0 ok
300  * \retval -1 error
301  */
302 static int SetupSuppressRule(DetectEngineCtx *de_ctx, uint32_t id, uint32_t gid,
303  uint8_t parsed_type, uint8_t parsed_track, uint32_t parsed_count,
304  uint32_t parsed_seconds, uint32_t parsed_timeout, uint8_t parsed_new_action,
305  const char *th_ip)
306 {
307  Signature *s = NULL;
308  SigMatch *sm = NULL;
309  DetectThresholdData *de = NULL;
310 
311  BUG_ON(parsed_type != TYPE_SUPPRESS);
312 
313  DetectThresholdData *orig_de = NULL;
314  if (parsed_track != TRACK_RULE) {
315  orig_de = SCCalloc(1, sizeof(DetectThresholdData));
316  if (unlikely(orig_de == NULL))
317  goto error;
318 
319  orig_de->type = TYPE_SUPPRESS;
320  orig_de->track = parsed_track;
321  orig_de->count = parsed_count;
322  orig_de->seconds = parsed_seconds;
323  orig_de->new_action = parsed_new_action;
324  orig_de->timeout = parsed_timeout;
325  if (DetectAddressParse((const DetectEngineCtx *)de_ctx, &orig_de->addrs, (char *)th_ip) <
326  0) {
327  SCLogError(SC_ERR_INVALID_IP_NETBLOCK, "failed to parse %s", th_ip);
328  goto error;
329  }
330  }
331 
332  /* Install it */
333  if (id == 0 && gid == 0) {
334  if (parsed_track == TRACK_RULE) {
335  SCLogWarning(SC_ERR_EVENT_ENGINE, "suppressing all rules");
336  }
337 
338  /* update each sig with our suppress info */
339  for (s = de_ctx->sig_list; s != NULL; s = s->next) {
340  /* tag the rule as noalert */
341  if (parsed_track == TRACK_RULE) {
342  s->flags |= SIG_FLAG_NOALERT;
343  continue;
344  }
345 
346  de = DetectThresholdDataCopy(orig_de);
347  if (unlikely(de == NULL))
348  goto error;
349 
350  sm = SigMatchAlloc();
351  if (sm == NULL) {
352  SCLogError(SC_ERR_MEM_ALLOC, "Error allocating SigMatch");
353  goto error;
354  }
355 
356  sm->type = DETECT_THRESHOLD;
357  sm->ctx = (void *)de;
359  }
360  } else if (id == 0 && gid > 0) {
361  if (parsed_track == TRACK_RULE) {
362  SCLogWarning(SC_ERR_EVENT_ENGINE, "suppressing all rules with gid %"PRIu32, gid);
363  }
364  /* set up suppression for each signature with a matching gid */
365  for (s = de_ctx->sig_list; s != NULL; s = s->next) {
366  if (s->gid != gid)
367  continue;
368 
369  /* tag the rule as noalert */
370  if (parsed_track == TRACK_RULE) {
371  s->flags |= SIG_FLAG_NOALERT;
372  continue;
373  }
374 
375  de = DetectThresholdDataCopy(orig_de);
376  if (unlikely(de == NULL))
377  goto error;
378 
379  sm = SigMatchAlloc();
380  if (sm == NULL) {
381  SCLogError(SC_ERR_MEM_ALLOC, "Error allocating SigMatch");
382  goto error;
383  }
384 
385  sm->type = DETECT_THRESHOLD;
386  sm->ctx = (void *)de;
387 
389  }
390  } else if (id > 0 && gid == 0) {
391  SCLogError(SC_ERR_INVALID_VALUE, "Can't use a event config that has "
392  "sid > 0 and gid == 0. Please fix this "
393  "in your threshold.config file");
394  goto error;
395  } else {
396  s = SigFindSignatureBySidGid(de_ctx, id, gid);
397  if (s == NULL) {
398  SCLogWarning(SC_ERR_EVENT_ENGINE, "can't suppress sid "
399  "%"PRIu32", gid %"PRIu32": unknown rule", id, gid);
400  } else {
401  if (parsed_track == TRACK_RULE) {
402  s->flags |= SIG_FLAG_NOALERT;
403  goto end;
404  }
405 
406  de = DetectThresholdDataCopy(orig_de);
407  if (unlikely(de == NULL))
408  goto error;
409 
410  sm = SigMatchAlloc();
411  if (sm == NULL) {
412  SCLogError(SC_ERR_MEM_ALLOC, "Error allocating SigMatch");
413  goto error;
414  }
415 
416  sm->type = DETECT_THRESHOLD;
417  sm->ctx = (void *)de;
418 
420  }
421  }
422 
423 end:
424  if (orig_de != NULL) {
425  DetectAddressHeadCleanup(&orig_de->addrs);
426  SCFree(orig_de);
427  }
428  return 0;
429 error:
430  if (orig_de != NULL) {
431  DetectAddressHeadCleanup(&orig_de->addrs);
432  SCFree(orig_de);
433  }
434  if (de != NULL) {
435  DetectAddressHeadCleanup(&de->addrs);
436  SCFree(de);
437  }
438  return -1;
439 }
440 
441 /** \internal
442  * \brief setup suppress rules
443  * \retval 0 ok
444  * \retval -1 error
445  */
446 static int SetupThresholdRule(DetectEngineCtx *de_ctx, uint32_t id, uint32_t gid,
447  uint8_t parsed_type, uint8_t parsed_track, uint32_t parsed_count,
448  uint32_t parsed_seconds, uint32_t parsed_timeout, uint8_t parsed_new_action,
449  const char *th_ip)
450 {
451  Signature *s = NULL;
452  SigMatch *sm = NULL;
453  DetectThresholdData *de = NULL;
454 
455  BUG_ON(parsed_type == TYPE_SUPPRESS);
456 
457  /* Install it */
458  if (id == 0 && gid == 0) {
459  for (s = de_ctx->sig_list; s != NULL; s = s->next) {
462  if (sm != NULL) {
463  SCLogWarning(SC_ERR_EVENT_ENGINE, "signature sid:%"PRIu32 " has "
464  "an event var set. The signature event var is "
465  "given precedence over the threshold.conf one. "
466  "We'll change this in the future though.", s->id);
467  continue;
468  }
469 
472  if (sm != NULL) {
473  SCLogWarning(SC_ERR_EVENT_ENGINE, "signature sid:%"PRIu32 " has "
474  "an event var set. The signature event var is "
475  "given precedence over the threshold.conf one. "
476  "We'll change this in the future though.", s->id);
477  continue;
478  }
479 
480  de = SCMalloc(sizeof(DetectThresholdData));
481  if (unlikely(de == NULL))
482  goto error;
483  memset(de,0,sizeof(DetectThresholdData));
484 
485  de->type = parsed_type;
486  de->track = parsed_track;
487  de->count = parsed_count;
488  de->seconds = parsed_seconds;
489  de->new_action = parsed_new_action;
490  de->timeout = parsed_timeout;
491 
492  sm = SigMatchAlloc();
493  if (sm == NULL) {
494  SCLogError(SC_ERR_MEM_ALLOC, "Error allocating SigMatch");
495  goto error;
496  }
497 
498  if (parsed_type == TYPE_RATE)
500  else
501  sm->type = DETECT_THRESHOLD;
502  sm->ctx = (void *)de;
503 
505  }
506 
507  } else if (id == 0 && gid > 0) {
508  for (s = de_ctx->sig_list; s != NULL; s = s->next) {
509  if (s->gid == gid) {
512  if (sm != NULL) {
513  SCLogWarning(SC_ERR_EVENT_ENGINE, "signature sid:%"PRIu32 " has "
514  "an event var set. The signature event var is "
515  "given precedence over the threshold.conf one. "
516  "We'll change this in the future though.", id);
517  continue;
518  }
519 
520  de = SCMalloc(sizeof(DetectThresholdData));
521  if (unlikely(de == NULL))
522  goto error;
523  memset(de,0,sizeof(DetectThresholdData));
524 
525  de->type = parsed_type;
526  de->track = parsed_track;
527  de->count = parsed_count;
528  de->seconds = parsed_seconds;
529  de->new_action = parsed_new_action;
530  de->timeout = parsed_timeout;
531 
532  sm = SigMatchAlloc();
533  if (sm == NULL) {
534  SCLogError(SC_ERR_MEM_ALLOC, "Error allocating SigMatch");
535  goto error;
536  }
537 
538  if (parsed_type == TYPE_RATE)
540  else
541  sm->type = DETECT_THRESHOLD;
542  sm->ctx = (void *)de;
543 
545  }
546  }
547  } else if (id > 0 && gid == 0) {
548  SCLogError(SC_ERR_INVALID_VALUE, "Can't use a event config that has "
549  "sid > 0 and gid == 0. Please fix this "
550  "in your threshold.conf file");
551  } else {
552  s = SigFindSignatureBySidGid(de_ctx, id, gid);
553  if (s == NULL) {
554  SCLogWarning(SC_ERR_EVENT_ENGINE, "can't suppress sid "
555  "%"PRIu32", gid %"PRIu32": unknown rule", id, gid);
556  } else {
557  if (parsed_type != TYPE_SUPPRESS && parsed_type != TYPE_THRESHOLD &&
558  parsed_type != TYPE_BOTH && parsed_type != TYPE_LIMIT)
559  {
562  if (sm != NULL) {
563  SCLogWarning(SC_ERR_EVENT_ENGINE, "signature sid:%"PRIu32 " has "
564  "a threshold set. The signature event var is "
565  "given precedence over the threshold.conf one. "
566  "Bug #425.", s->id);
567  goto end;
568  }
569 
572  if (sm != NULL) {
573  SCLogWarning(SC_ERR_EVENT_ENGINE, "signature sid:%"PRIu32 " has "
574  "a detection_filter set. The signature event var is "
575  "given precedence over the threshold.conf one. "
576  "Bug #425.", s->id);
577  goto end;
578  }
579 
580  /* replace threshold on sig if we have a global override for it */
581  } else if (parsed_type == TYPE_THRESHOLD || parsed_type == TYPE_BOTH || parsed_type == TYPE_LIMIT) {
584  if (sm != NULL) {
586  SigMatchFree(de_ctx, sm);
587  sm = NULL;
588  }
589  }
590 
591  de = SCMalloc(sizeof(DetectThresholdData));
592  if (unlikely(de == NULL))
593  goto error;
594  memset(de,0,sizeof(DetectThresholdData));
595 
596  de->type = parsed_type;
597  de->track = parsed_track;
598  de->count = parsed_count;
599  de->seconds = parsed_seconds;
600  de->new_action = parsed_new_action;
601  de->timeout = parsed_timeout;
602 
603  sm = SigMatchAlloc();
604  if (sm == NULL) {
605  SCLogError(SC_ERR_MEM_ALLOC, "Error allocating SigMatch");
606  goto error;
607  }
608 
609  if (parsed_type == TYPE_RATE)
611  else
612  sm->type = DETECT_THRESHOLD;
613  sm->ctx = (void *)de;
614 
616  }
617  }
618 end:
619  return 0;
620 error:
621  if (de != NULL) {
622  DetectAddressHeadCleanup(&de->addrs);
623  SCFree(de);
624  }
625  return -1;
626 }
627 
628 static int ParseThresholdRule(const DetectEngineCtx *de_ctx, char *rawstr, uint32_t *ret_id,
629  uint32_t *ret_gid, uint8_t *ret_parsed_type, uint8_t *ret_parsed_track,
630  uint32_t *ret_parsed_count, uint32_t *ret_parsed_seconds, uint32_t *ret_parsed_timeout,
631  uint8_t *ret_parsed_new_action, char **ret_th_ip)
632 {
633  char th_rule_type[32];
634  char th_gid[16];
635  char th_sid[16];
636  const char *rule_extend = NULL;
637  char th_type[16] = "";
638  char th_track[16] = "";
639  char th_count[16] = "";
640  char th_seconds[16] = "";
641  char th_new_action[16] = "";
642  char th_timeout[16] = "";
643  const char *th_ip = NULL;
644 
645  uint8_t parsed_type = 0;
646  uint8_t parsed_track = 0;
647  uint8_t parsed_new_action = 0;
648  uint32_t parsed_count = 0;
649  uint32_t parsed_seconds = 0;
650  uint32_t parsed_timeout = 0;
651 
652  int ret = 0;
653  uint32_t id = 0, gid = 0;
654  ThresholdRuleType rule_type;
655 
656  if (de_ctx == NULL)
657  return -1;
658 
659  ret = pcre2_match(
660  regex_base, (PCRE2_SPTR8)rawstr, strlen(rawstr), 0, 0, regex_base_match, NULL);
661  if (ret < 4) {
662  SCLogError(SC_ERR_PCRE_MATCH, "pcre2_match parse error, ret %" PRId32 ", string %s", ret,
663  rawstr);
664  goto error;
665  }
666 
667  /* retrieve the classtype name */
668  size_t copylen = sizeof(th_rule_type);
669  ret = pcre2_substring_copy_bynumber(
670  regex_base_match, 1, (PCRE2_UCHAR8 *)th_rule_type, &copylen);
671  if (ret < 0) {
672  SCLogError(SC_ERR_PCRE_COPY_SUBSTRING, "pcre2_substring_copy_bynumber failed");
673  goto error;
674  }
675 
676  /* retrieve the classtype name */
677  copylen = sizeof(th_gid);
678  ret = pcre2_substring_copy_bynumber(regex_base_match, 2, (PCRE2_UCHAR8 *)th_gid, &copylen);
679  if (ret < 0) {
680  SCLogError(SC_ERR_PCRE_COPY_SUBSTRING, "pcre2_substring_copy_bynumber failed");
681  goto error;
682  }
683 
684  copylen = sizeof(th_sid);
685  ret = pcre2_substring_copy_bynumber(regex_base_match, 3, (PCRE2_UCHAR8 *)th_sid, &copylen);
686  if (ret < 0) {
687  SCLogError(SC_ERR_PCRE_COPY_SUBSTRING, "pcre2_substring_copy_bynumber failed");
688  goto error;
689  }
690 
691  /* Use "get" for heap allocation */
692  ret = pcre2_substring_get_bynumber(
693  regex_base_match, 4, (PCRE2_UCHAR8 **)&rule_extend, &copylen);
694  if (ret < 0) {
695  SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre2_substring_get_bynumber failed");
696  goto error;
697  }
698 
699  /* get type of rule */
700  if (strcasecmp(th_rule_type,"event_filter") == 0) {
701  rule_type = THRESHOLD_TYPE_EVENT_FILTER;
702  } else if (strcasecmp(th_rule_type,"threshold") == 0) {
703  rule_type = THRESHOLD_TYPE_THRESHOLD;
704  } else if (strcasecmp(th_rule_type,"rate_filter") == 0) {
705  rule_type = THRESHOLD_TYPE_RATE;
706  } else if (strcasecmp(th_rule_type,"suppress") == 0) {
707  rule_type = THRESHOLD_TYPE_SUPPRESS;
708  } else {
709  SCLogError(SC_ERR_INVALID_VALUE, "rule type %s is unknown", th_rule_type);
710  goto error;
711  }
712 
713  /* get end of rule */
714  switch(rule_type) {
717  if (strlen(rule_extend) > 0) {
718  ret = pcre2_match(regex_threshold, (PCRE2_SPTR8)rule_extend, strlen(rule_extend), 0,
719  0, regex_threshold_match, NULL);
720  if (ret < 4) {
722  "pcre2_match parse error, ret %" PRId32 ", string %s", ret,
723  rule_extend);
724  goto error;
725  }
726 
727  copylen = sizeof(th_type);
728  ret = pcre2_substring_copy_bynumber(
729  regex_threshold_match, 1, (PCRE2_UCHAR8 *)th_type, &copylen);
730  if (ret < 0) {
731  SCLogError(SC_ERR_PCRE_COPY_SUBSTRING, "pcre2_substring_copy_bynumber failed");
732  goto error;
733  }
734 
735  copylen = sizeof(th_track);
736  ret = pcre2_substring_copy_bynumber(
737  regex_threshold_match, 2, (PCRE2_UCHAR8 *)th_track, &copylen);
738  if (ret < 0) {
739  SCLogError(SC_ERR_PCRE_COPY_SUBSTRING, "pcre2_substring_copy_bynumber failed");
740  goto error;
741  }
742 
743  copylen = sizeof(th_count);
744  ret = pcre2_substring_copy_bynumber(
745  regex_threshold_match, 3, (PCRE2_UCHAR8 *)th_count, &copylen);
746  if (ret < 0) {
747  SCLogError(SC_ERR_PCRE_COPY_SUBSTRING, "pcre2_substring_copy_bynumber failed");
748  goto error;
749  }
750 
751  copylen = sizeof(th_seconds);
752  ret = pcre2_substring_copy_bynumber(
753  regex_threshold_match, 4, (PCRE2_UCHAR8 *)th_seconds, &copylen);
754  if (ret < 0) {
755  SCLogError(SC_ERR_PCRE_COPY_SUBSTRING, "pcre2_substring_copy_bynumber failed");
756  goto error;
757  }
758 
759  if (strcasecmp(th_type,"limit") == 0)
760  parsed_type = TYPE_LIMIT;
761  else if (strcasecmp(th_type,"both") == 0)
762  parsed_type = TYPE_BOTH;
763  else if (strcasecmp(th_type,"threshold") == 0)
764  parsed_type = TYPE_THRESHOLD;
765  else {
766  SCLogError(SC_ERR_INVALID_ARGUMENTS, "limit type not supported: %s", th_type);
767  goto error;
768  }
769  } else {
770  SCLogError(SC_ERR_INVALID_ARGUMENTS, "rule invalid: %s", rawstr);
771  goto error;
772  }
773  break;
775  if (strlen(rule_extend) > 0) {
776  ret = pcre2_match(regex_suppress, (PCRE2_SPTR8)rule_extend, strlen(rule_extend), 0,
777  0, regex_suppress_match, NULL);
778  if (ret < 2) {
780  "pcre2_match parse error, ret %" PRId32 ", string %s", ret,
781  rule_extend);
782  goto error;
783  }
784  /* retrieve the track mode */
785  copylen = sizeof(th_seconds);
786  ret = pcre2_substring_copy_bynumber(
787  regex_suppress_match, 1, (PCRE2_UCHAR8 *)th_track, &copylen);
788  if (ret < 0) {
789  SCLogError(SC_ERR_PCRE_COPY_SUBSTRING, "pcre2_substring_copy_bynumber failed");
790  goto error;
791  }
792  /* retrieve the IP; use "get" for heap allocation */
793  ret = pcre2_substring_get_bynumber(
794  regex_suppress_match, 2, (PCRE2_UCHAR8 **)&th_ip, &copylen);
795  if (ret < 0) {
796  SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre2_substring_get_bynumber failed");
797  goto error;
798  }
799  } else {
800  parsed_track = TRACK_RULE;
801  }
802  parsed_type = TYPE_SUPPRESS;
803  break;
804  case THRESHOLD_TYPE_RATE:
805  if (strlen(rule_extend) > 0) {
806  ret = pcre2_match(regex_rate, (PCRE2_SPTR8)rule_extend, strlen(rule_extend), 0, 0,
807  regex_rate_match, NULL);
808  if (ret < 5) {
810  "pcre2_match parse error, ret %" PRId32 ", string %s", ret,
811  rule_extend);
812  goto error;
813  }
814 
815  copylen = sizeof(th_track);
816  ret = pcre2_substring_copy_bynumber(
817  regex_rate_match, 1, (PCRE2_UCHAR8 *)th_track, &copylen);
818  if (ret < 0) {
819  SCLogError(SC_ERR_PCRE_COPY_SUBSTRING, "pcre2_substring_copy_bynumber failed");
820  goto error;
821  }
822 
823  copylen = sizeof(th_count);
824  ret = pcre2_substring_copy_bynumber(
825  regex_rate_match, 2, (PCRE2_UCHAR8 *)th_count, &copylen);
826  if (ret < 0) {
827  SCLogError(SC_ERR_PCRE_COPY_SUBSTRING, "pcre2_substring_copy_bynumber failed");
828  goto error;
829  }
830 
831  copylen = sizeof(th_seconds);
832  ret = pcre2_substring_copy_bynumber(
833  regex_rate_match, 3, (PCRE2_UCHAR8 *)th_seconds, &copylen);
834  if (ret < 0) {
835  SCLogError(SC_ERR_PCRE_COPY_SUBSTRING, "pcre2_substring_copy_bynumber failed");
836  goto error;
837  }
838 
839  copylen = sizeof(th_new_action);
840  ret = pcre2_substring_copy_bynumber(
841  regex_rate_match, 4, (PCRE2_UCHAR8 *)th_new_action, &copylen);
842  if (ret < 0) {
843  SCLogError(SC_ERR_PCRE_COPY_SUBSTRING, "pcre2_substring_copy_bynumber failed");
844  goto error;
845  }
846 
847  copylen = sizeof(th_timeout);
848  ret = pcre2_substring_copy_bynumber(
849  regex_rate_match, 5, (PCRE2_UCHAR8 *)th_timeout, &copylen);
850  if (ret < 0) {
851  SCLogError(SC_ERR_PCRE_COPY_SUBSTRING, "pcre2_substring_copy_bynumber failed");
852  goto error;
853  }
854 
855  /* TODO: implement option "apply_to" */
856 
857  if (StringParseUint32(&parsed_timeout, 10, sizeof(th_timeout), th_timeout) <= 0) {
858  goto error;
859  }
860 
861  /* Get the new action to take */
862  if (strcasecmp(th_new_action, "alert") == 0)
863  parsed_new_action = TH_ACTION_ALERT;
864  if (strcasecmp(th_new_action, "drop") == 0)
865  parsed_new_action = TH_ACTION_DROP;
866  if (strcasecmp(th_new_action, "pass") == 0)
867  parsed_new_action = TH_ACTION_PASS;
868  if (strcasecmp(th_new_action, "reject") == 0)
869  parsed_new_action = TH_ACTION_REJECT;
870  if (strcasecmp(th_new_action, "log") == 0) {
871  SCLogInfo("log action for rate_filter not supported yet");
872  parsed_new_action = TH_ACTION_LOG;
873  }
874  if (strcasecmp(th_new_action, "sdrop") == 0) {
875  SCLogInfo("sdrop action for rate_filter not supported yet");
876  parsed_new_action = TH_ACTION_SDROP;
877  }
878  parsed_type = TYPE_RATE;
879  } else {
880  SCLogError(SC_ERR_INVALID_ARGUMENTS, "rule invalid: %s", rawstr);
881  goto error;
882  }
883  break;
884  }
885 
886  switch (rule_type) {
887  /* This part is common to threshold/event_filter/rate_filter */
890  case THRESHOLD_TYPE_RATE:
891  if (strcasecmp(th_track,"by_dst") == 0)
892  parsed_track = TRACK_DST;
893  else if (strcasecmp(th_track,"by_src") == 0)
894  parsed_track = TRACK_SRC;
895  else if (strcasecmp(th_track, "by_both") == 0) {
896  parsed_track = TRACK_BOTH;
897  }
898  else if (strcasecmp(th_track,"by_rule") == 0)
899  parsed_track = TRACK_RULE;
900  else {
901  SCLogError(SC_ERR_INVALID_VALUE, "Invalid track parameter %s in %s", th_track, rawstr);
902  goto error;
903  }
904 
905  if (StringParseUint32(&parsed_count, 10, sizeof(th_count), th_count) <= 0) {
906  goto error;
907  }
908  if (parsed_count == 0) {
909  SCLogError(SC_ERR_INVALID_VALUE, "rate filter count should be > 0");
910  goto error;
911  }
912 
913  if (StringParseUint32(&parsed_seconds, 10, sizeof(th_seconds), th_seconds) <= 0) {
914  goto error;
915  }
916 
917  break;
919  /* need to get IP if extension is provided */
920  if (strcmp("", th_track) != 0) {
921  if (strcasecmp(th_track,"by_dst") == 0)
922  parsed_track = TRACK_DST;
923  else if (strcasecmp(th_track,"by_src") == 0)
924  parsed_track = TRACK_SRC;
925  else if (strcasecmp(th_track,"by_either") == 0) {
926  parsed_track = TRACK_EITHER;
927  }
928  else {
929  SCLogError(SC_ERR_INVALID_VALUE, "Invalid track parameter %s in %s", th_track, rule_extend);
930  goto error;
931  }
932  }
933  break;
934  }
935 
936  if (StringParseUint32(&id, 10, sizeof(th_sid), th_sid) <= 0) {
937  goto error;
938  }
939 
940  if (StringParseUint32(&gid, 10, sizeof(th_gid), th_gid) <= 0) {
941  goto error;
942  }
943 
944  *ret_id = id;
945  *ret_gid = gid;
946  *ret_parsed_type = parsed_type;
947  *ret_parsed_track = parsed_track;
948  *ret_parsed_new_action = parsed_new_action;
949  *ret_parsed_count = parsed_count;
950  *ret_parsed_seconds = parsed_seconds;
951  *ret_parsed_timeout = parsed_timeout;
952  *ret_th_ip = NULL;
953  if (th_ip != NULL) {
954  *ret_th_ip = (char *)th_ip;
955  }
956  pcre2_substring_free((PCRE2_UCHAR8 *)rule_extend);
957  return 0;
958 
959 error:
960  if (rule_extend != NULL) {
961  pcre2_substring_free((PCRE2_UCHAR8 *)rule_extend);
962  }
963  if (th_ip != NULL) {
964  pcre2_substring_free((PCRE2_UCHAR8 *)th_ip);
965  }
966  return -1;
967 }
968 
969 /**
970  * \brief Parses a line from the threshold file and applies it to the
971  * detection engine
972  *
973  * \param rawstr Pointer to the string to be parsed.
974  * \param de_ctx Pointer to the Detection Engine Context.
975  *
976  * \retval 0 On success.
977  * \retval -1 On failure.
978  */
979 static int SCThresholdConfAddThresholdtype(char *rawstr, DetectEngineCtx *de_ctx)
980 {
981  uint8_t parsed_type = 0;
982  uint8_t parsed_track = 0;
983  uint8_t parsed_new_action = 0;
984  uint32_t parsed_count = 0;
985  uint32_t parsed_seconds = 0;
986  uint32_t parsed_timeout = 0;
987  char *th_ip = NULL;
988  uint32_t id = 0, gid = 0;
989 
990  int r = 0;
991  r = ParseThresholdRule(de_ctx, rawstr, &id, &gid, &parsed_type, &parsed_track,
992  &parsed_count, &parsed_seconds, &parsed_timeout, &parsed_new_action,
993  &th_ip);
994  if (r < 0)
995  goto error;
996 
997  if (parsed_type == TYPE_SUPPRESS) {
998  r = SetupSuppressRule(de_ctx, id, gid, parsed_type, parsed_track,
999  parsed_count, parsed_seconds, parsed_timeout, parsed_new_action,
1000  th_ip);
1001  } else {
1002  r = SetupThresholdRule(de_ctx, id, gid, parsed_type, parsed_track,
1003  parsed_count, parsed_seconds, parsed_timeout, parsed_new_action,
1004  th_ip);
1005  }
1006  if (r < 0) {
1007  goto error;
1008  }
1009 
1010  pcre2_substring_free((PCRE2_UCHAR8 *)th_ip);
1011  return 0;
1012 error:
1013  if (th_ip != NULL)
1014  pcre2_substring_free((PCRE2_UCHAR8 *)th_ip);
1015  return -1;
1016 }
1017 
1018 /**
1019  * \brief Checks if a string is a comment or a blank line.
1020  *
1021  * Comments lines are lines of the following format -
1022  * "# This is a comment string" or
1023  * " # This is a comment string".
1024  *
1025  * \param line String that has to be checked
1026  *
1027  * \retval 1 On the argument string being a comment or blank line
1028  * \retval 0 Otherwise
1029  */
1030 static int SCThresholdConfIsLineBlankOrComment(char *line)
1031 {
1032  while (*line != '\0') {
1033  /* we have a comment */
1034  if (*line == '#')
1035  return 1;
1036 
1037  /* this line is neither a comment line, nor a blank line */
1038  if (!isspace((unsigned char)*line))
1039  return 0;
1040 
1041  line++;
1042  }
1043 
1044  /* we have a blank line */
1045  return 1;
1046 }
1047 
1048 /**
1049  * \brief Checks if the rule is multiline, by searching an ending slash
1050  *
1051  * \param line String that has to be checked
1052  *
1053  * \retval the position of the slash making it multiline
1054  * \retval 0 Otherwise
1055  */
1056 static int SCThresholdConfLineIsMultiline(char *line)
1057 {
1058  int flag = 0;
1059  char *rline = line;
1060  int len = strlen(line);
1061 
1062  while (line < rline + len && *line != '\n') {
1063  /* we have a comment */
1064  if (*line == '\\')
1065  flag = line - rline;
1066  else
1067  if (!isspace((unsigned char)*line))
1068  flag = 0;
1069 
1070  line++;
1071  }
1072 
1073  /* we have a blank line */
1074  return flag;
1075 }
1076 
1077 /**
1078  * \brief Parses the Threshold Config file
1079  *
1080  * \param de_ctx Pointer to the Detection Engine Context.
1081  * \param fd Pointer to file descriptor.
1082  */
1084 {
1085  char line[8192] = "";
1086  int rule_num = 0;
1087 
1088  /* position of "\", on multiline rules */
1089  int esc_pos = 0;
1090 
1091  if (fp == NULL)
1092  return -1;
1093 
1094  while (fgets(line + esc_pos, (int)sizeof(line) - esc_pos, fp) != NULL) {
1095  if (SCThresholdConfIsLineBlankOrComment(line)) {
1096  continue;
1097  }
1098 
1099  esc_pos = SCThresholdConfLineIsMultiline(line);
1100  if (esc_pos == 0) {
1101  if (SCThresholdConfAddThresholdtype(line, de_ctx) < 0) {
1103  return -1;
1104  } else {
1105  SCLogDebug("Adding threshold.config rule num %" PRIu32 "( %s )", rule_num, line);
1106  rule_num++;
1107  }
1108  }
1109  }
1110 
1111  SCLogInfo("Threshold config parsed: %d rule(s) found", rule_num);
1112 
1113  return 0;
1114 }
1115 
1116 #ifdef UNITTESTS
1117 
1118 /**
1119  * \brief Creates a dummy threshold file, with all valid options, for testing purposes.
1120  *
1121  * \retval fd Pointer to file descriptor.
1122  */
1123 static FILE *SCThresholdConfGenerateValidDummyFD01(void)
1124 {
1125  FILE *fd = NULL;
1126  const char *buffer =
1127  "event_filter gen_id 1, sig_id 10, type limit, track by_src, count 1, seconds 60\n"
1128  "threshold gen_id 1, sig_id 100, type both, track by_dst, count 10, seconds 60\n"
1129  "event_filter gen_id 1, sig_id 1000, type threshold, track by_src, count 100, seconds 60\n";
1130 
1131  fd = SCFmemopen((void *)buffer, strlen(buffer), "r");
1132  if (fd == NULL)
1133  SCLogDebug("Error with SCFmemopen() called by Threshold Config test code");
1134 
1135  return fd;
1136 }
1137 
1138 /**
1139  * \brief Creates a dummy threshold file, with some valid options and a couple of invalid options.
1140  * For testing purposes.
1141  *
1142  * \retval fd Pointer to file descriptor.
1143  */
1144 static FILE *SCThresholdConfGenerateInValidDummyFD02(void)
1145 {
1146  FILE *fd;
1147  const char *buffer =
1148  "event_filter gen_id 1, sig_id 1000, type invalid, track by_src, count 100, seconds 60\n";
1149 
1150  fd = SCFmemopen((void *)buffer, strlen(buffer), "r");
1151  if (fd == NULL)
1152  SCLogDebug("Error with SCFmemopen() called by Threshold Config test code");
1153 
1154  return fd;
1155 }
1156 
1157 /**
1158  * \brief Creates a dummy threshold file, with all valid options, for testing purposes.
1159  *
1160  * \retval fd Pointer to file descriptor.
1161  */
1162 static FILE *SCThresholdConfGenerateValidDummyFD03(void)
1163 {
1164  FILE *fd;
1165  const char *buffer =
1166  "event_filter gen_id 0, sig_id 0, type threshold, track by_src, count 100, seconds 60\n";
1167 
1168  fd = SCFmemopen((void *)buffer, strlen(buffer), "r");
1169  if (fd == NULL)
1170  SCLogDebug("Error with SCFmemopen() called by Threshold Config test code");
1171 
1172  return fd;
1173 }
1174 
1175 /**
1176  * \brief Creates a dummy threshold file, with all valid options, but
1177  * with split rules (multiline), for testing purposes.
1178  *
1179  * \retval fd Pointer to file descriptor.
1180  */
1181 static FILE *SCThresholdConfGenerateValidDummyFD04(void)
1182 {
1183  FILE *fd = NULL;
1184  const char *buffer =
1185  "event_filter gen_id 1 \\\n, sig_id 10, type limit, track by_src, \\\ncount 1, seconds 60\n"
1186  "threshold gen_id 1, \\\nsig_id 100, type both\\\n, track by_dst, count 10, \\\n seconds 60\n"
1187  "event_filter gen_id 1, sig_id 1000, \\\ntype threshold, track \\\nby_src, count 100, seconds 60\n";
1188 
1189  fd = SCFmemopen((void *)buffer, strlen(buffer), "r");
1190  if (fd == NULL)
1191  SCLogDebug("Error with SCFmemopen() called by Threshold Config test code");
1192 
1193  return fd;
1194 }
1195 
1196 /**
1197  * \brief Creates a dummy threshold file, with all valid options, for testing purposes.
1198  *
1199  * \retval fd Pointer to file descriptor.
1200  */
1201 static FILE *SCThresholdConfGenerateValidDummyFD05(void)
1202 {
1203  FILE *fd = NULL;
1204  const char *buffer =
1205  "rate_filter gen_id 1, sig_id 10, track by_src, count 1, seconds 60, new_action drop, timeout 10\n"
1206  "rate_filter gen_id 1, sig_id 100, track by_dst, count 10, seconds 60, new_action pass, timeout 5\n"
1207  "rate_filter gen_id 1, sig_id 1000, track by_rule, count 100, seconds 60, new_action alert, timeout 30\n"
1208  "rate_filter gen_id 1, sig_id 10000, track by_both, count 1000, seconds 60, new_action reject, timeout 21\n";
1209 
1210  fd = SCFmemopen((void *)buffer, strlen(buffer), "r");
1211  if (fd == NULL)
1212  SCLogDebug("Error with SCFmemopen() called by Threshold Config test code");
1213 
1214  return fd;
1215 }
1216 
1217 /**
1218  * \brief Creates a dummy threshold file, with all valid options, but
1219  * with split rules (multiline), for testing purposes.
1220  *
1221  * \retval fd Pointer to file descriptor.
1222  */
1223 static FILE *SCThresholdConfGenerateValidDummyFD06(void)
1224 {
1225  FILE *fd = NULL;
1226  const char *buffer =
1227  "rate_filter \\\ngen_id 1, sig_id 10, track by_src, count 1, seconds 60\\\n, new_action drop, timeout 10\n"
1228  "rate_filter gen_id 1, \\\nsig_id 100, track by_dst, \\\ncount 10, seconds 60, new_action pass, timeout 5\n"
1229  "rate_filter gen_id 1, sig_id 1000, \\\ntrack by_rule, count 100, seconds 60, new_action alert, timeout 30\n"
1230  "rate_filter gen_id 1, sig_id 10000, track by_both, count 1000, \\\nseconds 60, new_action reject, timeout 21\n";
1231 
1232  fd = SCFmemopen((void *)buffer, strlen(buffer), "r");
1233  if (fd == NULL)
1234  SCLogDebug("Error with SCFmemopen() called by Threshold Config test code");
1235 
1236  return fd;
1237 }
1238 
1239 /**
1240  * \brief Creates a dummy threshold file, with all valid options, but
1241  * with split rules (multiline), for testing purposes.
1242  *
1243  * \retval fd Pointer to file descriptor.
1244  */
1245 static FILE *SCThresholdConfGenerateValidDummyFD07(void)
1246 {
1247  FILE *fd = NULL;
1248  const char *buffer =
1249  "rate_filter gen_id 1, sig_id 10, track by_src, count 3, seconds 3, new_action drop, timeout 10\n"
1250  "rate_filter gen_id 1, sig_id 11, track by_src, count 3, seconds 1, new_action drop, timeout 5\n";
1251 
1252  fd = SCFmemopen((void *)buffer, strlen(buffer), "r");
1253  if (fd == NULL)
1254  SCLogDebug("Error with SCFmemopen() called by Threshold Config test code");
1255 
1256  return fd;
1257 }
1258 
1259 /**
1260  * \brief Creates a dummy threshold file, for testing rate_filter, track by_rule
1261  *
1262  * \retval fd Pointer to file descriptor.
1263  */
1264 static FILE *SCThresholdConfGenerateValidDummyFD08(void)
1265 {
1266  FILE *fd = NULL;
1267  const char *buffer =
1268  "rate_filter gen_id 1, sig_id 10, track by_rule, count 3, seconds 3, new_action drop, timeout 10\n";
1269 
1270  fd = SCFmemopen((void *)buffer, strlen(buffer), "r");
1271  if (fd == NULL)
1272  SCLogDebug("Error with SCFmemopen() called by Threshold Config test code");
1273 
1274  return fd;
1275 }
1276 
1277 /**
1278  * \brief Creates a dummy threshold file, with all valid options, but
1279  * with split rules (multiline), for testing purposes.
1280  *
1281  * \retval fd Pointer to file descriptor.
1282  */
1283 static FILE *SCThresholdConfGenerateValidDummyFD09(void)
1284 {
1285  FILE *fd = NULL;
1286  const char *buffer =
1287  "event_filter gen_id 1 \\\n, sig_id 10, type limit, track by_src, \\\ncount 2, seconds 60\n"
1288  "threshold gen_id 1, \\\nsig_id 11, type threshold\\\n, track by_dst, count 3, \\\n seconds 60\n"
1289  "event_filter gen_id 1, sig_id 12, \\\ntype both, track \\\nby_src, count 2, seconds 60\n";
1290 
1291  fd = SCFmemopen((void *)buffer, strlen(buffer), "r");
1292  if (fd == NULL)
1293  SCLogDebug("Error with SCFmemopen() called by Threshold Config test code");
1294 
1295  return fd;
1296 }
1297 
1298 /**
1299  * \brief Creates a dummy threshold file, with all valid options, but
1300  * with split rules (multiline), for testing purposes.
1301  *
1302  * \retval fd Pointer to file descriptor.
1303  */
1304 static FILE *SCThresholdConfGenerateValidDummyFD10(void)
1305 {
1306  FILE *fd = NULL;
1307  const char *buffer =
1308  "event_filter gen_id 1 \\\n, sig_id 10, type limit, track by_src, \\\ncount 5, seconds 2\n"
1309  "threshold gen_id 1, \\\nsig_id 11, type threshold\\\n, track by_dst, count 5, \\\n seconds 2\n"
1310  "event_filter gen_id 1, sig_id 12, \\\ntype both, track \\\nby_src, count 5, seconds 2\n";
1311 
1312  fd = SCFmemopen((void *)buffer, strlen(buffer), "r");
1313  if (fd == NULL)
1314  SCLogDebug("Error with SCFmemopen() called by Threshold Config test code");
1315 
1316  return fd;
1317 }
1318 
1319 /**
1320  * \brief Creates a dummy threshold file, with all valid options, for testing purposes.
1321  *
1322  * \retval fd Pointer to file descriptor.
1323  */
1324 static FILE *SCThresholdConfGenerateValidDummyFD11(void)
1325 {
1326  FILE *fd = NULL;
1327  const char *buffer =
1328  "suppress gen_id 1, sig_id 10000\n"
1329  "suppress gen_id 1, sig_id 1000, track by_src, ip 192.168.1.1\n";
1330 
1331  fd = SCFmemopen((void *)buffer, strlen(buffer), "r");
1332  if (fd == NULL)
1333  SCLogDebug("Error with SCFmemopen() called by Threshold Config test code");
1334 
1335  return fd;
1336 }
1337 
1338 /**
1339  * \test Check if the threshold file is loaded and well parsed
1340  *
1341  * \retval 1 on success
1342  * \retval 0 on failure
1343  */
1344 static int SCThresholdConfTest01(void)
1345 {
1348  de_ctx->flags |= DE_QUIET;
1349 
1351  "alert tcp any any -> any any (msg:\"Threshold limit\"; gid:1; sid:10;)");
1352  FAIL_IF_NULL(sig);
1353 
1354  FAIL_IF_NOT_NULL(g_ut_threshold_fp);
1355  g_ut_threshold_fp = SCThresholdConfGenerateValidDummyFD01();
1356  FAIL_IF_NULL(g_ut_threshold_fp);
1358 
1360  DETECT_THRESHOLD, -1);
1361  FAIL_IF_NULL(m);
1362 
1364  FAIL_IF_NULL(de);
1365 
1366  FAIL_IF_NOT(de->type == TYPE_LIMIT && de->track == TRACK_SRC && de->count == 1 && de->seconds == 60);
1368  PASS;
1369 }
1370 
1371 /**
1372  * \test Check if the threshold file is loaded and well parsed
1373  *
1374  * \retval 1 on success
1375  * \retval 0 on failure
1376  */
1377 static int SCThresholdConfTest02(void)
1378 {
1381  de_ctx->flags |= DE_QUIET;
1382 
1384  "alert tcp any any -> any any (msg:\"Threshold limit\"; gid:1; sid:100;)");
1385  FAIL_IF_NULL(sig);
1386 
1387  FAIL_IF_NOT_NULL(g_ut_threshold_fp);
1388  g_ut_threshold_fp = SCThresholdConfGenerateValidDummyFD01();
1389  FAIL_IF_NULL(g_ut_threshold_fp);
1391 
1393  DETECT_THRESHOLD, -1);
1394  FAIL_IF_NULL(m);
1395 
1397  FAIL_IF_NULL(de);
1398 
1399  FAIL_IF_NOT(de->type == TYPE_BOTH && de->track == TRACK_DST && de->count == 10 && de->seconds == 60);
1401  PASS;
1402 }
1403 
1404 /**
1405  * \test Check if the threshold file is loaded and well parsed
1406  *
1407  * \retval 1 on success
1408  * \retval 0 on failure
1409  */
1410 static int SCThresholdConfTest03(void)
1411 {
1414  de_ctx->flags |= DE_QUIET;
1415 
1417  "alert tcp any any -> any any (msg:\"Threshold limit\"; gid:1; sid:1000;)");
1418  FAIL_IF_NULL(sig);
1419 
1420  FAIL_IF_NOT_NULL(g_ut_threshold_fp);
1421  g_ut_threshold_fp = SCThresholdConfGenerateValidDummyFD01();
1422  FAIL_IF_NULL(g_ut_threshold_fp);
1424 
1426  DETECT_THRESHOLD, -1);
1427  FAIL_IF_NULL(m);
1428 
1430  FAIL_IF_NULL(de);
1431 
1432  FAIL_IF_NOT(de->type == TYPE_THRESHOLD && de->track == TRACK_SRC && de->count == 100 && de->seconds == 60);
1434  PASS;
1435 }
1436 
1437 /**
1438  * \test Check if the threshold file is loaded and well parsed
1439  *
1440  * \retval 1 on success
1441  * \retval 0 on failure
1442  */
1443 static int SCThresholdConfTest04(void)
1444 {
1447  de_ctx->flags |= DE_QUIET;
1448 
1450  "alert tcp any any -> any any (msg:\"Threshold limit\"; gid:1; sid:1000;)");
1451  FAIL_IF_NULL(sig);
1452 
1453  FAIL_IF_NOT_NULL(g_ut_threshold_fp);
1454  g_ut_threshold_fp = SCThresholdConfGenerateInValidDummyFD02();
1455  FAIL_IF_NULL(g_ut_threshold_fp);
1457 
1459  DETECT_THRESHOLD, -1);
1461 
1463  PASS;
1464 }
1465 
1466 /**
1467  * \test Check if the threshold file is loaded and well parsed
1468  *
1469  * \retval 1 on success
1470  * \retval 0 on failure
1471  */
1472 static int SCThresholdConfTest05(void)
1473 {
1476  de_ctx->flags |= DE_QUIET;
1477 
1479  "alert tcp any any -> any any (msg:\"Threshold limit\"; gid:1; sid:1;)");
1480  FAIL_IF_NULL(sig);
1482  "alert tcp any any -> any 80 (msg:\"Threshold limit\"; gid:1; sid:10;)");
1483  FAIL_IF_NULL(sig);
1484 
1486  "alert tcp any any -> any 80 (msg:\"Threshold limit\"; gid:1; sid:100;)");
1487  FAIL_IF_NULL(sig);
1488 
1489  FAIL_IF_NOT_NULL(g_ut_threshold_fp);
1490  g_ut_threshold_fp = SCThresholdConfGenerateValidDummyFD03();
1491  FAIL_IF_NULL(g_ut_threshold_fp);
1493 
1494  Signature *s = de_ctx->sig_list;
1496  DETECT_THRESHOLD, -1);
1497  FAIL_IF_NULL(m);
1498  FAIL_IF_NULL(m->ctx);
1500  FAIL_IF_NOT(de->type == TYPE_THRESHOLD && de->track == TRACK_SRC && de->count == 100 && de->seconds == 60);
1501 
1502  s = de_ctx->sig_list->next;
1504  DETECT_THRESHOLD, -1);
1505  FAIL_IF_NULL(m);
1506  FAIL_IF_NULL(m->ctx);
1507  de = (DetectThresholdData *)m->ctx;
1508  FAIL_IF_NOT(de->type == TYPE_THRESHOLD && de->track == TRACK_SRC && de->count == 100 && de->seconds == 60);
1509 
1510  s = de_ctx->sig_list->next->next;
1512  DETECT_THRESHOLD, -1);
1513  FAIL_IF_NULL(m);
1514  FAIL_IF_NULL(m->ctx);
1515  de = (DetectThresholdData *)m->ctx;
1516  FAIL_IF_NOT(de->type == TYPE_THRESHOLD && de->track == TRACK_SRC && de->count == 100 && de->seconds == 60);
1517 
1518  PASS;
1519 }
1520 
1521 /**
1522  * \test Check if the threshold file is loaded and well parsed
1523  *
1524  * \retval 1 on success
1525  * \retval 0 on failure
1526  */
1527 static int SCThresholdConfTest06(void)
1528 {
1531  de_ctx->flags |= DE_QUIET;
1532 
1534  "alert tcp any any -> any any (msg:\"Threshold limit\"; gid:1; sid:10;)");
1535  FAIL_IF_NULL(sig);
1536 
1537  FAIL_IF_NOT_NULL(g_ut_threshold_fp);
1538  g_ut_threshold_fp = SCThresholdConfGenerateValidDummyFD04();
1539  FAIL_IF_NULL(g_ut_threshold_fp);
1541 
1543  DETECT_THRESHOLD, -1);
1544  FAIL_IF_NULL(m);
1545 
1547  FAIL_IF_NULL(de);
1548  FAIL_IF_NOT(de->type == TYPE_LIMIT && de->track == TRACK_SRC && de->count == 1 && de->seconds == 60);
1549 
1551  PASS;
1552 }
1553 
1554 /**
1555  * \test Check if the rate_filter rules are loaded and well parsed
1556  *
1557  * \retval 1 on success
1558  * \retval 0 on failure
1559  */
1560 static int SCThresholdConfTest07(void)
1561 {
1564  de_ctx->flags |= DE_QUIET;
1565 
1567  "alert tcp any any -> any any (msg:\"Threshold limit\"; gid:1; sid:10;)");
1568  FAIL_IF_NULL(sig);
1569 
1570  FAIL_IF_NOT_NULL(g_ut_threshold_fp);
1571  g_ut_threshold_fp = SCThresholdConfGenerateValidDummyFD05();
1572  FAIL_IF_NULL(g_ut_threshold_fp);
1574 
1577  FAIL_IF_NULL(m);
1578 
1580  FAIL_IF_NULL(de);
1581  FAIL_IF_NOT(de->type == TYPE_RATE && de->track == TRACK_SRC && de->count == 1 && de->seconds == 60);
1582 
1584  PASS;
1585 }
1586 
1587 /**
1588  * \test Check if the rate_filter rules are loaded and well parsed
1589  * with multilines
1590  *
1591  * \retval 1 on success
1592  * \retval 0 on failure
1593  */
1594 static int SCThresholdConfTest08(void)
1595 {
1598  de_ctx->flags |= DE_QUIET;
1599 
1601  "alert tcp any any -> any any (msg:\"Threshold limit\"; gid:1; sid:10;)");
1602  FAIL_IF_NULL(sig);
1603 
1604  FAIL_IF_NOT_NULL(g_ut_threshold_fp);
1605  g_ut_threshold_fp = SCThresholdConfGenerateValidDummyFD06();
1606  FAIL_IF_NULL(g_ut_threshold_fp);
1608 
1611  FAIL_IF_NULL(m);
1612 
1614  FAIL_IF_NULL(de);
1615  FAIL_IF_NOT(de->type == TYPE_RATE && de->track == TRACK_SRC && de->count == 1 && de->seconds == 60);
1616 
1618  PASS;
1619 }
1620 
1621 /**
1622  * \test Check if the rate_filter rules work
1623  *
1624  * \retval 1 on success
1625  * \retval 0 on failure
1626  */
1627 static int SCThresholdConfTest09(void)
1628 {
1629  ThreadVars th_v;
1630  memset(&th_v, 0, sizeof(th_v));
1631 
1633 
1634  struct timeval ts;
1635  memset (&ts, 0, sizeof(struct timeval));
1636  TimeGet(&ts);
1637 
1638  Packet *p = UTHBuildPacket((uint8_t*)"lalala", 6, IPPROTO_TCP);
1639  FAIL_IF_NULL(p);
1640 
1641  DetectEngineThreadCtx *det_ctx = NULL;
1642 
1645  de_ctx->flags |= DE_QUIET;
1646 
1648  "alert tcp any any -> any any (msg:\"ratefilter test\"; gid:1; sid:10;)");
1649  FAIL_IF_NULL(s);
1650 
1651  FAIL_IF_NOT_NULL(g_ut_threshold_fp);
1652  g_ut_threshold_fp = SCThresholdConfGenerateValidDummyFD07();
1653  FAIL_IF_NULL(g_ut_threshold_fp);
1655 
1657  DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
1658 
1659  TimeGet(&p->ts);
1660  p->alerts.cnt = 0;
1661  p->action = 0;
1662  SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
1663  FAIL_IF(p->alerts.cnt != 1 || PacketTestAction(p, ACTION_DROP));
1664  p->alerts.cnt = 0;
1665  p->action = 0;
1666  SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
1667  FAIL_IF(p->alerts.cnt != 1 || PacketTestAction(p, ACTION_DROP));
1668  p->alerts.cnt = 0;
1669  p->action = 0;
1670  SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
1671  FAIL_IF(p->alerts.cnt != 1 || PacketTestAction(p, ACTION_DROP));
1672 
1674  TimeGet(&p->ts);
1675 
1676  p->alerts.cnt = 0;
1677  p->action = 0;
1678  SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
1679  FAIL_IF(p->alerts.cnt != 1 || !(PacketTestAction(p, ACTION_DROP)));
1680 
1682  TimeGet(&p->ts);
1683 
1684  p->alerts.cnt = 0;
1685  p->action = 0;
1686  SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
1687  FAIL_IF(p->alerts.cnt != 1 || !(PacketTestAction(p, ACTION_DROP)));
1688 
1690  TimeGet(&p->ts);
1691 
1692  p->alerts.cnt = 0;
1693  p->action = 0;
1694  SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
1695  FAIL_IF(p->alerts.cnt != 1 || PacketTestAction(p, ACTION_DROP));
1696 
1697  p->alerts.cnt = 0;
1698  p->action = 0;
1699  SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
1700  FAIL_IF(p->alerts.cnt != 1 || PacketTestAction(p, ACTION_DROP));
1701 
1702  UTHFreePacket(p);
1703  DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
1705  HostShutdown();
1706  PASS;
1707 }
1708 
1709 /**
1710  * \test Check if the rate_filter rules work with track by_rule
1711  *
1712  * \retval 1 on success
1713  * \retval 0 on failure
1714  */
1715 static int SCThresholdConfTest10(void)
1716 {
1718 
1719  struct timeval ts;
1720  memset (&ts, 0, sizeof(struct timeval));
1721  TimeGet(&ts);
1722 
1723  /* Create two different packets falling to the same rule, and
1724  * because count:3, we should drop on match #4.
1725  */
1726  Packet *p1 = UTHBuildPacketSrcDst((uint8_t*)"lalala", 6, IPPROTO_TCP,
1727  "172.26.0.2", "172.26.0.11");
1728  FAIL_IF_NULL(p1);
1729  Packet *p2 = UTHBuildPacketSrcDst((uint8_t*)"lalala", 6, IPPROTO_TCP,
1730  "172.26.0.1", "172.26.0.10");
1731  FAIL_IF_NULL(p2);
1732 
1733  ThreadVars th_v;
1734  memset(&th_v, 0, sizeof(th_v));
1735 
1738  de_ctx->flags |= DE_QUIET;
1739  DetectEngineThreadCtx *det_ctx = NULL;
1740 
1742  "alert tcp any any -> any any (msg:\"ratefilter test\"; gid:1; sid:10;)");
1743  FAIL_IF_NULL(s);
1744 
1745  FAIL_IF_NOT_NULL(g_ut_threshold_fp);
1746  g_ut_threshold_fp = SCThresholdConfGenerateValidDummyFD08();
1747  FAIL_IF_NULL(g_ut_threshold_fp);
1749 
1751  DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
1752  TimeGet(&p1->ts);
1753  p2->ts = p1->ts;
1754 
1755  /* All should be alerted, none dropped */
1756  SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
1757  FAIL_IF(PacketTestAction(p1, ACTION_DROP));
1758  FAIL_IF(PacketAlertCheck(p1, 10) != 1);
1759  p1->action = 0;
1760  SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
1761  FAIL_IF(PacketTestAction(p2, ACTION_DROP));
1762  FAIL_IF(PacketAlertCheck(p2, 10) != 1);
1763  p2->action = 0;
1764  SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
1765  FAIL_IF(PacketTestAction(p1, ACTION_DROP));
1766  FAIL_IF(PacketAlertCheck(p1, 10) != 1);
1767  p1->action = 0;
1768 
1769  /* Match #4 should be dropped*/
1770  SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
1771  FAIL_IF_NOT(PacketTestAction(p2, ACTION_DROP));
1772  FAIL_IF(PacketAlertCheck(p2, 10) != 1);
1773  p2->action = 0;
1774 
1776  TimeGet(&p1->ts);
1777 
1778  /* Still dropped because timeout not expired */
1779  SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
1780  FAIL_IF_NOT(PacketTestAction(p1, ACTION_DROP));
1781  FAIL_IF(PacketAlertCheck(p1, 10) != 1);
1782  p1->action = 0;
1783 
1785  TimeGet(&p1->ts);
1786 
1787  /* Not dropped because timeout expired */
1788  SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
1789  FAIL_IF(PacketTestAction(p1, ACTION_DROP));
1790  FAIL_IF(PacketAlertCheck(p1, 10) != 1);
1791 
1792  /* Ensure that a Threshold entry was installed at the sig */
1794 
1795  UTHFreePacket(p1);
1796  UTHFreePacket(p2);
1797  DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
1799  HostShutdown();
1800  PASS;
1801 }
1802 
1803 /**
1804  * \test Check if the rate_filter rules work
1805  *
1806  * \retval 1 on success
1807  * \retval 0 on failure
1808  */
1809 static int SCThresholdConfTest11(void)
1810 {
1812 
1813  struct timeval ts;
1814  memset (&ts, 0, sizeof(struct timeval));
1815  TimeGet(&ts);
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 
1825  de_ctx->flags |= DE_QUIET;
1826  DetectEngineThreadCtx *det_ctx = NULL;
1827 
1829  "alert tcp any any -> any any (msg:\"event_filter test limit\"; gid:1; sid:10;)");
1830  FAIL_IF_NULL(s);
1832  "alert tcp any any -> any any (msg:\"event_filter test threshold\"; gid:1; sid:11;)");
1833  FAIL_IF_NULL(s);
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 = SCThresholdConfGenerateValidDummyFD09();
1840  FAIL_IF_NULL(g_ut_threshold_fp);
1842 
1844  DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
1845 
1846  TimeGet(&p->ts);
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  TimeGet(&p->ts);
1875 
1876  SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
1877  alerts10 += PacketAlertCheck(p, 10);
1878  alerts11 += PacketAlertCheck(p, 11);
1879 
1881  TimeGet(&p->ts);
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 == 4);
1901  /* One on the first interval, another on the second */
1902  FAIL_IF_NOT(alerts11 == 2);
1903  FAIL_IF_NOT(alerts12 == 2);
1904 
1905  UTHFreePacket(p);
1906  DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
1908  HostShutdown();
1909  PASS;
1910 }
1911 
1912 /**
1913  * \test Check if the rate_filter rules work
1914  *
1915  * \retval 1 on success
1916  * \retval 0 on failure
1917  */
1918 static int SCThresholdConfTest12(void)
1919 {
1921 
1922  struct timeval ts;
1923  memset (&ts, 0, sizeof(struct timeval));
1924  TimeGet(&ts);
1925 
1926  Packet *p = UTHBuildPacket((uint8_t*)"lalala", 6, IPPROTO_TCP);
1927  FAIL_IF_NULL(p);
1928 
1929  ThreadVars th_v;
1930  memset(&th_v, 0, sizeof(th_v));
1931 
1934  de_ctx->flags |= DE_QUIET;
1935  DetectEngineThreadCtx *det_ctx = NULL;
1936 
1938  "alert tcp any any -> any any (msg:\"event_filter test limit\"; gid:1; sid:10;)");
1939  FAIL_IF_NULL(s);
1941  "alert tcp any any -> any any (msg:\"event_filter test threshold\"; gid:1; sid:11;)");
1942  FAIL_IF_NULL(s);
1944  "alert tcp any any -> any any (msg:\"event_filter test both\"; gid:1; sid:12;)");
1945  FAIL_IF_NULL(s);
1946 
1947  FAIL_IF_NOT_NULL(g_ut_threshold_fp);
1948  g_ut_threshold_fp = SCThresholdConfGenerateValidDummyFD10();
1949  FAIL_IF_NULL(g_ut_threshold_fp);
1951 
1953  DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
1954 
1955  TimeGet(&p->ts);
1956 
1957  int alerts10 = 0;
1958  int alerts11 = 0;
1959  int alerts12 = 0;
1960 
1961  SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
1962  alerts10 += PacketAlertCheck(p, 10);
1963  alerts11 += PacketAlertCheck(p, 11);
1964  alerts12 += PacketAlertCheck(p, 12);
1965  SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
1966  alerts10 += PacketAlertCheck(p, 10);
1967  alerts11 += PacketAlertCheck(p, 11);
1968  alerts12 += PacketAlertCheck(p, 12);
1969  SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
1970  alerts10 += PacketAlertCheck(p, 10);
1971  alerts11 += PacketAlertCheck(p, 11);
1972  alerts12 += PacketAlertCheck(p, 12);
1973  SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
1974  alerts10 += PacketAlertCheck(p, 10);
1975  alerts11 += PacketAlertCheck(p, 11);
1976  alerts12 += PacketAlertCheck(p, 12);
1977  SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
1978  alerts10 += PacketAlertCheck(p, 10);
1979  alerts11 += PacketAlertCheck(p, 11);
1980  alerts12 += PacketAlertCheck(p, 12);
1981 
1982  TimeSetIncrementTime(100);
1983  TimeGet(&p->ts);
1984 
1985  SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
1986  alerts10 += PacketAlertCheck(p, 10);
1987  alerts11 += PacketAlertCheck(p, 11);
1988 
1990  TimeGet(&p->ts);
1991 
1992  SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
1993  alerts10 += PacketAlertCheck(p, 10);
1994  alerts11 += PacketAlertCheck(p, 11);
1995  alerts12 += PacketAlertCheck(p, 12);
1996  SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
1997  alerts10 += PacketAlertCheck(p, 10);
1998  alerts11 += PacketAlertCheck(p, 11);
1999  alerts12 += PacketAlertCheck(p, 12);
2000  SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
2001  alerts10 += PacketAlertCheck(p, 10);
2002  alerts11 += PacketAlertCheck(p, 11);
2003  alerts12 += PacketAlertCheck(p, 12);
2004  SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
2005  alerts10 += PacketAlertCheck(p, 10);
2006  alerts11 += PacketAlertCheck(p, 11);
2007  alerts12 += PacketAlertCheck(p, 12);
2008 
2009  FAIL_IF_NOT(alerts10 == 10);
2010  /* One on the first interval, another on the second */
2011  FAIL_IF_NOT(alerts11 == 1);
2012  FAIL_IF_NOT(alerts12 == 1);
2013 
2014  UTHFreePacket(p);
2015  DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
2017  HostShutdown();
2018  PASS;
2019 }
2020 
2021 /**
2022  * \test Check if the threshold file is loaded and well parsed
2023  *
2024  * \retval 1 on success
2025  * \retval 0 on failure
2026  */
2027 static int SCThresholdConfTest13(void)
2028 {
2031  de_ctx->flags |= DE_QUIET;
2032 
2034  "alert tcp any any -> any any (msg:\"Threshold limit\"; gid:1; sid:1000;)");
2035  FAIL_IF_NULL(sig);
2036 
2037  FAIL_IF_NOT_NULL(g_ut_threshold_fp);
2038  g_ut_threshold_fp = SCThresholdConfGenerateValidDummyFD11();
2039  FAIL_IF_NULL(g_ut_threshold_fp);
2041 
2044  FAIL_IF_NULL(m);
2045 
2047  FAIL_IF_NULL(de);
2048  FAIL_IF_NOT(de->type == TYPE_SUPPRESS && de->track == TRACK_SRC);
2049 
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 SCThresholdConfTest14(void)
2061 {
2063 
2064  Packet *p1 = UTHBuildPacketReal((uint8_t*)"lalala", 6, IPPROTO_TCP, "192.168.0.10",
2065  "192.168.0.100", 1234, 24);
2066  FAIL_IF_NULL(p1);
2067  Packet *p2 = UTHBuildPacketReal((uint8_t*)"lalala", 6, IPPROTO_TCP, "192.168.1.1",
2068  "192.168.0.100", 1234, 24);
2069  FAIL_IF_NULL(p2);
2070 
2071  DetectEngineThreadCtx *det_ctx = NULL;
2074  de_ctx->flags |= DE_QUIET;
2075 
2077  "alert tcp any any -> any any (msg:\"suppress test\"; gid:1; sid:10000;)");
2078  FAIL_IF_NULL(sig);
2080  "alert tcp any any -> any any (msg:\"suppress test 2\"; gid:1; sid:10;)");
2081  FAIL_IF_NULL(sig);
2083  "alert tcp any any -> any any (msg:\"suppress test 3\"; gid:1; sid:1000;)");
2084  FAIL_IF_NULL(sig);
2085 
2086  ThreadVars th_v;
2087  memset(&th_v, 0, sizeof(th_v));
2088 
2089  struct timeval ts;
2090  memset (&ts, 0, sizeof(struct timeval));
2091  TimeGet(&ts);
2092 
2093  FAIL_IF_NOT_NULL(g_ut_threshold_fp);
2094  g_ut_threshold_fp = SCThresholdConfGenerateValidDummyFD11();
2095  FAIL_IF_NULL(g_ut_threshold_fp);
2097 
2099  DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
2100 
2101  SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
2102  SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
2103 
2104  FAIL_IF_NOT(PacketAlertCheck(p1, 10000) == 0);
2105  FAIL_IF_NOT(PacketAlertCheck(p1, 10) == 1);
2106  FAIL_IF_NOT(PacketAlertCheck(p1, 1000) == 1);
2107  FAIL_IF_NOT(PacketAlertCheck(p2, 1000) == 0);
2108 
2109  UTHFreePacket(p1);
2110  UTHFreePacket(p2);
2111 
2112  DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
2114 
2115  HostShutdown();
2116  PASS;
2117 }
2118 
2119 /**
2120  * \test Check if the suppress rules work
2121  *
2122  * \retval 1 on success
2123  * \retval 0 on failure
2124  */
2125 static int SCThresholdConfTest15(void)
2126 {
2128 
2129  Packet *p = UTHBuildPacketReal((uint8_t*)"lalala", 6, IPPROTO_TCP, "192.168.0.10",
2130  "192.168.0.100", 1234, 24);
2131  FAIL_IF_NULL(p);
2132 
2133  ThreadVars th_v;
2134  memset(&th_v, 0, sizeof(th_v));
2135 
2136  DetectEngineThreadCtx *det_ctx = NULL;
2139  de_ctx->flags |= DE_QUIET;
2140 
2141  struct timeval ts;
2142  memset (&ts, 0, sizeof(struct timeval));
2143  TimeGet(&ts);
2144 
2146  "drop tcp any any -> any any (msg:\"suppress test\"; content:\"lalala\"; gid:1; sid:10000;)");
2147  FAIL_IF_NULL(sig);
2148 
2149  FAIL_IF_NOT_NULL(g_ut_threshold_fp);
2150  g_ut_threshold_fp = SCThresholdConfGenerateValidDummyFD11();
2151  FAIL_IF_NULL(g_ut_threshold_fp);
2153 
2155  DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
2156 
2157  SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
2158 
2159  /* 10000 shouldn't match */
2160  FAIL_IF(PacketAlertCheck(p, 10000) != 0);
2161  /* however, it should have set the drop flag */
2162  FAIL_IF(!(PacketTestAction(p, ACTION_DROP)));
2163 
2164  UTHFreePacket(p);
2165  DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
2167  HostShutdown();
2168  PASS;
2169 }
2170 
2171 /**
2172  * \test Check if the suppress rules work
2173  *
2174  * \retval 1 on success
2175  * \retval 0 on failure
2176  */
2177 static int SCThresholdConfTest16(void)
2178 {
2180 
2181  Packet *p = UTHBuildPacketReal((uint8_t*)"lalala", 6, IPPROTO_TCP, "192.168.1.1",
2182  "192.168.0.100", 1234, 24);
2183  FAIL_IF_NULL(p);
2184 
2185  ThreadVars th_v;
2186  memset(&th_v, 0, sizeof(th_v));
2187 
2188  DetectEngineThreadCtx *det_ctx = NULL;
2191  de_ctx->flags |= DE_QUIET;
2192 
2193  struct timeval ts;
2194  memset (&ts, 0, sizeof(struct timeval));
2195  TimeGet(&ts);
2196 
2198  "drop tcp any any -> any any (msg:\"suppress test\"; gid:1; sid:1000;)");
2199  FAIL_IF_NULL(sig);
2200 
2201  FAIL_IF_NOT_NULL(g_ut_threshold_fp);
2202  g_ut_threshold_fp = SCThresholdConfGenerateValidDummyFD11();
2203  FAIL_IF_NULL(g_ut_threshold_fp);
2205 
2207  DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
2208 
2209  SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
2210 
2211  FAIL_IF(PacketAlertCheck(p, 1000) != 0);
2212  /* however, it should have set the drop flag */
2213  FAIL_IF(!(PacketTestAction(p, ACTION_DROP)));
2214 
2215  UTHFreePacket(p);
2216  DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
2218  HostShutdown();
2219  PASS;
2220 }
2221 
2222 /**
2223  * \test Check if the suppress rules work - ip only rule
2224  *
2225  * \retval 1 on success
2226  * \retval 0 on failure
2227  */
2228 static int SCThresholdConfTest17(void)
2229 {
2231 
2232  Packet *p = UTHBuildPacketReal((uint8_t*)"lalala", 6, IPPROTO_TCP, "192.168.0.10",
2233  "192.168.0.100", 1234, 24);
2234  FAIL_IF_NULL(p);
2235 
2236  ThreadVars th_v;
2237  memset(&th_v, 0, sizeof(th_v));
2238 
2239  DetectEngineThreadCtx *det_ctx = NULL;
2242  de_ctx->flags |= DE_QUIET;
2243 
2244  struct timeval ts;
2245  memset (&ts, 0, sizeof(struct timeval));
2246  TimeGet(&ts);
2247 
2249  "drop tcp 192.168.0.10 any -> 192.168.0.100 any (msg:\"suppress test\"; gid:1; sid:10000;)");
2250  FAIL_IF_NULL(sig);
2251 
2252  FAIL_IF_NOT_NULL(g_ut_threshold_fp);
2253  g_ut_threshold_fp = SCThresholdConfGenerateValidDummyFD11();
2254  FAIL_IF_NULL(g_ut_threshold_fp);
2256 
2258  DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
2259 
2260  SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
2261 
2262  /* 10000 shouldn't match */
2263  FAIL_IF(PacketAlertCheck(p, 10000) != 0);
2264  /* however, it should have set the drop flag */
2265  FAIL_IF(!(PacketTestAction(p, ACTION_DROP)));
2266 
2267  UTHFreePacket(p);
2268  DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
2270  HostShutdown();
2271  PASS;
2272 }
2273 
2274 /**
2275  * \brief Creates a dummy threshold file, with all valid options, for testing purposes.
2276  *
2277  * \retval fd Pointer to file descriptor.
2278  */
2279 static FILE *SCThresholdConfGenerateInvalidDummyFD12(void)
2280 {
2281  FILE *fd = NULL;
2282  const char *buffer =
2283  "suppress gen_id 1, sig_id 2200029, track by_dst, ip fe80::/16\n"
2284  "suppress gen_id 1, sig_id 2200029, track by_stc, ip fe80::/16\n";
2285 
2286  fd = SCFmemopen((void *)buffer, strlen(buffer), "r");
2287  if (fd == NULL)
2288  SCLogDebug("Error with SCFmemopen() called by Threshold Config test code");
2289 
2290  return fd;
2291 }
2292 
2293 /**
2294  * \test Check if the suppress rule parsing handles errors correctly
2295  *
2296  * \retval 1 on success
2297  * \retval 0 on failure
2298  */
2299 static int SCThresholdConfTest18(void)
2300 {
2304  de_ctx->flags |= DE_QUIET;
2305 
2307  "alert tcp 192.168.0.10 any -> 192.168.0.100 any (msg:\"suppress test\"; gid:1; sid:2200029;)");
2308  FAIL_IF_NULL(s);
2309  FAIL_IF_NOT_NULL(g_ut_threshold_fp);
2310  g_ut_threshold_fp = SCThresholdConfGenerateInvalidDummyFD12();
2311  FAIL_IF_NULL(g_ut_threshold_fp);
2314 
2318  FAIL_IF_NULL(de);
2319  FAIL_IF_NOT(de->type == TYPE_SUPPRESS && de->track == TRACK_DST);
2320 
2322  HostShutdown();
2323  PASS;
2324 }
2325 
2326 /**
2327  * \brief Creates a dummy threshold file, with all valid options, for testing purposes.
2328  *
2329  * \retval fd Pointer to file descriptor.
2330  */
2331 static FILE *SCThresholdConfGenerateInvalidDummyFD13(void)
2332 {
2333  FILE *fd = NULL;
2334  const char *buffer =
2335  "suppress gen_id 1, sig_id 2200029, track by_stc, ip fe80::/16\n"
2336  "suppress gen_id 1, sig_id 2200029, track by_dst, ip fe80::/16\n";
2337 
2338  fd = SCFmemopen((void *)buffer, strlen(buffer), "r");
2339  if (fd == NULL)
2340  SCLogDebug("Error with SCFmemopen() called by Threshold Config test code");
2341 
2342  return fd;
2343 }
2344 
2345 /**
2346  * \test Check if the suppress rule parsing handles errors correctly
2347  *
2348  * \retval 1 on success
2349  * \retval 0 on failure
2350  */
2351 static int SCThresholdConfTest19(void)
2352 {
2356  de_ctx->flags |= DE_QUIET;
2358  "alert tcp 192.168.0.10 any -> 192.168.0.100 any (msg:\"suppress test\"; gid:1; sid:2200029;)");
2359  FAIL_IF_NULL(s);
2360  FAIL_IF_NOT_NULL(g_ut_threshold_fp);
2361  g_ut_threshold_fp = SCThresholdConfGenerateInvalidDummyFD13();
2362  FAIL_IF_NULL(g_ut_threshold_fp);
2368  FAIL_IF_NULL(de);
2369  FAIL_IF_NOT(de->type == TYPE_SUPPRESS && de->track == TRACK_DST);
2371  HostShutdown();
2372  PASS;
2373 }
2374 
2375 /**
2376  * \brief Creates a dummy threshold file, with all valid options, for testing purposes.
2377  *
2378  * \retval fd Pointer to file descriptor.
2379  */
2380 static FILE *SCThresholdConfGenerateValidDummyFD20(void)
2381 {
2382  FILE *fd = NULL;
2383  const char *buffer =
2384  "suppress gen_id 1, sig_id 1000, track by_src, ip 2.2.3.4\n"
2385  "suppress gen_id 1, sig_id 1000, track by_src, ip 1.2.3.4\n"
2386  "suppress gen_id 1, sig_id 1000, track by_src, ip 192.168.1.1\n";
2387 
2388  fd = SCFmemopen((void *)buffer, strlen(buffer), "r");
2389  if (fd == NULL)
2390  SCLogDebug("Error with SCFmemopen() called by Threshold Config test code");
2391 
2392  return fd;
2393 }
2394 
2395 /**
2396  * \test Check if the threshold file is loaded and well parsed
2397  *
2398  * \retval 1 on success
2399  * \retval 0 on failure
2400  */
2401 static int SCThresholdConfTest20(void)
2402 {
2406  de_ctx->flags |= DE_QUIET;
2408  "alert tcp any any -> any any (msg:\"Threshold limit\"; content:\"abc\"; sid:1000;)");
2409  FAIL_IF_NULL(s);
2410  FAIL_IF_NOT_NULL(g_ut_threshold_fp);
2411  g_ut_threshold_fp = SCThresholdConfGenerateValidDummyFD20();
2412  FAIL_IF_NULL(g_ut_threshold_fp);
2416 
2419  FAIL_IF_NULL(de);
2420  FAIL_IF_NOT(de->type == TYPE_SUPPRESS && de->track == TRACK_SRC);
2421  FAIL_IF(smd->is_last);
2422 
2423  smd++;
2424  de = (DetectThresholdData *)smd->ctx;
2425  FAIL_IF_NULL(de);
2426  FAIL_IF_NOT(de->type == TYPE_SUPPRESS && de->track == TRACK_SRC);
2427  FAIL_IF(smd->is_last);
2428 
2429  smd++;
2430  de = (DetectThresholdData *)smd->ctx;
2431  FAIL_IF_NULL(de);
2432  FAIL_IF_NOT(de->type == TYPE_SUPPRESS && de->track == TRACK_SRC);
2433  FAIL_IF_NOT(smd->is_last);
2434 
2436  HostShutdown();
2437  PASS;
2438 }
2439 
2440 /**
2441  * \test Check if the threshold file is loaded and well parsed, and applied
2442  * correctly to a rule with thresholding
2443  *
2444  * \retval 1 on success
2445  * \retval 0 on failure
2446  */
2447 static int SCThresholdConfTest21(void)
2448 {
2452  de_ctx->flags |= DE_QUIET;
2454  "alert tcp any any -> any any (msg:\"Threshold limit\"; content:\"abc\"; threshold: type limit, track by_dst, count 5, seconds 60; sid:1000;)");
2455  FAIL_IF_NULL(s);
2456  g_ut_threshold_fp = SCThresholdConfGenerateValidDummyFD20();
2457  FAIL_IF_NULL(g_ut_threshold_fp);
2461 
2464  FAIL_IF_NULL(de);
2465  FAIL_IF_NOT(de->type == TYPE_SUPPRESS && de->track == TRACK_SRC);
2466  FAIL_IF(smd->is_last);
2467 
2468  smd++;
2469  de = (DetectThresholdData *)smd->ctx;
2470  FAIL_IF_NULL(de);
2471  FAIL_IF_NOT(de->type == TYPE_SUPPRESS && de->track == TRACK_SRC);
2472  FAIL_IF(smd->is_last);
2473 
2474  smd++;
2475  de = (DetectThresholdData *)smd->ctx;
2476  FAIL_IF_NULL(de);
2477  FAIL_IF_NOT(de->type == TYPE_SUPPRESS && de->track == TRACK_SRC);
2478  FAIL_IF_NOT(smd->is_last);
2479 
2481  HostShutdown();
2482  PASS;
2483 }
2484 
2485 /**
2486 * \brief Creates a dummy rate_filter file, for testing rate filtering by_both source and destination
2487 *
2488 * \retval fd Pointer to file descriptor.
2489 */
2490 static FILE *SCThresholdConfGenerateValidDummyFD22(void)
2491 {
2492  FILE *fd = NULL;
2493  const char *buffer =
2494  "rate_filter gen_id 1, sig_id 10, track by_both, count 2, seconds 5, new_action drop, timeout 6\n";
2495 
2496  fd = SCFmemopen((void *)buffer, strlen(buffer), "r");
2497  if (fd == NULL)
2498  SCLogDebug("Error with SCFmemopen() called by Threshold Config test code");
2499 
2500  return fd;
2501 }
2502 
2503 /**
2504  * \test Check if the rate_filter rules work with track by_both
2505  *
2506  * \retval 1 on success
2507  * \retval 0 on failure
2508  */
2509 static int SCThresholdConfTest22(void)
2510 {
2511  ThreadVars th_v;
2512  memset(&th_v, 0, sizeof(th_v));
2513 
2515 
2516  struct timeval ts;
2517  memset(&ts, 0, sizeof(struct timeval));
2518  TimeGet(&ts);
2519 
2520  /* This packet will cause rate_filter */
2521  Packet *p1 = UTHBuildPacketSrcDst((uint8_t*)"lalala", 6, IPPROTO_TCP, "172.26.0.1", "172.26.0.10");
2522  FAIL_IF_NULL(p1);
2523 
2524  /* Should not be filtered for different destination */
2525  Packet *p2 = UTHBuildPacketSrcDst((uint8_t*)"lalala", 6, IPPROTO_TCP, "172.26.0.1", "172.26.0.2");
2526  FAIL_IF_NULL(p2);
2527 
2528  /* Should not be filtered when both src and dst the same */
2529  Packet *p3 = UTHBuildPacketSrcDst((uint8_t*)"lalala", 6, IPPROTO_TCP, "172.26.0.1", "172.26.0.1");
2530  FAIL_IF_NULL(p3);
2531 
2532  DetectEngineThreadCtx *det_ctx = NULL;
2533 
2536  de_ctx->flags |= DE_QUIET;
2537 
2539  "alert tcp any any -> any any (msg:\"ratefilter by_both test\"; gid:1; sid:10;)");
2540  FAIL_IF_NULL(sig);
2541 
2542  FAIL_IF_NOT_NULL(g_ut_threshold_fp);
2543  g_ut_threshold_fp = SCThresholdConfGenerateValidDummyFD22();
2544  FAIL_IF_NULL(g_ut_threshold_fp);
2546 
2548  DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
2549 
2550  TimeGet(&p1->ts);
2551  p2->ts = p3->ts = p1->ts;
2552 
2553  /* All should be alerted, none dropped */
2554  SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
2555  FAIL_IF(PacketTestAction(p1, ACTION_DROP));
2556  FAIL_IF(PacketAlertCheck(p1, 10) != 1);
2557 
2558  SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
2559  FAIL_IF(PacketTestAction(p2, ACTION_DROP));
2560  FAIL_IF(PacketAlertCheck(p2, 10) != 1);
2561 
2562  SigMatchSignatures(&th_v, de_ctx, det_ctx, p3);
2563  FAIL_IF(PacketTestAction(p3, ACTION_DROP));
2564  FAIL_IF(PacketAlertCheck(p3, 10) != 1);
2565 
2566  p1->action = p2->action = p3->action = 0;
2567 
2569  TimeGet(&p1->ts);
2570  p2->ts = p3->ts = p1->ts;
2571 
2572  /* p1 still shouldn't be dropped after 2nd alert */
2573  SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
2574  FAIL_IF(PacketTestAction(p1, ACTION_DROP));
2575  FAIL_IF(PacketAlertCheck(p1, 10) != 1);
2576 
2577  p1->action = 0;
2578 
2580  TimeGet(&p1->ts);
2581  p2->ts = p3->ts = p1->ts;
2582 
2583  /* All should be alerted, only p1 must be dropped due to rate_filter*/
2584  SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
2585  FAIL_IF_NOT(PacketTestAction(p1, ACTION_DROP));
2586  FAIL_IF(PacketAlertCheck(p1, 10) != 1);
2587 
2588  SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
2589  FAIL_IF(PacketTestAction(p2, ACTION_DROP));
2590  FAIL_IF(PacketAlertCheck(p2, 10) != 1);
2591 
2592  SigMatchSignatures(&th_v, de_ctx, det_ctx, p3);
2593  FAIL_IF(PacketTestAction(p3, ACTION_DROP));
2594  FAIL_IF(PacketAlertCheck(p3, 10) != 1);
2595 
2596  p1->action = p2->action = p3->action = 0;
2597 
2599  TimeGet(&p1->ts);
2600  p2->ts = p3->ts = p1->ts;
2601 
2602  /* All should be alerted, none dropped (because timeout expired) */
2603  SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
2604  FAIL_IF(PacketTestAction(p1, ACTION_DROP));
2605  FAIL_IF(PacketAlertCheck(p1, 10) != 1);
2606 
2607  SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
2608  FAIL_IF(PacketTestAction(p2, ACTION_DROP));
2609  FAIL_IF(PacketAlertCheck(p2, 10) != 1);
2610 
2611  SigMatchSignatures(&th_v, de_ctx, det_ctx, p3);
2612  FAIL_IF(PacketTestAction(p3, ACTION_DROP));
2613  FAIL_IF(PacketAlertCheck(p3, 10) != 1);
2614 
2615  UTHFreePacket(p3);
2616  UTHFreePacket(p2);
2617  UTHFreePacket(p1);
2618 
2619  DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
2621  IPPairShutdown();
2622  PASS;
2623 }
2624 
2625 /**
2626 * \brief Creates a dummy rate_filter file, for testing rate filtering by_both source and destination
2627 *
2628 * \retval fd Pointer to file descriptor.
2629 */
2630 static FILE *SCThresholdConfGenerateValidDummyFD23(void)
2631 {
2632  FILE *fd = NULL;
2633  const char *buffer =
2634  "rate_filter gen_id 1, sig_id 10, track by_both, count 1, seconds 5, new_action drop, timeout 6\n";
2635 
2636  fd = SCFmemopen((void *)buffer, strlen(buffer), "r");
2637  if (fd == NULL)
2638  SCLogDebug("Error with SCFmemopen() called by Threshold Config test code");
2639 
2640  return fd;
2641 }
2642 
2643 /**
2644  * \test Check if the rate_filter by_both work when similar packets
2645  * going in opposite direction
2646  *
2647  * \retval 1 on success
2648  * \retval 0 on failure
2649  */
2650 static int SCThresholdConfTest23(void)
2651 {
2652  ThreadVars th_v;
2653  memset(&th_v, 0, sizeof(th_v));
2654 
2656 
2657  struct timeval ts;
2658  memset(&ts, 0, sizeof(struct timeval));
2659  TimeGet(&ts);
2660 
2661  /* Create two packets between same addresses in opposite direction */
2662  Packet *p1 = UTHBuildPacketSrcDst((uint8_t*)"lalala", 6, IPPROTO_TCP, "172.26.0.1", "172.26.0.10");
2663  FAIL_IF_NULL(p1);
2664 
2665  Packet *p2 = UTHBuildPacketSrcDst((uint8_t*)"lalala", 6, IPPROTO_TCP, "172.26.0.10", "172.26.0.1");
2666  FAIL_IF_NULL(p2);
2667 
2668  DetectEngineThreadCtx *det_ctx = NULL;
2669 
2672  de_ctx->flags |= DE_QUIET;
2673 
2675  "alert tcp any any -> any any (msg:\"ratefilter by_both test\"; gid:1; sid:10;)");
2676  FAIL_IF_NULL(sig);
2677 
2678  FAIL_IF_NOT_NULL(g_ut_threshold_fp);
2679  g_ut_threshold_fp = SCThresholdConfGenerateValidDummyFD23();
2680  FAIL_IF_NULL(g_ut_threshold_fp);
2682 
2684  DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
2685 
2686  TimeGet(&p1->ts);
2687  SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
2688  /* First packet should be alerted, not dropped */
2689  FAIL_IF(PacketTestAction(p1, ACTION_DROP));
2690  FAIL_IF(PacketAlertCheck(p1, 10) != 1);
2691 
2693  TimeGet(&p2->ts);
2694  SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
2695 
2696  /* Second packet should be dropped because it considered as "the same pair"
2697  and rate_filter count reached*/
2698  FAIL_IF_NOT(PacketTestAction(p2, ACTION_DROP));
2699  FAIL_IF(PacketAlertCheck(p2, 10) != 1);
2700 
2701  UTHFreePacket(p2);
2702  UTHFreePacket(p1);
2703 
2704  DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
2706  IPPairShutdown();
2707  PASS;
2708 }
2709 #endif /* UNITTESTS */
2710 
2711 /**
2712  * \brief This function registers unit tests for Classification Config API.
2713  */
2715 {
2716 #ifdef UNITTESTS
2717  UtRegisterTest("SCThresholdConfTest01", SCThresholdConfTest01);
2718  UtRegisterTest("SCThresholdConfTest02", SCThresholdConfTest02);
2719  UtRegisterTest("SCThresholdConfTest03", SCThresholdConfTest03);
2720  UtRegisterTest("SCThresholdConfTest04", SCThresholdConfTest04);
2721  UtRegisterTest("SCThresholdConfTest05", SCThresholdConfTest05);
2722  UtRegisterTest("SCThresholdConfTest06", SCThresholdConfTest06);
2723  UtRegisterTest("SCThresholdConfTest07", SCThresholdConfTest07);
2724  UtRegisterTest("SCThresholdConfTest08", SCThresholdConfTest08);
2725  UtRegisterTest("SCThresholdConfTest09 - rate_filter",
2726  SCThresholdConfTest09);
2727  UtRegisterTest("SCThresholdConfTest10 - rate_filter",
2728  SCThresholdConfTest10);
2729  UtRegisterTest("SCThresholdConfTest11 - event_filter",
2730  SCThresholdConfTest11);
2731  UtRegisterTest("SCThresholdConfTest12 - event_filter",
2732  SCThresholdConfTest12);
2733  UtRegisterTest("SCThresholdConfTest13", SCThresholdConfTest13);
2734  UtRegisterTest("SCThresholdConfTest14 - suppress", SCThresholdConfTest14);
2735  UtRegisterTest("SCThresholdConfTest15 - suppress drop",
2736  SCThresholdConfTest15);
2737  UtRegisterTest("SCThresholdConfTest16 - suppress drop",
2738  SCThresholdConfTest16);
2739  UtRegisterTest("SCThresholdConfTest17 - suppress drop",
2740  SCThresholdConfTest17);
2741 
2742  UtRegisterTest("SCThresholdConfTest18 - suppress parsing",
2743  SCThresholdConfTest18);
2744  UtRegisterTest("SCThresholdConfTest19 - suppress parsing",
2745  SCThresholdConfTest19);
2746  UtRegisterTest("SCThresholdConfTest20 - suppress parsing",
2747  SCThresholdConfTest20);
2748  UtRegisterTest("SCThresholdConfTest21 - suppress parsing",
2749  SCThresholdConfTest21);
2750  UtRegisterTest("SCThresholdConfTest22 - rate_filter by_both",
2751  SCThresholdConfTest22);
2752  UtRegisterTest("SCThresholdConfTest23 - rate_filter by_both opposite",
2753  SCThresholdConfTest23);
2754 
2755 #endif /* UNITTESTS */
2756 }
2757 
2758 /**
2759  * @}
2760  */
TRACK_BOTH
#define TRACK_BOTH
Definition: detect-threshold.h:42
util-byte.h
DetectThresholdData_::timeout
uint32_t timeout
Definition: detect-threshold.h:63
host.h
len
uint8_t len
Definition: app-layer-dnp3.h:2
ts
uint64_t ts
Definition: source-erf-file.c:54
ippair.h
SC_ERR_PCRE_COMPILE
@ SC_ERR_PCRE_COMPILE
Definition: util-error.h:35
detect-engine.h
SigMatchRemoveSMFromList
void SigMatchRemoveSMFromList(Signature *s, SigMatch *sm, int sm_list)
Definition: detect-parse.c:387
FAIL_IF_NULL
#define FAIL_IF_NULL(expr)
Fail a test if expression evaluates to NULL.
Definition: util-unittest.h:89
DetectAddressParse
int DetectAddressParse(const DetectEngineCtx *de_ctx, DetectAddressHead *gh, const char *str)
Parses an address group sent as a character string and updates the DetectAddressHead sent as the argu...
Definition: detect-engine-address.c:1412
IPPairInitConfig
void IPPairInitConfig(bool quiet)
initialize the configuration
Definition: ippair.c:169
util-fmemopen.h
SigMatchFree
void SigMatchFree(DetectEngineCtx *de_ctx, SigMatch *sm)
free a SigMatch
Definition: detect-parse.c:250
SC_ERR_INVALID_VALUE
@ SC_ERR_INVALID_VALUE
Definition: util-error.h:160
SCThresholdConfGlobalFree
void SCThresholdConfGlobalFree(void)
Definition: util-threshold-config.c:156
Signature_::num
SigIntId num
Definition: detect.h:559
unlikely
#define unlikely(expr)
Definition: util-optimize.h:35
UtRegisterTest
void UtRegisterTest(const char *name, int(*TestFn)(void))
Register unit test.
Definition: util-unittest.c:103
SCLogDebug
#define SCLogDebug(...)
Definition: util-debug.h:298
DETECT_SUPPRESS_REGEX
#define DETECT_SUPPRESS_REGEX
Definition: util-threshold-config.c:81
DetectAddressHeadCleanup
void DetectAddressHeadCleanup(DetectAddressHead *gh)
Cleans a DetectAddressHead. The functions frees the address group heads(ipv4 and ipv6) inside the Det...
Definition: detect-engine-address.c:1491
PacketAlerts_::cnt
uint16_t cnt
Definition: decode.h:306
PacketAlertCheck
int PacketAlertCheck(Packet *p, uint32_t sid)
Check if a certain sid alerted, this is used in the test functions.
Definition: detect-engine-alert.c:137
DetectThresholdData_::count
uint32_t count
Definition: detect-threshold.h:58
SigMatchData_::ctx
SigMatchCtx * ctx
Definition: detect.h:333
Packet_::action
uint8_t action
Definition: decode.h:571
DETECT_SM_LIST_THRESHOLD
@ DETECT_SM_LIST_THRESHOLD
Definition: detect.h:104
UTHBuildPacketSrcDst
Packet * UTHBuildPacketSrcDst(uint8_t *payload, uint16_t payload_len, uint8_t ipproto, const char *src, const char *dst)
UTHBuildPacketSrcDst is a wrapper that build packets specifying IPs and defaulting ports.
Definition: util-unittest-helper.c:418
DetectEngineCtx_
main detection engine ctx
Definition: detect.h:811
TYPE_LIMIT
#define TYPE_LIMIT
Definition: detect-threshold.h:31
TH_ACTION_SDROP
#define TH_ACTION_SDROP
Definition: detect-threshold.h:49
DetectEngineCtxFree
void DetectEngineCtxFree(DetectEngineCtx *)
Free a DetectEngineCtx::
Definition: detect-engine.c:2433
TRACK_DST
#define TRACK_DST
Definition: detect-detection-filter.c:43
DetectThresholdData_::new_action
uint8_t new_action
Definition: detect-threshold.h:62
DE_QUIET
#define DE_QUIET
Definition: detect.h:295
UTHBuildPacket
Packet * UTHBuildPacket(uint8_t *payload, uint16_t payload_len, uint8_t ipproto)
UTHBuildPacket is a wrapper that build packets with default ip and port fields.
Definition: util-unittest-helper.c:337
SigMatchSignatures
void SigMatchSignatures(ThreadVars *tv, DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx, Packet *p)
wrapper for old tests
Definition: detect.c:1790
TH_ACTION_ALERT
#define TH_ACTION_ALERT
Definition: detect-threshold.h:45
Signature_::sm_arrays
SigMatchData * sm_arrays[DETECT_SM_LIST_MAX]
Definition: detect.h:603
m
SCMutex m
Definition: flow-hash.h:6
SC_ERR_PCRE_GET_SUBSTRING
@ SC_ERR_PCRE_GET_SUBSTRING
Definition: util-error.h:34
SCFmemopen
#define SCFmemopen
Definition: util-fmemopen.h:52
SCThresholdConfInitContext
int SCThresholdConfInitContext(DetectEngineCtx *de_ctx)
Inits the context to be used by the Threshold Config parsing API.
Definition: util-threshold-config.c:242
UTHBuildPacketReal
Packet * UTHBuildPacketReal(uint8_t *payload, uint16_t payload_len, uint8_t ipproto, const char *src, const char *dst, uint16_t sport, uint16_t dport)
UTHBuildPacketReal is a function that create tcp/udp packets for unittests specifying ip and port sou...
Definition: util-unittest-helper.c:242
SigMatchData_
Data needed for Match()
Definition: detect.h:330
SCThresholdConfGlobalInit
void SCThresholdConfGlobalInit(void)
Definition: util-threshold-config.c:104
SC_ERR_INVALID_ARGUMENTS
@ SC_ERR_INVALID_ARGUMENTS
Definition: util-error.h:82
Packet_::alerts
PacketAlerts alerts
Definition: decode.h:582
util-unittest.h
SC_WARN_THRESH_CONFIG
@ SC_WARN_THRESH_CONFIG
Definition: util-error.h:371
util-unittest-helper.h
FAIL_IF_NOT
#define FAIL_IF_NOT(expr)
Fail a test if expression evaluates to false.
Definition: util-unittest.h:82
DetectThresholdData_::type
uint8_t type
Definition: detect-threshold.h:60
DetectGetLastSMByListId
SigMatch * DetectGetLastSMByListId(const Signature *s, int list_id,...)
Returns the sm with the largest index (added last) from the list passed to us as an id.
Definition: detect-parse.c:540
DETECT_BASE_REGEX
#define DETECT_BASE_REGEX
Definition: util-threshold-config.c:68
Signature_::gid
uint32_t gid
Definition: detect.h:583
IPPairShutdown
void IPPairShutdown(void)
shutdown the flow engine
Definition: ippair.c:303
Signature_::next
struct Signature_ * next
Definition: detect.h:622
ConfGet
int ConfGet(const char *name, const char **vptr)
Retrieve the value of a configuration node.
Definition: conf.c:330
TRACK_RULE
#define TRACK_RULE
Definition: detect-threshold.h:40
THRESHOLD_TYPE_RATE
@ THRESHOLD_TYPE_RATE
Definition: util-threshold-config.c:58
FAIL_IF_NOT_NULL
#define FAIL_IF_NOT_NULL(expr)
Fail a test if expression evaluates to non-NULL.
Definition: util-unittest.h:96
util-debug.h
SC_ERR_PCRE_MATCH
@ SC_ERR_PCRE_MATCH
Definition: util-error.h:32
PASS
#define PASS
Pass the test.
Definition: util-unittest.h:105
util-error.h
de_ctx
DetectEngineCtx * de_ctx
Definition: fuzz_siginit.c:17
TYPE_RATE
#define TYPE_RATE
Definition: detect-threshold.h:35
RUNMODE_CONF_TEST
@ RUNMODE_CONF_TEST
Definition: runmodes.h:53
DetectEngineThreadCtx_
Definition: detect.h:1060
DETECT_THRESHOLD
@ DETECT_THRESHOLD
Definition: detect-engine-register.h:55
SigFindSignatureBySidGid
Signature * SigFindSignatureBySidGid(DetectEngineCtx *de_ctx, uint32_t sid, uint32_t gid)
Find a specific signature by sid and gid.
Definition: detect-engine-build.c:66
de
uint8_t de
Definition: app-layer-htp.c:563
TH_ACTION_PASS
#define TH_ACTION_PASS
Definition: detect-threshold.h:47
SC_ERR_PCRE_COPY_SUBSTRING
@ SC_ERR_PCRE_COPY_SUBSTRING
Definition: util-error.h:358
detect.h
ThreadVars_
Per thread variable structure.
Definition: threadvars.h:58
SC_ERR_INVALID_IP_NETBLOCK
@ SC_ERR_INVALID_IP_NETBLOCK
Definition: util-error.h:46
StringParseUint32
int StringParseUint32(uint32_t *res, int base, size_t len, const char *str)
Definition: util-byte.c:313
util-time.h
DetectEngineCtx_::ths_ctx
ThresholdCtx ths_ctx
Definition: detect.h:859
SigMatch_::ctx
SigMatchCtx * ctx
Definition: detect.h:324
BUG_ON
#define BUG_ON(x)
Definition: suricata-common.h:281
TYPE_BOTH
#define TYPE_BOTH
Definition: detect-threshold.h:32
Signature_::flags
uint32_t flags
Definition: detect.h:549
ThresholdCtx_::th_entry
DetectThresholdEntry ** th_entry
Definition: detect.h:760
TimeSetIncrementTime
void TimeSetIncrementTime(uint32_t tv_sec)
increment the time in the engine
Definition: util-time.c:182
SC_ERR_FOPEN
@ SC_ERR_FOPEN
Definition: util-error.h:74
Packet_
Definition: decode.h:427
TRACK_EITHER
#define TRACK_EITHER
Definition: detect-threshold.h:41
conf.h
ThresholdRuleType
ThresholdRuleType
Definition: util-threshold-config.c:55
THRESHOLD_TYPE_EVENT_FILTER
@ THRESHOLD_TYPE_EVENT_FILTER
Definition: util-threshold-config.c:56
SigMatchAlloc
SigMatch * SigMatchAlloc(void)
Definition: detect-parse.c:235
SCLogInfo
#define SCLogInfo(...)
Macro used to log INFORMATIONAL messages.
Definition: util-debug.h:217
SC_ERR_EVENT_ENGINE
@ SC_ERR_EVENT_ENGINE
Definition: util-error.h:242
SigGroupBuild
int SigGroupBuild(DetectEngineCtx *de_ctx)
Convert the signature list into the runtime match structure.
Definition: detect-engine-build.c:1948
DetectThresholdData_::track
uint8_t track
Definition: detect-threshold.h:61
DetectEngineCtx_::config_prefix
char config_prefix[64]
Definition: detect.h:943
IPPAIR_QUIET
#define IPPAIR_QUIET
Definition: ippair.h:90
TH_ACTION_REJECT
#define TH_ACTION_REJECT
Definition: detect-threshold.h:50
DetectEngineAppendSig
Signature * DetectEngineAppendSig(DetectEngineCtx *de_ctx, const char *sigstr)
Parse and append a Signature into the Detection Engine Context signature list.
Definition: detect-parse.c:2420
DetectEngineThreadCtxInit
TmEcode DetectEngineThreadCtxInit(ThreadVars *, void *, void **)
initialize thread specific detection engine context
Definition: detect-engine.c:3142
FAIL_IF
#define FAIL_IF(expr)
Fail a test if expression evaluates to true.
Definition: util-unittest.h:71
TH_ACTION_LOG
#define TH_ACTION_LOG
Definition: detect-threshold.h:48
SigMatchData_::is_last
uint8_t is_last
Definition: detect.h:332
Packet_::ts
struct timeval ts
Definition: decode.h:470
suricata-common.h
DetectEngineThreadCtxDeinit
TmEcode DetectEngineThreadCtxDeinit(ThreadVars *, void *)
Definition: detect-engine.c:3354
SigMatch_::type
uint16_t type
Definition: detect.h:322
DetectThresholdData_
Definition: detect-threshold.h:57
TYPE_SUPPRESS
#define TYPE_SUPPRESS
Definition: detect-threshold.h:36
ACTION_DROP
#define ACTION_DROP
Definition: action-globals.h:30
HOST_QUIET
#define HOST_QUIET
Definition: host.h:93
SCLogError
#define SCLogError(err_code,...)
Macro used to log ERROR messages.
Definition: util-debug.h:257
HostShutdown
void HostShutdown(void)
shutdown the flow engine
Definition: host.c:309
TH_ACTION_DROP
#define TH_ACTION_DROP
Definition: detect-threshold.h:46
FatalError
#define FatalError(x,...)
Definition: util-debug.h:532
DetectEngineCtx_::sig_list
Signature * sig_list
Definition: detect.h:817
SIG_FLAG_NOALERT
#define SIG_FLAG_NOALERT
Definition: detect.h:216
RunmodeGetCurrent
int RunmodeGetCurrent(void)
Definition: suricata.c:280
SCMalloc
#define SCMalloc(sz)
Definition: util-mem.h:47
TYPE_THRESHOLD
#define TYPE_THRESHOLD
Definition: detect-threshold.h:33
DETECT_DETECTION_FILTER
@ DETECT_DETECTION_FILTER
Definition: detect-engine-register.h:98
SCLogWarning
#define SCLogWarning(err_code,...)
Macro used to log WARNING messages.
Definition: util-debug.h:244
SCFree
#define SCFree(p)
Definition: util-mem.h:61
UTHFreePacket
void UTHFreePacket(Packet *p)
UTHFreePacket: function to release the allocated data from UTHBuildPacket and the packet itself.
Definition: util-unittest-helper.c:485
Signature_::id
uint32_t id
Definition: detect.h:582
detect-parse.h
Signature_
Signature container.
Definition: detect.h:548
SigMatch_
a single match condition for a signature
Definition: detect.h:321
THRESHOLD_CONF_DEF_CONF_FILEPATH
#define THRESHOLD_CONF_DEF_CONF_FILEPATH
Definition: util-threshold-config.c:87
detect-threshold.h
SCThresholdConfRegisterTests
void SCThresholdConfRegisterTests(void)
This function registers unit tests for Classification Config API.
Definition: util-threshold-config.c:2714
TimeGet
void TimeGet(struct timeval *tv)
Definition: util-time.c:153
DetectEngineCtxInit
DetectEngineCtx * DetectEngineCtxInit(void)
Definition: detect-engine.c:2394
SC_ERR_MEM_ALLOC
@ SC_ERR_MEM_ALLOC
Definition: util-error.h:31
TRACK_SRC
#define TRACK_SRC
Definition: detect-detection-filter.c:44
HostInitConfig
void HostInitConfig(bool quiet)
initialize the configuration
Definition: host.c:175
DetectEngineCtx_::flags
uint8_t flags
Definition: detect.h:812
DetectThresholdData_::seconds
uint32_t seconds
Definition: detect-threshold.h:59
THRESHOLD_TYPE_THRESHOLD
@ THRESHOLD_TYPE_THRESHOLD
Definition: util-threshold-config.c:57
THRESHOLD_TYPE_SUPPRESS
@ THRESHOLD_TYPE_SUPPRESS
Definition: util-threshold-config.c:59
SCThresholdConfParseFile
int SCThresholdConfParseFile(DetectEngineCtx *de_ctx, FILE *fp)
Parses the Threshold Config file.
Definition: util-threshold-config.c:1083
DETECT_THRESHOLD_REGEX
#define DETECT_THRESHOLD_REGEX
Definition: util-threshold-config.c:70
SCCalloc
#define SCCalloc(nm, sz)
Definition: util-mem.h:53
DetectThresholdDataCopy
DetectThresholdData * DetectThresholdDataCopy(DetectThresholdData *de)
Make a deep-copy of an extant DetectTHresholdData object.
Definition: detect-threshold.c:294
DETECT_SM_LIST_SUPPRESS
@ DETECT_SM_LIST_SUPPRESS
Definition: detect.h:103
SigMatchAppendSMToList
void SigMatchAppendSMToList(Signature *s, SigMatch *new, int list)
Append a SigMatch to the list type.
Definition: detect-parse.c:349
detect-engine-address.h
util-threshold-config.h
detect-engine-threshold.h
DETECT_RATE_REGEX
#define DETECT_RATE_REGEX
Definition: util-threshold-config.c:73
DetectThresholdData_::addrs
DetectAddressHead addrs
Definition: detect-threshold.h:65