/* Copyright (c) 2009 Open Information Security Foundation */ /** \file * \author Breno Silva * \author Victor Julien */ #include "suricata-common.h" #include "debug.h" #include "detect.h" #include "flow.h" #include "detect-parse.h" #include "detect-engine-sigorder.h" #include "detect-engine-siggroup.h" #include "detect-engine-address.h" #include "detect-engine-port.h" #include "detect-engine-mpm.h" #include "detect-engine-iponly.h" #include "detect-engine.h" #include "detect-engine-threshold.h" #include "detect-content.h" #include "detect-uricontent.h" #include "util-hash.h" #include "util-time.h" #include "util-error.h" #include "util-debug.h" #include "util-var-name.h" #include "tm-modules.h" /** * \brief Handle a packet and check if needs a threshold logic * * \param de_ctx Detection Context * \param sig Signature pointer * \param p Packet structure * */ void PacketAlertHandle(DetectEngineCtx *de_ctx, Signature *sig, Packet *p) { SCEnter(); /* retrieve the sig match data */ DetectThresholdData *td = SigGetThresholdType(sig,p); SCLogDebug("td %p", td); /* if have none just alert, otherwise handle thresholding */ if (td == NULL) { PacketAlertAppend(p, sig->gid, sig->id, sig->rev, sig->prio, sig->msg, sig->class_msg, sig->sigref); } else { PacketAlertThreshold(de_ctx, td, p, sig); } SCReturn; } /** * \brief Check if a certain signature has threshold option * * \param sig Signature pointer * \param p Packet structure * * \retval tsh Return the threshold data from signature or NULL if not found */ DetectThresholdData *SigGetThresholdType(Signature *sig, Packet *p) { SigMatch *sm = sig->match; DetectThresholdData *tsh = NULL; if(p == NULL) return NULL; while (sm != NULL) { if (sm->type == DETECT_THRESHOLD || sm->type == DETECT_DETECTION_FILTER) { tsh = (DetectThresholdData *)sm->ctx; return tsh; } sm = sm->next; } return NULL; } /** * \brief Search for a threshold data into threshold hash table * * \param de_ctx Dectection Context * \param tsh_ptr Threshold element * \param p Packet structure * * \retval lookup_tsh Return the threshold element */ DetectThresholdEntry *ThresholdHashSearch(DetectEngineCtx *de_ctx, DetectThresholdEntry *tsh_ptr, Packet *p) { SCEnter(); DetectThresholdEntry *lookup_tsh = NULL; SCLogDebug("tsh_ptr->track %u", tsh_ptr->track); if (tsh_ptr->track == TRACK_DST) { if (PKT_IS_IPV4(p)) { SCLogDebug("ipv4 dst"); lookup_tsh = HashListTableLookup(de_ctx->ths_ctx.threshold_hash_table_dst, tsh_ptr, sizeof(DetectThresholdEntry)); } else if (PKT_IS_IPV6(p)) { lookup_tsh = HashListTableLookup(de_ctx->ths_ctx.threshold_hash_table_dst_ipv6, tsh_ptr, sizeof(DetectThresholdEntry)); } } else if (tsh_ptr->track == TRACK_SRC) { if (PKT_IS_IPV4(p)) { SCLogDebug("ipv4 src"); lookup_tsh = HashListTableLookup(de_ctx->ths_ctx.threshold_hash_table_src, tsh_ptr, sizeof(DetectThresholdEntry)); } else if (PKT_IS_IPV6(p)) lookup_tsh = HashListTableLookup(de_ctx->ths_ctx.threshold_hash_table_src_ipv6, tsh_ptr, sizeof(DetectThresholdEntry)); } else { SCLogDebug("no track, weird"); } SCReturnPtr(lookup_tsh, "DetectThresholdEntry"); } /** * \brief Remove timeout threshold hash elements * * \param de_ctx Dectection Context * */ /** \todo In some conditions HashListtableRemove returns at dt->array = NULL * Must need to check it **/ void ThresholdTimeoutRemove(DetectEngineCtx *de_ctx) { struct timeval tv; DetectThresholdEntry *tsh = NULL; HashListTableBucket *next = NULL; memset(&tv, 0x00, sizeof(tv)); TimeGet(&tv); SCMutexLock(&de_ctx->ths_ctx.threshold_table_lock); next = HashListTableGetListHead(de_ctx->ths_ctx.threshold_hash_table_src); while (next != NULL) { tsh = HashListTableGetListData(next); if (tsh && ((tv.tv_sec - tsh->tv_sec1) > tsh->seconds)) { if (tsh->ipv == 4) { if (tsh->type == TRACK_SRC) { HashListTableRemove(de_ctx->ths_ctx.threshold_hash_table_src, tsh, sizeof(DetectThresholdEntry)); } else if (tsh->type == TRACK_DST) { HashListTableRemove(de_ctx->ths_ctx.threshold_hash_table_dst, tsh, sizeof(DetectThresholdEntry)); } } else if (tsh->ipv == 6) { if (tsh->type == TRACK_SRC) { HashListTableRemove(de_ctx->ths_ctx.threshold_hash_table_src_ipv6, tsh, sizeof(DetectThresholdEntry)); } else if (tsh->type == TRACK_DST) { HashListTableRemove(de_ctx->ths_ctx.threshold_hash_table_dst_ipv6, tsh, sizeof(DetectThresholdEntry)); } } } next = HashListTableGetListNext(next); } SCMutexUnlock(&de_ctx->ths_ctx.threshold_table_lock); return; } /** * \brief Add threshold element into hash table * * \param de_ctx Dectection Context * \param tsh_ptr Threshold element * \param p Packet structure * */ void ThresholdHashAdd(DetectEngineCtx *de_ctx, DetectThresholdEntry *tsh_ptr, Packet *p) { SCEnter(); int ret = 0; if (tsh_ptr->ipv == 4) { SCLogDebug("ipv4"); if (tsh_ptr->track == TRACK_DST) { SCLogDebug("dst"); ret = HashListTableAdd(de_ctx->ths_ctx.threshold_hash_table_dst, tsh_ptr, sizeof(DetectThresholdEntry)); } else if (tsh_ptr->track == TRACK_SRC) { SCLogDebug("src"); ret = HashListTableAdd(de_ctx->ths_ctx.threshold_hash_table_src, tsh_ptr, sizeof(DetectThresholdEntry)); } } else if (tsh_ptr->ipv == 6) { if (tsh_ptr->track == TRACK_DST) ret = HashListTableAdd(de_ctx->ths_ctx.threshold_hash_table_dst_ipv6, tsh_ptr, sizeof(DetectThresholdEntry)); else if (tsh_ptr->track == TRACK_SRC) ret = HashListTableAdd(de_ctx->ths_ctx.threshold_hash_table_src_ipv6, tsh_ptr, sizeof(DetectThresholdEntry)); } if(ret == -1) { SCLogError(SC_ERR_THRESHOLD_HASH_ADD, "failed to add element into the hash table"); } SCReturn; return; } /** * \brief Make the threshold logic for signatures * * \param de_ctx Dectection Context * \param tsh_ptr Threshold element * \param p Packet structure * \param s Signature structure * */ void PacketAlertThreshold(DetectEngineCtx *de_ctx, DetectThresholdData *td, Packet *p, Signature *s) { SCEnter(); struct timeval ts; DetectThresholdEntry *lookup_tsh = NULL; DetectThresholdEntry *ste = NULL; if (td == NULL) SCReturn; /* setup the Entry we use to search our hash with */ ste = SCMalloc(sizeof(DetectThresholdEntry)); if (ste == NULL) { SCLogError(SC_ERR_MEM_ALLOC, "SCMalloc failed: %s", strerror(errno)); SCReturn; } memset(ste, 0x00, sizeof(ste)); if (PKT_IS_IPV4(p)) ste->ipv = 4; else if (PKT_IS_IPV6(p)) ste->ipv = 6; ste->sid = s->id; ste->gid = s->gid; if (td->track == TRACK_DST) { COPY_ADDRESS(&p->dst, &ste->addr); } else if (td->track == TRACK_SRC) { COPY_ADDRESS(&p->src, &ste->addr); } ste->track = td->track; ste->seconds = td->seconds; SCLogDebug("ste %p", ste); memset(&ts, 0x00, sizeof(ts)); TimeGet(&ts); SCMutexLock(&de_ctx->ths_ctx.threshold_table_lock); switch(td->type) { case TYPE_LIMIT: { SCLogDebug("limit"); lookup_tsh = ThresholdHashSearch(de_ctx, ste, p); SCLogDebug("lookup_tsh %p", lookup_tsh); if (lookup_tsh != NULL) { if ((ts.tv_sec - lookup_tsh->tv_sec1) < td->seconds) { if (lookup_tsh->current_count < td->count) { PacketAlertAppend(p, s->gid, s->id, s->rev, s->prio, s->msg, s->class_msg, s->sigref); } lookup_tsh->current_count++; } else { lookup_tsh->tv_sec1 = ts.tv_sec; lookup_tsh->current_count = 1; PacketAlertAppend(p, s->gid, s->id, s->rev, s->prio, s->msg, s->class_msg, s->sigref); } } else { ste->tv_sec1 = ts.tv_sec; ste->current_count = 1; PacketAlertAppend(p, s->gid, s->id, s->rev, s->prio, s->msg, s->class_msg, s->sigref); ThresholdHashAdd(de_ctx, ste, p); ste = NULL; } break; } case TYPE_THRESHOLD: { SCLogDebug("threshold"); lookup_tsh = ThresholdHashSearch(de_ctx, ste, p); if (lookup_tsh != NULL) { if ((ts.tv_sec - lookup_tsh->tv_sec1) < td->seconds) { lookup_tsh->current_count++; if (lookup_tsh->current_count >= td->count) { PacketAlertAppend(p, s->gid, s->id, s->rev, s->prio, s->msg, s->class_msg, s->sigref); lookup_tsh->current_count = 0; } } else { lookup_tsh->tv_sec1 = ts.tv_sec; lookup_tsh->current_count = 1; } } else { ste->current_count = 1; ste->tv_sec1 = ts.tv_sec; if (td->count == 1) { PacketAlertAppend(p, s->gid, s->id, s->rev, s->prio, s->msg, s->class_msg, s->sigref); ste->current_count = 0; } else { ThresholdHashAdd(de_ctx,ste,p); ste = NULL; } } break; } case TYPE_BOTH: { SCLogDebug("both"); lookup_tsh = ThresholdHashSearch(de_ctx,ste,p); if (lookup_tsh != NULL) { if ((ts.tv_sec - lookup_tsh->tv_sec1) < td->seconds) { lookup_tsh->current_count++; if (lookup_tsh->current_count == td->count) { PacketAlertAppend(p, s->gid, s->id, s->rev, s->prio, s->msg, s->class_msg, s->sigref); } } else { lookup_tsh->tv_sec1 = ts.tv_sec; lookup_tsh->current_count = 1; } } else { ste->current_count = 1; ste->tv_sec1 = ts.tv_sec; if (td->count == 1) { PacketAlertAppend(p, s->gid, s->id, s->rev, s->prio, s->msg, s->class_msg, s->sigref); ste->current_count = 0; } else { ThresholdHashAdd(de_ctx,ste,p); ste = NULL; } } break; } /* detection_filter */ case TYPE_DETECTION: { SCLogDebug("detection_filter"); lookup_tsh = ThresholdHashSearch(de_ctx,ste,p); if (lookup_tsh != NULL) { if ((ts.tv_sec - lookup_tsh->tv_sec1) < td->seconds) { lookup_tsh->current_count++; if (lookup_tsh->current_count >= td->count) { PacketAlertAppend(p, s->gid, s->id, s->rev, s->prio, s->msg, s->class_msg, s->sigref); } } else { lookup_tsh->tv_sec1 = ts.tv_sec; lookup_tsh->current_count = 1; } } else { ste->current_count = 1; ste->tv_sec1 = ts.tv_sec; if (td->count == 1) { PacketAlertAppend(p, s->gid, s->id, s->rev, s->prio, s->msg, s->class_msg, s->sigref); } ThresholdHashAdd(de_ctx, ste, p); ste = NULL; } break; } } SCMutexUnlock(&de_ctx->ths_ctx.threshold_table_lock); if (ste != NULL) SCFree(ste); ThresholdTimeoutRemove(de_ctx); SCReturn; } void ThresholdFreeFunc(void *data) { if (data != NULL) SCFree(data); return; } /** * \brief Compare elements into the hash table * * \param data1 First element to compare * \param len1 length of first element * \param data2 Second element to compare * \param len2 length of second element * * \retval 1 Match or 0 No Match */ char ThresholdCompareFunc(void *data1, uint16_t len1, void *data2,uint16_t len2) { SCEnter(); DetectThresholdEntry *a = (DetectThresholdEntry *)data1; DetectThresholdEntry *b = (DetectThresholdEntry *)data2; if ((a->sid == b->sid) && (a->gid == b->gid) && (CMP_ADDR(&a->addr,&b->addr))) { SCReturnInt(1); } SCReturnInt(0); } /** * \brief Create the hash for threshold tables * * \param ht Hash Table * \param data Data that will be used to create the hash * \param datalen Data length * * \retval hash the hash */ uint32_t ThresholdHashFunc(HashListTable *ht, void *data, uint16_t datalen) { SCEnter(); DetectThresholdEntry *dt = (DetectThresholdEntry *)data; uint32_t hash = 0; if (dt->ipv == 4) hash = (dt->sid + dt->gid + dt->addr.addr_data32[0]); else if (dt->ipv == 6) hash = (dt->sid + dt->gid + dt->addr.addr_data32[0] + dt->addr.addr_data32[1] + dt->addr.addr_data32[2] + dt->addr.addr_data32[3]); else { SCLogDebug("no dt->ipv"); } SCReturnInt(hash % THRESHOLD_HASH_SIZE); } /** * \brief Init threshold context hash tables * * \param de_ctx Dectection Context * */ void ThresholdHashInit(DetectEngineCtx *de_ctx) { if (de_ctx->ths_ctx.threshold_hash_table_dst == NULL || de_ctx->ths_ctx.threshold_hash_table_src == NULL || de_ctx->ths_ctx.threshold_hash_table_src_ipv6 == NULL || de_ctx->ths_ctx.threshold_hash_table_dst_ipv6 == NULL) { de_ctx->ths_ctx.threshold_hash_table_dst = HashListTableInit(THRESHOLD_HASH_SIZE, ThresholdHashFunc, ThresholdCompareFunc, ThresholdFreeFunc); if(de_ctx->ths_ctx.threshold_hash_table_dst == NULL) { SCLogError(SC_ERR_MEM_ALLOC, "Threshold: Failed to initialize ipv4 dst hash table."); exit(EXIT_FAILURE); } de_ctx->ths_ctx.threshold_hash_table_src = HashListTableInit(THRESHOLD_HASH_SIZE, ThresholdHashFunc, ThresholdCompareFunc, ThresholdFreeFunc); if(de_ctx->ths_ctx.threshold_hash_table_dst == NULL) { SCLogError(SC_ERR_MEM_ALLOC, "Threshold: Failed to initialize ipv4 src hash table."); exit(EXIT_FAILURE); } de_ctx->ths_ctx.threshold_hash_table_src_ipv6 = HashListTableInit(THRESHOLD_HASH_SIZE, ThresholdHashFunc, ThresholdCompareFunc, ThresholdFreeFunc); if(de_ctx->ths_ctx.threshold_hash_table_dst == NULL) { SCLogError(SC_ERR_MEM_ALLOC, "Threshold: Failed to initialize ipv6 src hash table."); exit(EXIT_FAILURE); } de_ctx->ths_ctx.threshold_hash_table_dst_ipv6 = HashListTableInit(THRESHOLD_HASH_SIZE, ThresholdHashFunc, ThresholdCompareFunc, ThresholdFreeFunc); if(de_ctx->ths_ctx.threshold_hash_table_dst == NULL) { SCLogError(SC_ERR_MEM_ALLOC, "Threshold: Failed to initialize ipv6 dst hash table."); exit(EXIT_FAILURE); } if (SCMutexInit(&de_ctx->ths_ctx.threshold_table_lock, NULL) != 0) { SCLogError(SC_ERR_MEM_ALLOC, "Threshold: Failed to initialize hash table mutex."); exit(EXIT_FAILURE); } } } /** * \brief Destroy threshold context hash tables * * \param de_ctx Dectection Context * */ void ThresholdContextDestroy(DetectEngineCtx *de_ctx) { HashListTableFree(de_ctx->ths_ctx.threshold_hash_table_dst); HashListTableFree(de_ctx->ths_ctx.threshold_hash_table_src); HashListTableFree(de_ctx->ths_ctx.threshold_hash_table_dst_ipv6); HashListTableFree(de_ctx->ths_ctx.threshold_hash_table_src_ipv6); }