当前位置: 首页 > 工具软件 > Suricata > 使用案例 >

suricata 初始化做的那些事儿

连鸿
2023-12-01

前言

一、SuricataMain 程序入口

1.在引擎中注册所有运行模式。

2.解析执行命令

3.初始化全局变量

3.加载yaml配置文件

4.初始化日志

5.打印版本和CPUs/cores

6.解析接口设置名称

7.配置加载后的操作

A. 设置多模和单模匹配模式

B. 设置checksum

C. 初始化存储模块

D. 应用层协议设置

E. 设置捕获参数

F. 加载主机操作系统策略信息

G. 初始化检测引擎

H. 初始化queue handler(队列处理函数)

I. 初始化CIDR掩码数组

J. 建立IP层所承载的上层协议号和协议名的映射

K. 建立IP层所承载的上层协议号和协议名的映射

L.检查配置文件中”vars”选项 IP、port是否符合格式要求

M.注册特征比较函数

N.注册Suricata所支持的所有线程模块

O.HTTP对应

P.storage分配存储空间

Q.检查是否进入Daemon模式

R.初始化信号handler

S.设置并验证日志存储目录是否存在

T.设置并验证日志存储目录是否存在

U.若detect未开启,则设置tcp流重组为false

V.初始化Host engine

W.处理CoreDump相关配置

X.译码器的设置

Y.创建注册设备

Z.初始化全局变量stats_ctx

8.去除主线程的权限

9.在数据包开始流动之前我们需要运行的任务

10.Detect加载只有的操作

11.设置记录开始时间

12.初始化运行模式并创建一系列子线程

13.等待子线程初始化完成

14.继续运行暂停的线程

15.设置Sigusr2信号的处理函数

16.主循环

前言

此处只讨论 IDS模式, af-packet采集,workers工作模式下。

一、SuricataMain 程序入口

1.在引擎中注册所有运行模式。

代码如下:

if (InitGlobal() != 0) {
        exit(EXIT_FAILURE);
    }
int InitGlobal(void) {
    ...
       RunModeRegisterRunModes();
    ...
}

//主要是完成运行模式的注册,添加的所有运行模式都要通过这个函数注册
void RunModeRegisterRunModes(void)
{
    memset(runmodes, 0, sizeof(runmodes));

    RunModeIdsPcapRegister();
    RunModeFilePcapRegister();
    RunModeIdsPfringRegister();
    RunModeIpsNFQRegister();
    RunModeIpsIPFWRegister();
    RunModeErfFileRegister();
    RunModeErfDagRegister();
    RunModeNapatechRegister();
    RunModeIdsAFPRegister();
    RunModeIdsNetmapRegister();
    RunModeIdsNflogRegister();
    RunModeUnixSocketRegister();
    RunModeIpsWinDivertRegister();
#ifdef UNITTESTS
    UtRunModeRegister();
#endif
    return;
}
// IDS workers af-packet 模式下注册函数
void RunModeIdsAFPRegister(void)
{
    RunModeRegisterNewRunMode(RUNMODE_AFP_DEV, "workers",
                              "Workers af-packet mode, each thread does all"
                              " tasks from acquisition to logging",
                              RunModeIdsAFPWorkers);
    return;
}
void RunModeRegisterNewRunMode(enum RunModes runmode,
                               const char *name,
                               const char *description,
                               int (*RunModeFunc)(void))
{
    //注册一个相应的模式及回调函数到全局 static RunModes runmodes[RUNMODE_USER_MAX]; 
    //对应的回调函数 
    mode->RunModeFunc = RunModeFunc;     //即为int RunModeIdsAFPWorkers(void)函数 
                                         //这个函数将在创建线程时调用

}


