suricata源码解析

2023-09-21 08:59:57

SCInstanceInit

初始化suricata实例:程序名设置为程序文件名,其他变量复位。

InitGlobal


int InitGlobal(void)
{
	//调用rust代码初始化context
    rs_init(&suricata_context);
	//初始化引擎状态:
    SC_ATOMIC_INIT(engine_stage);

    /* initialize the logging subsys */
    //包括日志级别、日志格式、日志输出接口(默认屏幕)、日志输出过滤
    //后面还会用配置重新初始化一遍日志模块
    SCLogInitLogModule(NULL);
    //设置进程名,及ps看到的名字
    SCSetThreadName("Suricata-Main");

    /* Ignore SIGUSR2 as early as possible. We redeclare interest
     * once we're done launching threads. The goal is to either die
     * completely or handle any and all SIGUSR2s correctly.
     */
#ifndef OS_WIN32
    UtilSignalHandlerSetup(SIGUSR2, SIG_IGN);
    if (UtilSignalBlock(SIGUSR2)) {
        SCLogError("SIGUSR2 initialization error");
        return EXIT_FAILURE;
    }
#endif
	//初始化解析size的pcre正则表达式,用于解析xkb、xMB、xGb等
    ParseSizeInit();
    //注册运行模式
    RunModeRegisterRunModes();

    /* Initialize the configuration module. */
    //只是将配置模块初始化为一个尾队列
    ConfInit();

    return 0;
}

- RunModeRegisterRunModes

注册所有的运行模式
运行模式结构体:


/* Run mode */
enum RunModes {
    RUNMODE_UNKNOWN = 0,
    RUNMODE_PCAP_DEV,
    RUNMODE_PCAP_FILE,
    RUNMODE_PFRING,
    RUNMODE_NFQ,
    RUNMODE_NFLOG,
    RUNMODE_IPFW,
    RUNMODE_ERF_FILE,
    RUNMODE_DAG,
    RUNMODE_AFP_DEV,
    RUNMODE_AFXDP_DEV,
    RUNMODE_NETMAP,
    RUNMODE_DPDK,
    RUNMODE_UNITTEST,
    RUNMODE_NAPATECH,
    RUNMODE_UNIX_SOCKET,
    RUNMODE_WINDIVERT,
    RUNMODE_PLUGIN,
    RUNMODE_USER_MAX, /* Last standard running mode */
    RUNMODE_LIST_KEYWORDS,
    RUNMODE_LIST_APP_LAYERS,
    RUNMODE_LIST_RUNMODES,
    RUNMODE_PRINT_VERSION,
    RUNMODE_PRINT_BUILDINFO,
    RUNMODE_PRINT_USAGE,
    RUNMODE_DUMP_CONFIG,
    RUNMODE_CONF_TEST,
    RUNMODE_LIST_UNITTEST,
    RUNMODE_ENGINE_ANALYSIS,
#ifdef OS_WIN32
    RUNMODE_INSTALL_SERVICE,
    RUNMODE_REMOVE_SERVICE,
    RUNMODE_CHANGE_SERVICE_PARAMS,
#endif
    RUNMODE_DUMP_FEATURES,
    RUNMODE_MAX,
};
typedef struct RunMode_ {
    /* the runmode type */
    enum RunModes runmode;
    const char *name;//定制模式的名字,主要有single、workers、autofp
    const char *description;
    /* runmode function */
    int (*RunModeFunc)(void);//运行模式的入口函数
    void (*RunModeIsIPSEnabled)(void);
} RunMode;

typedef struct RunModes_ {
    int cnt;
    RunMode *runmodes;
} RunModes;

static RunModes runmodes[RUNMODE_USER_MAX];

ParseCommandLine

解析命令行

FinalizeRunMode

检查运行模式设置是否已设置,并确保suricata后台模式(daemon)下不能运行RUNMODE_PCAP_FILE、RUNMODE_UNITTEST两种运行模式。

StartInternalRunMode

启动内部运行模式,主要包括RUNMODE_LIST_KEYWORDS、RUNMODE_LIST_APP_LAYERS、RUNMODE_PRINT_VERSION、RUNMODE_PRINT_BUILDINFO、RUNMODE_PRINT_USAGE、RUNMODE_LIST_RUNMODES、RUNMODE_LIST_UNITTEST、RUNMODE_UNITTEST这几个运行模式,运行完响应的处理函数,直接退出程序。

