suricata学习--结构及代码解读

线程、槽和模块之间的关系

suricata中tv、slot和tm的关系必须要搞清楚,汇总如下:

tv:ThreadVars类型,线程。
slot:TmSlot类型,槽。
tm:TmModule类型,模块。

下面必须要结合三者的定义,阅读代码的时候也关注下三者关系。
----------------------------------------
线程的定义:
typedef struct ThreadVars_ {
    pthread_t t;                      // 线程id
    char *name;                       // 线程name
    char *thread_group_name;          // 线程group name
    SC_ATOMIC_DECLARE(unsigned short, flags); // 原子声明,不知道作用,暂且不管
    uint8_t aof;                      // 线程遇到故障时怎么做
    uint8_t type;                     // 线程类型,例如:TVT_PPT, TVT_MGMT
    uint8_t restarted;                // 线程重新启动失败的次数
    Tmq *inq;
    Tmq *outq;
    void *outctx;
    char *outqh_name
    struct Packet_ * (*tmqh_in)(struct ThreadVars_ *);
    void (*InShutdownHandler)(struct ThreadVars_ *);
    void (*tmqh_out)(struct ThreadVars_ *, struct Packet_ *);
    void *(*tm_func)(void *);
    struct TmSlot_ *tm_slots;
    uint8_t thread_setup_flags;
    uint16_t cpu_affinity;
    int thread_priority;                // 线程优先级
    SCPerfContext sc_perf_pctx;
    SCPerfCounterArray *sc_perf_pca;
    SCMutex *m;
    SCCondT *cond;

    uint8_t cap_flags;
    struct ThreadVars_ *next;
    struct ThreadVars_ *prev;
} ThreadVars;
-----------------------------------------
槽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;
-------------------------------------------------
模块定义:
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;
===============================================
将三者的定义放在一起目的是方便查看,其中部分变量目前还不清楚具体含义,日后补充。
三者之间关系如下图所示:


三者之间的关系,每一个线程都包含一个slot的链表,每个slot结点都悬挂着不同的模块,程序执行的时候会遍历slot链表,按照加入链表的熟悉执行模块。

再从main()函数看起 --- 模式、模块、线程和槽

 >
经过上面几篇Blog的学习步骤,到今天再回过头去看了下main函数的执行过程,把重点几个步骤重新整理了下,方便更深入的理解架构。

1. 注册各种运行模式
   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
   RunModeUnixSocketRegister();   // UnixSocket
   其中每一种运行模式调用RunModeRegisterNewRunMode注册各自的Custom mode(暂且翻译为“自定义模式”)
   
   RunModeRegisterNewRunMode设置各种运行模式的执行函数
   例如:RunModeRegisterNewRunMode(RUNMODE_PCAP_DEV, "single",
                              "Single threaded pcap live mode",
                              RunModeIdsPcapSingle);
   将执行函数添加到runmodes全局数组中。
   全局Runmodes类型数组runmodes保存运行模式,存储结构如下图:
   

2. 注册模块
   注册suricata所支持的所有线程模块
   
   TmModuleReceiveNFQRegister();
   TmModuleVerdictNFQRegister();
   TmModuleDecodeNFQRegister();
   
   TmModuleReceiveIPFWRegister();
   TmModuleVerdictIPFWRegister();
   TmModuleDecodeIPFWRegister();
   
   TmModuleReceivePcapRegister();
   TmModuleDecodePcapRegister();
   
   TmModuleReceivePcapFileRegister();
   TmModuleDecodePcapFileRegister();
   ……
   ……
   函数内部实现:
   void TmModuleReceivePcapRegister (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 = ReceivePcapThreadExitStats;
    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;
  
  存储结构如下图所示:

3. 模块初始化
   TmModuleRunInit()函数
   调用tmm_modules[TMM_SIZE]数组中模块各个模块初始化函数。
    for (i = 0; i < TMM_SIZE; i++)
    {
        t = &tmm_modules[i];
        t->Init(); // 注意这里执行的是模块全局初始化函数
    }

4. 运行模式调度
   RunModeDispatch()函数
  • 从配置中读取运行模式。
  • 获得该运行模式中默认的Custom mode(如:single、auto等)。
  • 执行Custom mode中设置的执行函数,如上图中所示的“执行函数”。

5. 运行模式执行函数
   例如:RunModeFilePcapSingle()
  • 通用模块初始化RunModeInitialize
  • 创建tv实例TmThreadCreatePacketHandler
  • 从tmm_modules中获得模块TmModuleGetByName
  • 插入槽slot
  • TmThreadSpawn真正创建线程函数


整理下执行顺序:
  • 运行模式注册,设置执行函数
  • 所有模块注册,设置模块相关函数
  • 所有模块初始化
  • 从配置获取运行模式类型,执行函数
  • 创建线程
  • 根据模块名称从全局数组tmm_modules中得到模块指针
  • 插入线程槽slot



  • 4
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值