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();