网络入侵检测系统之 Suricata(四)-- 初始化模块代码详解
Initial Module 初始化流程
初始化 Suricata instance 用来保存程序当前的一些状态、标志等上下文环境,通常是用来作为参数传递给各个模块的子函数 memset(suri, 0x00, sizeof(suri));// pointer to argv[0]suri->progname = progname;//运行模式 suri->run_mode = RUNMODE_UNKNOWN;memset(suri->pcap_dev, 0, sizeof(suri->pcap_dev));//签名文件 suri->sig_file = NULL;suri->sig_file_exclusive = FALSE;suri->pid_filename = NULL;//正则参数 suri->regex_arg = NULL;//延迟检测开关 suri->delayed_detect = 0;//守护进程 suri->daemon = 0;//离线模式 suri->offline = 0;//详细模式 suri->verbose = 0;/ use -1 as unknown */suri->checksum_validation = -1;//是否检测 g_detect_disabled = suri->disabled_detect = 1;2. 初始化原子变量 engine_stage –> 记录程序当前的运行阶段:SURICATA_INIT、SURICATA_RUNTIME、SURICATA_FINALIZE
suricata_context 初始化//打印相关 console or file 以及 levelsuricata_context.SCLogMessage = SCLogMessage;// 文件 io 相关的函数 suricata_context.FileOpenFileWithId = FileOpenFileWithId;suricata_context.FileCloseFileById = FileCloseFileById;suricata_context.FileAppendDataById = FileAppendDataById;suricata_context.FileAppendGAPById = FileAppendGAPById;suricata_context.FileContainerRecycle = FileContainerRecycle;suricata_context.FilePrune = FilePrune;suricata_context.FileSetTx = FileContainerSetTx;3. 以环境变量或缺省值对日志模块初始化
op_iface 日志输出接口:console or file or syslog 文件指针文件锁日志格式和 levelop_filter_regex 只输出匹配到这个正则的文件名 4. 设置当前主线程名字为“Suricata-Main”。在 gdb 调试时 info threads 可以看到各个线程名
初始化 ParserSize 模块,正则预编译,用于解析类似“10Mb”这种大小参数
#define PARSE_REGEX "^\s*(\d+(?:.\d+)?)\s*([a-zA-Z]{2})?\s*$"6. 注册各种运行模式。Suricata 对“运行模式”这个概念也进行了封装。运行模式存储在 runmodes 数组中,定义为 RunModes runmodes[RUNMODE_USER_MAX]
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_NETMAP,RUNMODE_UNITTEST,RUNMODE_NAPATECH,RUNMODE_UNIX_SOCKET,RUNMODE_WINDIVERT,RUNMODE_PLUGIN,}typedef struct RunModes_ {int cnt;RunMode *runmodes;} RunModes;7. 注册各种运行模式 以 IDS Pcap 运行模式为例,它由 3 个子运行模式组成,底层由 realloc 分配空间,IDS Pcap Mode =RunModeIdsPcapSingle(single thread pcap processing)+ RunModeIdsPcapAutoFp(默认子运行模式)+RunModeIdsPcapWorkers(Workers version of the PCAP LIVE processing)等后续初始化阶段确定了具体的运行模式后,就会调用这 3 个注册的对应的初始化函数,对该模式下的运行环境进行进一步配置
/*当前子模式:RunMode *mode = &runmodes[runmode].runmodes[runmodes[runmode].cnt];*/RunModeIdsPcapRegister();RunModeFilePcapRegister();RunModeIdsPfringRegister();RunModeIpsNFQRegister();RunModeIpsIPFWRegister();RunModeErfFileRegister();RunModeErfDagRegister();RunModeNapatechRegister();RunModeIdsAFPRegister();RunModeIdsNetmapRegister();RunModeIdsNflogRegister();RunModeUnixSocketRegister();RunModeIpsWinDivertRegister();8. 初始化配置模块,为后续解析 Suricata.yaml 建立配置节点树的根节点 root,底层调用为 alloc
typedef struct ConfNode_ {//filename: fast.log name:valchar *name;char *val;/**< Flag that sets this nodes value as final. */int final;//父节点 struct ConfNode_ *parent;//头节点,next 节点 TAILQ_HEAD(, ConfNode_) head;TAILQ_ENTRY(ConfNode_) next;} ConfNode;9. 命令行参数解析
//监控设备注册 typedef struct LiveDeviceName_ {char *dev; /**< the device (e.g. "eth0") */TAILQ_ENTRY(LiveDeviceName_) next;} LiveDeviceName;pd = SCCalloc(1, sizeof(LiveDeviceName));TAILQ_INSERT_TAIL(&pre_live_devices, pd, next);g_engine_mode 默认为 IDS,IPS 是可以 drop 包(--simulate-ips)EngineModeSetIPS();短命令行参数解析,并添加到 Conf Tree 中:ConfGetNodeOrCreate + setVal
“-v”选项可多次使用,每个 v 都能将当前日志等级提升一级 suri->verbose++;设置全局的 run_mode 变量,校验 daemon 模式是否兼容 run_mode(Pcap 文件模式及单元测试模式都不能在 daemon 开启下进行)10. GlobalsInitPreConfig 获取当前时间所用的 spin lock,以及设置时区(调用 tzset()即可) 多模快速匹配链表维护,按优先级排序 sm_fp_support_smlist_list,底层 SCMalloc 匹配阈值关键字相关,正则预编译
LoadYamlConfig 调用 LoadYamlConfig 读取 Yaml 格式配置文件。Yaml 格式解析是通过 libyaml 库来完成的,解析的结果存储在配置节点树(见 conf.c)中
SCLogLoadConfig 根据之前转化好的配置树,初始化日志模块一些全局配置
SCLogDebug("sc_log_global_log_level: %d", sc_log_global_log_level);SCLogDebug("sc_lc->log_format: %s", sc_log_config->log_format);SCLogDebug("SCLogSetOPFilter: filter: %s", sc_log_config->op_filter);typedef struct SCLogConfig_{
char *startup_message;
SCLogLevel log_level;
char *log_format;
char op_filter;/ compiled pcre filter expression */
pcre *op_filter_regex;
pcre_extra op_filter_regex_study;/ op ifaces used */
SCLogOPIfaceCtx op_ifaces;/ no of op ifaces */
uint8_t op_ifaces_cnt;} SCLogConfig;13. PostConfLoadedSetup 配置生效后,模块 setup
MpmTableSetup:设置多模式匹配表,该表中每一项就是一个实现了某种多模式匹配算法(如 WuManber、AC)的匹配器 ac,ac-ks,ac-bs,hsSpmTableSetup:设置单模匹配表, bm,hstypedef 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_ *);
} MpmTableElmt;
extern MpmTableElmt mpm_table[MPM_TABLE_SIZE];
typedef struct MpmCtx_ {void *ctx;uint8_t mpm_type;
} MpmCtx;
typedef struct SCACCtx_ {/* pattern arrays. We need this only during the goto table creation phase */MpmPattern **parray;
} SCACCtx;存储相关初始化 如 tag 关键字,由于这类信息依赖于规则,同时出现的频率不高,申请内存 STORAGE_HOST,STORAGE_FLOW,STORAGE_IPPAIR,STORAGE_DEVICEmemset(&storage_max_id, 0x00, sizeof(storage_max_id));storage_list = NULL;storage_map = NULL;storage_registraton_closed = 0;
注册 RegisterFlowBypassInfo 旁路流量卸载 STORAGE_FLOW 协议解析器注册 AppLayerParserProtoCtx ctxs[FLOW_PROTO_MAX][ALPROTO_MAX];enum {FLOW_PROTO_TCP = 0,FLOW_PROTO_UDP,FLOW_PROTO_ICMP,FLOW_PROTO_DEFAULT,
};enum AppProtoEnum {ALPROTO_UNKNOWN = 0,ALPROTO_HTTP,ALPROTO_FTP,ALPROTO_SMTP,ALPROTO_TLS, /* SSLv2, SSLv3 & TLSv1 */ALPROTO_SSH,ALPROTO_IMAP,ALPROTO_JABBER,ALPROTO_SMB,ALPROTO_DCERPC,ALPROTO_IRC,
#ifdef UNITTESTSALPROTO_TEST,#endif /* UNITESTS // keep last */ALPROTO_MAX,};
/**
\brief App layer protocol parser context./typedef struct AppLayerParserProtoCtx_{/ 0 - to_server, 1 - to_client. */AppLayerParserFPtr Parser[2];bool logger;uint32_t logger_bits; /**< registered loggers for this proto */
void *(*StateAlloc)(void *, AppProto);void (*StateFree)(void *);void (*StateTransactionFree)(void *, uint64_t);void *(*LocalStorageAlloc)(void);void (*LocalStorageFree)(void *);
void (*Truncate)(void *, uint8_t);FileContainer *(*StateGetFiles)(void *, uint8_t);AppLayerDecoderEvents *(*StateGetEvents)(void *);
int (*StateGetProgress)(void *alstate, uint8_t direction);uint64_t (*StateGetTxCnt)(void *alstate);void *(*StateGetTx)(void *alstate, uint64_t tx_id);AppLayerGetTxIteratorFunc StateGetTxIterator;int (*StateGetProgressCompletionStatus)(uint8_t direction);int (*StateGetEventInfoById)(int event_id, const char **event_name,AppLayerEventType *event_type);int (*StateGetEventInfo)(const char *event_name,int *event_id, AppLayerEventType *event_type);
DetectEngineState *(*GetTxDetectState)(void *tx);int (*SetTxDetectState)(void *tx, DetectEngineState *);
AppLayerTxData *(*GetTxData)(void *tx);bool (*ApplyTxConfig)(void *state, void *tx, int mode, AppLayerTxConfig);
void (*SetStreamDepthFlag)(void *tx, uint8_t flags);
/* each app-layer has its own value */uint32_t stream_depth;
/* Indicates the direction the parser is ready to see the data
the first time for a flow. Values accepted -
STREAM_TOSERVER, STREAM_TOCLIENT */uint8_t first_data_dir;
/* Option flags such as supporting gaps or not. /uint32_t option_flags;/ coccinelle: AppLayerParserProtoCtx:option_flags:APP_LAYER_PARSER_OPT_ */
uint32_t internal_flags;/* coccinelle: AppLayerParserProtoCtx:internal_flags:APP_LAYER_PARSER_INT_ */
#ifdef UNITTESTSvoid (*RegisterUnittests)(void);#endif} AppLayerParserProtoCtx;
http 解析器的 HTTP state memorytypedef struct HtpState_ {/* Connection parser structure for each connection */htp_connp_t connp;/ Connection structure for each connection */htp_conn_t *conn;Flow *f; /< Needed to retrieve the original flow when using HTPLib callbacks */uint64_t transaction_cnt;uint64_t store_tx_id;FileContainer *files_ts;FileContainer *files_tc;const struct HTPCfgRec_ *cfg;uint16_t flags;uint16_t events;uint16_t htp_messages_offset; /< offset into conn->messages list */uint32_t file_track_id; /**< used to assign file track ids to files */uint64_t last_request_data_stamp;uint64_t last_response_data_stamp;} HtpState;14. SCHInfoLoadFromConfig:从配置文件中载入 host os policy(主机 OS 策略)信息 radix tree 保存所有主机策略信息
SigTableSetup:初始化检测引擎,主要是注册检测引擎所支持的规则格式(跟 Snort 规则基本一致)中的关键字,比如 sid、priority、msg、within、distance 等等
typedef struct SigTableElmt_ {/** Packet match function pointer */int (*Match)(DetectEngineThreadCtx *, Packet *, const Signature *, const SigMatchCtx *);
#ifdef UNITTESTSvoid (RegisterTests)(void);#endifuint16_t flags;/ coccinelle: SigTableElmt:flags:SIGMATCH_ */
} SigTableElmt;
规则结构体
/** \brief Signature container /typedef struct Signature_ {uint32_t flags;/ coccinelle: Signature:flags:SIG_FLAG_ */
#ifdef PROFILINGuint16_t profiling_id;#endif
} Signature;17. TmqhSetup:Tm-queue 是各个模块(线程)之间传递数据的缓冲区初始化 queue handler(队列处理函数),这个是衔接线程模块和数据包队列之间的桥梁,目前共有 5 类 handler:simple, nfq, packetpool, flow, ringbuffer。每类 handler 内部都有一个 InHandler 和 OutHandler,一个用于从上一级队列中获取数据包,另一个用于处理完毕后将数据包送入下一级队列。
Packepool 通过 read 和 write 两个位置标记对 packetpool(ringbuffer)这个循环队列进行进出操作。
Simple 按照 FIFO(先进先出)原则对缓冲区内容进行进出操作。
Flow 出队的时候是按照 FIFO 进行,入队的时候对数据包的头部信息进行 hash,然后将具有相同 hash 值的数据包放到一个缓冲区。Tmqh tmqh_table[TMQH_SIZE];
RegisterAllModules:注册所有模块里面注册了 Suricata 所支持的所有线程模块(Thread Module)。以 pcap 相关模块为例,TmModuleReceivePcapRegister 函数注册了 Pcap 捕获模块,而 TmModuleDecodePcapRegister 函数注册了 Pcap 数据包解码模块。所谓注册,就是在 tmm_modules 模块数组中对应的那项中填充 TmModule 结构的所有字段,这些字段包括:模块名字、线程初始化函数、包处理或包获取函数、线程退出清理函数、一些标志位等等。
typedef struct TmModule_ {const char *name;
#ifdef UNITTESTSvoid (*RegisterTests)(void);#endifuint8_t cap_flags; /**< Flags to indicate the capability requierment ofthe given TmModule // Other flags used by the module */uint8_t flags;} TmModule;
extern TmModule tmm_modules[TMM_SIZE]
typedef enum {TMM_FLOWWORKER,TMM_DECODENFQ,TMM_VERDICTNFQ,TMM_RECEIVENFQ,TMM_RECEIVEPCAP,TMM_RECEIVEPCAPFILE,TMM_DECODEPCAP,TMM_DECODEPCAPFILE,TMM_RECEIVEPFRING,TMM_DECODEPFRING,TMM_RECEIVEPLUGIN,TMM_DECODEPLUGIN,TMM_RESPONDREJECT,TMM_DECODEIPFW,TMM_VERDICTIPFW,TMM_RECEIVEIPFW,TMM_RECEIVEERFFILE,TMM_DECODEERFFILE,TMM_RECEIVEERFDAG,TMM_DECODEERFDAG,TMM_RECEIVEAFP,TMM_DECODEAFP,TMM_RECEIVENETMAP,TMM_DECODENETMAP,TMM_ALERTPCAPINFO,TMM_RECEIVENAPATECH,TMM_DECODENAPATECH,TMM_STATSLOGGER,TMM_RECEIVENFLOG,TMM_DECODENFLOG,TMM_RECEIVEWINDIVERT,TMM_VERDICTWINDIVERT,TMM_DECODEWINDIVERT,
} TmmId;19. 规则加载 LoadSignatures-》得到 signutrue listDetectEngineCtx_->sig_list + sig_cnt->排序 ByAction,byFlowbits,,ByFlowvar,ByPktvar,ByHostbits,ByIPPairbit,ByPriority->runtime match structure (siggroup),siggroup 的过程目前看是把之前的所有规则 list 通过 ip,端口号和流分组到一个链表数组(sgh,并维护了一个 sigMask(规则有哪些匹配项)
20. 至此上电完毕,后面 suricata 则会根据 run mode 运行线程,循环抓包
初始化资源调度
版权声明: 本文为 InfoQ 作者【于顾而言】的原创文章。
原文链接:【http://xie.infoq.cn/article/fed4469b07155e860d6bc11d4】。文章转载请联系作者。
评论