From: Ruslan Usmanov Date: Fri, 27 Oct 2017 16:50:32 +0000 (-0400) Subject: rate_filter by_both through IPPair storage X-Git-Tag: suricata-4.1.0-beta1~516 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=1090ee9d8d739eb014540ad834509a23f96e0712;p=thirdparty%2Fsuricata.git rate_filter by_both through IPPair storage Ticket https://redmine.openinfosecfoundation.org/issues/2127 --- diff --git a/doc/userguide/configuration/global-thresholds.rst b/doc/userguide/configuration/global-thresholds.rst index adb5cb8b47..d63fa4b07a 100644 --- a/doc/userguide/configuration/global-thresholds.rst +++ b/doc/userguide/configuration/global-thresholds.rst @@ -53,6 +53,8 @@ track Where to track the rule matches. When using by_src/by_dst the tracking is done per IP-address. The Host table is used for storage. When using by_rule it's done globally for the rule. +Option by_both used to track per IP pair of source and destination. Packets +going to opposite directions between same addresses tracked as the same pair. count ^^^^^ diff --git a/src/detect-engine-threshold.c b/src/detect-engine-threshold.c index e639332bb1..18f75fe672 100644 --- a/src/detect-engine-threshold.c +++ b/src/detect-engine-threshold.c @@ -43,6 +43,9 @@ #include "host.h" #include "host-storage.h" +#include "ippair.h" +#include "ippair-storage.h" + #include "detect-parse.h" #include "detect-engine-sigorder.h" @@ -66,25 +69,36 @@ #include "util-var-name.h" #include "tm-threads.h" -static int threshold_id = -1; /**< host storage id for thresholds */ +static int host_threshold_id = -1; /**< host storage id for thresholds */ +static int ippair_threshold_id = -1; /**< ip pair storage id for thresholds */ int ThresholdHostStorageId(void) { - return threshold_id; + return host_threshold_id; } void ThresholdInit(void) { - threshold_id = HostStorageRegister("threshold", sizeof(void *), NULL, ThresholdListFree); - if (threshold_id == -1) { + host_threshold_id = HostStorageRegister("threshold", sizeof(void *), NULL, ThresholdListFree); + if (host_threshold_id == -1) { SCLogError(SC_ERR_HOST_INIT, "Can't initiate host storage for thresholding"); exit(EXIT_FAILURE); } + ippair_threshold_id = IPPairStorageRegister("threshold", sizeof(void *), NULL, ThresholdListFree); + if (ippair_threshold_id == -1) { + SCLogError(SC_ERR_HOST_INIT, "Can't initiate IP pair storage for thresholding"); + exit(EXIT_FAILURE); + } } int ThresholdHostHasThreshold(Host *host) { - return HostGetStorageById(host, threshold_id) ? 1 : 0; + return HostGetStorageById(host, host_threshold_id) ? 1 : 0; +} + +int ThresholdIPPairHasThreshold(IPPair *pair) +{ + return IPPairGetStorageById(pair, ippair_threshold_id) ? 1 : 0; } /** @@ -143,49 +157,61 @@ const DetectThresholdData *SigGetThresholdTypeIter(const Signature *sig, /** * \brief Remove timeout threshold hash elements * - * \param de_ctx Dectection Context + * \param head Current head element of storage + * \param tv Current time + * + * \retval DetectThresholdEntry Return new head element or NULL if all expired * */ -int ThresholdTimeoutCheck(Host *host, struct timeval *tv) +static DetectThresholdEntry* ThresholdTimeoutCheck(DetectThresholdEntry *head, struct timeval *tv) { - DetectThresholdEntry *tde = NULL; - DetectThresholdEntry *tmp = NULL; + DetectThresholdEntry *tmp = head; DetectThresholdEntry *prev = NULL; - int retval = 1; - - tmp = HostGetStorageById(host, threshold_id); - if (tmp == NULL) - return 1; + DetectThresholdEntry *new_head = head; - prev = NULL; while (tmp != NULL) { if ((tv->tv_sec - tmp->tv_sec1) <= tmp->seconds) { prev = tmp; tmp = tmp->next; - retval = 0; continue; } /* timed out */ + DetectThresholdEntry *tde = tmp; if (prev != NULL) { prev->next = tmp->next; + } + else { + new_head = tmp->next; + } + tmp = tde->next; + SCFree(tde); + } - tde = tmp; - tmp = tde->next; - - SCFree(tde); - } else { - HostSetStorageById(host, threshold_id, tmp->next); - tde = tmp; - tmp = tde->next; + return new_head; +} - SCFree(tde); - } +int ThresholdHostTimeoutCheck(Host *host, struct timeval *tv) +{ + DetectThresholdEntry* head = HostGetStorageById(host, host_threshold_id); + DetectThresholdEntry* new_head = ThresholdTimeoutCheck(head, tv); + if (new_head != head) { + HostSetStorageById(host, host_threshold_id, new_head); } + return new_head == NULL; +} - return retval; + +int ThresholdIPPairTimeoutCheck(IPPair *pair, struct timeval *tv) +{ + DetectThresholdEntry* head = IPPairGetStorageById(pair, ippair_threshold_id); + DetectThresholdEntry* new_head = ThresholdTimeoutCheck(head, tv); + if (new_head != head) { + IPPairSetStorageById(pair, ippair_threshold_id, new_head); + } + return new_head == NULL; } static inline DetectThresholdEntry * @@ -213,7 +239,19 @@ static DetectThresholdEntry *ThresholdHostLookupEntry(Host *h, uint32_t sid, uin { DetectThresholdEntry *e; - for (e = HostGetStorageById(h, threshold_id); e != NULL; e = e->next) { + for (e = HostGetStorageById(h, host_threshold_id); e != NULL; e = e->next) { + if (e->sid == sid && e->gid == gid) + break; + } + + return e; +} + +static DetectThresholdEntry *ThresholdIPPairLookupEntry(IPPair *pair, uint32_t sid, uint32_t gid) +{ + DetectThresholdEntry *e; + + for (e = IPPairGetStorageById(pair, ippair_threshold_id); e != NULL; e = e->next) { if (e->sid == sid && e->gid == gid) break; } @@ -281,6 +319,106 @@ static inline void RateFilterSetAction(Packet *p, PacketAlert *pa, uint8_t new_a } } +/** +* \brief Check if the entry reached threshold count limit +* +* \param lookup_tsh Current threshold entry +* \param td Threshold settings +* \param packet_time used to compare against previous detection and to set timeouts +* +* \retval int 1 if threshold reached for this entry +* +*/ +static int IsThresholdReached(DetectThresholdEntry* lookup_tsh, const DetectThresholdData *td, uint32_t packet_time) +{ + int ret = 0; + + /* Check if we have a timeout enabled, if so, + * we still matching (and enabling the new_action) */ + if (lookup_tsh->tv_timeout != 0) { + if ((packet_time - lookup_tsh->tv_timeout) > td->timeout) { + /* Ok, we are done, timeout reached */ + lookup_tsh->tv_timeout = 0; + } + else { + /* Already matching */ + ret = 1; + } /* else - if ((packet_time - lookup_tsh->tv_timeout) > td->timeout) */ + + } + else { + /* Update the matching state with the timeout interval */ + if ((packet_time - lookup_tsh->tv_sec1) < td->seconds) { + lookup_tsh->current_count++; + if (lookup_tsh->current_count > td->count) { + /* Then we must enable the new action by setting a + * timeout */ + lookup_tsh->tv_timeout = packet_time; + ret = 1; + } + } + else { + lookup_tsh->tv_sec1 = packet_time; + lookup_tsh->current_count = 1; + } + } /* else - if (lookup_tsh->tv_timeout != 0) */ + + return ret; +} + +static void AddEntryToHostStorage(Host *h, DetectThresholdEntry *e, uint32_t packet_time) +{ + if (h && e) { + e->current_count = 1; + e->tv_sec1 = packet_time; + e->tv_timeout = 0; + e->next = HostGetStorageById(h, host_threshold_id); + HostSetStorageById(h, host_threshold_id, e); + } +} + +static void AddEntryToIPPairStorage(IPPair *pair, DetectThresholdEntry *e, uint32_t packet_time) +{ + if (pair && e) { + e->current_count = 1; + e->tv_sec1 = packet_time; + e->tv_timeout = 0; + e->next = IPPairGetStorageById(pair, ippair_threshold_id); + IPPairSetStorageById(pair, ippair_threshold_id, e); + } +} + +static int ThresholdHandlePacketIPPair(IPPair *pair, Packet *p, const DetectThresholdData *td, + uint32_t sid, uint32_t gid, PacketAlert *pa) +{ + int ret = 0; + + DetectThresholdEntry *lookup_tsh = ThresholdIPPairLookupEntry(pair, sid, gid); + SCLogDebug("ippair lookup_tsh %p sid %u gid %u", lookup_tsh, sid, gid); + + switch (td->type) { + case TYPE_RATE: + { + SCLogDebug("rate_filter"); + ret = 1; + if (lookup_tsh && IsThresholdReached(lookup_tsh, td, p->ts.tv_sec)) { + RateFilterSetAction(p, pa, td->new_action); + } else if (!lookup_tsh) { + DetectThresholdEntry *e = DetectThresholdEntryAlloc(td, p, sid, gid); + AddEntryToIPPairStorage(pair, e, p->ts.tv_sec); + } + break; + } + default: + { + SCLogError(SC_ERR_INVALID_VALUE, "type %d is not supported", td->type); + break; + } + } + + return ret; +} + /** * \retval 2 silent match (no alert but apply actions) * \retval 1 normal match @@ -325,8 +463,8 @@ static int ThresholdHandlePacketHost(Host *h, Packet *p, const DetectThresholdDa ret = 1; - e->next = HostGetStorageById(h, threshold_id); - HostSetStorageById(h, threshold_id, e); + e->next = HostGetStorageById(h, host_threshold_id); + HostSetStorageById(h, host_threshold_id, e); } break; } @@ -358,8 +496,8 @@ static int ThresholdHandlePacketHost(Host *h, Packet *p, const DetectThresholdDa e->current_count = 1; e->tv_sec1 = p->ts.tv_sec; - e->next = HostGetStorageById(h, threshold_id); - HostSetStorageById(h, threshold_id, e); + e->next = HostGetStorageById(h, host_threshold_id); + HostSetStorageById(h, host_threshold_id, e); } } break; @@ -398,8 +536,8 @@ static int ThresholdHandlePacketHost(Host *h, Packet *p, const DetectThresholdDa e->current_count = 1; e->tv_sec1 = p->ts.tv_sec; - e->next = HostGetStorageById(h, threshold_id); - HostSetStorageById(h, threshold_id, e); + e->next = HostGetStorageById(h, host_threshold_id); + HostSetStorageById(h, host_threshold_id, e); /* for the first match we return 1 to * indicate we should alert */ @@ -442,8 +580,8 @@ static int ThresholdHandlePacketHost(Host *h, Packet *p, const DetectThresholdDa e->tv_sec1 = p->ts.tv_sec; e->tv_usec1 = p->ts.tv_usec; - e->next = HostGetStorageById(h, threshold_id); - HostSetStorageById(h, threshold_id, e); + e->next = HostGetStorageById(h, host_threshold_id); + HostSetStorageById(h, host_threshold_id, e); } break; } @@ -451,56 +589,12 @@ static int ThresholdHandlePacketHost(Host *h, Packet *p, const DetectThresholdDa case TYPE_RATE: { SCLogDebug("rate_filter"); - ret = 1; - - if (lookup_tsh != NULL) { - /* Check if we have a timeout enabled, if so, - * we still matching (and enabling the new_action) */ - if (lookup_tsh->tv_timeout != 0) { - if ((p->ts.tv_sec - lookup_tsh->tv_timeout) > td->timeout) { - /* Ok, we are done, timeout reached */ - lookup_tsh->tv_timeout = 0; - } else { - /* Already matching */ - /* Take the action to perform */ - RateFilterSetAction(p, pa, td->new_action); - ret = 1; - } /* else - if ((p->ts.tv_sec - lookup_tsh->tv_timeout) > td->timeout) */ - - } else { - /* Update the matching state with the timeout interval */ - if ( (p->ts.tv_sec - lookup_tsh->tv_sec1) < td->seconds) { - lookup_tsh->current_count++; - if (lookup_tsh->current_count > td->count) { - /* Then we must enable the new action by setting a - * timeout */ - lookup_tsh->tv_timeout = p->ts.tv_sec; - /* Take the action to perform */ - RateFilterSetAction(p, pa, td->new_action); - ret = 1; - } - } else { - lookup_tsh->tv_sec1 = p->ts.tv_sec; - lookup_tsh->current_count = 1; - } - } /* else - if (lookup_tsh->tv_timeout != 0) */ - } else { - if (td->count == 1) { - ret = 1; - } - + if (lookup_tsh && IsThresholdReached(lookup_tsh, td, p->ts.tv_sec)) { + RateFilterSetAction(p, pa, td->new_action); + } else if (!lookup_tsh) { DetectThresholdEntry *e = DetectThresholdEntryAlloc(td, p, sid, gid); - if (e == NULL) { - break; - } - - e->current_count = 1; - e->tv_sec1 = p->ts.tv_sec; - e->tv_timeout = 0; - - e->next = HostGetStorageById(h, threshold_id); - HostSetStorageById(h, threshold_id, e); + AddEntryToHostStorage(h, e, p->ts.tv_sec); } break; } @@ -603,6 +697,12 @@ int PacketAlertThreshold(DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx ret = ThresholdHandlePacketHost(dst,p,td,s->id,s->gid,pa); HostRelease(dst); } + } else if (td->track == TRACK_BOTH) { + IPPair *pair = IPPairGetIPPairFromHash(&p->src, &p->dst); + if (pair) { + ret = ThresholdHandlePacketIPPair(pair, p, td, s->id, s->gid, pa); + IPPairRelease(pair); + } } else if (td->track == TRACK_RULE) { SCMutexLock(&de_ctx->ths_ctx.threshold_table_lock); ret = ThresholdHandlePacketRule(de_ctx,p,td,s,pa); diff --git a/src/detect-engine-threshold.h b/src/detect-engine-threshold.h index 76a0759d35..93f83f039c 100644 --- a/src/detect-engine-threshold.h +++ b/src/detect-engine-threshold.h @@ -27,12 +27,15 @@ #include "detect.h" #include "host.h" +#include "ippair.h" void ThresholdInit(void); int ThresholdHostStorageId(void); int ThresholdHostHasThreshold(Host *); +int ThresholdIPPairHasThreshold(IPPair *pair); + const DetectThresholdData *SigGetThresholdTypeIter(const Signature *, Packet *, const SigMatchData **, int list); int PacketAlertThreshold(DetectEngineCtx *, DetectEngineThreadCtx *, @@ -42,7 +45,8 @@ int PacketAlertThreshold(DetectEngineCtx *, DetectEngineThreadCtx *, void ThresholdHashInit(DetectEngineCtx *); void ThresholdContextDestroy(DetectEngineCtx *); -int ThresholdTimeoutCheck(Host *, struct timeval *); +int ThresholdHostTimeoutCheck(Host *, struct timeval *); +int ThresholdIPPairTimeoutCheck(IPPair *, struct timeval *); void ThresholdListFree(void *ptr); #endif /* __DETECT_ENGINE_THRESHOLD_H__ */ diff --git a/src/detect-threshold.h b/src/detect-threshold.h index 50e1d27048..534a6eb4ae 100644 --- a/src/detect-threshold.h +++ b/src/detect-threshold.h @@ -39,6 +39,7 @@ #define TRACK_SRC 2 #define TRACK_RULE 3 #define TRACK_EITHER 4 /**< either src or dst: only used by suppress */ +#define TRACK_BOTH 5 /* used by rate_filter to match detections by both src and dst addresses */ /* Get the new action to take */ #define TH_ACTION_ALERT 0x01 diff --git a/src/host-timeout.c b/src/host-timeout.c index 48efb5ab74..ccb9214983 100644 --- a/src/host-timeout.c +++ b/src/host-timeout.c @@ -73,7 +73,7 @@ static int HostHostTimedOut(Host *h, struct timeval *ts) if (TagHostHasTag(h) && TagTimeoutCheck(h, ts) == 0) { tags = 1; } - if (ThresholdHostHasThreshold(h) && ThresholdTimeoutCheck(h, ts) == 0) { + if (ThresholdHostHasThreshold(h) && ThresholdHostTimeoutCheck(h, ts) == 0) { thresholds = 1; } if (HostHasHostBits(h) && HostBitsTimedoutCheck(h, ts) == 0) { diff --git a/src/ippair-timeout.c b/src/ippair-timeout.c index 074200c878..47f420b17c 100644 --- a/src/ippair-timeout.c +++ b/src/ippair-timeout.c @@ -25,6 +25,7 @@ #include "ippair.h" #include "ippair-bit.h" #include "ippair-timeout.h" +#include "detect-engine-threshold.h" uint32_t IPPairGetSpareCount(void) { @@ -48,6 +49,7 @@ uint32_t IPPairGetActiveCount(void) static int IPPairTimedOut(IPPair *h, struct timeval *ts) { int vars = 0; + int thresholds = 0; /** never prune a ippair that is used by a packet * we are currently processing in one of the threads */ @@ -59,7 +61,11 @@ static int IPPairTimedOut(IPPair *h, struct timeval *ts) vars = 1; } - if (vars) { + if (ThresholdIPPairHasThreshold(h) && ThresholdIPPairTimeoutCheck(h, ts) == 0) { + thresholds = 1; + } + + if (vars || thresholds) { return 0; } diff --git a/src/util-threshold-config.c b/src/util-threshold-config.c index da7ac5af3c..370a9e476d 100644 --- a/src/util-threshold-config.c +++ b/src/util-threshold-config.c @@ -33,6 +33,7 @@ #include "suricata-common.h" #include "host.h" +#include "ippair.h" #include "detect.h" #include "detect-engine.h" @@ -68,7 +69,7 @@ static FILE *g_ut_threshold_fp = NULL; #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*$" /* TODO: "apply_to" */ -#define DETECT_RATE_REGEX "^,\\s*track\\s*(by_dst|by_src|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*$" +#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*$" /* * suppress has two form: @@ -916,6 +917,9 @@ static int ParseThresholdRule(DetectEngineCtx *de_ctx, char *rawstr, parsed_track = TRACK_DST; else if (strcasecmp(th_track,"by_src") == 0) parsed_track = TRACK_SRC; + else if (strcasecmp(th_track, "by_both") == 0) { + parsed_track = TRACK_BOTH; + } else if (strcasecmp(th_track,"by_rule") == 0) parsed_track = TRACK_RULE; else { @@ -1219,7 +1223,8 @@ static FILE *SCThresholdConfGenerateValidDummyFD05(void) const char *buffer = "rate_filter gen_id 1, sig_id 10, track by_src, count 1, seconds 60, new_action drop, timeout 10\n" "rate_filter gen_id 1, sig_id 100, track by_dst, count 10, seconds 60, new_action pass, timeout 5\n" - "rate_filter gen_id 1, sig_id 1000, track by_rule, count 100, seconds 60, new_action alert, timeout 30\n"; + "rate_filter gen_id 1, sig_id 1000, track by_rule, count 100, seconds 60, new_action alert, timeout 30\n" + "rate_filter gen_id 1, sig_id 10000, track by_both, count 1000, seconds 60, new_action reject, timeout 21\n"; fd = SCFmemopen((void *)buffer, strlen(buffer), "r"); if (fd == NULL) @@ -1240,7 +1245,8 @@ static FILE *SCThresholdConfGenerateValidDummyFD06(void) const char *buffer = "rate_filter \\\ngen_id 1, sig_id 10, track by_src, count 1, seconds 60\\\n, new_action drop, timeout 10\n" "rate_filter gen_id 1, \\\nsig_id 100, track by_dst, \\\ncount 10, seconds 60, new_action pass, timeout 5\n" - "rate_filter gen_id 1, sig_id 1000, \\\ntrack by_rule, count 100, seconds 60, new_action alert, timeout 30\n"; + "rate_filter gen_id 1, sig_id 1000, \\\ntrack by_rule, count 100, seconds 60, new_action alert, timeout 30\n" + "rate_filter gen_id 1, sig_id 10000, track by_both, count 1000, \\\nseconds 60, new_action reject, timeout 21\n"; fd = SCFmemopen((void *)buffer, strlen(buffer), "r"); if (fd == NULL) @@ -2475,6 +2481,230 @@ static int SCThresholdConfTest21(void) PASS; } +/** +* \brief Creates a dummy rate_filter file, for testing rate filtering by_both source and destination +* +* \retval fd Pointer to file descriptor. +*/ +static FILE *SCThresholdConfGenerateValidDummyFD22(void) +{ + FILE *fd = NULL; + const char *buffer = + "rate_filter gen_id 1, sig_id 10, track by_both, count 2, seconds 5, new_action drop, timeout 6\n"; + + fd = SCFmemopen((void *)buffer, strlen(buffer), "r"); + if (fd == NULL) + SCLogDebug("Error with SCFmemopen() called by Threshold Config test code"); + + return fd; +} + +/** +* \test Check if the rate_filter rules work with track by_both +* +* \retval 1 on succces +* \retval 0 on failure +*/ +static int SCThresholdConfTest22(void) +{ + ThreadVars th_v; + memset(&th_v, 0, sizeof(th_v)); + + IPPairInitConfig(IPPAIR_QUIET); + + struct timeval ts; + memset(&ts, 0, sizeof(struct timeval)); + TimeGet(&ts); + + /* This packet will cause rate_filter */ + Packet *p1 = UTHBuildPacketSrcDst((uint8_t*)"lalala", 6, IPPROTO_TCP, "172.26.0.1", "172.26.0.10"); + FAIL_IF_NULL(p1); + + /* Should not be filtered for different destination */ + Packet *p2 = UTHBuildPacketSrcDst((uint8_t*)"lalala", 6, IPPROTO_TCP, "172.26.0.1", "172.26.0.2"); + FAIL_IF_NULL(p2); + + /* Should not be filtered when both src and dst the same */ + Packet *p3 = UTHBuildPacketSrcDst((uint8_t*)"lalala", 6, IPPROTO_TCP, "172.26.0.1", "172.26.0.1"); + FAIL_IF_NULL(p3); + + DetectEngineThreadCtx *det_ctx = NULL; + + DetectEngineCtx *de_ctx = DetectEngineCtxInit(); + FAIL_IF_NULL(de_ctx); + de_ctx->flags |= DE_QUIET; + + Signature *sig = DetectEngineAppendSig(de_ctx, + "alert tcp any any -> any any (msg:\"ratefilter by_both test\"; gid:1; sid:10;)"); + FAIL_IF_NULL(sig); + + FAIL_IF_NOT_NULL(g_ut_threshold_fp); + g_ut_threshold_fp = SCThresholdConfGenerateValidDummyFD22(); + FAIL_IF_NULL(g_ut_threshold_fp); + SCThresholdConfInitContext(de_ctx); + + SigGroupBuild(de_ctx); + DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx); + + TimeGet(&p1->ts); + p2->ts = p3->ts = p1->ts; + + /* All should be alerted, none dropped */ + SigMatchSignatures(&th_v, de_ctx, det_ctx, p1); + FAIL_IF(PACKET_TEST_ACTION(p1, ACTION_DROP)); + FAIL_IF(PacketAlertCheck(p1, 10) != 1); + + SigMatchSignatures(&th_v, de_ctx, det_ctx, p2); + FAIL_IF(PACKET_TEST_ACTION(p2, ACTION_DROP)); + FAIL_IF(PacketAlertCheck(p2, 10) != 1); + + SigMatchSignatures(&th_v, de_ctx, det_ctx, p3); + FAIL_IF(PACKET_TEST_ACTION(p3, ACTION_DROP)); + FAIL_IF(PacketAlertCheck(p3, 10) != 1); + + p1->action = p2->action = p3->action = 0; + + TimeSetIncrementTime(2); + TimeGet(&p1->ts); + p2->ts = p3->ts = p1->ts; + + /* p1 still shouldn't be dropped after 2nd alert */ + SigMatchSignatures(&th_v, de_ctx, det_ctx, p1); + FAIL_IF(PACKET_TEST_ACTION(p1, ACTION_DROP)); + FAIL_IF(PacketAlertCheck(p1, 10) != 1); + + p1->action = 0; + + TimeSetIncrementTime(2); + TimeGet(&p1->ts); + p2->ts = p3->ts = p1->ts; + + /* All should be alerted, only p1 must be dropped due to rate_filter*/ + SigMatchSignatures(&th_v, de_ctx, det_ctx, p1); + FAIL_IF_NOT(PACKET_TEST_ACTION(p1, ACTION_DROP)); + FAIL_IF(PacketAlertCheck(p1, 10) != 1); + + SigMatchSignatures(&th_v, de_ctx, det_ctx, p2); + FAIL_IF(PACKET_TEST_ACTION(p2, ACTION_DROP)); + FAIL_IF(PacketAlertCheck(p2, 10) != 1); + + SigMatchSignatures(&th_v, de_ctx, det_ctx, p3); + FAIL_IF(PACKET_TEST_ACTION(p3, ACTION_DROP)); + FAIL_IF(PacketAlertCheck(p3, 10) != 1); + + p1->action = p2->action = p3->action = 0; + + TimeSetIncrementTime(7); + TimeGet(&p1->ts); + p2->ts = p3->ts = p1->ts; + + /* All should be alerted, none dropped (because timeout expired) */ + SigMatchSignatures(&th_v, de_ctx, det_ctx, p1); + FAIL_IF(PACKET_TEST_ACTION(p1, ACTION_DROP)); + FAIL_IF(PacketAlertCheck(p1, 10) != 1); + + SigMatchSignatures(&th_v, de_ctx, det_ctx, p2); + FAIL_IF(PACKET_TEST_ACTION(p2, ACTION_DROP)); + FAIL_IF(PacketAlertCheck(p2, 10) != 1); + + SigMatchSignatures(&th_v, de_ctx, det_ctx, p3); + FAIL_IF(PACKET_TEST_ACTION(p3, ACTION_DROP)); + FAIL_IF(PacketAlertCheck(p3, 10) != 1); + + UTHFreePacket(p3); + UTHFreePacket(p2); + UTHFreePacket(p1); + + DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx); + DetectEngineCtxFree(de_ctx); + IPPairShutdown(); + PASS; +} + +/** +* \brief Creates a dummy rate_filter file, for testing rate filtering by_both source and destination +* +* \retval fd Pointer to file descriptor. +*/ +static FILE *SCThresholdConfGenerateValidDummyFD23(void) +{ + FILE *fd = NULL; + const char *buffer = + "rate_filter gen_id 1, sig_id 10, track by_both, count 1, seconds 5, new_action drop, timeout 6\n"; + + fd = SCFmemopen((void *)buffer, strlen(buffer), "r"); + if (fd == NULL) + SCLogDebug("Error with SCFmemopen() called by Threshold Config test code"); + + return fd; +} + +/** +* \test Check if the rate_filter by_both work when similar packets +* going in opposite direction +* +* \retval 1 on succces +* \retval 0 on failure +*/ +static int SCThresholdConfTest23(void) +{ + ThreadVars th_v; + memset(&th_v, 0, sizeof(th_v)); + + IPPairInitConfig(IPPAIR_QUIET); + + struct timeval ts; + memset(&ts, 0, sizeof(struct timeval)); + TimeGet(&ts); + + /* Create two packets between same addresses in opposite direction */ + Packet *p1 = UTHBuildPacketSrcDst((uint8_t*)"lalala", 6, IPPROTO_TCP, "172.26.0.1", "172.26.0.10"); + FAIL_IF_NULL(p1); + + Packet *p2 = UTHBuildPacketSrcDst((uint8_t*)"lalala", 6, IPPROTO_TCP, "172.26.0.10", "172.26.0.1"); + FAIL_IF_NULL(p2); + + DetectEngineThreadCtx *det_ctx = NULL; + + DetectEngineCtx *de_ctx = DetectEngineCtxInit(); + FAIL_IF_NULL(de_ctx); + de_ctx->flags |= DE_QUIET; + + Signature *sig = DetectEngineAppendSig(de_ctx, + "alert tcp any any -> any any (msg:\"ratefilter by_both test\"; gid:1; sid:10;)"); + FAIL_IF_NULL(sig); + + FAIL_IF_NOT_NULL(g_ut_threshold_fp); + g_ut_threshold_fp = SCThresholdConfGenerateValidDummyFD23(); + FAIL_IF_NULL(g_ut_threshold_fp); + SCThresholdConfInitContext(de_ctx); + + SigGroupBuild(de_ctx); + DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx); + + TimeGet(&p1->ts); + SigMatchSignatures(&th_v, de_ctx, det_ctx, p1); + /* First packet should be alerted, not dropped */ + FAIL_IF(PACKET_TEST_ACTION(p1, ACTION_DROP)); + FAIL_IF(PacketAlertCheck(p1, 10) != 1); + + TimeSetIncrementTime(2); + TimeGet(&p2->ts); + SigMatchSignatures(&th_v, de_ctx, det_ctx, p2); + + /* Second packet should be dropped because it considered as "the same pair" + and rate_filter count reached*/ + FAIL_IF_NOT(PACKET_TEST_ACTION(p2, ACTION_DROP)); + FAIL_IF(PacketAlertCheck(p2, 10) != 1); + + UTHFreePacket(p2); + UTHFreePacket(p1); + + DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx); + DetectEngineCtxFree(de_ctx); + IPPairShutdown(); + PASS; +} #endif /* UNITTESTS */ /** @@ -2516,6 +2746,11 @@ void SCThresholdConfRegisterTests(void) SCThresholdConfTest20); UtRegisterTest("SCThresholdConfTest21 - suppress parsing", SCThresholdConfTest21); + UtRegisterTest("SCThresholdConfTest22 - rate_filter by_both", + SCThresholdConfTest22); + UtRegisterTest("SCThresholdConfTest23 - rate_filter by_both opposite", + SCThresholdConfTest23); + #endif /* UNITTESTS */ }