GlobalsInitPreConfig

Initializations for global vars, queues, etc (memsets, mutex init…)

void GlobalsInitPreConfig(void)
{
	//初始化时间锁和时区
    TimeInit();
    //Registers the keywords(SMs) that should be given fp support.
    //g_fp_support_smlist_list:存储单模匹配链表ID和优先级的对应关系,单模匹配链表ID类型为enum DetectSigmatchListEnum
    SupportFastPatternForSigMatchTypes();
    //初始化解析阈值规则的相关正则表达式
    SCThresholdConfGlobalInit();
    //初始化一个哈希表,用于存储协议名和协议号的对应关系
    SCProtoNameInit();
    //
    FrameConfigInit();
}

LoadYamlConfig

从suricata.yaml加载配置到内存

SetupUserMode

这个是设置非system模式的情况下,把日志和数据目录设置到当前目录;suricata有system模式和user模式

InitRunAs

Initialize the user and group Suricata is to run as。即初始化suricata运行的用户名和组名

SCLogLoadConfig

读取logging.outputs配置节点,调用SCLogInitLogModule重新初始化日志模块

LogVersion

打印suricata版本和模式(user模式或system模式)

UtilCpuPrintSummary

Print a summary of CPUs detected (configured and online)。
sysconf(_SC_NPROCESSORS_CONF)返回系统可以使用的核数,但是其值会包括系统中禁用的核的数目,因此该值并不代表当前系统中可用的核数。而 sysconf(_SC_NPROCESSORS_ONLN)的返回值真正的代表了系统当前可用的核数.

ParseInterfacesList

根据运行模式,从配置读取网卡名或者设置网卡配置:
对于RUNMODE_PCAP_DEV、RUNMODE_DPDK:如果命令行未设置网卡,则从配置读取网卡名,存到设备名链表pre_live_devices。
对于RUNMODE_PFRING:将从命令行读取的网卡名存到配置节点pfring.live-interface

PostConfLoadedSetup

This function is meant to contain code that needs to be run once the configuration has been loaded.即加载完配置后首先运行这个函数。

  • MpmTableSetup:注册多模匹配算法,包括AC、ACBS、ACTile
  • SpmTableSetup:注册单模匹配算法,包括BM、hyperscan
  • StorageInit:初始化Storage,结构体如下:

typedef struct StorageMapping_ {
    const char *name;
    StorageEnum type; // host, flow, tx, stream, ssn, etc
    unsigned int size;
    void *(*Alloc)(unsigned int);
    void (*Free)(void *);
} StorageMapping;

/** \brief list of StorageMapping used at registration time */
typedef struct StorageList_ {
    StorageMapping map;
    int id;
    struct StorageList_ *next;
} StorageList;

static StorageList *storage_list = NULL;
static int storage_max_id[STORAGE_MAX];
static int storage_registration_closed = 0;
static StorageMapping **storage_map = NULL;
  • RegisterFlowBypassInfo:注册一个名为bypass_counters的FlowStorage
  • MacSetRegisterFlowStorage:在eve日志开启ethernet开关时,注册一个名为macset的FlowStorage
  • SetMasterExceptionPolicy:设置主异常策略,异常策略有:

enum ExceptionPolicy {
    EXCEPTION_POLICY_NOT_SET = 0,
    EXCEPTION_POLICY_PASS_PACKET,
    EXCEPTION_POLICY_PASS_FLOW,
    EXCEPTION_POLICY_BYPASS_FLOW,
    EXCEPTION_POLICY_DROP_PACKET,
    EXCEPTION_POLICY_DROP_FLOW,
    EXCEPTION_POLICY_REJECT,
};

  • LiveDeviceFinalize:前面获取了网卡名,这里创建对应的网卡设备信息,结构体如下:
/** storage for live device names */
typedef struct LiveDevice_ {
    char *dev;  /**< the device (e.g. "eth0") */
    char dev_short[MAX_DEVNAME + 1];
    bool tenant_id_set;

    int id;

    SC_ATOMIC_DECLARE(uint64_t, pkts);
    SC_ATOMIC_DECLARE(uint64_t, drop);
    SC_ATOMIC_DECLARE(uint64_t, bypassed);
    SC_ATOMIC_DECLARE(uint64_t, invalid_checksums);
    TAILQ_ENTRY(LiveDevice_) next;

    uint32_t tenant_id;     /**< tenant id in multi-tenancy */
    uint32_t offload_orig;  /**< original offload settings to restore @exit */
} LiveDevice;
  • RunModeEngineIsIPS:前面注册了运行模式,这里根据命令行设置最终的运行模式
  • AppLayerSetup:

