本文以windows实时拓展Kithara RTS安装目录下的smp文件夹内的PacketRawEthernetMonitor工程源码为例,
该源码实现了一个简单的网络监视程序,可以实时监测网卡接收的原始数据。
该工程主要使用了Kernel,Packet模块,有三个函数组成:主函数runSample,回调函数_callBack,线程_recvThread,_callBack和_recvThread之间存在着同步关系,线程_recvThread运行时会堵塞等待事件,回调函数设置该事件后线程才能继续运行。
程序运行过程如下:
1、进入主函数,打开kithara RTS,建立共享内存区域,枚举以太网卡并选择一个需要的网卡,建立一个管道,用于回调函数与接收线程之间的数据交互
2、创建一事件(event),用于回调函数_callBack和线程_recvThread之间的同步。
3、创建线程_recvThread,该线程开始即处于阻塞状态,等待事件被设置。事件被设置后,本线程将继续运行,从管道中取出数据并在屏幕上显示。
4、创建回调函数_callBack,在网卡接收到数据时将触发该回调函数运行,该函数的作用是将接收的数据包存入管道中,然后设置事件以触发_recvThread继续运行。
5、创建一个标志位,如果置为false,recvThread线程进入while循环,等待事件触发其读取管道中的数据并显示;如果置为ture,线程函数不再读取数据,
6、使用函数KS_installPacketHandler将网卡数据接收事件和回调函数_callBack关联,然后设置事件,此时程序进入正式的工作状态。
7、最后退出程序,首先停止数据读取,取消回调函数关联,然后进行资源回收处理工作。
// Used Modules: Kernel Module, Packet Module//使用到的模块 // // Descript.: Sample application showing how to implement a raw // ethernet monitor application //形成一个以太网监视应用程序 // Purpose: // // This example shows how to program a raw ethernet monitoring application. //该例子展示了如何编制一个基本的以太网监视程序 // First, the program opens the driver and the Ethernet adapter.//首先,打开驱动及网卡适配器 // We install the receiver callback.安装接收器回调函数 // A receive thread is displaying the mac header, which is submitted by a pipe. 接收线程通过管道显示mac头 // Finally we release all used resources and close the driver.最后清理资源 // // Attention! It is nessary that there is no switch between our adapter and the adapter we want to trace!不能对适配器进行切换 // Otherwise, only broadcast messages will be monitored! 否则只能监视反馈回来的信息 // // ATTENTION! Network cards can only be used here, when the driver of the card has been switched to Kithara! // It is not possible to use the original driver for real-time! Please refer to the manual, chapter 16 // "Packet Module" for further instructions on switching the driver to Kithara! #include "..\_KitharaSmp\_KitharaSmp.h" const char* pCustomerNumber = "DEMO"; //-------------------------------------------------------------------------------------------------------------- // This is a user defined argument structure to the callback routine.用户定义回调函数结构体 // You are free to define your own arguments in any record/class. //-------------------------------------------------------------------------------------------------------------- //------ CallBackData ------ struct CallBackData { //回调数据的结构体, Handle hAdapter_; //适配器句柄 Handle hCallBack_; //回调函数句柄 Handle hEvent_; //事件句柄 Handle hPipe_; //管道句柄 bool finished_; //标志位 }; //当接收到数据包时,将数据包地址存在管道中 //-------------------------------------------------------------------------------------------------------------- // These are user defined callback routines. They are called every time a packet is received.一下定义回调函数,当有数据包接收到的时候调用 // We put the data in a pipe and set the receive event. 将数据放入管道中,设置接收数据的事件 //-------------------------------------------------------------------------------------------------------------- //------ _callBack ------回调函数的定义 static Error __stdcall _callBack(void* pArgs, void* pContext) { CallBackData* pData = (CallBackData*)pArgs; PacketUserContext* pUserContext = (PacketUserContext*)pContext; Error ksError; ksError = KS_putPipe( // 将数据包地址放入管道中 pData->hPipe_, // Pipe handle,管道对应的句柄 &pUserContext->pPacketApp, // Address of pointer to packet,待存入管道中的数据内存地址,这里是接收的数据包地址 1, // Item count,数量 NULL, // Number of bytes transmitted ,Number of bytes transmitted so far. 0); // Flags, here none if (ksError != KS_OK) return ksError; ksError = KS_setEvent( // 设置事件,触发线程继续运行 pData->hEvent_); // Event handle事件句柄 if (ksError != KS_OK) return ksError; return KS_OK; } //-------------------------------------------------------------------------------------------------------------- // This is the user defined thread function. It contains a loop in which the thread waits for the event // signalization whenever a packet is received.线程,等待事件信号,然后将管道中的数据取出并显示数据包头,最后释放数据包的内存对象 // After displaying the packet header, the packet must be released with KS_releasePacket. //-------------------------------------------------------------------------------------------------------------- Error __stdcall _recvThread(void* pArgs) {//也是一个回调函数,接收数据的线程 Error ksError = KS_OK; CallBackData* pData = (CallBackData*)pArgs; while (!pData->finished_) { // //---------------------------------------------------------------------------------------------------------- // Wait for the event to be set. //---------------------------------------------------------------------------------------------------------- ksError = KS_waitForEvent( // 等待接收数据 pData->hEvent_, // Event handle在回调函数中定义的事件 0, // Flags 0); // Time-out, in 100-ns-units if (ksError != KS_OK) return ksError; KSMACHeader* pHeader; while (KS_getPipe(pData->hPipe_, &pHeader, 1, NULL, 0) == KS_OK) { outputTxt(" ", true);//读出管道中的数据,并显示出来 for (int i = 0; i < 3; ++i) outputHex(KS_ntohs(*(ushort*)&pHeader->source[i * 2]), "", "", false); outputTxt(" -> ", false); for (int i = 0; i < 3; ++i) outputHex(KS_ntohs(*(ushort*)&pHeader->destination[i * 2]), "", "", false); outputHex(KS_ntohs(pHeader->typeOrLength), " typeOrLength: ", "", false); //-------------------------------------------------------------------------------------------------------- // Finally we release the packet.最后清空数据包 //-------------------------------------------------------------------------------------------------------- ksError = KS_releasePacket(//释放数据包 pData->hAdapter_, // Adapter handle,网卡句柄 pHeader, // Pointer to packet,指向数据包的指针 KSF_RAW_ETHERNET_PACKET); // Flags,标志 if (ksError != KS_OK) return ksError; } } outputTxt("Thread has been finished."); return ksError; } //-------------------------------------------------------------------------------------------------------------- // This is the main program 主函数 //-------------------------------------------------------------------------------------------------------------- void runSample() { outputTxt("***** Kithara example program ‘PacketRawEthernetMonitor‘ *****"); Error ksError; /////////////////////////////////////////////////////////////////////// //////1.程序初始化 /////////////////////////////////////////////////////////////////////// //------------------------------------------------------------------------------------------------------------ // Opening the driver is always the first step! After that, we can use other functions. If the opening fails, // no other function can be called. // // This function takes your customer number with Kithara (or "DEMO" or "BETA" if applicable) as a parameter. //------------------------------------------------------------------------------------------------------------ ksError = KS_openDriver(//第一步,打开驱动 pCustomerNumber); // Customer number 已定义const char* pCustomerNumber = "DEMO"; if (ksError) { outputErr(ksError, "KS_openDriver", "Maybe incorrect customer number?"); return; } //------------------------------------------------------------------------------------------------------------ // Allocation of Sharedmem分配共享内存 //------------------------------------------------------------------------------------------------------------ CallBackData* pAppPtr; //结构体指针 CallBackData* pSysPtr; ksError = KS_createSharedMem(//创建共享内存 (void**)&pAppPtr, // App Pointer (void**)&pSysPtr, // Sys Pointer "MyPacketRawEthernetMonitorMemory", // Name sizeof(CallBackData), // Size大小为定义的结构体大小 0); // Flags if (ksError != KS_OK) { outputErr(ksError, "KS_createSharedMem", "Failed to allocate shared memory"); KS_closeDriver(); return; } //------------------------------------------------------------------------------------------------------------ // First, the names of all network adapters are queried and displayed首先,查询并显示所有网络适配器 //------------------------------------------------------------------------------------------------------------ char pDeviceName[256];//定义一个大小为256的char型数组 outputTxt(" "); outputTxt("Following network adapters found:"); for (int i = 0; ; ++i) { //---------------------------------------------------------------------------------------------------------- // The following function can be used to query the names of all network adapters assigned to the Kithara // Driver. The number ‘i‘ runs beginning from zero. //枚举可用的网卡适配器,选择一个使用 // ATTENTION! Network cards can only be used here, when the driver of the card has been switched to Kithara! // It is not possible to use the original driver for real-time! Please refer to the manual, chapter 16 // "Packet Module" for further instructions on switching the driver to Kithara! //---------------------------------------------------------------------------------------------------------- ksError = KS_enumDevices(//枚举设备的函数 "NET", // Searches for network devices,专门寻找有关网络的设备 i, // Count, starting with zero计数 pDeviceName, // Buffer for device name 设备名称 0); // Flags if (ksError != KS_OK) { if (KSERROR_CODE(ksError) != KSERROR_DEVICE_NOT_FOUND) outputErr(ksError, "KS_enumDevices", "Unable to query network device name!"); if (KSERROR_CODE(ksError) == KSERROR_DEVICE_NOT_FOUND && !i) { outputTxt("No devices found"); outputTxt(" "); outputTxt("Did you switch the network card driver to Kithara?"); outputTxt("Please refer to the manual, chapter 16 \"Packet Module\""); outputTxt("for further instructions!"); KS_closeDriver(); return; } break; } outputDec(i, "", ": ", false); outputTxt(pDeviceName); } outputTxt(" "); //------------------------------------------------------------------------------------------------------------ // Please enter the index of the adapter, which should be opened上一个函数时枚举,现在选择,index不同,选择好,打开网络适配器 //------------------------------------------------------------------------------------------------------------ int deviceIndex = inputDec("Device number: ", ""); ksError = KS_enumDevices( "NET", // Searches for network devices deviceIndex, // Index of device pDeviceName, // Buffer for device name 0); // Flags if (ksError != KS_OK) { outputErr(ksError, "KS_enumDevices", "Unable to query network device name!"); KS_closeDriver(); return; } outputTxt("Selected device: ", false); outputTxt(pDeviceName); //------------------------------------------------------------------------------------------------------------ // Then we open the network adapter. // // Please enter a part of the hardware ID of your network controller. // e.g. // Intel: "VEN_8086" // Realtek: "PCI\\VEN_10EC&DEV_8139" or "DEV_8139" //------------------------------------------------------------------------------------------------------------ //选择好之后,打开选择好的网卡适配器 ksError = KS_openAdapter( &pAppPtr->hAdapter_, // Adapter handle pDeviceName, // Device name of the adapter 100, // Receive Pool Length 0, // Send Pool Length KSF_RAW_ETHERNET_PACKET | KSF_ACCEPT_ALL); // Flags if (ksError != KS_OK) { outputErr(ksError, "KS_openAdapter", "Failed to open adapter"); KS_closeDriver(); return; } //------------------------------------------------------------------------------------------------------------ // Here we set a mac multicast address, cause we want to display all packets going to this address.设置一个mac地址,来显示经过此地址的数据包 // It is necessary that there is no network switch between our adapter and the adapter we want to trace //注意:适配器跟要追溯的适配器不能切换 //------------------------------------------------------------------------------------------------------------ byte multi[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; ksError = KS_execAdapterCommand(//对网络适配器进行操作 pAppPtr->hAdapter_, // Adapter handle KS_PACKET_SET_MAC_MULTICAST, // Command, Adds a MAC multicast address to the reception filter. multi, // Data 数据包存在这里面啦 0); // Flags if (ksError != KS_OK) { outputErr(ksError, "KS_execAdapterCommand", "Failed to execute command"); KS_closeDriver(); return; } //------------------------------------------------------------------------------------------------------------ // For data transfer between the async callbacks and the receive thread we use a pipe. // 用一个管道来进行异步回调与接收线程之间数据传输 //------------------------------------------------------------------------------------------------------------ ksError = KS_createPipe( //创建管道 &pAppPtr->hPipe_, // Pipe Handle "MyPacketRawEthernetMonitorPipe", // Name管道名称 sizeof(void*), // Item size, here pointer size 100, // Item count NULL, // Object to signal 0); // Flags if (ksError != KS_OK) { outputErr(ksError, "KS_createPipe", "Unable to create pipe!"); KS_closeDriver(); return; } //------------------------------------------------------------------------------------------------------------ // Create an event object, which will be signalled when a packet arrives. 创建一事件,当有数据包到达时,有相应信号 //每当有数据接收到时 有相应信号 //------------------------------------------------------------------------------------------------------------ ksError = KS_createEvent( &pAppPtr->hEvent_, // Event handle "MyPacketRawEthernetMonitorEvent", // Name of event 0); // Flags, here 0 if (ksError != KS_OK) { outputErr(ksError, "KS_createEvent", "Unable to create finish event!"); KS_closeDriver(); return; } //------------------------------------------------------------------------------------------------------------ // Now create the thread.创建线程 //------------------------------------------------------------------------------------------------------------ ksError = KS_createThread( //调用线程回调函数,当有信号(event产生),说明有数据,将数据从管道中读出 _recvThread, // Thread function,调用回调函数,开始等待信号,有event信号,开始读出数据 pAppPtr, // Parameter to the thread NULL); // Address of thread handle, // not needed here if (ksError != KS_OK) { outputErr(ksError, "KS_createThread", "Unable to create the thread!"); KS_closeDriver(); return; } //------------------------------------------------------------------------------------------------------------ // For signalization we use a callback 回调函数 //------------------------------------------------------------------------------------------------------------ ksError = KS_createCallBack( &pAppPtr->hCallBack_, // Address of callback handle 句柄地址 _callBack, // Callback Routine 接收数据 将接收的数据放入管道中,并设置event,触发waitforevent,读出这里写入的数据 pSysPtr, // Reference parameter to the callback KSF_DIRECT_EXEC, // Flags, here kernel level in realtime context 0); // Priority if (ksError != KS_OK) { outputErr(ksError, "KS_createCallBack", "Failed to create callback"); KS_closeDriver(); return; } //------------------------------------------------------------------------------------------------------------ // Finally we start the packet receiver with KS_recvPackets.程序中没有KS_recvPackets的,源码注释错误。 // The signalization object must be a callback create with DIRECT_EXEC. // KS_installPacketHandler函数的作用是为网卡的某个事件(这里是收到数据包时触发的事件KS_PACKET_RECV)关联一个回调函数(pAppPrt->hCallBack),在事件触发时该回调函数将会被执行 //------------------------------------------------------------------------------------------------------------ pAppPtr->finished_ = false; // false表示线程中开始接收数据 ksError = KS_installPacketHandler( // 安装数据包处理函数 pAppPtr->hAdapter_, // Adapter handle KS_PACKET_RECV, // Event code 安装好用该函数进行数据包的接收,开始接收数据包 pAppPtr->hCallBack_, // Callback handle,调用回调函数,将采集到的数据包的地址放入管道中 KSF_RAW_ETHERNET_PACKET); // Flags Enables the raw ethernet packet mode for the handle,处理的是原始数据包 if (ksError != KS_OK) { outputErr(ksError, "KS_installPacketHandler", "Failed to install handler"); KS_closeDriver(); return; } inputTxt("Press <enter> to stop... "); outputTxt(" "); /////以下为程序退出部分 //------------------------------------------------------------------------------------------------------------ // Set the event, so the thread can be finished. Before that, we set the variable ‘finished_‘ to true, so the // thread knows it should terminate. //------------------------------------------------------------------------------------------------------------ pAppPtr->finished_ = true; //首先线程中接收到数据显示,然后线程关闭,以下是停止接收数据 //------------------------------------------------------------------------------------------------------------ // To stop the receiver, we set the Callback handle to NULL. 停止接收数据 //------------------------------------------------------------------------------------------------------------ ksError = KS_installPacketHandler( pAppPtr->hAdapter_, // Adapter handle KS_PACKET_RECV, // Event code NULL, // Callback handle句柄为null,停止接收数据 KSF_RAW_ETHERNET_PACKET); // Flags if (ksError != KS_OK) { outputErr(ksError, "KS_installPacketHandler", "Failed to uninstall handler"); KS_closeDriver(); return; } ksError = KS_setEvent(pAppPtr->hEvent_); if (ksError != KS_OK) outputErr(ksError, "KS_setEvent", "Setting event failed!"); waitTime(500 * ms); //------------------------------------------------------------------------------------------------------------ // Display the adapter state 以下是显示适配器信息 //------------------------------------------------------------------------------------------------------------ KSAdapterState state; state.structSize = sizeof(KSAdapterState); ksError = KS_getAdapterState( //获取适配器信息函数 pAppPtr->hAdapter_, // Adapter handle &state, // Pointer to Buffer 0); // Flags if (ksError != KS_OK) outputErr(ksError, "KS_getAdapterState", "Querying the adapter state failed!"); else { outputTxt(" "); outputDec(state.numPacketsSendOk, "Number of packets sent successfully: ", ""); outputDec(state.numPacketsSendError, "Number of packets failed to send: ", ""); outputDec(state.numPacketsSendARPTimeout, "Number of packets failed because of ARP timeout: ", ""); outputDec(state.numPacketsWaitingForARP, "Number of packets waiting for ARP: ", ""); outputDec(state.numPacketsRecvOk, "Number of packets received successfully: ", ""); outputDec(state.numPacketsRecvError, "Number of packets failed to receive: ", ""); outputTxt(" "); } //------------------------------------------------------------------------------------------------------------ // Close the Adapter. //------------------------------------------------------------------------------------------------------------ ksError = KS_closeAdapter( //关闭适配器 pAppPtr->hAdapter_); // Adapter handle if (ksError != KS_OK) outputErr(ksError, "KS_closeAdapter", "Unable to close Adapter!"); //------------------------------------------------------------------------------------------------------------ // Remove callbacks. //------------------------------------------------------------------------------------------------------------ ksError = KS_removeCallBack( pAppPtr->hCallBack_); // Callback handle if (ksError != KS_OK) outputErr(ksError, "KS_removeCallBack", "Unable to remove the callback!"); //------------------------------------------------------------------------------------------------------------ // Close the event object //------------------------------------------------------------------------------------------------------------ ksError = KS_closeEvent( pAppPtr->hEvent_); // Event handle if (ksError != KS_OK) outputErr(ksError, "KS_closeEvent", "Unable to close event!"); //------------------------------------------------------------------------------------------------------------ // Remove the data pipe //------------------------------------------------------------------------------------------------------------ ksError = KS_removePipe( pAppPtr->hPipe_); // Pipe handle if (ksError != KS_OK) outputErr(ksError, "KS_removePipe", "Unable to remove the pipe!"); //------------------------------------------------------------------------------------------------------------ // Remove the shared memory //------------------------------------------------------------------------------------------------------------ ksError = KS_freeSharedMem( pAppPtr); // Application pointer if (ksError != KS_OK) outputErr(ksError, "KS_freeSharedMem", "Unable to remove shared memory!"); //------------------------------------------------------------------------------------------------------------ // At last we have to close the driver to free any allocated resources. //------------------------------------------------------------------------------------------------------------ ksError = KS_closeDriver(); if (ksError != KS_OK) outputErr(ksError, "KS_closeDriver", "Unable to close the driver!"); waitTime(500 * ms); outputTxt("End of program ‘PacketRawEthernetMonitor‘."); outputTxt("Press <enter> to close."); }
实时捕捉以太网原始数据包(PacketRawEthernetMonitor)----Kithara RTS工程源代码解析