suricata构成-线程分布-功能讲解

运行模式

suricata启动的线程组成取决于他的运行模式,通过suricatasc -c running-mode获取运行模式;

其运行模式由两处位置决定:

  • 配置文件runmode参数

    

  • 代码中设置default变量

workers模式

af-packet收包

1、线程

线程名称

数量

作用

入口函数

suricata-main

1

主进程

main

woker

配置文件决定

  • 对应配置项threads参数
  • 模式下default配置项threads参数

无配置

  • 核数决定

工作线程

TmThreadsSlotPktAcqLoop

FM

1

流管理线程

TmThreadsManagement

FlowManager

FR

1

流回收线程

TmThreadsManagement

FlowRecycler

CS

1

管理线程

CM

1

线程状态

US

1

管理工具suricatasc

2、woker

woker负责获取数据包,解码并完成一系列的包处理动作。在af-packet模式中:

收包通过ReceiveAFPLoop完成,解码通过DecodeAFP,包处理通过FlowWorker完成

2.1、创建过程

main->ParseCommandLine:完成进程执行句柄的解析,对af-packet设置suri->run_mode运行模式

在af-packet worker情况下启动器RunModeIdsAFPWorkers

void RunModeIdsAFPRegister(void)
{
    RunModeRegisterNewRunMode(RUNMODE_AFP_DEV, "single",
                              "Single threaded af-packet mode",
                              RunModeIdsAFPSingle);
    RunModeRegisterNewRunMode(RUNMODE_AFP_DEV, "workers",
                              "Workers af-packet mode, each thread does all"
                              " tasks from acquisition to logging",
                              RunModeIdsAFPWorkers);
    default_mode_workers = "workers";
    RunModeRegisterNewRunMode(RUNMODE_AFP_DEV, "autofp",
                              "Multi socket AF_PACKET mode.  Packets from "
                              "each flow are assigned to a single detect "
                              "thread.",
                              RunModeIdsAFPAutoFp);
    return;
}

main->RunModeDispatch  这里根据run_mode获取af-packet worker模式的入口。

    if (custom_mode == NULL) {
        const char *val = NULL;
                // 获取配置文件runmode 内容
        if (ConfGet("runmode", &val) != 1) {
            custom_mode = NULL;
        } else {
            custom_mode = val;
        }
    }
…………如果没有或者内容被注释,根据启动模块给定default_runmode

if (custom_mode == NULL || strcmp(custom_mode, "auto") == 0) {
        switch (runmode) {
            case RUNMODE_AFP_DEV:
                                // 对于af-packet而言,他的默认runmodeworker
                custom_mode = RunModeAFPGetDefaultMode();
                break;
            default:
                SCLogError(SC_ERR_UNKNOWN_RUN_MODE, "Unknown runtime mode. Aborting");
                exit(EXIT_FAILURE);
        }
    }

// 拿到对应的运行结构

RunMode *mode = RunModeGetCustomMode(runmode, custom_mode);

// 运行提供的启动接口,对于af-packet worker启动接口为 RunModeIdsAFPWorkers

mode->RunModeFunc();

 

int RunModeIdsAFPWorkers(void) - 解析yaml配置文件中af-packet项,完成模式启动

这里使用框架提供的启动函数,只需要提供收包和解码回调函数即可。

………………
 

        // ParseAFPConfig负责解析配置文件,后边将配置conf数据传到运行流程进行处理

// 提供接口对应项配置解析器(af-packet),提供获取线程数量的回调接口。

// 这里提到的recv 和 decode 为收包和解码模块

// thread_name_workers 默认为W

// live_dev 为程序运行时写的接口名称

    ret = RunModeSetLiveCaptureWorkers(ParseAFPConfig,
                                    AFPConfigGeThreadsCount,
                                    "ReceiveAFP",
                                    "DecodeAFP", thread_name_workers,
                                    live_dev);