//创建相应的工作线程,同时工作线程处理流程
//(ReceiveAFP --> DecodeAFP --> FlowWorker --> RespondReject)
int RunModeIdsAFPWorkers(void)
{
    SCEnter();
#ifdef HAVE_AF_PACKET
    int ret;
    const char *live_dev = NULL;

    RunModeInitialize();
    TimeModeSetLive();

    (void)ConfGet("af-packet.live-interface", &live_dev);

    if (AFPPeersListInit() != TM_ECODE_OK) {
        FatalError(SC_ERR_FATAL, "Unable to init peers list.");
    }

    ret = RunModeSetLiveCaptureWorkers(ParseAFPConfig,
                                    AFPConfigGeThreadsCount,
                                    "ReceiveAFP",
                                    "DecodeAFP", thread_name_workers,
                                    live_dev);
    if (ret != 0) {
        FatalError(SC_ERR_FATAL, "Unable to start runmode");
    }

    /* In IPS mode each threads must have a peer */
    if (AFPPeersListCheck() != TM_ECODE_OK) {
        FatalError(SC_ERR_FATAL, "Some IPS capture threads did not peer.");
    }

    SCLogDebug("RunModeIdsAFPWorkers initialised");

#endif /* HAVE_AF_PACKET */
    SCReturnInt(0);
}

2.解析执行命令

if (ParseCommandLine(argc, argv, &suricata) != TM_ECODE_OK) {
        exit(EXIT_FAILURE);
    }

3.初始化全局变量

   /* Initializations for global vars, queues, etc (memsets, mutex init..) 数据包队列trans_q、数据队列data_queues(干嘛的?)、对应的mutex和cond、建立小写字母表。*/
    GlobalsInitPreConfig();
void GlobalsInitPreConfig(void)
{
    TimeInit();
    SupportFastPatternForSigMatchTypes();
    SCThresholdConfGlobalInit();
}

TimeInit() 初始化时间。包括:获取当前时间所用的spin lock,以及设置时区(调用tzset()即可)。

SupportFastPatternForSigMatchTypes() 为快速模式匹配注册关键字。调用SupportFastPatternForSigMatchList函数,按照优先级大小插入到sm_fp_support_smlist_list链表中。

SCThresholdConfGlobalInit() 初始化阈值正则系统

3.加载yaml配置文件

    if (LoadYamlConfig(&suricata) != TM_ECODE_OK) {
        exit(EXIT_FAILURE);
    }

调用LoadYamlConfig读取Yaml格式配置文件。若用户未在输入参数(-c)中指定配置文件,则使用默认配置文件(/etc/suricata/suricata.yaml)。Yaml格式解析是通过libyaml库来完成的,解析的结果存储在配置节点树(见conf.c)中。对include机制的支持:在第一遍调用ConfYamlLoadFile载入主配置文件后,将在当前配置节点树中搜寻“include”节点,并对其每个子节点的值(即通过include语句所指定的子配置文件路径),同样调用ConfYamlLoadFile进行载入。


4.初始化日志

    /* Since our config is now loaded we can finish configurating the
     * logging module. */
    SCLogLoadConfig(suricata.daemon, suricata.verbose);

再次初始化日志模块。这次,程序将会根据配置文件中日志输出配置(logging.outputs)填充SCLogInitData类型的结构体,调用SCLogInitLogModule重新初始化日志模块。

5.打印版本和CPUs/cores

    LogVersion(&suricata); //打印版本信息。这是Suricata启动开始后第一条打印信息。
    UtilCpuPrintSummary(); //打印当前机器的CPU/核个数,这些信息是通过sysconf系统函数获取的。

6.解析接口设置名称

    if (ParseInterfacesList(suricata.aux_run_mode, suricata.pcap_dev) != TM_ECODE_OK) {
        exit(EXIT_FAILURE);
    }

解析接口设置名称(网络设备,如eth0,eth1等)。 如果suri.pcap_dev为NULL,则从配置文件获取设备名称。 如果suri.pcap_dev不为NULL,则将读入的配置结点的设备名称改为suri.pcap_dev。

7.配置加载后的操作

    if (PostConfLoadedSetup(&suricata) != TM_ECODE_OK) {
        exit(EXIT_FAILURE);
    }

