static int SCAtomicTest01(void) { int result = 0; int a = 10; int b = 20; int *temp_int = NULL; SC_ATOMIC_DECL_AND_INIT(void *, temp); temp_int = SC_ATOMIC_GET(temp); if (temp_int != NULL) goto end; (void)SC_ATOMIC_SET(temp, &a); temp_int = SC_ATOMIC_GET(temp); if (temp_int == NULL) goto end; if (*temp_int != a) goto end; (void)SC_ATOMIC_SET(temp, &b); temp_int = SC_ATOMIC_GET(temp); if (temp_int == NULL) goto end; if (*temp_int != b) goto end; result = 1; end: return result; }
static TmEcode FlowWorkerThreadDeinit(ThreadVars *tv, void *data) { FlowWorkerThreadData *fw = data; DecodeThreadVarsFree(tv, fw->dtv); /* free TCP */ StreamTcpThreadDeinit(tv, (void *)fw->stream_thread); /* free DETECT */ void *detect_thread = SC_ATOMIC_GET(fw->detect_thread); if (detect_thread != NULL) { DetectEngineThreadCtxDeinit(tv, detect_thread); SC_ATOMIC_SET(fw->detect_thread, NULL); } /* Free output. */ OutputLoggerThreadDeinit(tv, fw->output_thread); /* free pq */ BUG_ON(fw->pq.len); SCMutexDestroy(&fw->pq.mutex_q); SC_ATOMIC_DESTROY(fw->detect_thread); SCFree(fw); return TM_ECODE_OK; }
/** * \brief select the queue to output in a round robin fashion. * * \param tv thread vars * \param p packet */ void TmqhOutputFlowRoundRobin(ThreadVars *tv, Packet *p) { int32_t qid = 0; TmqhFlowCtx *ctx = (TmqhFlowCtx *)tv->outctx; /* if no flow we use the first queue, * should be rare */ if (p->flow != NULL) { qid = SC_ATOMIC_GET(p->flow->autofp_tmqh_flow_qid); if (qid == -1) { qid = SC_ATOMIC_ADD(ctx->round_robin_idx, 1); if (qid >= ctx->size) { SC_ATOMIC_RESET(ctx->round_robin_idx); qid = 0; } (void) SC_ATOMIC_ADD(ctx->queues[qid].total_flows, 1); (void) SC_ATOMIC_SET(p->flow->autofp_tmqh_flow_qid, qid); } } else { qid = ctx->last++; if (ctx->last == ctx->size) ctx->last = 0; } (void) SC_ATOMIC_ADD(ctx->queues[qid].total_packets, 1); PacketQueue *q = ctx->queues[qid].q; SCMutexLock(&q->mutex_q); PacketEnqueue(q, p); SCCondSignal(&q->cond_q); SCMutexUnlock(&q->mutex_q); return; }
static TmEcode FlowWorkerThreadInit(ThreadVars *tv, const void *initdata, void **data) { FlowWorkerThreadData *fw = SCCalloc(1, sizeof(*fw)); if (fw == NULL) return TM_ECODE_FAILED; SC_ATOMIC_INIT(fw->detect_thread); SC_ATOMIC_SET(fw->detect_thread, NULL); fw->dtv = DecodeThreadVarsAlloc(tv); if (fw->dtv == NULL) { FlowWorkerThreadDeinit(tv, fw); return TM_ECODE_FAILED; } /* setup TCP */ if (StreamTcpThreadInit(tv, NULL, &fw->stream_thread_ptr) != TM_ECODE_OK) { FlowWorkerThreadDeinit(tv, fw); return TM_ECODE_FAILED; } if (DetectEngineEnabled()) { /* setup DETECT */ void *detect_thread = NULL; if (DetectEngineThreadCtxInit(tv, NULL, &detect_thread) != TM_ECODE_OK) { FlowWorkerThreadDeinit(tv, fw); return TM_ECODE_FAILED; } SC_ATOMIC_SET(fw->detect_thread, detect_thread); } /* Setup outputs for this thread. */ if (OutputLoggerThreadInit(tv, initdata, &fw->output_thread) != TM_ECODE_OK) { FlowWorkerThreadDeinit(tv, fw); return TM_ECODE_FAILED; } DecodeRegisterPerfCounters(fw->dtv, tv); AppLayerRegisterThreadCounters(tv); /* setup pq for stream end pkts */ memset(&fw->pq, 0, sizeof(PacketQueue)); SCMutexInit(&fw->pq.mutex_q, NULL); *data = fw; return TM_ECODE_OK; }
/** * \brief Print some stats to the log at program exit. * * \param tv Pointer to ThreadVars. * \param data Pointer to data, ErfFileThreadVars. */ void ReceiveErfDagThreadExitStats(ThreadVars *tv, void *data) { ErfDagThreadVars *ewtn = (ErfDagThreadVars *)data; (void)SC_ATOMIC_SET(ewtn->livedev->pkts, (uint64_t)SCPerfGetLocalCounterValue(ewtn->packets, tv->sc_perf_pca)); (void)SC_ATOMIC_SET(ewtn->livedev->drop, (uint64_t)SCPerfGetLocalCounterValue(ewtn->drops, tv->sc_perf_pca)); SCLogInfo("Stream: %d; Bytes: %"PRIu64"; Packets: %"PRIu64 "; Drops: %"PRIu64, ewtn->dagstream, ewtn->bytes, (uint64_t)SCPerfGetLocalCounterValue(ewtn->packets, tv->sc_perf_pca), (uint64_t)SCPerfGetLocalCounterValue(ewtn->drops, tv->sc_perf_pca)); }
/** * \brief Print some stats to the log at program exit. * * \param tv Pointer to ThreadVars. * \param data Pointer to data, ErfFileThreadVars. */ void ReceiveErfDagThreadExitStats(ThreadVars *tv, void *data) { ErfDagThreadVars *ewtn = (ErfDagThreadVars *)data; (void)SC_ATOMIC_SET(ewtn->livedev->pkts, StatsGetLocalCounterValue(tv, ewtn->packets)); (void)SC_ATOMIC_SET(ewtn->livedev->drop, StatsGetLocalCounterValue(tv, ewtn->drops)); SCLogInfo("Stream: %d; Bytes: %"PRIu64"; Packets: %"PRIu64 "; Drops: %"PRIu64, ewtn->dagstream, ewtn->bytes, StatsGetLocalCounterValue(tv, ewtn->packets), StatsGetLocalCounterValue(tv, ewtn->drops)); }
/** * \brief Update memcap value * * \param size new memcap value */ int IPPairSetMemcap(uint64_t size) { if ((uint64_t)SC_ATOMIC_GET(ippair_memuse) < size) { SC_ATOMIC_SET(ippair_config.memcap, size); return 1; } return 0; }
static inline void PcapDumpCounters(PcapThreadVars *ptv) { struct pcap_stat pcap_s; if (likely((pcap_stats(ptv->pcap_handle, &pcap_s) >= 0))) { StatsSetUI64(ptv->tv, ptv->capture_kernel_packets, pcap_s.ps_recv); StatsSetUI64(ptv->tv, ptv->capture_kernel_drops, pcap_s.ps_drop); (void) SC_ATOMIC_SET(ptv->livedev->drop, pcap_s.ps_drop); StatsSetUI64(ptv->tv, ptv->capture_kernel_ifdrops, pcap_s.ps_ifdrop); } }
void LogFilestoreRegister (void) { OutputRegisterFiledataModule(LOGGER_FILE_STORE, MODULE_NAME, "file", LogFilestoreLogInitCtx, LogFilestoreLogger, LogFilestoreLogThreadInit, LogFilestoreLogThreadDeinit, LogFilestoreLogExitPrintStats); OutputRegisterFiledataModule(LOGGER_FILE_STORE, MODULE_NAME, "file-store", LogFilestoreLogInitCtx, LogFilestoreLogger, LogFilestoreLogThreadInit, LogFilestoreLogThreadDeinit, LogFilestoreLogExitPrintStats); SC_ATOMIC_INIT(filestore_open_file_cnt); SC_ATOMIC_SET(filestore_open_file_cnt, 0); SCLogDebug("registered"); }
/** * \brief select the queue to output to based on queue lengths. * * \param tv thread vars * \param p packet */ void TmqhOutputFlowActivePackets(ThreadVars *tv, Packet *p) { int32_t qid = 0; TmqhFlowCtx *ctx = (TmqhFlowCtx *)tv->outctx; /* if no flow we use the first queue, * should be rare */ if (p->flow != NULL) { qid = SC_ATOMIC_GET(p->flow->autofp_tmqh_flow_qid); if (qid == -1) { uint16_t i = 0; int lowest_id = 0; TmqhFlowMode *queues = ctx->queues; uint32_t lowest = queues[i].q->len; for (i = 1; i < ctx->size; i++) { if (queues[i].q->len < lowest) { lowest = queues[i].q->len; lowest_id = i; } } qid = lowest_id; (void) SC_ATOMIC_SET(p->flow->autofp_tmqh_flow_qid, lowest_id); (void) SC_ATOMIC_ADD(ctx->queues[qid].total_flows, 1); } } else { qid = ctx->last++; if (ctx->last == ctx->size) ctx->last = 0; } (void) SC_ATOMIC_ADD(ctx->queues[qid].total_packets, 1); PacketQueue *q = ctx->queues[qid].q; SCMutexLock(&q->mutex_q); PacketEnqueue(q, p); #ifdef __tile__ q->cond_q = 1; #else SCCondSignal(&q->cond_q); #endif SCMutexUnlock(&q->mutex_q); return; }
/** * \brief select the queue to output based on address hash. * * \param tv thread vars. * \param p packet. */ void TmqhOutputFlowHash(ThreadVars *tv, Packet *p) { int32_t qid = 0; TmqhFlowCtx *ctx = (TmqhFlowCtx *)tv->outctx; /* if no flow we use the first queue, * should be rare */ if (p->flow != NULL) { qid = SC_ATOMIC_GET(p->flow->autofp_tmqh_flow_qid); if (qid == -1) { #if __WORDSIZE == 64 uint64_t addr = (uint64_t)p->flow; #else uint32_t addr = (uint32_t)p->flow; #endif addr >>= 7; /* we don't have to worry about possible overflow, since * ctx->size will be lesser than 2 ** 31 for sure */ qid = addr % ctx->size; (void) SC_ATOMIC_SET(p->flow->autofp_tmqh_flow_qid, qid); (void) SC_ATOMIC_ADD(ctx->queues[qid].total_flows, 1); }
void FlowWorkerReplaceDetectCtx(void *flow_worker, void *detect_ctx) { FlowWorkerThreadData *fw = flow_worker; SC_ATOMIC_SET(fw->detect_thread, detect_ctx); }
/** \internal * \brief Get a flow from the hash directly. * * Called in conditions where the spare queue is empty and memcap is reached. * * Walks the hash until a flow can be freed. Timeouts are disregarded, use_cnt * is adhered to. "flow_prune_idx" atomic int makes sure we don't start at the * top each time since that would clear the top of the hash leading to longer * and longer search times under high pressure (observed). * * \param tv thread vars * \param dtv decode thread vars (for flow log api thread data) * * \retval f flow or NULL */ static Flow *FlowGetUsedFlow(ThreadVars *tv, DecodeThreadVars *dtv) { uint32_t idx = SC_ATOMIC_GET(flow_prune_idx) % flow_config.hash_size; uint32_t cnt = flow_config.hash_size; while (cnt--) { if (++idx >= flow_config.hash_size) idx = 0; FlowBucket *fb = &flow_hash[idx]; if (FBLOCK_TRYLOCK(fb) != 0) continue; Flow *f = fb->tail; if (f == NULL) { FBLOCK_UNLOCK(fb); continue; } if (FLOWLOCK_TRYWRLOCK(f) != 0) { FBLOCK_UNLOCK(fb); continue; } /** never prune a flow that is used by a packet or stream msg * we are currently processing in one of the threads */ if (SC_ATOMIC_GET(f->use_cnt) > 0) { FBLOCK_UNLOCK(fb); FLOWLOCK_UNLOCK(f); continue; } /* remove from the hash */ if (f->hprev != NULL) f->hprev->hnext = f->hnext; if (f->hnext != NULL) f->hnext->hprev = f->hprev; if (fb->head == f) fb->head = f->hnext; if (fb->tail == f) fb->tail = f->hprev; f->hnext = NULL; f->hprev = NULL; f->fb = NULL; SC_ATOMIC_SET(fb->next_ts, 0); FBLOCK_UNLOCK(fb); int state = SC_ATOMIC_GET(f->flow_state); if (state == FLOW_STATE_NEW) f->flow_end_flags |= FLOW_END_FLAG_STATE_NEW; else if (state == FLOW_STATE_ESTABLISHED) f->flow_end_flags |= FLOW_END_FLAG_STATE_ESTABLISHED; else if (state == FLOW_STATE_CLOSED) f->flow_end_flags |= FLOW_END_FLAG_STATE_CLOSED; else if (state == FLOW_STATE_CAPTURE_BYPASSED) f->flow_end_flags |= FLOW_END_FLAG_STATE_BYPASSED; else if (state == FLOW_STATE_LOCAL_BYPASSED) f->flow_end_flags |= FLOW_END_FLAG_STATE_BYPASSED; f->flow_end_flags |= FLOW_END_FLAG_FORCED; if (SC_ATOMIC_GET(flow_flags) & FLOW_EMERGENCY) f->flow_end_flags |= FLOW_END_FLAG_EMERGENCY; /* invoke flow log api */ if (dtv && dtv->output_flow_thread_data) (void)OutputFlowLog(tv, dtv->output_flow_thread_data, f); FlowClearMemory(f, f->protomap); FlowUpdateState(f, FLOW_STATE_NEW); FLOWLOCK_UNLOCK(f); (void) SC_ATOMIC_ADD(flow_prune_idx, (flow_config.hash_size - cnt)); return f; } return NULL; }
/** \brief initialize the configuration * \warning Not thread safe */ void IPPairInitConfig(char quiet) { SCLogDebug("initializing ippair engine..."); if (IPPairStorageSize() > 0) g_ippair_size = sizeof(IPPair) + IPPairStorageSize(); memset(&ippair_config, 0, sizeof(ippair_config)); //SC_ATOMIC_INIT(flow_flags); SC_ATOMIC_INIT(ippair_counter); SC_ATOMIC_INIT(ippair_memuse); SC_ATOMIC_INIT(ippair_prune_idx); SC_ATOMIC_INIT(ippair_config.memcap); IPPairQueueInit(&ippair_spare_q); /* set defaults */ ippair_config.hash_rand = (uint32_t)RandomGet(); ippair_config.hash_size = IPPAIR_DEFAULT_HASHSIZE; ippair_config.prealloc = IPPAIR_DEFAULT_PREALLOC; SC_ATOMIC_SET(ippair_config.memcap, IPPAIR_DEFAULT_MEMCAP); /* Check if we have memcap and hash_size defined at config */ const char *conf_val; uint32_t configval = 0; /** set config values for memcap, prealloc and hash_size */ uint64_t ippair_memcap; if ((ConfGet("ippair.memcap", &conf_val)) == 1) { if (ParseSizeStringU64(conf_val, &ippair_memcap) < 0) { SCLogError(SC_ERR_SIZE_PARSE, "Error parsing ippair.memcap " "from conf file - %s. Killing engine", conf_val); exit(EXIT_FAILURE); } else { SC_ATOMIC_SET(ippair_config.memcap, ippair_memcap); } } if ((ConfGet("ippair.hash-size", &conf_val)) == 1) { if (ByteExtractStringUint32(&configval, 10, strlen(conf_val), conf_val) > 0) { ippair_config.hash_size = configval; } } if ((ConfGet("ippair.prealloc", &conf_val)) == 1) { if (ByteExtractStringUint32(&configval, 10, strlen(conf_val), conf_val) > 0) { ippair_config.prealloc = configval; } else { WarnInvalidConfEntry("ippair.prealloc", "%"PRIu32, ippair_config.prealloc); } } SCLogDebug("IPPair config from suricata.yaml: memcap: %"PRIu64", hash-size: " "%"PRIu32", prealloc: %"PRIu32, SC_ATOMIC_GET(ippair_config.memcap), ippair_config.hash_size, ippair_config.prealloc); /* alloc hash memory */ uint64_t hash_size = ippair_config.hash_size * sizeof(IPPairHashRow); if (!(IPPAIR_CHECK_MEMCAP(hash_size))) { SCLogError(SC_ERR_IPPAIR_INIT, "allocating ippair hash failed: " "max ippair memcap is smaller than projected hash size. " "Memcap: %"PRIu64", Hash table size %"PRIu64". Calculate " "total hash size by multiplying \"ippair.hash-size\" with %"PRIuMAX", " "which is the hash bucket size.", SC_ATOMIC_GET(ippair_config.memcap), hash_size, (uintmax_t)sizeof(IPPairHashRow)); exit(EXIT_FAILURE); } ippair_hash = SCMallocAligned(ippair_config.hash_size * sizeof(IPPairHashRow), CLS); if (unlikely(ippair_hash == NULL)) { SCLogError(SC_ERR_FATAL, "Fatal error encountered in IPPairInitConfig. Exiting..."); exit(EXIT_FAILURE); } memset(ippair_hash, 0, ippair_config.hash_size * sizeof(IPPairHashRow)); uint32_t i = 0; for (i = 0; i < ippair_config.hash_size; i++) { HRLOCK_INIT(&ippair_hash[i]); } (void) SC_ATOMIC_ADD(ippair_memuse, (ippair_config.hash_size * sizeof(IPPairHashRow))); if (quiet == FALSE) { SCLogConfig("allocated %"PRIu64" bytes of memory for the ippair hash... " "%" PRIu32 " buckets of size %" PRIuMAX "", SC_ATOMIC_GET(ippair_memuse), ippair_config.hash_size, (uintmax_t)sizeof(IPPairHashRow)); } /* pre allocate ippairs */ for (i = 0; i < ippair_config.prealloc; i++) { if (!(IPPAIR_CHECK_MEMCAP(g_ippair_size))) { SCLogError(SC_ERR_IPPAIR_INIT, "preallocating ippairs failed: " "max ippair memcap reached. Memcap %"PRIu64", " "Memuse %"PRIu64".", SC_ATOMIC_GET(ippair_config.memcap), ((uint64_t)SC_ATOMIC_GET(ippair_memuse) + g_ippair_size)); exit(EXIT_FAILURE); } IPPair *h = IPPairAlloc(); if (h == NULL) { SCLogError(SC_ERR_IPPAIR_INIT, "preallocating ippair failed: %s", strerror(errno)); exit(EXIT_FAILURE); } IPPairEnqueue(&ippair_spare_q,h); } if (quiet == FALSE) { SCLogConfig("preallocated %" PRIu32 " ippairs of size %" PRIu16 "", ippair_spare_q.len, g_ippair_size); SCLogConfig("ippair memory usage: %"PRIu64" bytes, maximum: %"PRIu64, SC_ATOMIC_GET(ippair_memuse), SC_ATOMIC_GET(ippair_config.memcap)); } return; }