int RunModeSetLiveCaptureWorkers(ConfigIfaceParserFunc ConfigParser,
                              ConfigIfaceThreadsCountFunc ModThreadsCount,
                              const char *recv_mod_name,
                              const char *decode_mod_name, const char *thread_name,
                              const char *live_dev

根据提供的接口完成woker线程的启动,这个接口里面的循环次数取决于yaml配置文件af-packet项内有几个接口。

    for (ldev = 0; ldev < nlive; ldev++) {
        const char *live_dev_c = NULL;
        if ((nlive <= 1) && (live_dev != NULL)) {
                        // 根据提供的配置初始化解析接口完成yaml配置文件中的变量解析。
            aconf = ConfigParser(live_dev);
            live_dev_c = live_dev;
            if (unlikely(live_dev_c == NULL)) {
                SCLogError(SC_ERR_MEM_ALLOC, "Can't allocate interface name");
                exit(EXIT_FAILURE);
            }
        } else {
            live_dev_c = LiveGetDeviceName(ldev);
            aconf = ConfigParser(live_dev_c);
        }
                // 传入线程数量获取接口,收包,解码,线程名称,接口名称,接口对应配置信息
        RunModeSetLiveCaptureWorkersForDevice(ModThreadsCount,
                recv_mod_name,
                decode_mod_name,
                thread_name,
                live_dev_c,
                aconf,
                0);
    }

static int RunModeSetLiveCaptureWorkersForDevice(ConfigIfaceThreadsCountFunc ModThreadsCount,
                              const char *recv_mod_name,
                              const char *decode_mod_name, const char *thread_name,
                              const char *live_dev, void *aconf,
                              unsigned char single_mode)

接口根据给定接口完成worker启动,接口里面启动线程数量取决于线程回调运行结果。

这里注意tv这个实例,每个woker线程一个

        // 单例模式
    if (single_mode) {
        threads_count = 1;
    } else {
            // 根据接口返回结果启动线程数量
        threads_count = ModThreadsCount(aconf);
        SCLogInfo("Going to use %" PRId32 " thread(s)", threads_count);
    }

………………

// tv类似包处理实例,存在对包处理相关队里和各种回调
                // pktacqloop 使用的启动器
        tv = TmThreadCreatePacketHandler(tname,
                "packetpool", "packetpool",
                "packetpool", "packetpool",
                "pktacqloop");

        if (tv == NULL) {
            SCLogError(SC_ERR_THREAD_CREATE, "TmThreadsCreate failed");
            exit(EXIT_FAILURE);
        }
        tv->printable_name = printable_threadname;
        // 获取对应包处理槽接口
        tm_module = TmModuleGetByName(recv_mod_name);
        if (tm_module == NULL) {
            SCLogError(SC_ERR_INVALID_VALUE, "TmModuleGetByName failed for %s", recv_mod_name);
            exit(EXIT_FAILURE);
        }
        TmSlotSetFuncAppend(tv, tm_module, aconf);

tm_module = TmModuleGetByName(decode_mod_name);
        if (tm_module == NULL) {
            SCLogError(SC_ERR_INVALID_VALUE, "TmModuleGetByName %s failed", decode_mod_name);
            exit(EXIT_FAILURE);
        }
        TmSlotSetFuncAppend(tv, tm_module, NULL);

tm_module = TmModuleGetByName("FlowWorker");
        if (tm_module == NULL) {
            SCLogError(SC_ERR_RUNMODE, "TmModuleGetByName for FlowWorker failed");
            exit(EXIT_FAILURE);
        }
        TmSlotSetFuncAppend(tv, tm_module, NULL);

//将对应的处理接口放在tv->slot中

// 上面的槽函数,在tv-slot 构成一个链表,第一个是收包模块
        TmThreadSetCPU(tv, WORKER_CPU_SET);

// 开始启动每个处理模块
        if (TmThreadSpawn(tv) != TM_ECODE_OK) {
            SCLogError(SC_ERR_THREAD_SPAWN, "TmThreadSpawn failed");
            exit(EXIT_FAILURE);
        }

TmEcode TmThreadSpawn(ThreadVars *tv)

// 接口完成主线程的创建,并将线程交给主线程的tv_root

//启动器:TmThreadsSlotPktAcqLoop

 int rc = pthread_create(&tv->t, &attr, tv->tm_func, (void *)tv);
    if (rc) {
        printf("ERROR; return code from pthread_create() is %" PRId32 "\n", rc);
        return TM_ECODE_FAILED;
    }

TmThreadWaitForFlag(tv, THV_INIT_DONE | THV_RUNNING_DONE);

TmThreadAppend(tv, tv->type);

static void *TmThreadsSlotPktAcqLoop(void *td)

//启动器完成收包功能初始化,原始套接字创建

// 完成每个模块的初始化动作

for (slot = s; slot != NULL; slot = slot->slot_next) {
        if (slot->SlotThreadInit != NULL) {
            void *slot_data = NULL;
                // slot->slot_initdata这个数据是根据配置完成的初始化数据,就是yaml配置文件里面的内容

            r = slot->SlotThreadInit(tv, slot->slot_initdata, &slot_data);
            if (r != TM_ECODE_OK) {
                if (r == TM_ECODE_DONE) {
                    EngineDone();
                    TmThreadsSetFlag(tv, THV_CLOSED | THV_INIT_DONE | THV_RUNNING_DONE);
                    goto error;
                } else {
                    TmThreadsSetFlag(tv, THV_CLOSED | THV_RUNNING_DONE);
                    goto error;
                }
            }
                  // 将初始化得到的数据放在原子变量里面
            (void)SC_ATOMIC_SET(slot->slot_data, slot_data);
        }

 

while(run) {
        if (TmThreadsCheckFlag(tv, THV_PAUSE)) {
            TmThreadsSetFlag(tv, THV_PAUSED);
            TmThreadTestThreadUnPaused(tv);
            TmThreadsUnsetFlag(tv, THV_PAUSED);
        }
//进入收包模块  ReceiveAFPLoop

r = s->PktAcqLoop(tv, SC_ATOMIC_GET(s->slot_data), s);

if (r == TM_ECODE_FAILED) {
            TmThreadsSetFlag(tv, THV_FAILED);
            run = 0;
        }
        if (TmThreadsCheckFlag(tv, THV_KILL_PKTACQ) || suricata_ctl_flags) {
            run = 0;
        }
        if (r == TM_ECODE_DONE) {
            run = 0;
        }
    }

TmEcode ReceiveAFPLoop(ThreadVars *tv, void *data, void *slot)

//接口实现,激活收包流程,处理数据包

   if (ptv->flags & AFP_RING_MODE) {

// 根据标志选择收包接口

        if (ptv->flags & AFP_TPACKET_V3) {
            AFPReadFunc = AFPReadFromRingV3;
        } else {
            AFPReadFunc = AFPReadFromRing;
        }
    } else {
        AFPReadFunc = AFPRead;
    }

……

// 创建原始套接字,并对网口状态进行配置。

r = AFPCreateSocket(ptv, ptv->iface, 1);

……

// 设置fd轮训监视,无阻塞处理

    fds.fd = ptv->socket;

    fds.events = POLLIN;

// 监视描述符状态。

 r = poll(&fds, 1, POLL_TIMEOUT);

// 进入流处理

lse if (r > 0) {

            r = AFPReadFunc(ptv);

这里的func是上面根据模式给出的。一般就是AFPReadFromRing

static int AFPReadFromRing(AFPThreadVars *ptv)

接口将在socket的环形缓冲区接收数据包,判断数据包是否能够处理,并锁定数据包。

// 遍历整个环形缓冲区,读数据包

h.raw = (((union thdr **)ptv->ring_v2)[ptv->frame_offset]);

        if (unlikely(h.raw == NULL)) {

            /* Impossible we reach this point in normal condition, so trigger

             * a failure in reading */

            SCReturnInt(AFP_READ_FAILURE);

        }

//中间将申请数据包实例,并填入对应信息

//对数据包进行解码和包处理逻辑

 if (TmThreadsSlotProcessPkt(ptv->tv, ptv->slot, p) != TM_ECODE_OK) {

            h.h2->tp_status = TP_STATUS_KERNEL;

            if (++ptv->frame_offset >= ptv->req.tp_frame_nr) {

                ptv->frame_offset = 0;

            }

            TmqhOutputPacketpool(ptv->tv, p);

            SCReturnInt(AFP_SURI_FAILURE);

        }

static inline TmEcode TmThreadsSlotProcessPkt(ThreadVars *tv, TmSlot *s, Packet *p);

接口将遍历run_mode注册的包处理槽函数,对接收环形区内的数据进行处理。这里默认就有了解码(网络层,传输层,应用层数据获取。)flow_worker

TmThreadsSlotVarRun

TmEcode TmThreadsSlotVarRun(ThreadVars *tv, Packet *p,

                                          TmSlot *slot)

接口调用每个槽函数。

TmEcode DecodeAFP(ThreadVars *tv, Packet *p, void *data, PacketQueue *pq, PacketQueue *postpq)

接口完成对各类操作计数器的运算,同时完成网络层的数据解析。

TmEcode DecodeAFP(ThreadVars *tv, Packet *p, void *data, PacketQueue *pq, PacketQueue *postpq)

{

    SCEnter();

    DecodeThreadVars *dtv = (DecodeThreadVars *)data;

    /* XXX HACK: flow timeout can call us for injected pseudo packets

     *           see bug: Bug #1107: flow timeout causes decoders to run on pseudo packets - Suricata - Open Information Security Foundation */

    if (p->flags & PKT_PSEUDO_STREAM_END)

        return TM_ECODE_OK;

    // 更新计数,包含包数,字节数,data中的counter值都是信息位置id值。

    /* update counters */

    DecodeUpdatePacketCounters(tv, dtv, p);

    /* If suri has set vlan during reading, we increase vlan counter */

    // 如果存在vlan信息,也处理vlan计数器

    if (p->vlan_idx) {

        StatsIncr(tv, dtv->counter_vlan);

    }

    /* call the decoder */

    // 根据不同的链路类型,对raw解析到p中。

    switch (p->datalink) {

        case LINKTYPE_ETHERNET:

            DecodeEthernet(tv, dtv, p,GET_PKT_DATA(p), GET_PKT_LEN(p), pq);

            break;

        case LINKTYPE_LINUX_SLL:

            DecodeSll(tv, dtv, p, GET_PKT_DATA(p), GET_PKT_LEN(p), pq);

            break;

        case LINKTYPE_PPP:

            DecodePPP(tv, dtv, p, GET_PKT_DATA(p), GET_PKT_LEN(p), pq);

            break;

        case LINKTYPE_RAW:

        case LINKTYPE_GRE_OVER_IP:

            DecodeRaw(tv, dtv, p, GET_PKT_DATA(p), GET_PKT_LEN(p), pq);

            break;

        case LINKTYPE_NULL:

            DecodeNull(tv, dtv, p, GET_PKT_DATA(p), GET_PKT_LEN(p), pq);

            break;

        default:

            SCLogError(SC_ERR_DATALINK_UNIMPLEMENTED, "Error: datalink type %" PRId32 " not yet supported in module DecodeAFP", p->datalink);

            break;

    }

    // 解码成功计数器

    PacketDecodeFinalize(tv, dtv, p);

    SCReturnInt(TM_ECODE_OK);

}

你可能感兴趣的:(网络,数据库,linux,运维,开源,学习)