Suricata之源代碼(一)

        第一次系統性的寫blog,寫的不好,請大家多多包涵。

        在介紹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)創建線程。

如有錯誤請告訴我。


發佈了28 篇原創文章 · 獲贊 1 · 訪問量 4萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章