运行模式
suricata启动的线程组成取决于他的运行模式,通过suricatasc -c running-mode获取运行模式;
其运行模式由两处位置决定:
workers模式
af-packet收包
1、线程
线程名称 |
数量 |
作用 |
入口函数 |
suricata-main |
1 |
主进程 |
main |
woker |
配置文件决定
- 对应配置项threads参数
- 模式下default配置项threads参数
无配置
|
工作线程 |
TmThreadsSlotPktAcqLoop |
FM |
1 |
流管理线程 |
TmThreadsManagement FlowManager |
FR |
1 |
流回收线程 |
TmThreadsManagement FlowRecycler |
CS |
1 |
管理线程 |
|
CM |
1 |
线程状态 |
|
US |
1 |
管理工具suricatasc |
|
2、woker
woker负责获取数据包,解码并完成一系列的包处理动作。在af-packet模式中:
收包通过ReceiveAFPLoop完成,解码通过DecodeAFP,包处理通过FlowWorker完成
2.1、创建过程
main->ParseCommandLine:完成进程执行句柄的解析,对af-packet设置suri->run_mode运行模式
在af-packet worker情况下启动器RunModeIdsAFPWorkers
void RunModeIdsAFPRegister(void) { RunModeRegisterNewRunMode(RUNMODE_AFP_DEV, "single", "Single threaded af-packet mode", RunModeIdsAFPSingle); RunModeRegisterNewRunMode(RUNMODE_AFP_DEV, "workers", "Workers af-packet mode, each thread does all" " tasks from acquisition to logging", RunModeIdsAFPWorkers); default_mode_workers = "workers"; RunModeRegisterNewRunMode(RUNMODE_AFP_DEV, "autofp", "Multi socket AF_PACKET mode. Packets from " "each flow are assigned to a single detect " "thread.", RunModeIdsAFPAutoFp); return; } |
main->RunModeDispatch 这里根据run_mode获取af-packet worker模式的入口。
if (custom_mode == NULL) { const char *val = NULL; // 获取配置文件runmode 内容 if (ConfGet("runmode", &val) != 1) { custom_mode = NULL; } else { custom_mode = val; } } …………如果没有或者内容被注释,根据启动模块给定default_runmode if (custom_mode == NULL || strcmp(custom_mode, "auto") == 0) { switch (runmode) { case RUNMODE_AFP_DEV: // 对于af-packet而言,他的默认runmode为worker custom_mode = RunModeAFPGetDefaultMode(); break; default: SCLogError(SC_ERR_UNKNOWN_RUN_MODE, "Unknown runtime mode. Aborting"); exit(EXIT_FAILURE); } } // 拿到对应的运行结构 RunMode *mode = RunModeGetCustomMode(runmode, custom_mode); // 运行提供的启动接口,对于af-packet worker启动接口为 RunModeIdsAFPWorkers mode->RunModeFunc(); |
int RunModeIdsAFPWorkers(void) - 解析yaml配置文件中af-packet项,完成模式启动
这里使用框架提供的启动函数,只需要提供收包和解码回调函数即可。
……………… // ParseAFPConfig负责解析配置文件,后边将配置conf数据传到运行流程进行处理 // 提供接口对应项配置解析器(af-packet),提供获取线程数量的回调接口。 // 这里提到的recv 和 decode 为收包和解码模块 // thread_name_workers 默认为W // live_dev 为程序运行时写的接口名称 ret = RunModeSetLiveCaptureWorkers(ParseAFPConfig, AFPConfigGeThreadsCount, "ReceiveAFP", "DecodeAFP", thread_name_workers, live_dev); |
int RunModeSetLiveCaptureWorkers(ConfigIfaceParserFunc ConfigParser,
ConfigIfaceThreadsCountFunc ModThreadsCount,
const char *recv_mod_name,
const char *decode_mod_name, const char *thread_name,
const char *live_dev)
根据提供的接口完成woker线程的启动,这个接口里面的循环次数取决于yaml配置文件af-packet项内有几个接口。
for (ldev = 0; ldev < nlive; ldev++) { const char *live_dev_c = NULL; if ((nlive <= 1) && (live_dev != NULL)) { // 根据提供的配置初始化解析接口完成yaml配置文件中的变量解析。 aconf = ConfigParser(live_dev); live_dev_c = live_dev; if (unlikely(live_dev_c == NULL)) { SCLogError(SC_ERR_MEM_ALLOC, "Can't allocate interface name"); exit(EXIT_FAILURE); } } else { live_dev_c = LiveGetDeviceName(ldev); aconf = ConfigParser(live_dev_c); } // 传入线程数量获取接口,收包,解码,线程名称,接口名称,接口对应配置信息 RunModeSetLiveCaptureWorkersForDevice(ModThreadsCount, recv_mod_name, decode_mod_name, thread_name, live_dev_c, aconf, 0); } |
static int RunModeSetLiveCaptureWorkersForDevice(ConfigIfaceThreadsCountFunc ModThreadsCount,
const char *recv_mod_name,
const char *decode_mod_name, const char *thread_name,
const char *live_dev, void *aconf,
unsigned char single_mode)
接口根据给定接口完成worker启动,接口里面启动线程数量取决于线程回调运行结果。
这里注意tv这个实例,每个woker线程一个
// 单例模式 if (single_mode) { threads_count = 1; } else { // 根据接口返回结果启动线程数量 threads_count = ModThreadsCount(aconf); SCLogInfo("Going to use %" PRId32 " thread(s)", threads_count); } ……………… // tv类似包处理实例,存在对包处理相关队里和各种回调 // pktacqloop 使用的启动器 tv = TmThreadCreatePacketHandler(tname, "packetpool", "packetpool", "packetpool", "packetpool", "pktacqloop"); if (tv == NULL) { SCLogError(SC_ERR_THREAD_CREATE, "TmThreadsCreate failed"); exit(EXIT_FAILURE); } tv->printable_name = printable_threadname; // 获取对应包处理槽接口 tm_module = TmModuleGetByName(recv_mod_name); if (tm_module == NULL) { SCLogError(SC_ERR_INVALID_VALUE, "TmModuleGetByName failed for %s", recv_mod_name); exit(EXIT_FAILURE); } TmSlotSetFuncAppend(tv, tm_module, aconf); tm_module = TmModuleGetByName(decode_mod_name); if (tm_module == NULL) { SCLogError(SC_ERR_INVALID_VALUE, "TmModuleGetByName %s failed", decode_mod_name); exit(EXIT_FAILURE); } TmSlotSetFuncAppend(tv, tm_module, NULL); tm_module = TmModuleGetByName("FlowWorker"); if (tm_module == NULL) { SCLogError(SC_ERR_RUNMODE, "TmModuleGetByName for FlowWorker failed"); exit(EXIT_FAILURE); } TmSlotSetFuncAppend(tv, tm_module, NULL); //将对应的处理接口放在tv->slot中 // 上面的槽函数,在tv-slot 构成一个链表,第一个是收包模块 TmThreadSetCPU(tv, WORKER_CPU_SET); // 开始启动每个处理模块 if (TmThreadSpawn(tv) != TM_ECODE_OK) { SCLogError(SC_ERR_THREAD_SPAWN, "TmThreadSpawn failed"); exit(EXIT_FAILURE); } |
TmEcode TmThreadSpawn(ThreadVars *tv)
// 接口完成主线程的创建,并将线程交给主线程的tv_root
//启动器:TmThreadsSlotPktAcqLoop
int rc = pthread_create(&tv->t, &attr, tv->tm_func, (void *)tv); if (rc) { printf("ERROR; return code from pthread_create() is %" PRId32 "\n", rc); return TM_ECODE_FAILED; } TmThreadWaitForFlag(tv, THV_INIT_DONE | THV_RUNNING_DONE); TmThreadAppend(tv, tv->type); |
static void *TmThreadsSlotPktAcqLoop(void *td)
//启动器完成收包功能初始化,原始套接字创建
// 完成每个模块的初始化动作
for (slot = s; slot != NULL; slot = slot->slot_next) { if (slot->SlotThreadInit != NULL) { void *slot_data = NULL; // slot->slot_initdata这个数据是根据配置完成的初始化数据,就是yaml配置文件里面的内容 r = slot->SlotThreadInit(tv, slot->slot_initdata, &slot_data); if (r != TM_ECODE_OK) { if (r == TM_ECODE_DONE) { EngineDone(); TmThreadsSetFlag(tv, THV_CLOSED | THV_INIT_DONE | THV_RUNNING_DONE); goto error; } else { TmThreadsSetFlag(tv, THV_CLOSED | THV_RUNNING_DONE); goto error; } } // 将初始化得到的数据放在原子变量里面 (void)SC_ATOMIC_SET(slot->slot_data, slot_data); } while(run) { if (TmThreadsCheckFlag(tv, THV_PAUSE)) { TmThreadsSetFlag(tv, THV_PAUSED); TmThreadTestThreadUnPaused(tv); TmThreadsUnsetFlag(tv, THV_PAUSED); } //进入收包模块 ReceiveAFPLoop r = s->PktAcqLoop(tv, SC_ATOMIC_GET(s->slot_data), s); if (r == TM_ECODE_FAILED) { TmThreadsSetFlag(tv, THV_FAILED); run = 0; } if (TmThreadsCheckFlag(tv, THV_KILL_PKTACQ) || suricata_ctl_flags) { run = 0; } if (r == TM_ECODE_DONE) { run = 0; } } |
TmEcode ReceiveAFPLoop(ThreadVars *tv, void *data, void *slot)
//接口实现,激活收包流程,处理数据包
if (ptv->flags & AFP_RING_MODE) { // 根据标志选择收包接口 if (ptv->flags & AFP_TPACKET_V3) { AFPReadFunc = AFPReadFromRingV3; } else { AFPReadFunc = AFPReadFromRing; } } else { AFPReadFunc = AFPRead; } …… // 创建原始套接字,并对网口状态进行配置。 r = AFPCreateSocket(ptv, ptv->iface, 1); …… // 设置fd轮训监视,无阻塞处理 fds.fd = ptv->socket; fds.events = POLLIN; // 监视描述符状态。 r = poll(&fds, 1, POLL_TIMEOUT); // 进入流处理 lse if (r > 0) { r = AFPReadFunc(ptv); 这里的func是上面根据模式给出的。一般就是AFPReadFromRing |
static int AFPReadFromRing(AFPThreadVars *ptv)
接口将在socket的环形缓冲区接收数据包,判断数据包是否能够处理,并锁定数据包。
// 遍历整个环形缓冲区,读数据包
h.raw = (((union thdr **)ptv->ring_v2)[ptv->frame_offset]); if (unlikely(h.raw == NULL)) { /* Impossible we reach this point in normal condition, so trigger * a failure in reading */ SCReturnInt(AFP_READ_FAILURE); } |
//中间将申请数据包实例,并填入对应信息 //对数据包进行解码和包处理逻辑
if (TmThreadsSlotProcessPkt(ptv->tv, ptv->slot, p) != TM_ECODE_OK) { h.h2->tp_status = TP_STATUS_KERNEL; if (++ptv->frame_offset >= ptv->req.tp_frame_nr) { ptv->frame_offset = 0; } TmqhOutputPacketpool(ptv->tv, p); SCReturnInt(AFP_SURI_FAILURE); } |
|
static inline TmEcode TmThreadsSlotProcessPkt(ThreadVars *tv, TmSlot *s, Packet *p);
接口将遍历run_mode注册的包处理槽函数,对接收环形区内的数据进行处理。这里默认就有了解码(网络层,传输层,应用层数据获取。)flow_worker
TmEcode TmThreadsSlotVarRun(ThreadVars *tv, Packet *p,
TmSlot *slot)
接口调用每个槽函数。
TmEcode DecodeAFP(ThreadVars *tv, Packet *p, void *data, PacketQueue *pq, PacketQueue *postpq)
接口完成对各类操作计数器的运算,同时完成网络层的数据解析。
TmEcode DecodeAFP(ThreadVars *tv, Packet *p, void *data, PacketQueue *pq, PacketQueue *postpq) { SCEnter(); DecodeThreadVars *dtv = (DecodeThreadVars *)data; /* XXX HACK: flow timeout can call us for injected pseudo packets * see bug: Bug #1107: flow timeout causes decoders to run on pseudo packets - Suricata - Open Information Security Foundation */ if (p->flags & PKT_PSEUDO_STREAM_END) return TM_ECODE_OK; // 更新计数,包含包数,字节数,data中的counter值都是信息位置id值。 /* update counters */ DecodeUpdatePacketCounters(tv, dtv, p); /* If suri has set vlan during reading, we increase vlan counter */ // 如果存在vlan信息,也处理vlan计数器 if (p->vlan_idx) { StatsIncr(tv, dtv->counter_vlan); } /* call the decoder */ // 根据不同的链路类型,对raw解析到p中。 switch (p->datalink) { case LINKTYPE_ETHERNET: DecodeEthernet(tv, dtv, p,GET_PKT_DATA(p), GET_PKT_LEN(p), pq); break; case LINKTYPE_LINUX_SLL: DecodeSll(tv, dtv, p, GET_PKT_DATA(p), GET_PKT_LEN(p), pq); break; case LINKTYPE_PPP: DecodePPP(tv, dtv, p, GET_PKT_DATA(p), GET_PKT_LEN(p), pq); break; case LINKTYPE_RAW: case LINKTYPE_GRE_OVER_IP: DecodeRaw(tv, dtv, p, GET_PKT_DATA(p), GET_PKT_LEN(p), pq); break; case LINKTYPE_NULL: DecodeNull(tv, dtv, p, GET_PKT_DATA(p), GET_PKT_LEN(p), pq); break; default: SCLogError(SC_ERR_DATALINK_UNIMPLEMENTED, "Error: datalink type %" PRId32 " not yet supported in module DecodeAFP", p->datalink); break; } // 解码成功计数器 PacketDecodeFinalize(tv, dtv, p); SCReturnInt(TM_ECODE_OK); } |
|