Suricata-流的處理

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

1. 流的內存處理

在suricata中跟蹤流就須要使用內存。流越多,所需的內存就越多。
所以咱們要保持對內存使用的控制,有幾個選項:api

  • 用於設置流引擎將使用的最大字節數的memcap選項
  • 用於設置哈希表大小的哈希大小
  • 用於如下內容的預分配:less

    • 對於還不屬於流的數據包,Suricata建立了一個新的流。這是一個相對昂貴的行動。
    • 由此帶來的風險是,攻擊者/黑客能夠在此部分攻擊引擎系統。
    • 當它們確保一臺計算機得到許多具備不一樣元組的數據包時,引擎必須生成許多新的流。
    • 這樣,攻擊者就能夠淹沒系統。
    • 爲了減輕引擎過載,此選項指示Suricata在內存中保持多個流就緒。這樣一來,Suricata就不那麼容易受到此類攻擊。
流引擎有一個獨立於包處理的管理線程。這個線程稱爲流管理器。該線程確保儘量在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.

1.1 memcap選項

memcap選項用於設置流引擎將使用的最大字節數。dom

  • 默認memcap爲32M,即33554432字節。
#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)))

1.2 hash_size選項

hash_size選項用於設置哈希表大小的哈希大小。ui

  • hash種子是一個隨機數。
  • hash大小默認爲65536。
#define FLOW_DEFAULT_HASHSIZE    65536
flow_config.hash_rand   = (uint32_t)RandomGet();
flow_config.hash_size   = FLOW_DEFAULT_HASHSIZE;

1.3 prealloc選項

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

1.4 emergency_recovery選項

emergency_recovery選項使得流引擎進入緊急模式。在此模式下,引擎將利用較短的超時時間。其讓流利用較短的超時時間,它使流以更積極的方式過時,所以將有更多空間容納新的流。spa

  • 緊急恢復。緊急恢復設置爲30。這是預分配流的百分比,在此百分比以後,流引擎將恢復正常(當10000個流中的30%完成時)。
  • 修剪流。若是在緊急模式中,過分超時沒有所需的結果,則此選項是最終的解決方案。它結束了一些流,即便他們尚未達到他們的超時時間。修剪流選項顯示每次設置新流時將終止的流的數量。
#define FLOW_DEFAULT_EMERGENCY_RECOVERY 30
flow_config.emergency_recovery = FLOW_DEFAULT_EMERGENCY_RECOVERY;

1.4.1 緊急模式進入

  1. 獲取新的Flow
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 */
  1. 若是達到MEMCAP後,進入緊急模式,超時時間改成緊急超時時間。
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);
  1. 遍歷哈希,直到能夠釋放流。線程

    • 不要修剪包或流消息在使用的流。
    • 輸出日誌。
    • 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;
        }
  1. 從hash中刪除,設置FORCED和EMERGENCY標誌。
/* 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;
  1. log記錄,清除舊內存,初始爲新狀態,增長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));

1.4.1 緊急模式退出

  1. 獲取spare隊列中的flow數
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);
  1. 若是可用flow與預分配流的百分比大於emergency_recovery選項的配置。
if (len * 100 / flow_config.prealloc > flow_config.emergency_recovery) {
                SC_ATOMIC_AND(flow_flags, ~FLOW_EMERGENCY);
  1. 恢復正常的超時時間,退出緊急狀態。
FlowTimeoutsReset();

未完待續...

相關文章
相關標籤/搜索