A-A+

suricata 3.1 源码分析5

2017年08月09日 suricata 暂无评论 阅读 47 次

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

执行PostConfLoadedSetup,即运行那些需要在配置载入完成后就立马执行的函数。这里面涉及的流程和函数非常多

    /**
     * This function is meant to contain code that needs
     * to be run once the configuration has been loaded.
     */
    static int PostConfLoadedSetup(SCInstance *suri)
    {
        char *hostmode = NULL;

        /* load the pattern matchers */
        MpmTableSetup();

MpmTableSetup:设置多模式匹配表,该表中每一项就是一个实现了某种多模式匹配算法(如WuManber、AC)的匹配器。 以注册AC匹配器为例,MpmTableSetup会调用MpmACRegister函数实现AC注册,函数内部其实只是填充mpm_table中对应 AC的那一项(mpm_table[MPM_AC])的各个字段,如:匹配器名称(”ac”)、初始化函数(SCACInitCtx)、增加模式函数 (SCACAddPatternCS)、实际的搜索执行函数(SCACSearch)。

    SpmTableSetup();

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

    switch (suri->checksum_validation) {
        case 0:
            ConfSet("stream.checksum-validation", "0");
            break;
        case 1:
            ConfSet("stream.checksum-validation", "1");
            break;
    }

设置checksum是否生效。

    AppLayerSetup();

应用层协议设置。其 中,AppLayerProtoDetectSetup函数初始化该模块所用到的多模式匹配器;AppLayerParserSetup函数通过传输层和应用层协议号构建了一个二维数组,并在该数组中为相应的流重组深度赋值;AppLayerParserRegisterProtocolParsers函数注册各种应用层协议的解析器 (如RegisterHTPParsers函数对应HTTP协议,这个后面会详细分析);AppLayerProtoDetectPrepareState函数的功能暂时也没弄明白。

    /* 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。

    if (ConfigCheckLogDirectory(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 (ConfigGetCaptureValue(suri) != TM_ECODE_OK) {
        SCReturnInt(TM_ECODE_FAILED);
    }

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

    if (ConfGet("host-mode", &hostmode) == 1) {
        if (!strcmp(hostmode, "router")) {
            host_mode = SURI_HOST_IS_ROUTER;
        } else if (!strcmp(hostmode, "sniffer-only")) {
            host_mode = SURI_HOST_IS_SNIFFER_ONLY;
        } else {
            if (strcmp(hostmode, "auto") != 0) {
                WarnInvalidConfEntry("host-mode", "%s", "auto");
            }
            if (EngineModeIsIPS()) {
                host_mode = SURI_HOST_IS_ROUTER;
            } else {
                host_mode = SURI_HOST_IS_SNIFFER_ONLY;
            }
        }
    } else {
        if (EngineModeIsIPS()) {
            host_mode = SURI_HOST_IS_ROUTER;
            SCLogInfo("No 'host-mode': suricata is in IPS mode, using "
                    "default setting 'router'");
        } else {
            host_mode = SURI_HOST_IS_SNIFFER_ONLY;
            SCLogInfo("No 'host-mode': suricata is in IDS mode, using "
                    "default setting 'sniffer-only'");
        }
    }

设置host_mode(主机模式),两种模式:router和sniffer-only,而如果设置为“auto”,则会进行自动选择:IPS模式下运行为router,否则为sniffer-only。

#ifdef NFQ
    if (suri->run_mode == RUNMODE_NFQ)
        NFQInitConfig(FALSE);
#endif

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

SCHInfoLoadFromConfig:从配置文件中载入host os policy(主机OS策略)信息。网络入侵通常是针对某些特定OS的漏洞,因此如果能够获取部署环境中主机的OS信息,肯定对入侵检测大有裨益。具体这些信息是怎么使用的,暂时也还不清楚。

    if (suri->run_mode != RUNMODE_UNIX_SOCKET) {
        DefragInit();

DefragInit:初始化IP分片重组模块。
}

    if (suri->run_mode == RUNMODE_ENGINE_ANALYSIS) {
        SCLogInfo("== Carrying out Engine Analysis ==");
        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 */

SigTableSetup:初始化检测引擎,主要是注册检测引擎所支持的规则格式(跟Snort规则基本一致)中的关键字,比如sid、priority、msg、within、distance等等。
TmqhSetup();
TmqhSetup:初始化queue handler(队列处理函数),这个是衔接线程模块和数据包队列之间的桥梁,目前共有5类handler:simple, nfq, packetpool, flow, ringbuffer。每类handler内部都有一个InHandler和OutHandler,一个用于从上一级队列中获取数据包,另一个用于处理完毕后将数据包送入下一级队列。

    StorageInit();

StorageInit:初始化存储模块,这个模块可以用来临时存储一些数据,数据类型目前有两种:host、flow。具体在何种场景下用,目前未知。
CIDRInit();
CIDRInit:初始化CIDR掩码数组,cidrs[i]对应前i位为1的掩码。
SigParsePrepare();
SigParsePrepare:为规则签名解析器的正则表达式进行编译(pcre_compile)和预处理(pcre_study)。

#ifdef PROFILING
    if (suri->run_mode != RUNMODE_UNIX_SOCKET) {
        SCProfilingRulesGlobalInit();
        SCProfilingKeywordsGlobalInit();
        SCProfilingSghsGlobalInit();
        SCProfilingInit();
    }

几个Profiling模块的初始化函数。Profiling模块提供内建的模块性能分析功能,可以用来分析模块性能、各种锁的实际使用情况(竞争时间)、规则的性能等。

#endif /* PROFILING */
    SCReputationInitCtx();

SCReputationInitCtx:初始化IP声望模块。IP声望数据在内部是以Radix tree的形式存储的,但目前还不知道数据源是从哪来的,而且也没看到这个模块的函数在哪调用。

    SCProtoNameInit();

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

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

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

    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端口号)是否符合格式要求。

    RegisterAllModules();