这个函数包含在配置加载后需要运行操作,操作很多。

A. 设置多模和单模匹配模式

    MpmTableSetup();
    SpmTableSetup();


//多模匹配模式种类
enum {
    MPM_NOTSET = 0,

    /* aho-corasick */
    MPM_AC,
    MPM_AC_BS,
    MPM_AC_KS,
    MPM_HS,
    /* table size */
    MPM_TABLE_SIZE,
};

//单模匹配模式种类
enum {
    SPM_BM, /* Boyer-Moore */
    SPM_HS, /* Hyperscan */
    /* Other SPM matchers will go here. */
    SPM_TABLE_SIZE
};

设置多模式匹配表,
设置单模式匹配表,基本和上面的多模式匹配相同,有两个匹配项BM和HS(HYPERSCAN)。

已多模注册AC匹配器为例,MpmTableSetup()会调用MpmACRegister(void) 函数实现AC注册,函数内部其实只是填充mpm_table中对应 AC的那一项(mpm_table[MPM_AC])的各个字段,如:匹配器名称(”ac”)、初始化函数(SCACInitCtx)、增加模式函数 (SCACAddPatternCS)、实际的搜索执行函数(SCACSearch), 注册了一些列的回调函数。

B. 设置checksum

if (suri->checksum_validation == -1) {
        const char *cv = NULL;
        if (ConfGetValue("capture.checksum-validation", &cv) == 1) {
            if (strcmp(cv, "none") == 0) {
                suri->checksum_validation = 0;
            } else if (strcmp(cv, "all") == 0) {
                suri->checksum_validation = 1;
            }
        }
    }
    switch (suri->checksum_validation) {
        case 0:
            ConfSet("stream.checksum-validation", "0");
            break;
        case 1:
            ConfSet("stream.checksum-validation", "1");
            break;
    }

C. 初始化存储模块

    StorageInit();

StorageInit:初始化存储模块,这个模块可以用来临时存储一些数据,数据类型目前有四种:host、flow、ippair、livedevice。具体在何种场景下用,目前未知。

D. 应用层协议设置

    AppLayerSetup();
int AppLayerSetup(void)
{
    SCEnter();

    AppLayerProtoDetectSetup();
    AppLayerParserSetup();

    AppLayerParserRegisterProtocolParsers();
    AppLayerProtoDetectPrepareState();

    AppLayerSetupCounters();

    SCReturnInt(0);
}

AppLayerProtoDetectSetup() 函数初始化该模块所用到的多模式匹配器;

AppLayerParserSetup() 函数通过传输层和应用层协议号构建了一个二维数组,并在该数组中为相应的流重组深度赋值;

AppLayerParserRegisterProtocolParsers() 函数注册各种应用层协议的解析器 (如RegisterHTPParsers函数对应HTTP协议,这个后面会详细分析);

AppLayerProtoDetectPrepareState() 函数的功能暂时也没弄明白。

AppLayerSetupCounters() 函数设置Counter name

E. 设置捕获参数

    if (ConfigGetCaptureValue(suri) != TM_ECODE_OK) {
        SCReturnInt(TM_ECODE_FAILED);
    }

获取与包捕获相关的一些配置参数,目前包括:max-pending-packets、default-packet-size。

F. 加载主机操作系统策略信息

    /* Load the Host-OS lookup. */
    SCHInfoLoadFromConfig();

针对碎片整理和TCP流重组的主机特定策略。主机操作系统查找是使用基数树完成的,就像路由表一样,以便匹配最特定的条目。

