Thank Zhihao Tao for your hard work. The document spent countless nights and weekends, using his hard work to make it convenient for everyone.
If you have any questions, please send a email to zhihao.tao@outlook.com
在suricata中跟踪流就须要使用内存。流越多,所需的内存就越多。
所以咱们要保持对内存使用的控制,有几个选项:api
用于如下内容的预分配:less
流引擎有一个独立于包处理的管理线程。这个线程称为流管理器。该线程确保尽量在Memcap内。将准备10000个流。
flow: memcap: 33554432 #The maximum amount of bytes the flow-engine will make use of. hash_size: 65536 #Flows will be organized in a hash-table. With this option you can set the #size of the hash-table. Prealloc: 10000 #The amount of flows Suricata has to keep ready in memory. emergency_recovery: 30 #Percentage of 1000 prealloc'd flows. prune_flows: 5 #Amount of flows being terminated during the emergency mode.
memcap选项用于设置流引擎将使用的最大字节数。dom
#define FLOW_DEFAULT_MEMCAP (32 * 1024 * 1024) /* 32 MB */ SC_ATOMIC_SET(flow_config.memcap, FLOW_DEFAULT_MEMCAP);
FLOW_CHECK_MEMCAP
来检查内存分配的字节数是否超过了memcap。/** \brief check if a memory alloc would fit in the memcap * * \param size memory allocation size to check * * \retval 1 it fits * \retval 0 no fit */ #define FLOW_CHECK_MEMCAP(size) \ ((((uint64_t)SC_ATOMIC_GET(flow_memuse) + (uint64_t)(size)) <= SC_ATOMIC_GET(flow_config.memcap)))
hash_size选项用于设置哈希表大小的哈希大小。ui
#define FLOW_DEFAULT_HASHSIZE 65536 flow_config.hash_rand = (uint32_t)RandomGet(); flow_config.hash_size = FLOW_DEFAULT_HASHSIZE;
prealloc选项用于设置内存中预分配流的数量。this
#define FLOW_DEFAULT_PREALLOC 10000 flow_config.prealloc = FLOW_DEFAULT_PREALLOC;
/* pre allocate flows */ for (i = 0; i < flow_config.prealloc; i++) { if (!(FLOW_CHECK_MEMCAP(sizeof(Flow) + FlowStorageSize()))) { SCLogError(SC_ERR_FLOW_INIT, "preallocating flows failed: " "max flow memcap reached. Memcap %"PRIu64", " "Memuse %"PRIu64".", SC_ATOMIC_GET(flow_config.memcap), ((uint64_t)SC_ATOMIC_GET(flow_memuse) + (uint64_t)sizeof(Flow))); exit(EXIT_FAILURE); } Flow *f = FlowAlloc(); if (f == NULL) { SCLogError(SC_ERR_FLOW_INIT, "preallocating flow failed: %s", strerror(errno)); exit(EXIT_FAILURE); } FlowEnqueue(&flow_spare_q,f); }
emergency_recovery
选项使得流引擎进入紧急模式。在此模式下,引擎将利用较短的超时时间。其让流利用较短的超时时间,它使流以更积极的方式过时,所以将有更多空间容纳新的流。spa
修剪流
选项显示每次设置新流时将终止的流的数量。#define FLOW_DEFAULT_EMERGENCY_RECOVERY 30 flow_config.emergency_recovery = FLOW_DEFAULT_EMERGENCY_RECOVERY;
static Flow *FlowGetNew(ThreadVars *tv, DecodeThreadVars *dtv, const Packet *p) { ... f = FlowDequeue(&flow_spare_q); if (f == NULL) { /* If we reached the max memcap, we get a used flow */
if (!(FLOW_CHECK_MEMCAP(sizeof(Flow) + FlowStorageSize()))) { /* declare state of emergency */ if (!(SC_ATOMIC_GET(flow_flags) & FLOW_EMERGENCY)) { SC_ATOMIC_OR(flow_flags, FLOW_EMERGENCY); FlowTimeoutsEmergency(); /* under high load, waking up the flow mgr each time leads * to high cpu usage. Flows are not timed out much faster if * we check a 1000 times a second. */ FlowWakeupFlowManagerThread(); } f = FlowGetUsedFlow(tv, dtv);
遍历哈希,直到能够释放流。线程
flow_prune_idx
确保咱们不会每次都从顶部开始,由于那样会清除散列的顶部,从而致使在高压下搜索时间愈来愈长。static Flow *FlowGetUsedFlow(ThreadVars *tv, DecodeThreadVars *dtv) { ... if (SC_ATOMIC_GET(f->use_cnt) > 0) { FBLOCK_UNLOCK(fb); FLOWLOCK_UNLOCK(f); continue; }
/* remove from the hash */ f->flow_end_flags |= FLOW_END_FLAG_FORCED; if (SC_ATOMIC_GET(flow_flags) & FLOW_EMERGENCY) f->flow_end_flags |= FLOW_END_FLAG_EMERGENCY;
flow_prune_idx
。/* 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));
static TmEcode FlowManager(ThreadVars *th_v, void *thread_data) { ... uint32_t len = 0; FQLOCK_LOCK(&flow_spare_q); len = flow_spare_q.len; FQLOCK_UNLOCK(&flow_spare_q); StatsSetUI64(th_v, ftd->flow_mgr_spare, (uint64_t)len);
emergency_recovery
选项的配置。if (len * 100 / flow_config.prealloc > flow_config.emergency_recovery) { SC_ATOMIC_AND(flow_flags, ~FLOW_EMERGENCY);
FlowTimeoutsReset();