suricata官方文档https://suricata.readthedocs.io/en/latest/performance/runmodes.html#different-runmodes
suricata的总体架构https://www.cnblogs.com/zlslch/p/7382176.html
suricata是基于模块(TMmoudle)的,所谓模块是suricata中的数据结构:
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;
每个模块代表一个特定的功能。
例如获取报文、解码报文、检测报文、记录日志这四个模块。
前一个模块的输出是后一个模块的输入,它们各自拥有一个线程。多线程是suricata具有较高性能的保证。
模块和模块之间的联系通过suricata中的插槽(slot)这一数据结构相连。
typedef struct TmSlot_ {
ThreadVars *tv; // 拥有该slot的线程
SC_ATOMIC_DECLARE(TmSlotFunc, SlotFunc);// 函数指针
TmEcode (*PktAcqLoop)(ThreadVars *, void *, void *); // 模块数据包获取函数
TmEcode (*SlotThreadInit)(ThreadVars *, void *, void **); // 模块初始化执行函数
void (*SlotThreadExitPrintStats)(ThreadVars *, void *); // 模块退出打印函数
TmEcode (*SlotThreadDeinit)(ThreadVars *, void *); // 模块清理执行函数
void *slot_initdata; // 数据存储
SC_ATOMIC_DECLARE(void *, slot_data);
PacketQueue slot_pre_pq;
PacketQueue slot_post_pq;
int tm_id; // tm ID
int id; // slot ID
struct TmSlot_ *slot_next;
} TmSlot;
每个线程运行一个slot对象,slot负责管理其上的模块。包括该模块的初始化,以及对模块数据包的获取和模块的退出和清理。
数据从一个模块到另一个模块是通过slot结构体中的 struct TmSlot_* 指针指向下一个slot,并将数据包输入至下一个slot管理的TmModule上。
图中有3个插槽,每个插槽各有一个模块,3个插槽各自启动了一个线程。类似生产者和消费者原理,模块i是模块i-1的消费者,模块i-1是模块i的生产者。使用模块-插槽结构的好处是便于添加中间组件对数据包进行处理。同时模块化也便于利用多线程。
suricata的模块需要被注册,注册函数名为 TmModuleXXXRegister(void),模块被注册后便被存入全局数组 TmModule tmm_modules[TMM_SIZE]中。该数组使用了枚举-索引方式。用枚举的方式对数组每个位命名。
typedef enum{
TMM_DECODEDFQ,
TMM_XXXX
};
枚举数正好对应TMM数组的index。
在suricata中,线程,模块和队列排列在一起的方式称作运行方式。
suricata中的AF_PACKET和PF_RING方法可以捕获数据包。
手机扫一扫
移动阅读更方便
你可能感兴趣的文章