目录
线程类型
0. 主线程
注册runmodes
根据配置初始化
注册应用协议检测器
注册应用层协议解析器
加载规则关键字
tag初始化
几种存储结构的初始化与注册
注册所有模块的函数
检测引擎初始化:
创建工作线程
创建非工作线程的子线程
主线程进入主循环:
suricata 除了主线程外还有若干子线程,子线程类型见下表:
序号 | 线程类型 | name | 线程数量 | tv->type |
tv->tm_func()对应的函数 | tv->tm_slots 对应的slot表(TmModule) | 备注 | |
1 | worker threads | W | suricata.yaml threading配置 |
packetpool |
TVT_PPT | TmThreadsSlotPktAcqLoop | Receive ->Decode->FlowWorker |
|
2 | FlowManagerThread | FM | suricata.yaml flow.managers |
management |
TVT_MGMT |
TmThreadsManagement | FlowManager | 默认为1个 |
3 | FlowRecyclerThread | FR | suricata.yaml flow.recyclers |
management |
TVT_MGMT |
TmThreadsManagement | FlowRecycler | 默认为1个 |
4 | BypassedFlowManagerThread | FB | 1 | management |
TVT_MGMT |
TmThreadsManagement | BypassedFlowManager | |
5 | StatsWakeupThread |
CW | 1 | custom |
TVT_MGMT |
StatsWakeupThread |
无 | |
6 | StatsMgmtThread |
CS | 1 | custom |
TVT_MGMT |
StatsMgmtThread |
无 | |
7 | UnixManagerThread |
US | 2 or 0 |
command | TVT_CMD |
TmThreadsManagement |
UnixManager |
RUNMODE_UNIX_SOCKET mode是才创建,否则没有 |
8 | DetectLoaderThread |
DL | suricata.yaml multi-detect.loaders or 0 |
command | TVT_CMD |
TmThreadsManagement |
DetectLoader |
MT(多租户)被启用才创建,默认的配置没有此项。 |
主线程函数: int main(int argc, char **argv),可参见suricata 初始化做的那些事儿_xuwaiwai的博客-CSDN博客_suricata目录前言一、SuricataMain 程序入口1.在引擎中注册所有运行模式。2.解析执行命令3.初始化全局变量3.加载yaml配置文件4.初始化日志5.打印版本和CPUs/cores6.解析接口设置名称7.配置加载后的操作A. 设置多模和单模匹配模式B. 设置checksumC. 初始化存储模块D. 应用层协议设置E. 设置捕获参数F. 加载主机操作系统策略信息G. 初始化检测引擎H. 初始化queue handler(队列处理函数)https://blog.csdn.net/xuwaiwai/article/details/120045259?spm=1001.2014.3001.5501
注册各种的运行模式到 runmodes 数据中(这里的运行模式是采集数据来源和IPS/IDS 的组合,比如 RUNMODE_PCAP_DEV,RUNMODE_NFQ,RUNMODE_AFP_DEV), 在每个运行模式具体的注册函数中,分别注册各自支持的工作模式("single","workers","autofp"),runmodes 结构如下。实际运行时会全部注册各种运行模式的工作模式,但是会根据运行命令和配置只会初始化并使用一种(运行模式 + 工作模式),如(RUNMODE_AFP_DEV+ "workers")。
typedef struct RunMode_ { //每种工作模式
/* the runmode type */
enum RunModes runmode; //运行模式 RUNMODE_AFP_DEV
const char *name; //工作模式名字 "workers"
const char *description; //描述
/* runmode function */
int (*RunModeFunc)(void); //初始化的回调函数
} RunMode;
typedef struct RunModes_ {
int cnt;
RunMode *runmodes; //容纳多个工作模式(single, workers, autofp)
} RunModes;
static RunModes runmodes[RUNMODE_USER_MAX]; //容纳多种运行模式
//eg. (RUNMODE_AFP_DEV+ "workers")将注册如下几种
void RunModeIdsAFPRegister(void)
{
RunModeRegisterNewRunMode(RUNMODE_AFP_DEV, "single",
"Single threaded af-packet mode",
RunModeIdsAFPSingle);
RunModeRegisterNewRunMode(RUNMODE_AFP_DEV, "workers",
"Workers af-packet mode, each thread does all"
" tasks from acquisition to logging",
RunModeIdsAFPWorkers);
RunModeRegisterNewRunMode(RUNMODE_AFP_DEV, "autofp",
"Multi socket AF_PACKET mode. Packets from "
"each flow are assigned to a single detect "
"thread.",
RunModeIdsAFPAutoFp);
return;
}
解析执行命令,记载 suricata.yaml配置文件,初始化日志系统。
:这个主要是根据相应的协议做对应的规则检测,初始化并保存在全局{static AppLayerProtoDetectCtx alpd_ctx}中。
多模:放入 MpmTableElmt mpm_table[MPM_TABLE_SIZE] 中,总共有四种, 分别注册一些回调函数。实际使用时根据配置只用一种。
enum {
MPM_NOTSET = 0,
/* aho-corasick */
MPM_AC,
MPM_AC_BS,
MPM_AC_KS,
MPM_HS,
/* table size */
MPM_TABLE_SIZE,
};
typedef struct MpmTableElmt_ {
const char *name;
void (*InitCtx)(struct MpmCtx_ *);
void (*InitThreadCtx)(struct MpmCtx_ *, struct MpmThreadCtx_ *);
void (*DestroyCtx)(struct MpmCtx_ *);
void (*DestroyThreadCtx)(struct MpmCtx_ *, struct MpmThreadCtx_ *);
int (*AddPattern)(struct MpmCtx_ *, uint8_t *, uint16_t, uint16_t, uint16_t, uint32_t, SigIntId, uint8_t);
int (*AddPatternNocase)(struct MpmCtx_ *, uint8_t *, uint16_t, uint16_t, uint16_t, uint32_t, SigIntId, uint8_t);
int (*Prepare)(struct MpmCtx_ *);
uint32_t (*Search)(const struct MpmCtx_ *, struct MpmThreadCtx_ *, PrefilterRuleStore *, const uint8_t *, uint32_t);
void (*PrintCtx)(struct MpmCtx_ *);
void (*PrintThreadCtx)(struct MpmThreadCtx_ *);
void (*RegisterUnittests)(void);
uint8_t flags;
} MpmTableElmt;
MpmTableElmt mpm_table[MPM_TABLE_SIZE];
单模:放入 SpmTableElmt spm_table[SPM_TABLE_SIZE] 中;总共有两种,分别注册一些回调函数。实际使用时根据配置只用一种。
enum {
SPM_BM, /* Boyer-Moore */
SPM_HS, /* Hyperscan */
/* Other SPM matchers will go here. */
SPM_TABLE_SIZE
};
typedef struct SpmTableElmt_ {
const char *name;
SpmGlobalThreadCtx *(*InitGlobalThreadCtx)(void);
void (*DestroyGlobalThreadCtx)(SpmGlobalThreadCtx *g_thread_ctx);
SpmThreadCtx *(*MakeThreadCtx)(const SpmGlobalThreadCtx *g_thread_ctx);
void (*DestroyThreadCtx)(SpmThreadCtx *thread_ctx);
SpmCtx *(*InitCtx)(const uint8_t *needle, uint16_t needle_len, int nocase,
SpmGlobalThreadCtx *g_thread_ctx);
void (*DestroyCtx)(SpmCtx *);
uint8_t *(*Scan)(const SpmCtx *ctx, SpmThreadCtx *thread_ctx,
const uint8_t *haystack, uint32_t haystack_len);
} SpmTableElmt;
extern SpmTableElmt spm_table[SPM_TABLE_SIZE];
根据配置中的匹配方式,初始化相应的单模和多模匹配,其中单模调用其中的 spm_table[matcher].InitGlobalThreadCtx() 回调函数来初始化,多模则会根据 三种协议(TCP,UDP,ICMP)和 两个方向(to_client, to_server)共六个组合分别初始化 mpm_table[matcher].InitCtx(mpm_ctx);
typedef struct AppLayerProtoDetectCtx_ {
/* Context per ip_proto.
* \todo Modify ctx_ipp to hold for only tcp and udp. The rest can be
* implemented if needed. Waste of space otherwise. */
AppLayerProtoDetectCtxIpproto ctx_ipp[FLOW_PROTO_DEFAULT]; //多模扫描
/* Global SPM thread context prototype. */
SpmGlobalThreadCtx *spm_global_thread_ctx; //单模扫描
AppLayerProtoDetectProbingParser *ctx_pp;
/* Indicates the protocols that have registered themselves
* for protocol detection. This table is independent of the
* ipproto. */
const char *alproto_names[ALPROTO_MAX];
} AppLayerProtoDetectCtx;
/* The global app layer proto detection context. */
static AppLayerProtoDetectCtx alpd_ctx;
:主要是根据应用协议做协议解析,注册各种协议的回调函数,分配相应内存并保存在全局{static AppLayerParserCtx alp_ctx}中
void AppLayerParserRegisterProtocolParsers(void)
{
SCEnter();
RegisterHTPParsers();
RegisterSSLParsers();
...
RegisterRdpParsers();
RegisterHTTP2Parsers();
/** IMAP */
AppLayerProtoDetectRegisterProtocol(ALPROTO_IMAP, "imap");
if (AppLayerProtoDetectConfProtoDetectionEnabled("tcp", "imap")) {
if (AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_IMAP,
"1|20|capability", 12, 0, STREAM_TOSERVER) < 0)
{
SCLogInfo("imap proto registration failure");
exit(EXIT_FAILURE);
}
} else {
SCLogInfo("Protocol detection and parser disabled for %s protocol.",
"imap");
}
ValidateParsers();
return;
}
:void SigTableSetup(void) 函数中执行,规则中各种关键字的注册,如sid, content, http.host 等。
void SigTableSetup(void)
{
memset(sigmatch_table, 0, sizeof(sigmatch_table));
DetectSidRegister();
...
DetectContentRegister();
DetectUricontentRegister();
/* NOTE: the order of these currently affects inspect
* engine registration order and ultimately the order
* of inspect engines in the rule. Which in turn affects
* state keeping */
DetectHttpUriRegister();
...
/* close keyword registration */
DetectBufferTypeCloseRegistration();
}
/* Table with all SigMatch registrations */
SigTableElmt sigmatch_table[DETECT_TBLSIZE];
初始化queue handler(队列处理函数),这个是衔接线程模块和数据包队列之间的桥梁,目前共有3 类handler:simple, packetpool, flow。每类handler内部都有一个InHandler和OutHandler,一个用于从上一级队列中获取数据包,另一个用于处理完毕后将数据包送入下一级队列。
enum {
TMQH_NOT_SET,
TMQH_SIMPLE,
TMQH_PACKETPOOL,
TMQH_FLOW,
TMQH_SIZE,
};
Tmqh tmqh_table[TMQH_SIZE];
void TmqhSetup (void)
{
memset(&tmqh_table, 0, sizeof(tmqh_table));
TmqhSimpleRegister();
TmqhPacketpoolRegister();
TmqhFlowRegister();
}
tag作用:根据命中规则的tag信息分流或者是host记录前后的包数据,即标记规则命中之后的指定流量信息,用于指导日志对这些流量进行输出,方便分析(tag的作用和具体用法详情suricata源码之tag_村中少年的专栏-CSDN博客_suricata 源码分析)。
alter标记结构初始化:
/** tag signature we use for tag alerts */
static Signature g_tag_signature;
/** tag packet alert structure for tag alerts */
static PacketAlert g_tag_pa;
void PacketAlertTagInit(void)
{
memset(&g_tag_signature, 0x00, sizeof(g_tag_signature));
g_tag_signature.id = TAG_SIG_ID;
g_tag_signature.gid = TAG_SIG_GEN;
g_tag_signature.num = TAG_SIG_ID;
g_tag_signature.rev = 1;
g_tag_signature.prio = 2;
memset(&g_tag_pa, 0x00, sizeof(g_tag_pa));
g_tag_pa.action = ACTION_ALERT;
g_tag_pa.s = &g_tag_signature;
}
StorageInit();TagInitCtx();ThresholdInit();HostBitInitCtx();IPPairBitInitCtx();这几个函数都在干这种事。
typedef enum StorageEnum_ {
STORAGE_HOST,
STORAGE_FLOW,
STORAGE_IPPAIR,
STORAGE_DEVICE,
STORAGE_MAX,
} StorageEnum;
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;
模块保存到TmModule tmm_modules[TMM_SIZE];实际线程中数据在一连串的模块中依次执行 slot。
typedef struct TmModule_ {
const char *name;
/** thread handling */
TmEcode (*ThreadInit)(ThreadVars *, const void *, void **);
void (*ThreadExitPrintStats)(ThreadVars *, void *);
TmEcode (*ThreadDeinit)(ThreadVars *, void *);
/** the packet processing function */
TmEcode (*Func)(ThreadVars *, Packet *, void *);
TmEcode (*PktAcqLoop)(ThreadVars *, void *, void *);
/** terminates the capture loop in PktAcqLoop */
TmEcode (*PktAcqBreakLoop)(ThreadVars *, void *);
TmEcode (*Management)(ThreadVars *, void *);
/** global Init/DeInit */
TmEcode (*Init)(void);
TmEcode (*DeInit)(void);
#ifdef UNITTESTS
void (*RegisterTests)(void);
#endif
uint8_t cap_flags; /**< Flags to indicate the capability requierment of
the given TmModule */
/* Other flags used by the module */
uint8_t flags;
} TmModule;
TmModule tmm_modules[TMM_SIZE];
void RegisterAllModules(void)
{
// zero all module storage
memset(tmm_modules, 0, TMM_SIZE * sizeof(TmModule));
/* commanders */
TmModuleUnixManagerRegister();
/* managers */
TmModuleFlowManagerRegister();
TmModuleFlowRecyclerRegister();
TmModuleBypassedFlowManagerRegister();
...
/* af-packet */
TmModuleReceiveAFPRegister();
TmModuleDecodeAFPRegister();
...
TmModuleDebugList();
/* nflog */
TmModuleReceiveNFLOGRegister();
TmModuleDecodeNFLOGRegister();
/* windivert */
TmModuleReceiveWinDivertRegister();
TmModuleVerdictWinDivertRegister();
TmModuleDecodeWinDivertRegister();
}
获取一个检测引擎实例: 初始化检测引擎,设置对应的单模和多模匹配, 根据suricara.yaml 中{detect:}节点设置相应配置, 初始化改引擎实例中保存SigGroupHeads 的哈希表, 初始化改引擎实例中保存MpmStore 的哈希表,初始化改引擎实例中用来剔除重复符号的哈希表,初始化用于存储解析器结果的字符串表示的哈希表等各种哈希表,初始化 IP Reputation,加载classification.config 配置文件中的classification 规则,加载reference.config 配置文件中的reference 规则。加载规则文件的签名规则
static int LoadSignatures(DetectEngineCtx *de_ctx, SCInstance *suri)
{
if (SigLoadSignatures(de_ctx, suri->sig_file, suri->sig_file_exclusive) < 0) {
SCLogError(SC_ERR_NO_RULES_LOADED, "Loading signatures failed.");
if (de_ctx->failure_fatal)
return TM_ECODE_FAILED;
}
return TM_ECODE_OK;
}
实例化的检测引擎 放入全局的 g_master_de_ctx 中
typedef struct DetectEngineMasterCtx_ {
SCMutex lock;
/** enable multi tenant mode */
int multi_tenant_enabled;
/** version, incremented after each 'apply to threads' */
uint32_t version;
/** list of active detection engines. This list is used to generate the
* threads det_ctx's */
DetectEngineCtx *list;
/** free list, containing detection engines that will be removed but may
* still be referenced by det_ctx's. Freed as soon as all references are
* gone. */
DetectEngineCtx *free_list;
enum DetectEngineTenantSelectors tenant_selector;
/** list of tenant mappings. Updated under lock. Used to generate lookup
* structures. */
DetectEngineTenantMapping *tenant_mapping_list;
/** list of keywords that need thread local ctxs,
* only updated by keyword registration at start up. Not
* covered by the lock. */
DetectEngineThreadKeywordCtxItem *keyword_list;
int keyword_id;
} DetectEngineMasterCtx;
static DetectEngineMasterCtx g_master_de_ctx = { SCMUTEX_INITIALIZER,
0, 99, NULL, NULL, TENANT_SELECTOR_UNKNOWN, NULL, NULL, 0};
根据{ 运行模式 + 工作模式 } 获取对应的工作模式RunMode *mode = RunModeGetCustomMode(runmode, custom_mode); 同时mode->RunModeFunc();
执行之前注册数回调函数,例如,这里(RUNMODE_AFP_DEV+ "workers")这种模式将执行RunModeIdsAFPWorkers() 函数,在RunModeSetLiveCaptureWorkersForDevice函数中,根据配置创建多个worker threads。
static int RunModeSetLiveCaptureWorkersForDevice(ConfigIfaceThreadsCountFunc ModThreadsCount,
const char *recv_mod_name,
const char *decode_mod_name, const char *thread_name,
const char *live_dev, void *aconf,
unsigned char single_mode)
{
...
/* create the threads */
for (int thread = 0; thread < threads_count; thread++) {
...
TmModule *tm_module = NULL;
ThreadVars *tv = TmThreadCreatePacketHandler(tname,
"packetpool", "packetpool",
"packetpool", "packetpool",
"pktacqloop");
if (tv == NULL) {
FatalError(SC_ERR_THREAD_CREATE, "TmThreadsCreate failed");
}
tv->printable_name = printable_threadname;
tm_module = TmModuleGetByName(recv_mod_name);
if (tm_module == NULL) {
FatalError(SC_ERR_INVALID_VALUE, "TmModuleGetByName failed for %s", recv_mod_name);
}
TmSlotSetFuncAppend(tv, tm_module, aconf);
tm_module = TmModuleGetByName(decode_mod_name);
if (tm_module == NULL) {
FatalError(SC_ERR_INVALID_VALUE, "TmModuleGetByName %s failed", decode_mod_name);
}
TmSlotSetFuncAppend(tv, tm_module, NULL);
tm_module = TmModuleGetByName("FlowWorker");
if (tm_module == NULL) {
FatalError(SC_ERR_RUNMODE, "TmModuleGetByName for FlowWorker failed");
}
...
if (TmThreadSpawn(tv) != TM_ECODE_OK) {
FatalError(SC_ERR_THREAD_SPAWN, "TmThreadSpawn failed");
}
}
return 0;
}
首先创建在每个工作线程结构 ThreadVars *tv(创建是将线程tv->flags 设置为THV_PAUSE,目的是在后续的子线程创建完成后,在所有的初始完成才运行线程逻辑),在对应的tv 中添加一系列TmSlot。
TmSlot的添加:根据 recv->decode->FlowWorker 相应的模块找到对饮的TmModule,在调用void TmSlotSetFuncAppend(ThreadVars *tv, TmModule *tm, const void *data)函数添加:
void TmSlotSetFuncAppend(ThreadVars *tv, TmModule *tm, const void *data)
{
TmSlot *slot = SCMalloc(sizeof(TmSlot));
if (unlikely(slot == NULL))
return;
memset(slot, 0, sizeof(TmSlot));
SC_ATOMIC_INITPTR(slot->slot_data);
slot->SlotThreadInit = tm->ThreadInit;
slot->slot_initdata = data;
if (tm->Func) {
slot->SlotFunc = tm->Func;
} else if (tm->PktAcqLoop) {
slot->PktAcqLoop = tm->PktAcqLoop;
} else if (tm->Management) {
slot->Management = tm->Management;
}
slot->SlotThreadExitPrintStats = tm->ThreadExitPrintStats;
slot->SlotThreadDeinit = tm->ThreadDeinit;
/* we don't have to check for the return value "-1". We wouldn't have
* received a TM as arg, if it didn't exist */
slot->tm_id = TmModuleGetIDForTM(tm);
tv->tmm_flags |= tm->flags;
tv->cap_flags |= tm->cap_flags;
if (tv->tm_slots == NULL) {
tv->tm_slots = slot;
} else {
TmSlot *a = (TmSlot *)tv->tm_slots, *b = NULL;
/* get the last slot */
for ( ; a != NULL; a = a->slot_next) {
b = a;
}
/* append the new slot */
if (b != NULL) {
b->slot_next = slot;
}
}
return;
}
新建一个 TmSlot *slot,将找到的TmModule 中的函数指针赋值给slot对应的函数指针,并把slot节点加入到tv->tm_slots队列中,这一个 slot 与 相应模块的 TmModule *tm是对应的。
在TmEcode TmThreadSpawn(ThreadVars *tv)函数中创建子线程线程(线程函数为tv->tm_func()绑定的回调函数,函数内部绑定cpu,初始化并等待tv->flags 取消THV_PAUSE), 同时将此线程结构tv 放入ThreadVars *tv_root[TVT_MAX] 中管理,worker threads的类型为TVT_PPT。
TmEcode TmThreadSpawn(ThreadVars *tv)
{
pthread_attr_t attr;
if (tv->tm_func == NULL) {
printf("ERROR: no thread function set\n");
return TM_ECODE_FAILED;
}
/* Initialize and set thread detached attribute */
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
int rc = pthread_create(&tv->t, &attr, tv->tm_func, (void *)tv);
if (rc) {
printf("ERROR; return code from pthread_create() is %" PRId32 "\n", rc);
return TM_ECODE_FAILED;
}
TmThreadWaitForFlag(tv, THV_INIT_DONE | THV_RUNNING_DONE);
TmThreadAppend(tv, tv->type);
return TM_ECODE_OK;
}
enum {
TVT_PPT,
TVT_MGMT,
TVT_CMD,
TVT_MAX,
};
/* root of the threadvars list */
ThreadVars *tv_root[TVT_MAX] = { NULL };
FlowManager线程,FlowRecycler线程, UnixManager线程,wakeup线程, management线程等,创建方法同worker threas的创建。子线程循环函数为tv->tm_func(),在此函数中完成线程的初始化,然后等待线程的 tv->flags 设置为非 THV_PAUSE状态后再执行线程逻辑。
if (runmode != RUNMODE_UNIX_SOCKET) {
/* spawn management threads */
FlowManagerThreadSpawn();
FlowRecyclerThreadSpawn();
if (RunModeNeedsBypassManager()) {
BypassedFlowManagerThreadSpawn();
}
StatsSpawnThreads();
}
各子线程函数 tv->tm_func() 对应的回调函数:slot 中的回调函数取自之前注册的 TmModule,各线程的功能见下表:
序号 | 线程类型 | name | 线程数量 | tv->type |
tv->tm_func()对应的函数 | tv->tm_slots 对应的slot表(TmModule) | 备注 | |
1 | worker threads | W | suricata.yaml threading配置 |
packetpool |
TVT_PPT | TmThreadsSlotPktAcqLoop | Receive ->Decode->FlowWorker |
|
2 | FlowManagerThread | FM | suricata.yaml flow.managers |
management |
TVT_MGMT |
TmThreadsManagement | FlowManager | 默认为1个 |
3 | FlowRecyclerThread | FR | suricata.yaml flow.recyclers |
management |
TVT_MGMT |
TmThreadsManagement | FlowRecycler | 默认为1个 |
4 | BypassedFlowManagerThread | FB | 1 | management |
TVT_MGMT |
TmThreadsManagement | BypassedFlowManager | |
5 | StatsWakeupThread |
CW | 1 | custom |
TVT_MGMT |
StatsWakeupThread |
无 | |
6 | StatsMgmtThread |
CS | 1 | custom |
TVT_MGMT |
StatsMgmtThread |
无 | |
7 | UnixManagerThread |
US | 2 or 0 |
command | TVT_CMD |
TmThreadsManagement |
UnixManager |
RUNMODE_UNIX_SOCKET mode是才创建,否则没有 |
8 | DetectLoaderThread |
DL | suricata.yaml multi-detect.loaders or 0 |
command | TVT_CMD |
TmThreadsManagement |
DetectLoader |
MT(多租户)被启用才创建,默认的配置没有此项。 |
所有子线程创建完成后,等待所有子线程初始化完成,设置max_pending_return_packets值 ,取消所有线程的暂停标志,这样所有线程都能愉快的玩耍了。
主循环中处理SIGTERM(kill 不加参数)、SIGINT(ctrl + c)信号,当有这两个信号中的其中之一时,退出主循环,此时程序优雅的退出;
检测 上面的各个子线程的状态,若有线程为 THV_FAILED状态(线程遇到错误并失败),这是退出程序,exit(0),不优雅;
处理SIGHUP 信号
处理SIGUSR2 信号,以用来动态加载规则,发送信号方式 { kill -USR2 $(pidof suricata) };
主循环退出后,关闭所有子线程,释放所有的资源,程序优雅的退出,至此,主线程完结!
凡是过往,即为序章