RegisterAllModules:这是个非常重要的函数!里面注册了Suricata所支持的所有线程模块(Thread Module)。以 pcap相关模块为例,TmModuleReceivePcapRegister函数注册了Pcap捕获模块,而 TmModuleDecodePcapRegister函数注册了Pcap数据包解码模块。所谓注册,就是在tmm_modules模块数组中对应的那项 中填充TmModule结构的所有字段,这些字段包括:模块名字、线程初始化函数、包处理或包获取函数、线程退出清理函数、一些标志位等等。

    AppLayerHtpNeedFileInspection();

AppLayerHtpNeedFileInspection:设置suricata内部模块与libhtp(HTTP处理库)对接关系的函数,具体细节暂时不管。

    DetectEngineRegisterAppInspectionEngines();

注册与http相关的DetectEngine模块,将数据分为toServer和toClient两种,分别处理。

    StorageFinalize();

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

    TmModuleRunInit();

TmModuleRunInit:调用之前注册的线程模块的初始化函数进行初始化,目前tmm_modules->Init指针上并没有挂初始化函数。

    PcapLogProfileSetup();

如果使用了“–enable-profiling”则此函数读取的配置生效,具体功能不太清楚,相关解释在yaml配置文件中,没看明白。

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

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

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

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

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

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

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

    }

    HostInitConfig(HOST_VERBOSE);

初始化Host engine。这货好像跟之前的host类型的storage有关系。具体到配置文件中只有3项:hash-size, prealloc, memcap。

    if (MagicInit() != 0)
        SCReturnInt(TM_ECODE_FAILED);

初始化Magic模块。Magic模块只是对libmagic库进行了一层封装,通过文件中的magic字段来检测文件的类型(如”PDF-1.3“对应PDF文件)默认此配置不开启。

    SCAsn1LoadConfig();

设置asn1流的最大解析个数,默认是256。

    CoredumpLoadConfig();

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

    SCReturnInt(TM_ECODE_OK);
}
标签:

给我留言

Copyright © 九毛的官方博客 保留所有权利.   Theme  Ality

用户登录