G. 初始化检测引擎

    if (suri->run_mode == RUNMODE_ENGINE_ANALYSIS) {
        SCLogInfo("== Carrying out Engine Analysis ==");
        const char *temp = NULL;
        if (ConfGet("engine-analysis", &temp) == 0) {
            SCLogInfo("no engine-analysis parameter(s) defined in conf file.  "
                      "Please define/enable them in the conf to use this "
                      "feature.");
            SCReturnInt(TM_ECODE_FAILED);
        }
    }

    /* hardcoded initialization code */
    SigTableSetup(); /* load the rule keywords */
    SigTableApplyStrictCommandlineOption(suri->strict_rule_parsing_string);

SigTableSetup:初始化检测引擎,主要是注册检测引擎所支持的规则格式(跟Snort规则基本一致)中的关键字,比如sid、priority、msg、within、distance等等。

SigTableApplyStrictCommandlineOption(suri->strict_rule_parsing_string) 为指定的检测关键字设置严格匹配。

H. 初始化queue handler(队列处理函数)

    TmqhSetup();

初始化queue handler(队列处理函数),这个是衔接线程模块和数据包队列之间的桥梁,目前共有3 类handler:simple,  packetpool, flow。每类handler内部都有一个InHandler和OutHandler,一个用于从上一级队列中获取数据包,另一个用于处理完毕后将数据包送入下一级队列。

I. 初始化CIDR掩码数组

    CIDRInit();

CIDRInit:初始化CIDR掩码数组,cidrs[i]对应前i位为1的掩码。

J. 建立IP层所承载的上层协议号和协议名的映射

   SCProtoNameInit();

读取/etc/protocols文件,建立IP层所承载的上层协议号和协议名的映射(如6-> ”TCP”,17-> ”UDP“)。

K. 建立IP层所承载的上层协议号和协议名的映射

    TagInitCtx();
    PacketAlertTagInit();
    ThresholdInit();
    HostBitInitCtx();
    IPPairBitInitCtx();

TagInitCtx、ThresholdInit:与规则中的tag、threshould关键字的实现相关,这里用到了Storage模块,调用 HostStorageRegister和FlowStorageRegister注册了几个(与流/主机绑定的?)存储区域。

L.检查配置文件中”vars”选项 IP、port是否符合格式要求

    if (DetectAddressTestConfVars() < 0) {
        SCLogError(SC_ERR_INVALID_YAML_CONF_ENTRY,
                "basic address vars test failed. Please check %s for errors",
                suri->conf_filename);
        SCReturnInt(TM_ECODE_FAILED);
    }
    if (DetectPortTestConfVars() < 0) {
        SCLogError(SC_ERR_INVALID_YAML_CONF_ENTRY,
                "basic port vars test failed. Please check %s for errors",
                suri->conf_filename);
        SCReturnInt(TM_ECODE_FAILED);
    }

DetectAddressTestConfVars、DetectPortTestConfVars:检查配置文件中”vars”选项下所预定义的一些IP地址(如局域网地址块)、端口变量(如HTTP端口号)是否符合格式要求。

M.注册特征比较函数

    FeatureTrackingRegister(); /* must occur prior to output mod registration */

FeatureTrackingRegister:注册特征比较函数。

N.注册Suricata所支持的所有线程模块

    RegisterAllModules();
void RegisterAllModules(void)
{
    // zero all module storage
    memset(tmm_modules, 0, TMM_SIZE * sizeof(TmModule));

    ...

    /* af-packet */
    TmModuleReceiveAFPRegister();
    TmModuleDecodeAFPRegister();
   
    ...

    /* flow worker */
    TmModuleFlowWorkerRegister();
    /* respond-reject */
    TmModuleRespondRejectRegister();

    /* log api */
    TmModuleLoggerRegister();
    TmModuleStatsLoggerRegister();

    ...

    /* nflog */
    TmModuleReceiveNFLOGRegister();
    TmModuleDecodeNFLOGRegister();

    ...
}

 以IDS模式, af-packet采集,workers 为例,所谓注册,就是在tmm_modules模块数组中对应的那项 中填充TmModule结构的所有字段,这些字段包括:模块名字、线程初始化函数、包处理或包获取函数、线程退出清理函数、一些标志位等等。

