在介绍Suricata源代码之前,大致介绍一下Suricata的工作流程。在suricata中主要使用了回调函数将所有的模块连接起来的。最后是通过DetectEngineCtx *global_de_ctx这个结构体启动起来的。整个的启动过程我用鞭炮来进行比喻,回调函数就好像鞭炮的引线一样,将所有的小的鞭炮连接起来,连接起来之后如果要放鞭炮就的要使用火柴将引线点燃。所以我将global_de_ctx比喻为火柴。在suricata中也是这样,最后通过global_de_ctx将suricata运行起来的。就好比是global_de_ctx打通了suricata的任督二脉。
DetectEngineCtx结构体在detect.h中。
typedef structDetectEngineCtx_ {
uint8_t flags;
int failure_fatal;
Signature *sig_list;
uint32_t sig_cnt;
/* version of the srep data */
uint32_t srep_version;
Signature **sig_array;
uint32_t sig_array_size; /* size in bytes*/
uint32_t sig_array_len; /* size in array members */
uint32_t signum;
/* used by the signature ordering module */
struct SCSigOrderFunc_ *sc_sig_order_funcs;
struct SCSigSignatureWrapper_*sc_sig_sig_wrapper;
/*hash table used for holding the classification config info */
HashTable *class_conf_ht;
/* hash table used for holding thereference config info */
HashTable *reference_conf_ht;
/* main sigs */
DetectEngineLookupFlowflow_gh[FLOW_STATES];
uint32_t mpm_unique, mpm_reuse, mpm_none,
mpm_uri_unique, mpm_uri_reuse,mpm_uri_none;
uint32_t gh_unique, gh_reuse;
uint32_t mpm_max_patcnt, mpm_min_patcnt,mpm_tot_patcnt,
mpm_uri_max_patcnt, mpm_uri_min_patcnt,mpm_uri_tot_patcnt;
/* init phase vars */
HashListTable *sgh_hash_table;
…….
}DetectEngineCtx;
下面还有很长没有贴出来,这个结构体非常的大。想想它确实是应该这么大的。毕竟它就是suricata的心脏嘛!
Suricata是c语言开发的,而c语言的起始点是从main()函数开始的,为了好找到入口点,所以在写代码的时候就规定了从main函数开始。而suricata的入口main函数是在suricata.c文件中。
int main(intargc, char **argv)
{
int opt;
char pcap_dev[128];
char *sig_file = NULL;
int sig_file_exclusive = FALSE;
int conf_test = 0;
char *pid_filename = NULL;
#ifdef UNITTESTS
char *regex_arg = NULL;
#endif
…….
Suricata中的main函数接下来所做的事情是:
/* initializethe logging subsys */
SCLogInitLogModule(NULL);//初始化日志系统
if(SCSetThreadName("Suricata-Main") < 0) {//给主线程设置线程名称
SCLogWarning(SC_ERR_THREAD_INIT,"Unable to set thread name");
}
RunModeRegisterRunModes();//设置运行模式
/* By default use IDS mode, but if nfq oripfw
* are specified, IPS mode will overwritethis */
SET_ENGINE_MODE_IDS(engine_mode);//suricata默认情况下是运行IDS模式。而在RunModeRegisterRunModes()函数中,主要是在引擎中注册所有的运行模式。函数所在位置是在runmodes.c中。
/**
* \brief Register all runmodes in the engine.
*/
voidRunModeRegisterRunModes(void)
{
memset(runmodes, 0, sizeof(runmodes));
RunModeIdsPcapRegister();
RunModeFilePcapRegister();
RunModeIdsPfringRegister();
RunModeIpsNFQRegister();
RunModeIpsIPFWRegister();
RunModeErfFileRegister();
RunModeErfDagRegister();
RunModeNapatechRegister();
RunModeIdsAFPRegister();
RunModeUnixSocketRegister();
#ifdef UNITTESTS
UtRunModeRegister();
#endif
return;
}
其中我主要说RunModeIpsNFQRegister函数,因为这个模式下实现了IPS的功能。其中这个函数又是做了哪些事情呢?
voidRunModeIpsNFQRegister(void)
57 {
58 default_mode = "autofp";
59 RunModeRegisterNewRunMode(RUNMODE_NFQ, "auto",
60 "Multithreaded NFQ IPS mode",
61 RunModeIpsNFQAuto);
62
63 RunModeRegisterNewRunMode(RUNMODE_NFQ, "autofp",
64 "Multithreaded NFQ IPS mode with respect to flow",
65 RunModeIpsNFQAutoFp);
66
67 RunModeRegisterNewRunMode(RUNMODE_NFQ, "workers",
68 "Multiqueue NFQ IPS mode with one thread per queue",
69 RunModeIpsNFQWorker);
70 return;
71 }
里面注册了3个回调函数,这个3个回调函数主要针对的是auto模式、autofp模式和workers模式。
其中我要说的是workers模式下的情况,而在RunModeIpsNFQWorker(这个函数在runmode-nfq.c)这个函数中主要调用了函数RunModeSetIPSWorker。
ret =RunModeSetIPSWorker(de_ctx,
142 NFQGetThread,
143 "ReceiveNFQ",
144 "VerdictNFQ",
145 "DecodeNFQ");
在RunModeSetIPSWorker中就完成将所有的模块连接起来。包括了ReceiveNFQ模块(主要进行数据包的接受)、Decode模块(主要是对数据包的协议进行分析)、StreamTcp模块(主要是将对应的数据包组成stream的形式)、Detect模块(主要是使用DetectEngineCtx里面的特征对数据进行匹配)、Verdict模块以及RespondReject模块。
代码实现是:
int RunModeSetIPSWorker(DetectEngineCtx *de_ctx,
1150 ConfigIPSParserFunc ConfigParser,
1151 char*recv_mod_name,
1152 char*verdict_mod_name,
1153 char*decode_mod_name)
1154 {
1155 char tname[16];
1156 ThreadVars *tv =NULL;
1157 TmModule *tm_module =NULL;
1158 char *cur_queue =NULL;
1159
1160 int nqueue =LiveGetDeviceCount();
1161
1162 for (int i = 0; i< nqueue; i++) {
1163 /* create thethreads */
1164 cur_queue =LiveGetDeviceName(i);
1165 if (cur_queue ==NULL) {
1166 printf("ERROR: Invalid queue number\n");
1167 exit(EXIT_FAILURE);
1168 }
1169 memset(tname, 0,sizeof(tname));
1170 snprintf(tname,sizeof(tname), "Worker-Q%s", cur_queue);
1171
1172 char *thread_name= SCStrdup(tname);
1173 if(unlikely(thread_name == NULL)) {
1174 SCLogError(SC_ERR_RUNMODE, "Error allocating memory");
1175 exit(EXIT_FAILURE);
1176 }
1177 tv =TmThreadCreatePacketHandler(thread_name,
1178 "packetpool", "packetpool",
1179 "packetpool", "packetpool",
1180 "pktacqloop");
1181 if (tv == NULL) {
SCLogError(SC_ERR_THREAD_CREATE, "TmThreadsCreatefailed");
1183 exit(EXIT_FAILURE);
1184 }
1185
1186 tm_module =TmModuleGetByName(recv_mod_name);
1187 if (tm_module ==NULL) {
1188 SCLogError(SC_ERR_INVALID_VALUE, "TmModuleGetByName failed for%s", recv_mod_name);
1189 exit(EXIT_FAILURE);
1190 }
1191 TmSlotSetFuncAppend(tv, tm_module, (void *) ConfigParser(i));
1192
1193 tm_module =TmModuleGetByName(decode_mod_name);
1194 if (tm_module ==NULL) {
1195 SCLogError(SC_ERR_INVALID_VALUE, "TmModuleGetByName %sfailed", decode_mod_name);
1196 exit(EXIT_FAILURE);
1197 }
1198 TmSlotSetFuncAppend(tv,tm_module, NULL);
1199
1200 tm_module =TmModuleGetByName("StreamTcp");
1201 if (tm_module ==NULL) {
1202 SCLogError(SC_ERR_RUNMODE, "TmModuleGetByName StreamTcpfailed");
1203 exit(EXIT_FAILURE);
1204 }
1205 TmSlotSetFuncAppend(tv, tm_module, NULL);
1206
1207 tm_module =TmModuleGetByName("Detect");
1208 if (tm_module ==NULL) {
1209 SCLogError(SC_ERR_RUNMODE, "TmModuleGetByName Detect failed");
1210 exit(EXIT_FAILURE);
1211 }
1212 TmSlotSetFuncAppendDelayed(tv, tm_module,
1213 (void*)de_ctx, de_ctx->delayed_detect);
1214
1215 tm_module =TmModuleGetByName(verdict_mod_name);
1216 if (tm_module ==NULL) {
1217 SCLogError(SC_ERR_RUNMODE, "TmModuleGetByName %s failed",verdict_mod_name);
exit(EXIT_FAILURE);
1219 }
1220
1221 TmSlotSetFuncAppend(tv, tm_module, (void *)de_ctx);
1222
1223 tm_module =TmModuleGetByName("RespondReject");
1224 if (tm_module ==NULL) {
1225 printf("ERROR: TmModuleGetByName for RespondReject failed\n");
1226 exit(EXIT_FAILURE);
1227 }
1228 TmSlotSetFuncAppend(tv,tm_module, NULL);
1229
1230 SetupOutputs(tv);
1231
1232 TmThreadSetCPU(tv, DETECT_CPU_SET);
1233
1234 if(TmThreadSpawn(tv) != TM_ECODE_OK) {
1235 SCLogError(SC_ERR_RUNMODE, "TmThreadSpawn failed");
1236 exit(EXIT_FAILURE);
1237 }
1238 }
1239
1240 return 0;
1241 }
最后通过函数TmThreadSpawn(tv)创建线程。
如有错误请告诉我。