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