O.HTTP对应

    AppLayerHtpNeedFileInspection();

AppLayerHtpNeedFileInspection:设置suricata内部模块与libhtp(HTTP处理库)对接关系的函数。

P.storage分配存储空间

    StorageFinalize();

StorageFinalize:关闭storage模块的注册,为已注册的storage实际分配存储空间。

Q.检查是否进入Daemon模式

    if (MayDaemonize(suri) != TM_ECODE_OK)
        SCReturnInt(TM_ECODE_FAILED);

检查是否进入Daemon模式。若需要进入Daemon模式,则会检测pidfile是否已经存在(daemon下只 能有一个实例运行),然后进行Daemonize,最后创建一个pidfile。Daemonize的主要思路是:fork->子进程调用 setsid创建一个新的session,关闭stdin、stdout、stderr,并告诉父进程 –> 父进程等待子进程通知,然后退出 –> 子进程继续执行。

R.初始化信号handler

    if (InitSignalHandler(suri) != TM_ECODE_OK)
        SCReturnInt(TM_ECODE_FAILED);

初始化信号handler。首先为SIGINT(ctrl-c触发)和SIGTERM(不带参数kill时触发)这两个常规退出信号分别注册handler,对SIGINT的处理是设置程序的状态标志为STOP,即让程序优雅地退出;而对SIGTERM是设置为KILL,即强杀。接着,程序会忽略SIGPIPE(这个信号通常是在Socket通信时向已关闭的连接另一端发送数据时收到)和SIGSYS(当进程尝试执行一个不存在的系统调用时收到)信号,以加强程序的容错性和健壮性。

S.设置并验证日志存储目录是否存在

    /* Check for the existance of the default logging directory which we pick
     * from suricata.yaml.  If not found, shut the engine down */
    suri->log_dir = ConfigGetLogDirectory();

设置并验证日志存储目录是否存在。若配置文件中未指定,则使用默认目录,linux下默认为/var/log/suricata。

T.设置并验证日志存储目录是否存在

    /* Check for the existance of the default logging directory which we pick
     * from suricata.yaml.  If not found, shut the engine down */
    suri->log_dir = ConfigGetLogDirectory();

    if (ConfigCheckLogDirectoryExists(suri->log_dir) != TM_ECODE_OK) {
        SCLogError(SC_ERR_LOGDIR_CONFIG, "The logging directory \"%s\" "
                "supplied by %s (default-log-dir) doesn't exist. "
                "Shutting down the engine", suri->log_dir, suri->conf_filename);
        SCReturnInt(TM_ECODE_FAILED);
    }
    if (!IsLogDirectoryWritable(suri->log_dir)) {
        SCLogError(SC_ERR_LOGDIR_CONFIG, "The logging directory \"%s\" "
                "supplied by %s (default-log-dir) is not writable. "
                "Shutting down the engine", suri->log_dir, suri->conf_filename);
        SCReturnInt(TM_ECODE_FAILED);
    }

设置并验证日志存储目录是否存在。若配置文件中未指定,则使用默认目录,linux下默认为/var/log/suricata。

U.若detect未开启,则设置tcp流重组为false

#ifdef HAVE_NSS
    if (suri->run_mode != RUNMODE_CONF_TEST) {
        /* init NSS for hashing */
        PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0);
        NSS_NoDB_Init(NULL);
    }
#endif

    if (suri->disabled_detect) {
        SCLogConfig("detection engine disabled");
        /* disable raw reassembly */
        (void)ConfSetFinal("stream.reassembly.raw", "false");
    }

若detect未开启,则设置tcp流重组为false。

V.初始化Host engine

HostInitConfig(HOST_VERBOSE);

初始化Host engine。具体到配置文件中只有3项:hash-size, prealloc, memcap。

W.处理CoreDump相关配置

    CoredumpLoadConfig();

处理CoreDump相关配置。Linux下可用prctl函数获取和设置进程dumpable状态,设置corefile大小则是通过通用的setrlimit函数。

