/* get the bit with idx from the host */ static XBit *HostBitGet(Host *h, uint16_t idx) { GenericVar *gv = HostGetStorageById(h, host_bit_id); for ( ; gv != NULL; gv = gv->next) { if (gv->type == DETECT_XBITS && gv->idx == idx) { return (XBit *)gv; } } return NULL; }
static DetectThresholdEntry *ThresholdHostLookupEntry(Host *h, uint32_t sid, uint32_t gid) { DetectThresholdEntry *e; for (e = HostGetStorageById(h, threshold_id); e != NULL; e = e->next) { if (e->sid == sid && e->gid == gid) break; } return e; }
/** \retval 1 host timed out wrt xbits * \retval 0 host still has active (non-expired) xbits */ int HostBitsTimedoutCheck(Host *h, struct timeval *ts) { GenericVar *gv = HostGetStorageById(h, host_bit_id); for ( ; gv != NULL; gv = gv->next) { if (gv->type == DETECT_XBITS) { XBit *xb = (XBit *)gv; if (xb->expire > (uint32_t)ts->tv_sec) return 0; } } return 1; }
static void HostBitRemove(Host *h, uint16_t idx) { XBit *fb = HostBitGet(h, idx); if (fb == NULL) return; GenericVar *gv = HostGetStorageById(h, host_bit_id); if (gv) { GenericVarRemove(&gv, (GenericVar *)fb); HostSetStorageById(h, host_bit_id, gv); } }
/** * \brief Removes the entries exceding the max timeout value * * \param tag_ctx Tag context * \param ts the current time * * \retval 1 no tags or tags removed -- host is free to go (from tag perspective) * \retval 0 still active tags */ int TagTimeoutCheck(Host *host, struct timeval *tv) { DetectTagDataEntry *tde = NULL; DetectTagDataEntry *tmp = NULL; DetectTagDataEntry *prev = NULL; int retval = 1; tmp = HostGetStorageById(host, host_tag_id); if (tmp == NULL) return 1; prev = NULL; while (tmp != NULL) { if ((tv->tv_sec - tmp->last_ts) <= TAG_MAX_LAST_TIME_SEEN) { prev = tmp; tmp = tmp->next; retval = 0; continue; } /* timed out */ if (prev != NULL) { prev->next = tmp->next; tde = tmp; tmp = tde->next; SCFree(tde); (void) SC_ATOMIC_SUB(num_tags, 1); } else { HostSetStorageById(host, host_tag_id, tmp->next); tde = tmp; tmp = tde->next; SCFree(tde); (void) SC_ATOMIC_SUB(num_tags, 1); } } return retval; }
int ThresholdTimeoutCheck(Host *host, struct timeval *tv) { DetectThresholdEntry *tde = NULL; DetectThresholdEntry *tmp = NULL; DetectThresholdEntry *prev = NULL; int retval = 1; tmp = HostGetStorageById(host, threshold_id); if (tmp == NULL) return 1; prev = NULL; while (tmp != NULL) { if ((tv->tv_sec - tmp->tv_sec1) <= tmp->seconds) { prev = tmp; tmp = tmp->next; retval = 0; continue; } /* timed out */ if (prev != NULL) { prev->next = tmp->next; tde = tmp; tmp = tde->next; SCFree(tde); } else { HostSetStorageById(host, threshold_id, tmp->next); tde = tmp; tmp = tde->next; SCFree(tde); } } return retval; }
/* add a flowbit to the flow */ static void HostBitAdd(Host *h, uint16_t idx, uint32_t expire) { XBit *fb = HostBitGet(h, idx); if (fb == NULL) { fb = SCMalloc(sizeof(XBit)); if (unlikely(fb == NULL)) return; fb->type = DETECT_XBITS; fb->idx = idx; fb->next = NULL; fb->expire = expire; GenericVar *gv = HostGetStorageById(h, host_bit_id); GenericVarAppend(&gv, (GenericVar *)fb); HostSetStorageById(h, host_bit_id, gv); // bit already set, lets update it's time } else { fb->expire = expire; } }
/* lock before using this */ int HostHasHostBits(Host *host) { if (host == NULL) return 0; return HostGetStorageById(host, host_bit_id) ? 1 : 0; }
int TagHostHasTag(Host *host) { return HostGetStorageById(host, host_tag_id) ? 1 : 0; }
void TagHandlePacketHost(Host *host, Packet *p) { DetectTagDataEntry *tde = NULL; DetectTagDataEntry *prev = NULL; DetectTagDataEntry *iter; uint8_t flag_added = 0; iter = HostGetStorageById(host, host_tag_id); prev = NULL; while (iter != NULL) { /* update counters */ iter->last_ts = p->ts.tv_sec; switch (iter->metric) { case DETECT_TAG_METRIC_PACKET: iter->packets++; break; case DETECT_TAG_METRIC_BYTES: iter->bytes += GET_PKT_LEN(p); break; } /* If this packet triggered the rule with tag, we dont need * to log it (the alert will log it) */ if (!(iter->flags & TAG_ENTRY_FLAG_SKIPPED_FIRST)) { iter->flags |= TAG_ENTRY_FLAG_SKIPPED_FIRST; } else { /* Update metrics; remove if tag expired; and set alerts */ switch (iter->metric) { case DETECT_TAG_METRIC_PACKET: if (iter->packets > iter->count) { /* tag expired */ if (prev != NULL) { tde = iter; prev->next = iter->next; iter = iter->next; SCFree(tde); (void) SC_ATOMIC_SUB(num_tags, 1); continue; } else { tde = iter; iter = iter->next; SCFree(tde); (void) SC_ATOMIC_SUB(num_tags, 1); HostSetStorageById(host, host_tag_id, iter); continue; } } else if (flag_added == 0) { /* It's matching the tag. Add it to be logged and * update "flag_added" to add the packet once. */ p->flags |= PKT_HAS_TAG; flag_added++; } break; case DETECT_TAG_METRIC_BYTES: if (iter->bytes > iter->count) { /* tag expired */ if (prev != NULL) { tde = iter; prev->next = iter->next; iter = iter->next; SCFree(tde); (void) SC_ATOMIC_SUB(num_tags, 1); continue; } else { tde = iter; iter = iter->next; SCFree(tde); (void) SC_ATOMIC_SUB(num_tags, 1); HostSetStorageById(host, host_tag_id, iter); continue; } } else if (flag_added == 0) { /* It's matching the tag. Add it to be logged and * update "flag_added" to add the packet once. */ p->flags |= PKT_HAS_TAG; flag_added++; } break; case DETECT_TAG_METRIC_SECONDS: /* last_ts handles this metric, but also a generic time based * expiration to prevent dead sessions/hosts */ if (iter->last_ts - iter->first_ts > iter->count) { /* tag expired */ if (prev != NULL) { tde = iter; prev->next = iter->next; iter = iter->next; SCFree(tde); (void) SC_ATOMIC_SUB(num_tags, 1); continue; } else { tde = iter; iter = iter->next; SCFree(tde); (void) SC_ATOMIC_SUB(num_tags, 1); HostSetStorageById(host, host_tag_id, iter); continue; } } else if (flag_added == 0) { /* It's matching the tag. Add it to be logged and * update "flag_added" to add the packet once. */ p->flags |= PKT_HAS_TAG; flag_added++; } break; } } prev = iter; iter = iter->next; } }
/** * \brief Add a tag entry for a host. If it already exist, update it. * * \param tag_ctx Tag context for hosts * \param tde Tag data * \param p packet * * \retval 0 if it was added, 1 if it was updated */ int TagHashAddTag(DetectTagDataEntry *tde, Packet *p) { SCEnter(); uint8_t updated = 0; uint16_t num_tags = 0; Host *host = NULL; /* Lookup host in the hash. If it doesn't exist yet it's * created. */ if (tde->flags & TAG_ENTRY_FLAG_DIR_SRC) { host = HostGetHostFromHash(&p->src); } else if (tde->flags & TAG_ENTRY_FLAG_DIR_DST) { host = HostGetHostFromHash(&p->dst); } /* no host for us */ if (host == NULL) { return -1; } void *tag = HostGetStorageById(host, host_tag_id); if (tag == NULL) { /* get a new tde as the one we have is on the stack */ DetectTagDataEntry *new_tde = DetectTagDataCopy(tde); if (new_tde != NULL) { HostSetStorageById(host, host_tag_id, new_tde); (void) SC_ATOMIC_ADD(num_tags, 1); } } else { /* Append the tag to the list of this host */ /* First iterate installed entries searching a duplicated sid/gid */ DetectTagDataEntry *iter = NULL; for (iter = tag; 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 = iter->last_ts = tde->first_ts; iter->packets = 0; iter->bytes = 0; } updated = 1; break; } } /* If there was no entry of this rule, append the new tde */ if (updated == 0 && num_tags < DETECT_TAG_MAX_TAGS) { /* get a new tde as the one we have is on the stack */ DetectTagDataEntry *new_tde = DetectTagDataCopy(tde); if (new_tde != NULL) { (void) SC_ATOMIC_ADD(num_tags, 1); new_tde->next = tag; HostSetStorageById(host, host_tag_id, new_tde); } } else if (num_tags == DETECT_TAG_MAX_TAGS) { SCLogDebug("Max tags for sessions reached (%"PRIu16")", num_tags); } } HostRelease(host); SCReturnInt(updated); }
int ThresholdHostHasThreshold(Host *host) { return HostGetStorageById(host, threshold_id) ? 1 : 0; }
/** * \retval 2 silent match (no alert but apply actions) * \retval 1 normal match * \retval 0 no match */ int ThresholdHandlePacketHost(Host *h, Packet *p, const DetectThresholdData *td, uint32_t sid, uint32_t gid, PacketAlert *pa) { int ret = 0; DetectThresholdEntry *lookup_tsh = ThresholdHostLookupEntry(h, sid, gid); SCLogDebug("lookup_tsh %p sid %u gid %u", lookup_tsh, sid, gid); switch(td->type) { case TYPE_LIMIT: { SCLogDebug("limit"); if (lookup_tsh != NULL) { if ((p->ts.tv_sec - lookup_tsh->tv_sec1) < td->seconds) { lookup_tsh->current_count++; if (lookup_tsh->current_count <= td->count) { ret = 1; } else { ret = 2; } } else { lookup_tsh->tv_sec1 = p->ts.tv_sec; lookup_tsh->current_count = 1; ret = 1; } } else { DetectThresholdEntry *e = DetectThresholdEntryAlloc(td, p, sid, gid); if (e == NULL) { break; } e->tv_sec1 = p->ts.tv_sec; e->current_count = 1; ret = 1; e->next = HostGetStorageById(h, threshold_id); HostSetStorageById(h, threshold_id, e); } break; } case TYPE_THRESHOLD: { SCLogDebug("threshold"); if (lookup_tsh != NULL) { if ((p->ts.tv_sec - lookup_tsh->tv_sec1) < td->seconds) { lookup_tsh->current_count++; if (lookup_tsh->current_count >= td->count) { ret = 1; lookup_tsh->current_count = 0; } } else { lookup_tsh->tv_sec1 = p->ts.tv_sec; lookup_tsh->current_count = 1; } } else { if (td->count == 1) { ret = 1; } else { DetectThresholdEntry *e = DetectThresholdEntryAlloc(td, p, sid, gid); if (e == NULL) { break; } e->current_count = 1; e->tv_sec1 = p->ts.tv_sec; e->next = HostGetStorageById(h, threshold_id); HostSetStorageById(h, threshold_id, e); } } break; } case TYPE_BOTH: { SCLogDebug("both"); if (lookup_tsh != NULL) { if ((p->ts.tv_sec - lookup_tsh->tv_sec1) < td->seconds) { /* within time limit */ lookup_tsh->current_count++; if (lookup_tsh->current_count == td->count) { ret = 1; } else if (lookup_tsh->current_count > td->count) { /* silent match */ ret = 2; } } else { /* expired, so reset */ lookup_tsh->tv_sec1 = p->ts.tv_sec; lookup_tsh->current_count = 1; /* if we have a limit of 1, this is a match */ if (lookup_tsh->current_count == td->count) { ret = 1; } } } else { DetectThresholdEntry *e = DetectThresholdEntryAlloc(td, p, sid, gid); if (e == NULL) { break; } e->current_count = 1; e->tv_sec1 = p->ts.tv_sec; e->next = HostGetStorageById(h, threshold_id); HostSetStorageById(h, threshold_id, e); /* for the first match we return 1 to * indicate we should alert */ if (td->count == 1) { ret = 1; } } break; } /* detection_filter */ case TYPE_DETECTION: { SCLogDebug("detection_filter"); if (lookup_tsh != NULL) { long double time_diff = ((p->ts.tv_sec + p->ts.tv_usec/1000000.0) - (lookup_tsh->tv_sec1 + lookup_tsh->tv_usec1/1000000.0)); if (time_diff < td->seconds) { /* within timeout */ lookup_tsh->current_count++; if (lookup_tsh->current_count > td->count) { ret = 1; } } else { /* expired, reset */ lookup_tsh->tv_sec1 = p->ts.tv_sec; lookup_tsh->tv_usec1 = p->ts.tv_usec; lookup_tsh->current_count = 1; } } else { DetectThresholdEntry *e = DetectThresholdEntryAlloc(td, p, sid, gid); if (e == NULL) { break; } e->current_count = 1; 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); } break; } /* rate_filter */ 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; } 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); } break; } /* case TYPE_SUPPRESS: is not handled here */ default: SCLogError(SC_ERR_INVALID_VALUE, "type %d is not supported", td->type); } return ret; }
/** * \retval 2 silent match (no alert but apply actions) * \retval 1 normal match * \retval 0 no match */ int ThresholdHandlePacketHost(Host *h, Packet *p, DetectThresholdData *td, uint32_t sid, uint32_t gid) { int ret = 0; DetectThresholdEntry *lookup_tsh = ThresholdHostLookupEntry(h, sid, gid); SCLogDebug("lookup_tsh %p sid %u gid %u", lookup_tsh, sid, gid); switch(td->type) { case TYPE_LIMIT: { SCLogDebug("limit"); if (lookup_tsh != NULL) { if ((p->ts.tv_sec - lookup_tsh->tv_sec1) < td->seconds) { lookup_tsh->current_count++; if (lookup_tsh->current_count <= td->count) { ret = 1; } else { ret = 2; } } else { lookup_tsh->tv_sec1 = p->ts.tv_sec; lookup_tsh->current_count = 1; ret = 1; } } else { DetectThresholdEntry *e = DetectThresholdEntryAlloc(td, p, sid, gid); if (e == NULL) { break; } e->tv_sec1 = p->ts.tv_sec; e->current_count = 1; ret = 1; e->next = HostGetStorageById(h, threshold_id); HostSetStorageById(h, threshold_id, e); } break; } case TYPE_THRESHOLD: { SCLogDebug("threshold"); if (lookup_tsh != NULL) { if ((p->ts.tv_sec - lookup_tsh->tv_sec1) < td->seconds) { lookup_tsh->current_count++; if (lookup_tsh->current_count >= td->count) { ret = 1; lookup_tsh->current_count = 0; } } else { lookup_tsh->tv_sec1 = p->ts.tv_sec; lookup_tsh->current_count = 1; } } else { if (td->count == 1) { ret = 1; } else { DetectThresholdEntry *e = DetectThresholdEntryAlloc(td, p, sid, gid); if (e == NULL) { break; } e->current_count = 1; e->tv_sec1 = p->ts.tv_sec; e->next = HostGetStorageById(h, threshold_id); HostSetStorageById(h, threshold_id, e); } } break; } case TYPE_BOTH: { SCLogDebug("both"); if (lookup_tsh != NULL) { if ((p->ts.tv_sec - lookup_tsh->tv_sec1) < td->seconds) { /* within time limit */ lookup_tsh->current_count++; if (lookup_tsh->current_count == td->count) { ret = 1; } else if (lookup_tsh->current_count > td->count) { /* silent match */ ret = 2; } } else { /* expired, so reset */ lookup_tsh->tv_sec1 = p->ts.tv_sec; lookup_tsh->current_count = 1; /* if we have a limit of 1, this is a match */ if (lookup_tsh->current_count == td->count) { ret = 1; } } } else { DetectThresholdEntry *e = DetectThresholdEntryAlloc(td, p, sid, gid); if (e == NULL) { break; } e->current_count = 1; e->tv_sec1 = p->ts.tv_sec; e->next = HostGetStorageById(h, threshold_id); HostSetStorageById(h, threshold_id, e); /* for the first match we return 1 to * indicate we should alert */ if (td->count == 1) { ret = 1; } } break; } /* detection_filter */ case TYPE_DETECTION: { SCLogDebug("detection_filter"); if (lookup_tsh != NULL) { long double time_diff = ((p->ts.tv_sec + p->ts.tv_usec/1000000.0) - (lookup_tsh->tv_sec1 + lookup_tsh->tv_usec1/1000000.0)); if (time_diff < td->seconds) { /* within timeout */ lookup_tsh->current_count++; if (lookup_tsh->current_count > td->count) { ret = 1; } } else { /* expired, reset */ lookup_tsh->tv_sec1 = p->ts.tv_sec; lookup_tsh->tv_usec1 = p->ts.tv_usec; lookup_tsh->current_count = 1; } } else { DetectThresholdEntry *e = DetectThresholdEntryAlloc(td, p, sid, gid); if (e == NULL) { break; } e->current_count = 1; 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); } break; } /* rate_filter */ 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 */ switch (td->new_action) { case TH_ACTION_ALERT: PACKET_ALERT(p); break; case TH_ACTION_DROP: PACKET_DROP(p); break; case TH_ACTION_REJECT: PACKET_REJECT(p); break; case TH_ACTION_PASS: PACKET_PASS(p); break; default: /* Weird, leave the default action */ break; } 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 */ switch (td->new_action) { case TH_ACTION_ALERT: PACKET_ALERT(p); break; case TH_ACTION_DROP: PACKET_DROP(p); break; case TH_ACTION_REJECT: PACKET_REJECT(p); break; case TH_ACTION_PASS: PACKET_PASS(p); break; default: /* Weird, leave the default action */ break; } 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; } 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); } break; } case TYPE_SUPPRESS: { int res = 0; switch (td->track) { case TRACK_DST: res = DetectAddressMatch(td->addr, &p->dst); break; case TRACK_SRC: res = DetectAddressMatch(td->addr, &p->src); break; case TRACK_RULE: default: SCLogError(SC_ERR_INVALID_VALUE, "track mode %d is not supported", td->track); break; } if (res == 0) ret = 1; else ret = 2; /* suppressed but still need actions */ break; } default: SCLogError(SC_ERR_INVALID_VALUE, "type %d is not supported", td->type); } return ret; }
static int DetectThresholdTestSig3(void) { Packet *p = NULL; Signature *s = NULL; ThreadVars th_v; DetectEngineThreadCtx *det_ctx; int result = 0; int alerts = 0; struct timeval ts; DetectThresholdEntry *lookup_tsh = NULL; HostInitConfig(HOST_QUIET); memset (&ts, 0, sizeof(struct timeval)); TimeGet(&ts); memset(&th_v, 0, sizeof(th_v)); p = UTHBuildPacketReal((uint8_t *)"A",1,IPPROTO_TCP, "1.1.1.1", "2.2.2.2", 1024, 80); DetectEngineCtx *de_ctx = DetectEngineCtxInit(); if (de_ctx == NULL) { goto end; } de_ctx->flags |= DE_QUIET; s = de_ctx->sig_list = SigInit(de_ctx,"alert tcp any any -> any 80 (msg:\"Threshold limit\"; threshold: type limit, track by_dst, count 5, seconds 60; sid:10;)"); if (s == NULL) { goto end; } SigGroupBuild(de_ctx); DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx); TimeGet(&p->ts); SigMatchSignatures(&th_v, de_ctx, det_ctx, p); SigMatchSignatures(&th_v, de_ctx, det_ctx, p); SigMatchSignatures(&th_v, de_ctx, det_ctx, p); Host *host = HostLookupHostFromHash(&p->dst); if (host == NULL) { printf("host not found: "); goto cleanup; } if (!(ThresholdHostHasThreshold(host))) { HostRelease(host); printf("host has no threshold: "); goto cleanup; } HostRelease(host); TimeSetIncrementTime(200); TimeGet(&p->ts); SigMatchSignatures(&th_v, de_ctx, det_ctx, p); SigMatchSignatures(&th_v, de_ctx, det_ctx, p); SigMatchSignatures(&th_v, de_ctx, det_ctx, p); host = HostLookupHostFromHash(&p->dst); if (host == NULL) { printf("host not found: "); goto cleanup; } HostRelease(host); lookup_tsh = HostGetStorageById(host, ThresholdHostStorageId()); if (lookup_tsh == NULL) { HostRelease(host); printf("lookup_tsh is NULL: "); goto cleanup; } alerts = lookup_tsh->current_count; if (alerts == 3) result = 1; else { printf("alerts %u != 3: ", alerts); goto cleanup; } cleanup: SigGroupCleanup(de_ctx); SigCleanSignatures(de_ctx); DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx); DetectEngineCtxFree(de_ctx); end: UTHFreePackets(&p, 1); HostShutdown(); return result; }