《线程、槽和模块之间的关系》
suricata中tv、slot和tm的关系必须要搞清楚,汇总如下:
tv:ThreadVars类型,线程。
slot:TmSlot类型,槽。
tm:TmModule类型,模块。
下面必须要结合三者的定义,阅读代码的时候也关注下三者关系。
----------------------------------------
线程的定义:
typedef struct ThreadVars_ {
} ThreadVars;
-----------------------------------------
槽slot的定义:
typedef struct TmSlot_ {
} TmSlot;
-------------------------------------------------
模块定义:
typedef struct TmModule_ {
} TmModule;
===============================================
将三者的定义放在一起目的是方便查看,其中部分变量目前还不清楚具体含义,日后补充。
三者之间关系如下图所示:
![](https://img-blog.csdn.net/20140717093204890?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvc2hlbndhbnNhbmd6/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
RunModeRegisterRunModes()函数
RunModeIdsPcapRegister(); // IDS+pcap
RunModeFilePcapRegister(); // File+pcap
RunModeIdsPfringRegister (); // IDS+pfring
RunModeIpsIPFWRegister(); // IPS+ipfw
RunModeIpsNFQRegister(); // IPS+nfq
RunModeErfFileRegister(); // erf+file
RunModeErfDagRegister(); // erf+dag
RunModeNapatechRegister(); // napatech
RunModeIdsAFPRegister(); // IDS+AFP
RunModeUnixSocketRegiste r(); // UnixSocket
其中每一种运行模式调用RunModeRegisterNewRunMod e注册各自的Custom mode(暂且翻译为“自定义模式”)
RunModeRegisterNewRunMod e设置各种运行模式的执行函数
例如:RunModeRegisterNewRunMod e(RUNMODE_PCAP_DEV, "single",
"Single threaded pcap live mode",
RunModeIdsPcapSingle);
将执行函数添加到runmodes全局数组中。
全局Runmodes类型数组runmodes保存运行模式,存储结构如下图:
![](https://img-blog.csdn.net/20140717093334107?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvc2hlbndhbnNhbmd6/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
注册suricata所支持的所有线程模块
TmModuleReceiveNFQRegist er();
TmModuleVerdictNFQRegist er();
TmModuleDecodeNFQRegiste r();
TmModuleReceiveIPFWRegis ter();
TmModuleVerdictIPFWRegis ter();
TmModuleDecodeIPFWRegist er();
TmModuleReceivePcapRegis ter();
TmModuleDecodePcapRegist er();
TmModuleReceivePcapFileR egister();
TmModuleDecodePcapFileRe gister();
……
……
函数内部实现:
void TmModuleReceivePcapRegis ter (void)
{
tmm_modules[TMM_RECEIVEPCAP].name = "ReceivePcap";
tmm_modules[TMM_RECEIVEPCAP].ThreadInit = ReceivePcapThreadInit;
tmm_modules[TMM_RECEIVEPCAP].Func = NULL;
tmm_modules[TMM_RECEIVEPCAP].PktAcqLoop = ReceivePcapLoop;
tmm_modules[TMM_RECEIVEPCAP].ThreadExitPrintStats = ReceivePcapThreadExitSta ts;
tmm_modules[TMM_RECEIVEPCAP].ThreadDeinit = NULL;
tmm_modules[TMM_RECEIVEPCAP].RegisterTests = NULL;
tmm_modules[TMM_RECEIVEPCAP].cap_flags = SC_CAP_NET_RAW;
tmm_modules[TMM_RECEIVEPCAP].flags = TM_FLAG_RECEIVE_TM;
}
保存在全局TmModule tmm_modules[TMM_SIZE]数组中。
typedef struct TmModule_
{
char *name;
TmEcode (*ThreadInit)(ThreadVars *, void *, void **); // 线程初始化函数
void (*ThreadExitPrintStats)(ThreadVars *, void *); // 线程退出打印函数
TmEcode (*ThreadDeinit)(ThreadVars *, void *); // 线程关闭函数
TmEcode (*Func)(ThreadVars *, Packet *, void *, PacketQueue *, PacketQueue *);
TmEcode (*PktAcqLoop)(ThreadVars *, void *, void *);
TmEcode (*Init)(void);// 全局初始化模块函数
TmEcode (*DeInit)(void);// 全局关闭模块函数
void (*RegisterTests)(void);
uint8_t cap_flags;
uint8_t flags;
} TmModule;
存储结构如下图所示:
![](https://img-blog.csdn.net/20140717093418567?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvc2hlbndhbnNhbmd6/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
TmModuleRunInit()函数
调用tmm_modules[TMM_SIZE]数组中模块各个模块初始化函数。
for (i = 0; i < TMM_SIZE; i++)
{
t = &tmm_modules[i];
t->Init(); // 注意这里执行的是模块全局初始化函数
}
RunModeDispatch()函数
例如:RunModeFilePcapSingle()
三者之间的关系,每一个线程都包含一个slot的链表,每个slot结点都悬挂着不同的模块,程序执行的时候会遍历slot链表,按照加入链表的熟悉执行模块。
再从main()函数看起 --- 模式、模块、线程和槽
>
经过上面几篇Blog的学习步骤,到今天再回过头去看了下main函数的执行过程,把重点几个步骤重新整理了下,方便更深入的理解架构。
1. 注册各种运行模式
2. 注册模块
3. 模块初始化
4. 运行模式调度
- 从配置中读取运行模式。
- 获得该运行模式中默认的Custom mode(如:single、auto等)。
- 执行Custom mode中设置的执行函数,如上图中所示的“执行函数”。
5. 运行模式执行函数
- 通用模块初始化RunModeInitialize
- 创建tv实例TmThreadCreatePacketHand
ler - 从tmm_modules中获得模块TmModuleGetByName
- 插入槽slot
- TmThreadSpawn真正创建线程函数
整理下执行顺序:
- 运行模式注册,设置执行函数
- 所有模块注册,设置模块相关函数
- 所有模块初始化
- 从配置获取运行模式类型,执行函数
- 创建线程
- 根据模块名称从全局数组tmm_modules中得到模块指针
- 插入线程槽slot