X.译码器的设置

    DecodeGlobalConfig();

译码器的设置, teredo,vxlan,vntag,geneve,max-layers

Y.创建注册设备

    LiveDeviceFinalize();

创建注册设备 ,这个函数创建所有需要的LiveDevice,通过LiveRegisterDevice()创建的LiveDeviceName列表  

Z.初始化全局变量stats_ctx

    PreRunInit(suri->run_mode);
/* initialization code for both the main modes and for
 * unix socket mode.
 *
 * Will be run once per pcap in unix-socket mode */
void PreRunInit(const int runmode)
{
    /* Initialize Datasets to be able to use them with unix socket */
    DatasetsInit();
    if (runmode == RUNMODE_UNIX_SOCKET)
        return;

    StatsInit();
#ifdef PROFILING
    SCProfilingRulesGlobalInit();
    SCProfilingKeywordsGlobalInit();
    SCProfilingPrefilterGlobalInit();
    SCProfilingSghsGlobalInit();
    SCProfilingInit();
#endif /* PROFILING */
    DefragInit();
    FlowInitConfig(FLOW_QUIET);
    IPPairInitConfig(FLOW_QUIET);
    StreamTcpInitConfig(STREAM_VERBOSE);
    AppLayerParserPostStreamSetup();
    AppLayerRegisterGlobalCounters();
    OutputFilestoreRegisterGlobalCounters();
}

若运行模式不为RUNMODE_UNIX_SOCKET调用StatsInit()初始化全局变量stats_ctx。

DefragInit()  初始化IP分片重组模块。

FlowInitConfig(FLOW_QUIET)  初始化Flow engine。用来表示一条TCP/UDP/ICMP/SCTP流的,程序当前所记录的所有流便组成了流表,在flow引擎中,流表为flow_hash这个全局变量,其类型为FlowBucket *,而FlowBucket中则能够存储一个Flow链表,典型的一张chained hash Table。在初始化函数FlowInitConfig中,首先会使用配置文件信息填充flow_config,然后会按照配置中的hash_size为流表实际分配内存,接着按照prealloc进行流的预分配(FlowAlloc->FlowEnqueue,存储在flow_spare_q这个FlowQueue类型的队列中),最后调用FlowInitFlowProto为流表所用于的各种流协议进行配置,主要是设置timeout时间。

IPPairInitConfig(FLOW_QUIET)  初始化ippair engine,默认不开启。

StreamTcpInitConfig(STREAM_VERBOSE) 初始化Stream TCP模块。其中调用了StreamTcpReassembleInit函数进行重组模块初始化。

AppLayerParserPostStreamSetup() 为流深度设置一个默认值

AppLayerRegisterGlobalCounters() 注册应用层全局计数器
“http.memuse”,”http.memcap”,”ftp.memuse”,”ftp.memcap”,”app_layer.expectations”到stats_ctx中。

8.去除主线程的权限

    SCDropMainThreadCaps(suricata.userid, suricata.groupid);

去除主线程的权限。这个是通过libcap-ng实现的,首先调用capng_clear清空所有权限,然后根据运行模式添加一些必要权限(主要是为了抓包),最后调用capng_change_id设置新的uid和gid。主线程的权限应该会被新建的子线程继承,因此只需要在主线程设置即可。

9.在数据包开始流动之前我们需要运行的任务

PreRunPostPrivsDropInit(suricata.run_mode);
/* tasks we need to run before packets start flowing,
 * but after we dropped privs */
void PreRunPostPrivsDropInit(const int runmode)
{
    StatsSetupPostConfigPreOutput();
    RunModeInitializeOutputs();

    if (runmode == RUNMODE_UNIX_SOCKET) {
        /* As the above did some necessary startup initialization, it
         * also setup some outputs where only one is allowed, so
         * deinitialize to the state that unix-mode does after every
         * pcap. */
        PostRunDeinit(RUNMODE_PCAP_FILE, NULL);
        return;
    }

    StatsSetupPostConfigPostOutput();
}

