diff --git a/src/detect-engine-tag.c b/src/detect-engine-tag.c index 5dbff553e7..16ad978fe7 100644 --- a/src/detect-engine-tag.c +++ b/src/detect-engine-tag.c @@ -35,8 +35,8 @@ static void TagTimeoutRemove(DetectTagHostCtx *tag_ctx, struct timeval *tv); SC_ATOMIC_DECLARE(unsigned int, num_tags); /**< Atomic counter, to know if we - have tagged hosts/sessions, - to avoid locking */ + have tagged hosts/sessions, + to avoid locking */ /* Global Ctx for tagging hosts */ DetectTagHostCtx *tag_ctx = NULL; @@ -136,10 +136,13 @@ void TagDestroyCtx(void) tag_ctx->tag_hash_table_ipv6 = NULL; SCMutexDestroy(&tag_ctx->lock); - SC_ATOMIC_DESTROY(num_tags); SCFree(tag_ctx); tag_ctx = NULL; +#ifdef DEBUG + BUG_ON(SC_ATOMIC_GET(num_tags) != 0); +#endif + SC_ATOMIC_DESTROY(num_tags); } /** \brief Reset the tagging engine context @@ -207,7 +210,7 @@ DetectTagDataEntryList *TagHashSearch(DetectTagHostCtx *tag_ctx, DetectTagDataEn * \param p Packet structure * */ -void TagHashAdd(DetectTagHostCtx *tag_ctx, DetectTagDataEntryList *dtde, Packet *p) +int TagHashAdd(DetectTagHostCtx *tag_ctx, DetectTagDataEntryList *dtde, Packet *p) { SCEnter(); @@ -223,7 +226,7 @@ void TagHashAdd(DetectTagHostCtx *tag_ctx, DetectTagDataEntryList *dtde, Packet dtde, sizeof(DetectTagDataEntry)); } - SCReturn; + SCReturnInt((ret == 0)); } /** @@ -237,20 +240,19 @@ void TagHashAdd(DetectTagHostCtx *tag_ctx, DetectTagDataEntryList *dtde, Packet */ int TagHashAddTag(DetectTagHostCtx *tag_ctx, DetectTagDataEntry *tde, Packet *p) { + SCEnter(); + + DetectTagDataEntryList *entry = NULL; uint8_t updated = 0; uint16_t num_tags = 0; - /* local, just for searching */ DetectTagDataEntryList tdl; - tdl.header_entry = NULL; tdl.header_entry = tde; - SCEnter(); SCMutexLock(&tag_ctx->lock); /* first search if we already have an entry of this host */ - DetectTagDataEntryList *entry = NULL; if (PKT_IS_IPV4(p)) { tdl.ipv = 4; if (tde->td->direction == DETECT_TAG_DIR_SRC) { @@ -272,7 +274,23 @@ int TagHashAddTag(DetectTagHostCtx *tag_ctx, DetectTagDataEntry *tde, Packet *p) DetectTagDataEntryList *new = SCMalloc(sizeof(DetectTagDataEntryList)); if (new != NULL) { memcpy(new, &tdl, sizeof(DetectTagDataEntryList)); - TagHashAdd(tag_ctx, new, p); + + /* get a new tde as the one we have is on the stack */ + DetectTagDataEntry *new_tde = DetectTagDataCopy(tde); + if (new_tde == NULL) { + SCFree(new); + } else { + new->header_entry = new_tde; + } + + /* increment num_tags before adding to prevent a minor race, + * on setting and checking the first tag */ + SC_ATOMIC_ADD(num_tags, 1); + if (!(TagHashAdd(tag_ctx, new, p))) { + SC_ATOMIC_SUB(num_tags, 1); + SCFree(new_tde); + SCFree(new); + } } else { SCLogDebug("Failed to allocate a new session"); } @@ -301,12 +319,16 @@ int TagHashAddTag(DetectTagHostCtx *tag_ctx, DetectTagDataEntry *tde, Packet *p) /* If there was no entry of this rule, append the new tde */ if (updated == 0 && num_tags < DETECT_TAG_MAX_TAGS) { - tde->next = entry->header_entry; - entry->header_entry = tde; + /* get a new tde as the one we have is on the stack */ + DetectTagDataEntry *new_tde = DetectTagDataCopy(tde); + if (new_tde != NULL) { + SC_ATOMIC_ADD(num_tags, 1); + new_tde->next = entry->header_entry; + entry->header_entry = new_tde; + } } else if (num_tags == DETECT_TAG_MAX_TAGS) { SCLogDebug("Max tags for sessions reached (%"PRIu16")", num_tags); } - } SCMutexUnlock(&tag_ctx->lock); @@ -321,8 +343,9 @@ int TagHashAddTag(DetectTagHostCtx *tag_ctx, DetectTagDataEntry *tde, Packet *p) * \param p packet * */ -void TagHandlePacket(DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx, - Packet *p) { +void TagHandlePacket(DetectEngineCtx *de_ctx, + DetectEngineThreadCtx *det_ctx, Packet *p) +{ DetectTagDataEntry *tde = NULL; DetectTagDataEntry *prev = NULL; @@ -331,14 +354,12 @@ void TagHandlePacket(DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx, DetectTagDataEntryList *tde_src = NULL; DetectTagDataEntryList *tde_dst = NULL; - unsigned int current_tags = SC_ATOMIC_GET(num_tags); /* If there's no tag, get out of here */ + unsigned int current_tags = SC_ATOMIC_GET(num_tags); if (current_tags == 0) return; uint8_t flag_added = 0; - struct timeval ts = { 0, 0 }; - TimeGet(&ts); /* First update and get session tags */ if (p->flow != NULL) { @@ -348,13 +369,15 @@ void TagHandlePacket(DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx, prev = NULL; while (iter != NULL) { /* update counters */ - iter->last_ts.tv_sec = ts.tv_sec; + iter->last_ts.tv_sec = p->ts.tv_sec; iter->packets++; iter->bytes += GET_PKT_LEN(p); /* If this packet triggered the rule with tag, we dont need * to log it (the alert will log it) */ - if (iter->first_time++ > 0) { + if (iter->skipped_first == 0) { + iter->skipped_first = 1; + } else if (iter->td != NULL) { /* Update metrics; remove if tag expired; and set alerts */ switch (iter->td->metric) { case DETECT_TAG_METRIC_PACKET: @@ -451,9 +474,9 @@ void TagHandlePacket(DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx, SCMutexLock(&tag_ctx->lock); /* Check for timeout tags if we reached the interval for checking it */ - if (ts.tv_sec - tag_ctx->last_ts.tv_sec > TAG_TIMEOUT_CHECK_INTERVAL) { - TagTimeoutRemove(tag_ctx, &ts); - tag_ctx->last_ts.tv_sec = ts.tv_sec; + if (p->ts.tv_sec - tag_ctx->last_ts.tv_sec > TAG_TIMEOUT_CHECK_INTERVAL) { + TagTimeoutRemove(tag_ctx, &p->ts); + tag_ctx->last_ts.tv_sec = p->ts.tv_sec; } if (PKT_IS_IPV4(p)) { @@ -481,13 +504,15 @@ void TagHandlePacket(DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx, prev = NULL; while (iter != NULL) { /* update counters */ - iter->last_ts.tv_sec = ts.tv_sec; + iter->last_ts.tv_sec = p->ts.tv_sec; iter->packets++; iter->bytes += GET_PKT_LEN(p); /* If this packet triggered the rule with tag, we dont need * to log it (the alert will log it) */ - if (iter->first_time++ > 0 && iter->td != NULL) { + if (iter->skipped_first == 0) { + iter->skipped_first = 1; + } else if (iter->td != NULL) { /* Update metrics; remove if tag expired; and set alerts */ switch (iter->td->metric) { case DETECT_TAG_METRIC_PACKET: @@ -497,14 +522,15 @@ void TagHandlePacket(DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx, tde = iter; prev->next = iter->next; iter = iter->next; - DetectTagDataEntryFree(tde); + SCFree(tde); + SC_ATOMIC_SUB(num_tags, 1); continue; } else { tde = iter; iter = iter->next; SCFree(tde); SC_ATOMIC_SUB(num_tags, 1); - tde_src->header_entry = NULL; + tde_src->header_entry = iter; continue; } } else if (flag_added == 0) { @@ -521,14 +547,15 @@ void TagHandlePacket(DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx, tde = iter; prev->next = iter->next; iter = iter->next; - DetectTagDataEntryFree(tde); + SCFree(tde); + SC_ATOMIC_SUB(num_tags, 1); continue; } else { tde = iter; iter = iter->next; SCFree(tde); SC_ATOMIC_SUB(num_tags, 1); - tde_src->header_entry = NULL; + tde_src->header_entry = iter; continue; } } else if (flag_added == 0) { @@ -547,14 +574,15 @@ void TagHandlePacket(DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx, tde = iter; prev->next = iter->next; iter = iter->next; - DetectTagDataEntryFree(tde); + SCFree(tde); + SC_ATOMIC_SUB(num_tags, 1); continue; } else { tde = iter; iter = iter->next; SCFree(tde); SC_ATOMIC_SUB(num_tags, 1); - tde_src->header_entry = NULL; + tde_src->header_entry = iter; continue; } } else if (flag_added == 0) { @@ -567,6 +595,7 @@ void TagHandlePacket(DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx, } } + prev = iter; iter = iter->next; } @@ -577,13 +606,15 @@ void TagHandlePacket(DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx, prev = NULL; while (iter != NULL) { /* update counters */ - iter->last_ts.tv_sec = ts.tv_sec; + iter->last_ts.tv_sec = p->ts.tv_sec; iter->packets++; iter->bytes += GET_PKT_LEN(p); /* If this packet triggered the rule with tag, we dont need * to log it (the alert will log it) */ - if (iter->first_time++ > 0 && iter->td != NULL) { + if (iter->skipped_first == 0) { + iter->skipped_first = 1; + } else if (iter->td != NULL) { /* Update metrics; remove if tag expired; and set alerts */ switch (iter->td->metric) { case DETECT_TAG_METRIC_PACKET: @@ -593,14 +624,15 @@ void TagHandlePacket(DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx, tde = iter; prev->next = iter->next; iter = iter->next; - DetectTagDataEntryFree(tde); + SCFree(tde); + SC_ATOMIC_SUB(num_tags, 1); continue; } else { tde = iter; iter = iter->next; SCFree(tde); SC_ATOMIC_SUB(num_tags, 1); - tde_dst->header_entry = NULL; + tde_dst->header_entry = iter; continue; } } else if (flag_added == 0) { @@ -617,14 +649,15 @@ void TagHandlePacket(DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx, tde = iter; prev->next = iter->next; iter = iter->next; - DetectTagDataEntryFree(tde); + SCFree(tde); + SC_ATOMIC_SUB(num_tags, 1); continue; } else { tde = iter; iter = iter->next; SCFree(tde); SC_ATOMIC_SUB(num_tags, 1); - tde_dst->header_entry = NULL; + tde_dst->header_entry = iter; continue; } } else if (flag_added == 0) { @@ -643,14 +676,15 @@ void TagHandlePacket(DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx, tde = iter; prev->next = iter->next; iter = iter->next; - DetectTagDataEntryFree(tde); + SCFree(tde); + SC_ATOMIC_SUB(num_tags, 1); continue; } else { tde = iter; iter = iter->next; SCFree(tde); SC_ATOMIC_SUB(num_tags, 1); - tde_dst->header_entry = NULL; + tde_dst->header_entry = iter; continue; } } else if (flag_added == 0) { @@ -683,9 +717,9 @@ static void TagTimeoutRemove(DetectTagHostCtx *tag_ctx, struct timeval *tv) HashListTableBucket *next = NULL; HashListTableBucket *buck = NULL; - DetectTagDataEntry *tde= NULL; + DetectTagDataEntry *tde = NULL; DetectTagDataEntry *tmp = NULL; - DetectTagDataEntry *prev= NULL; + DetectTagDataEntry *prev = NULL; DetectTagDataEntryList *tdl = NULL; diff --git a/src/detect-tag.c b/src/detect-tag.c index 589702566d..5da1864fd8 100644 --- a/src/detect-tag.c +++ b/src/detect-tag.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2007-2010 Open Information Security Foundation +/* Copyright (C) 2007-2011 Open Information Security Foundation * * You can copy, redistribute or modify this Program under the terms of * the GNU General Public License version 2 as published by the Free @@ -49,9 +49,7 @@ #include "util-debug.h" #include "threads.h" -extern SCSpinlock num_tags_sc_lock__; -extern unsigned int num_tags_sc_atomic__; - +SC_ATOMIC_EXTERN(unsigned int, num_tags); extern DetectTagHostCtx *tag_ctx; /* format: tag: , , , [direction]; */ @@ -99,6 +97,24 @@ error: return; } +DetectTagDataEntry *DetectTagDataCopy(DetectTagDataEntry *dtd) { + DetectTagDataEntry *tde = SCMalloc(sizeof(DetectTagDataEntry)); + if (tde == NULL) { + return NULL; + } + memset(tde, 0, sizeof(DetectTagDataEntry)); + + tde->sid = dtd->sid; + tde->gid = dtd->gid; + + tde->td = dtd->td; + tde->first_ts.tv_sec = dtd->first_ts.tv_sec; + tde->first_ts.tv_usec = dtd->first_ts.tv_usec; + tde->last_ts.tv_sec = dtd->last_ts.tv_sec; + tde->last_ts.tv_usec = dtd->last_ts.tv_usec; + return tde; +} + /** * \brief This function is used to add a tag to a session (type session) * or update it if it's already installed. The number of times to @@ -130,36 +146,41 @@ int DetectTagFlowAdd(Packet *p, DetectTagDataEntry *tde) { memset(p->flow->tag_list, 0, sizeof(DetectTagDataEntryList)); } else { iter = p->flow->tag_list->header_entry; - } - /* First iterate installed entries searching a duplicated sid/gid */ - for (; iter != NULL; iter = iter->next) { - num_tags++; - if (iter->sid == tde->sid && iter->gid == tde->gid) { - iter->cnt_match++; - /* If so, update data, unless the maximum MATCH limit is - * reached. This prevents possible DOS attacks */ - if (iter->cnt_match < DETECT_TAG_MATCH_LIMIT) { - /* Reset time and counters */ - iter->first_ts.tv_sec = iter->last_ts.tv_sec = tde->first_ts.tv_sec; - iter->packets = 0; - iter->bytes = 0; + /* First iterate installed entries searching a duplicated sid/gid */ + for (; iter != NULL; iter = iter->next) { + num_tags++; + + if (iter->sid == tde->sid && iter->gid == tde->gid) { + iter->cnt_match++; + + /* If so, update data, unless the maximum MATCH limit is + * reached. This prevents possible DOS attacks */ + if (iter->cnt_match < DETECT_TAG_MATCH_LIMIT) { + /* Reset time and counters */ + iter->first_ts.tv_sec = iter->last_ts.tv_sec = tde->first_ts.tv_sec; + iter->packets = 0; + iter->bytes = 0; + } + updated = 1; + break; } - updated = 1; - break; } } /* If there was no entry of this rule, prepend the new tde */ if (updated == 0 && num_tags < DETECT_TAG_MAX_TAGS) { - tde->next = p->flow->tag_list->header_entry; - p->flow->tag_list->header_entry = tde; + DetectTagDataEntry *new_tde = DetectTagDataCopy(tde); + if (new_tde != NULL) { + new_tde->next = p->flow->tag_list->header_entry; + p->flow->tag_list->header_entry = new_tde; + SC_ATOMIC_ADD(num_tags, 1); + } } else if (num_tags == DETECT_TAG_MAX_TAGS) { SCLogDebug("Max tags for sessions reached (%"PRIu16")", num_tags); } SCMutexUnlock(&p->flow->m); - return updated; error: @@ -181,50 +202,35 @@ error: int DetectTagMatch (ThreadVars *t, DetectEngineThreadCtx *det_ctx, Packet *p, Signature *s, SigMatch *m) { DetectTagData *td = (DetectTagData *) m->ctx; - DetectTagDataEntry *tde = NULL; - tde = SCMalloc(sizeof(DetectTagDataEntry)); - if (tde == NULL) { - return 1; - } - memset(tde, 0, sizeof(DetectTagDataEntry)); - - tde->sid = s->id; - tde->gid = s->gid; - - tde->td = td; - TimeGet(&tde->first_ts); - tde->last_ts.tv_sec = tde->first_ts.tv_sec; + DetectTagDataEntry tde; + memset(&tde, 0, sizeof(DetectTagDataEntry)); + tde.sid = s->id; + tde.gid = s->gid; + tde.td = td; + tde.last_ts.tv_sec = tde.first_ts.tv_sec = p->ts.tv_usec; switch (td->type) { case DETECT_TAG_TYPE_HOST: - if (td->direction == DETECT_TAG_DIR_SRC || td->direction == DETECT_TAG_DIR_DST) { - SCLogDebug("Tagging Host with sid %"PRIu32":%"PRIu32"", s->id, s->gid); - if (TagHashAddTag(tag_ctx, tde, p) == 1) - SCFree(tde); - else - SC_ATOMIC_ADD(num_tags, 1); - - } else { - SCLogError(SC_ERR_INVALID_VALUE, "Error on direction of a tag keyword (not src nor dst)"); - SCFree(tde); - } +#ifdef DEBUG + BUG_ON(!(td->direction == DETECT_TAG_DIR_SRC || td->direction == DETECT_TAG_DIR_DST)); +#endif + SCLogDebug("Tagging Host with sid %"PRIu32":%"PRIu32"", s->id, s->gid); + TagHashAddTag(tag_ctx, &tde, p); break; case DETECT_TAG_TYPE_SESSION: if (p->flow != NULL) { /* If it already exists it will be updated */ - if (DetectTagFlowAdd(p, tde) == 1) - SCFree(tde); - else - SC_ATOMIC_ADD(num_tags, 1); + DetectTagFlowAdd(p, &tde); } else { SCLogDebug("No flow to append the session tag"); - SCFree(tde); } break; +#ifdef DEBUG default: - SCLogError(SC_ERR_INVALID_VALUE, "Error on type of a tag keyword (not session nor host)"); - SCFree(tde); + SCLogDebug("unknown type of a tag keyword (not session nor host)"); + BUG_ON(1); break; +#endif } return 1; @@ -392,8 +398,15 @@ error: void DetectTagDataListFree(void *ptr) { if (ptr != NULL) { DetectTagDataEntryList *list = (DetectTagDataEntryList *)ptr; - DetectTagDataEntryFree(list->header_entry); - SCFree(ptr); + DetectTagDataEntry *entry = list->header_entry; + + while (entry != NULL) { + DetectTagDataEntry *next_entry = entry->next; + DetectTagDataEntryFree(entry); + SC_ATOMIC_SUB(num_tags, 1); + entry = next_entry; + } + SCFree(list); } } /** @@ -405,9 +418,6 @@ void DetectTagDataListFree(void *ptr) { void DetectTagDataEntryFree(void *ptr) { if (ptr != NULL) { DetectTagDataEntry *dte = (DetectTagDataEntry *)ptr; - if (dte->next != NULL) - DetectTagDataEntryFree(dte->next); - dte->next = NULL; SCFree(dte); } } diff --git a/src/detect-tag.h b/src/detect-tag.h index 67f2562f64..25138b0d6f 100644 --- a/src/detect-tag.h +++ b/src/detect-tag.h @@ -64,9 +64,9 @@ enum { /** This will be the rule options/parameters */ typedef struct DetectTagData_ { uint8_t type; /**< tag type */ + uint8_t direction; /**< host direction */ uint32_t count; /**< count */ uint32_t metric; /**< metric */ - uint8_t direction; /**< host direction */ } DetectTagData; /** This is the installed data at the session/global or host table */ @@ -81,7 +81,7 @@ typedef struct DetectTagDataEntry_ { struct DetectTagDataEntry_ *next; /**< Pointer to the next tag of this * session/src_host/dst_host (if any from other rule) */ uint16_t cnt_match; /**< number of times this tag was reset/updated */ - uint8_t first_time; /**< Used at unified output. The first packet write the + uint8_t skipped_first; /**< Used for output. The first packet write the header with the data of the sig. The next packets use gid/sid/rev of the tagging engine */ } DetectTagDataEntry; @@ -90,14 +90,14 @@ typedef struct DetectTagDataEntryList_ { DetectTagDataEntry *header_entry; Address addr; /**< Var used to store dst or src addr */ uint8_t ipv; /**< IP Version */ - SCMutex lock; -}DetectTagDataEntryList; +} DetectTagDataEntryList; /* prototypes */ void DetectTagRegister (void); void DetectTagDataFree(void *ptr); void DetectTagDataEntryFree(void *ptr); void DetectTagDataListFree(void *ptr); +DetectTagDataEntry *DetectTagDataCopy(DetectTagDataEntry *dtd); #endif /* __DETECT_TAG_H__ */