/***** Setup/General Registration *****/

int AppLayerSetup(void)
{
    SCEnter();
	//初始化应用层协议检测context(主要是初始化单模匹配算法、多模匹配算法)
	//注册名为expectation的IPPairStorage和FlowStorage
    AppLayerProtoDetectSetup();
    //memset AppLayerParserCtx
    AppLayerParserSetup();
	//注册应用层处理函数
    AppLayerParserRegisterProtocolParsers();
    //SSL和IMAP在注册协议处理函数时,会注册一些模式匹配填充到alpd_ctx。这里将注册的模式填充到多模匹配总表mpm_table
    AppLayerProtoDetectPrepareState();
    //设置每个应用层协议计数器的名字
    /*typedef struct AppLayerCounterNames_ {
    char name[MAX_COUNTER_SIZE];
    char tx_name[MAX_COUNTER_SIZE];
    char gap_error[MAX_COUNTER_SIZE];
    char parser_error[MAX_COUNTER_SIZE];
    char internal_error[MAX_COUNTER_SIZE];
    char alloc_error[MAX_COUNTER_SIZE];
} AppLayerCounterNames;*/
    AppLayerSetupCounters();

    SCReturnInt(0);
}
  • ConfigGetCaptureValue

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

  • SCHInfoLoadFromConfig

从配置文件中载入host os policy(主机OS策略)信息,
radix tree保存所有主机策略信息。网络入侵通常是针对某些特定OS的漏洞,因此如果能够获取部署环境中主机的OS信息,肯定对入侵检测大有裨益。

  • SigTableSetup

注册检测引擎所支持的规则格式中的关键字,比如sid、priority、msg、within、distance等等

  • SigTableApplyStrictCommandlineOption

为指定的检测关键字设置严格匹配。

  • TmqhSetup

初始化queue handler(队列处理函数),这个是衔接线程模块和数据包队列之间的桥梁,这里注册了3类handler:simple, packetpool, flow。每类handler内部都有一个InHandler和OutHandler,simple和flow的InHandler都是从inQueue队列中获取数据包,simple的OutHandler将数据包送入outQueue。packetpool的InHandler从packetpool获取数据包,OutHandler将数据包送入packetpool。

  • TagInitCtx

注册名为tag的HostStorage和FlowStorage

  • PacketAlertTagInit

初始化了tag signature和 tag packet alert structure。
tag signature we use for tag alerts。
tag packet alert structure for tag alerts。
tag signature 的sid为1.

  • ThresholdInit

注册名为threshold的HostStorage和IPPairStorage

  • HostBitInitCtx

注册名为bit的HostStorage

  • IPPairBitInitCtx

注册名为bit的IPPairStorage

  • DetectAddressTestConfVars

检测配置文件里面的address-groups配置是否正确

  • DetectPortTestConfVars

检测配置文件里面的port-groups配置是否正确

  • FeatureTrackingRegister

初始化feature_hash_table

  • RegisterAllModules

注册所有线程模块

  • AppLayerHtpNeedFileInspection

Sets a flag that informs the HTP app layer that some module in the engine needs the http request file。即设置htp_config_flags,有4个flag:HTP_REQUIRE_REQUEST_BODY、HTP_REQUIRE_REQUEST_MULTIPART、HTP_REQUIRE_REQUEST_FILE、HTP_REQUIRE_RESPONSE_BODY

  • StorageFinalize

前面注册了一些storage,存储在StorageList。这里将其挪到StorageMapping,即注册时采用链表数组形式,为了实现快速查找,将其挪到二维数组里,可通过type:id快速访问对应的storage

  • TmModuleRunInit

前面注册了各个线程模块,这里对每个模块进行初始化

  • MayDaemonize

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

  • InitSignalHandler

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

  • ConfigGetLogDirectory

获取程序日志目录

  • ConfigCheckLogDirectoryExists

检查日志目录是否存在

  • IsLogDirectoryWritable

检查日志目录是否可写

  • HostInitConfig

initializing host engine:读取配置文件host节点的hash-size, prealloc, memcap配置,初始化大小为hash-size、元素为HostHashRow的hash,并预分配prealloc个Host,放在host_spare_q里面。

  • CoredumpLoadConfig

Configures the core dump size

  • DecodeGlobalConfig

加载解码器decoder配置,设置packet_alert_max(后面会分配packet_alert_max个PacketAlert)

  • PostConfLoadedSetupHostMode

设置Host mode: set if box is sniffing only or is a router

  • PreRunInit

/* 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)
{
	//初始化一个名为byterange的hash,不知道干啥用的
    HttpRangeContainersInit();
    if (runmode == RUNMODE_UNIX_SOCKET)
        return;
	//初始化stats_ctx:Holds the output interface context for the counter api
    StatsInit();
#ifdef PROFILING
    SCProfilingRulesGlobalInit();
    SCProfilingKeywordsGlobalInit();
    SCProfilingPrefilterGlobalInit();
    SCProfilingSghsGlobalInit();
    SCProfilingInit();
#endif /* PROFILING */
	//初始化IP分片重组模块
    DefragInit();
    //初始化flow_hash
    FlowInitConfig(FLOW_QUIET);
    //初始化ippair_hash
    IPPairInitConfig(FLOW_QUIET);
    //初始化Stream TCP配置。其中调用了StreamTcpReassembleInit函数进行重组模块初始化
    StreamTcpInitConfig(STREAM_VERBOSE);
    //为流深度设置一个默认值
    AppLayerParserPostStreamSetup();
    // 注册应用层全局计数器
    AppLayerRegisterGlobalCounters();
    //Register a file data output module
    OutputFilestoreRegisterGlobalCounters();
}

SCDropMainThreadCaps

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

CoredumpEnable

Enable coredumps on systems where coredumps can and need to be enabled.

PreRunPostPrivsDropInit

/* tasks we need to run before packets start flowing,
 * but after we dropped privs */
void PreRunPostPrivsDropInit(const int runmode)
{
	//Initializes stats context
    StatsSetupPostConfigPreOutput();
    //Initialize the output modules
    //前面注册线程模块时注册了日志模块,这里初始化输出日志模块,并激活
    RunModeInitializeOutputs();
    DatasetsInit();

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

PostConfLoadedDetectSetup

void PostConfLoadedDetectSetup(SCInstance *suri)
{
    DetectEngineCtx *de_ctx = NULL;
    if (!suri->disabled_detect) {
    	//初始化解析规则classtype的正则表达式
        SCClassConfInit();
        //初始化解析reference的正则表达式
        SCReferenceConfInit();
        //设置是否延迟检测。若delayed-detect为yes,则系统将在载入规则集之前就开始处理数据包,这样能够在IPS模式下减少系统的down time(宕机时间)
        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(suri->unix_socket_enabled) == -1) {
            FatalError("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("initializing detection engine 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();
    }
}

SCSetStartTime

设置suricata开始时间

RunModeDispatch

获取suricata运行模式,调用对应的运行模式函数,创建flow管理线程(只有一个FlowManager模块)、flow回收线程(只有一个FlowRecycler模块)、StatsWakeupThread、StatsMgmtThread

TmThreadWaitOnThreadInit

等待所有线程初始化完成

SC_ATOMIC_SET(engine_stage, SURICATA_RUNTIME);

设置引擎状态为RUNTIME

PacketPoolPostRunmodes

Set the max_pending_return_packets value

TmThreadContinueThreads

Un-pause all the paused threads

TmThreadWaitOnThreadRunning

Waits for all threads to be in a running state

PostRunStartedDetectSetup

注册信号处理函数,重新加载检测引擎

更多推荐

MySQL什么情况下会死锁,发生了死锁怎么处理呢?

🏆作者简介,黑夜开发者,CSDN领军人物,全栈领域优质创作者✌,CSDN博客专家,阿里云社区专家博主,2023年6月CSDN上海赛道top4。🏆数年电商行业从业经验,历任核心研发工程师,项目技术负责人。🏆本文已收录于PHP专栏:MySQL的100个知识点。🎉欢迎👍点赞✍评论⭐收藏文章目录🚀一、前言-关于数据

数据库 MVCC 详解

目录1.什么是MVCC?2.MVCC的好处?3.快照读?当前读分别是什么?怎么理解?3.1快照读3.2当前读4.MVCC实现原理4.1隐藏字段4.2undolog(版本链)4.3readView5.readView深层详解6.数据库的四种隔离级别7.读已提交和可重复读的区别?7.1MVCC主要作用体现在读已提交和可重复

【Rust 基础篇】Rust 非对象安全

导言在Rust中,Trait是一种用于实现共享行为和抽象的重要特性。然而,并非所有的Trait都是对象安全的。当Trait不满足对象安全的条件时,就被称为非对象安全的Trait。本篇博客将深入探讨Rust中的非对象安全问题,解释什么是非对象安全,为什么会出现这种情况,以及如何处理和避免非对象安全的问题。让我们开始吧!什

「大数据-0」虚拟机VMware安装、配置、使用、创建虚拟机集群教程

目录一、下载VMwareWworkstationPro16二、安装VMwareWworkstationPro16三、检查与设置VMware的网卡1.检查2.设置VMware网段四、在VMware上安装Linux虚拟机五、对安装好的虚拟机进行设置1.打开设置2.设置中文3.修改字体大小4.修改终端字体大小5.关闭虚拟机六

4.1.9-映射应用程序体系结构

映射应用程序体系结构IDWSTG-INFO-10总结为了有效地测试应用程序,并能够就如何解决所识别的任何问题提供有意义的建议,了解实际测试的内容非常重要。此外,确定是否应将特定组件视为超出测试范围可能会有所帮助。现代Web应用程序的复杂性差异很大,从在单个服务器上运行的简单脚本到分布在数十个不同系统、语言和组件的高度复

华为云云耀云服务器L实例评测-基于华为云服务器的测试及简单配置

引言云计算已经成为现代企业和个人的重要组成部分。在云计算市场上,华为云一直以来都以其出色的性能和服务质量而闻名。周末的时候,利用华为云云耀云服务器搭建了一个基于hexo的个人博客,我用的是2核2G的3M带宽的配置,访问起来挺丝滑的,记录一下本次对华为云的一些测试及看法,探讨其性能、可靠性以及适用场景,帮助您更好地了解这

【python数据分析基础】—pandas中loc()与iloc()的介绍与区别

文章目录前言一、loc[]函数二、iloc[]函数三、详细用法loc方法iloc方法总结共同点不同点前言我们经常在寻找数据的某行或者某列的时常用到Pandas中的两种方法iloc和loc,两种方法都接收两个参数,第一个参数是行的范围,第二个参数是列的范围。一、loc[]函数loc:接收的是行、列的名称或标签。在切片是按

【送书活动】网络安全(黑客)自学

前言「作者主页」:雪碧有白泡泡「个人网站」:雪碧的个人网站「推荐专栏」:★java一站式服务★★React从入门到精通★★前端炫酷代码分享★★从0到英雄,vue成神之路★★uniapp-从构建到提升★★从0到英雄,vue成神之路★★解决算法,一个专栏就够了★★架构咱们从0说★★数据流通的精妙之道★★后端进阶之路★文章目

以太网媒体接口MII/RMII/SMII/GMII/RGMII/SGMII

以太网媒体接口MII/RMII/SMII/GMII/RGMII/SGMIIGMAC系统框架(EMAC是百兆mac,GMAC是千兆mac)网卡网卡系统框架结构PHY(PhysicalLayer,物理层)MAC(MediaAccessControl、媒体访问控制器)以太网结构大框架MAC硬件框图MII(MediaIndep

使用cpolar配合Plex搭建私人媒体站

文章目录1.前言2.Plex网站搭建2.1Plex下载和安装2.2Plex网页测试2.3cpolar的安装和注册3.本地网页发布3.1Cpolar云端设置3.2Cpolar本地设置4.公网访问测试5.结语1.前言用手机或者平板电脑看视频,已经算是生活中稀松平常的场景了,特别是各种碎片时间(追剧下饭、地铁上刷剧等等),看

设计模式:单例模式

目录什么是单例模式为什么使用单例模式常见的单例写法1.懒汉式(LazyInitialization)2.双重检查锁定(Double-CheckedLocking)3.饿汉式(EagerInitialization)4.枚举实现单例总结什么是单例模式单例模式是一种创建型设计模式,确保一个类只有一个实例,并提供全局访问点来

热文推荐