在数据包开始流动之前我们需要运行的任务,但在我们丢弃了privs之后   

RunModeInitializeOutputs() 初始化所有Output模块。这些模块之前在线程模块注册函数里已经都注册了,这里会根据配置文件再进行配置和初始化,最后把当前配置下启用了的output模块放到RunModeOutputs链表中。目前我了解到的全部的log都是在这里初始化的,包括自己扩展的log模块也在这里。

10.Detect加载只有的操作

PostConfLoadedDetectSetup(&suricata);
void PostConfLoadedDetectSetup(SCInstance *suri)
{
    DetectEngineCtx *de_ctx = NULL;
    if (!suri->disabled_detect) {
        SCClassConfInit();
        SCReferenceConfInit();
        SetupDelayedDetect(suri);
        int mt_enabled = 0;
        (void)ConfGetBool("multi-detect.enabled", &mt_enabled);
        int default_tenant = 0;
        if (mt_enabled)
            (void)ConfGetBool("multi-detect.default", &default_tenant);
        if (DetectEngineMultiTenantSetup() == -1) {
            FatalError(SC_ERR_FATAL, "initializing multi-detect "
                       "detection engine contexts failed.");
        }
        if (suri->delayed_detect && suri->run_mode != RUNMODE_CONF_TEST) {
            de_ctx = DetectEngineCtxInitStubForDD();
        } else if (mt_enabled && !default_tenant && suri->run_mode != RUNMODE_CONF_TEST) {
            de_ctx = DetectEngineCtxInitStubForMT();
        } else {
            de_ctx = DetectEngineCtxInit();
        }
        if (de_ctx == NULL) {
            FatalError(SC_ERR_FATAL, "initializing detection engine "
                       "context failed.");
        }

        if (de_ctx->type == DETECT_ENGINE_TYPE_NORMAL) {
            if (LoadSignatures(de_ctx, suri) != TM_ECODE_OK)
                exit(EXIT_FAILURE);
        }

        gettimeofday(&de_ctx->last_reload, NULL);
        DetectEngineAddToMaster(de_ctx);
        DetectEngineBumpVersion();
    }
}

detect设为启用时,

SCClassConfInit()  解析Class相关正则

SCReferenceConfInit() 解析Refference相关正则

SetupDelayedDetect(&suri) 设置是否延迟检测。若delayed-detect为yes,则系统将在载入规则集之前就开始处理数据包,这样能够在IPS模式下减少系统的down time(宕机时间)

初始化Decect engine。若配置文件中未指定mpm(多模式匹配器),则默认使用AC,即使用 mpm_table中AC那一项。SRepInit函数(与前面的SCReputationInitCtx不同!)会初始化检测引擎中域 reputaion相关信息,即从配置文件中指定的文件中读取声望数据

11.设置记录开始时间

    SCSetStartTime(&suricata);

设置记录开始时间。

12.初始化运行模式并创建一系列子线程

    RunModeDispatch(suricata.run_mode, suricata.runmode_custom_mode,
            suricata.capture_plugin_name, suricata.capture_plugin_args);
    mode->RunModeFunc();

    if (local_custom_mode != NULL)
        SCFree(local_custom_mode);

    /* Check if the alloted queues have at least 1 reader and writer */
    TmValidateQueueState();

    if (runmode != RUNMODE_UNIX_SOCKET) {
        /* spawn management threads */
        FlowManagerThreadSpawn();
        FlowRecyclerThreadSpawn();
        if (RunModeNeedsBypassManager()) {
            BypassedFlowManagerThreadSpawn();
        }
        StatsSpawnThreads();
    }

