乐趣区

关于网络安全: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 中跟踪流就须要应用内存。流越多,所需的内存就越多。
因而咱们要放弃对内存应用的管制,有几个选项:

  • 用于设置流引擎将应用的最大字节数的 memcap 选项
  • 用于设置哈希表大小的哈希大小
  • 用于以下内容的预调配:

    • 对于还不属于流的数据包,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 选项用于设置流引擎将应用的最大字节数。

  • 默认 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 选项用于设置哈希表大小的哈希大小。

  • 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 选项用于设置内存中预调配流的数量。

#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选项使得流引擎进入紧急模式。在此模式下,引擎将利用较短的超时工夫。其让流利用较短的超时工夫,它使流以更踊跃的形式过期,因而将有更多空间包容新的流。

  • 紧急复原。紧急复原设置为 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();

未完待续 …

退出移动版