suricata源码解析

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

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

你可能感兴趣的:(opensource,suricata)