初始化运行模式。首先,根据配置文件和程序中的默认值来配置运行模式(single、auto这些),而运行模式类 型(PCAP_DEV、PCAPFILE这些)也在之前已经确定了,因此运行模式已经固定下来,可以从runmodes表中获取到特定的RunMode 了,接着就调用RunMode中的RunModeFunc,进入当前运行模式的初始化函数(mode->RunModeFunc())。以 AFP 类型下的workers模式为例,该模式的初 始化函数为:RunModeIdsAFPWorkers。

FlowManagerThreadSpawn() : 创建Flow管理线程,用于对流表进行超时删除处理。管理线程创建是通过TmThreadCreateMgmtThread函数,类型为TVT_MGMT,执行函数为FlowManager。

FlowRecyclerThreadSpawn(): 同样是对流超时进行处理,执行函数为FlowRecycler。

StatsSpawnThreads() : 创建性能计数相关线程,包括一个定期对各计数器进行同步的唤醒线程(StatsWakeupThread),和一个定期输出计数值的管理线程(StatsMgmtThread)。

13.等待子线程初始化完成

    /* Wait till all the threads have been initialized */
    if (TmThreadWaitOnThreadInit() == TM_ECODE_FAILED) {
        FatalError(SC_ERR_FATAL, "Engine initialization failed, "
                   "aborting...");
    }

等待子线程初始化完成。检查是否初始化完成的方式是遍历tv_root,调用TmThreadsCheckFlag检查子线程的状态标志。

14.继续运行暂停的线程

    SC_ATOMIC_SET(engine_stage, SURICATA_RUNTIME);
    PacketPoolPostRunmodes();

    /* Un-pause all the paused threads */
    TmThreadContinueThreads();

继续运行暂停的线程

15.设置Sigusr2信号的处理函数

PostRunStartedDetectSetup(&suricata);
static void PostRunStartedDetectSetup(const SCInstance *suri)
{
#ifndef OS_WIN32
    /* registering signal handlers we use. We setup usr2 here, so that one
     * can't call it during the first sig load phase or while threads are still
     * starting up. */
    if (DetectEngineEnabled() && suri->delayed_detect == 0) {
        UtilSignalHandlerSetup(SIGUSR2, SignalHandlerSigusr2);
        UtilSignalUnblock(SIGUSR2);
    }
#endif
    if (suri->delayed_detect) {
        /* force 'reload', this will load the rules and swap engines */
        DetectEngineReload(suri);
        SCLogNotice("Signature(s) loaded, Detect thread(s) activated.");
#ifndef OS_WIN32
        UtilSignalHandlerSetup(SIGUSR2, SignalHandlerSigusr2);
        UtilSignalUnblock(SIGUSR2);
#endif
    }
}

设置Sigusr2信号的处理函数

DetectEngineReload(&suri) 重新加载detect engine

16.主循环

SuricataMainLoop(&suricata);
static void SuricataMainLoop(SCInstance *suri)
{
    while(1) {
        if (sigterm_count || sigint_count) {
            suricata_ctl_flags |= SURICATA_STOP;
        }

        if (suricata_ctl_flags & SURICATA_STOP) {
            SCLogNotice("Signal Received.  Stopping engine.");
            break;
        }

        TmThreadCheckThreadState();

        if (sighup_count > 0) {
            OutputNotifyFileRotation();
            sighup_count--;
        }

        if (sigusr2_count > 0) {
            if (!(DetectEngineReloadIsStart())) {
                DetectEngineReloadStart();
                DetectEngineReload(suri);
                DetectEngineReloadSetIdle();
                sigusr2_count--;
            }

        } else if (DetectEngineReloadIsStart()) {
            DetectEngineReload(suri);
            DetectEngineReloadSetIdle();
        }

        usleep(10* 1000);
    }
}

 若收到引擎退出信号(SURICATA_KILL或SURICATA_STOP),则退出循环,执行后续退出操作

TmThreadCheckThreadState() 检查各线程的状态,决定是否进行结束线程、重启线程、终止程序等操作

OutputNotifyFileRotation() 循环设置注册文件的flags。


 凡是过往,即为序章

 类似资料: