首页 > 代码库 > pcapReader——源码分析

pcapReader——源码分析

一、简介 

           pcapReader是ndpi开源中的一个example。大家可以从<ndpi directory>/example/pcapReader.c中找到它的源代码。通过pcaplib和ndpi相结合,进行深度包检测。虽然只有短短的几行代码,但是他将展现的不仅是pcaplib和ndpi的使用方法,还有包分析的一些技巧。看完之后其实外国人写的程序也就是那样,并没有什么特别之处。我们先来一起看看基本的函数结构。

         注:我们只对源码中的linux平台部分进行解释

         在main函数中,通过调用test_lib()对程序进行整合。   

         <script src="https://code.csdn.net/snippets/395684.js" type="text/javascript"></script>
         这里限于篇幅,主要对runPcapLoop()函数中的动作进行分析。如果想理解其他函数或者更加详细的技术细节,可以阅读博客最后的源码附录。里面有比较详细的注释。如果还有问题,可以留言或者发一下私信。欢迎大家一起讨论。

二、包分析
        runPcapLoop()函数中通过pcap_loop(_pcap_handle, -1, &pcap_packet_callback, NULL)进行循环抓包。pcap_loop是pcaplib中提供的api。_pcap_handle指向的是网卡设备,pcap_packet_callback是循环抓包之后的包处理函数,-1代表的是不停地抓直到抓包出错的时候停止。接下来我们针对pcap_packet_callback函数中的包处理进行分析

pcap_packet_callback函数中,按顺序分成4个主要部分:
         1、ndpi_ethhdr进行数据链路层的拆包分析。针对Linux Cooked Capture 和vlan的特殊包结构。对包头和信息进行了对应的偏移,并且记录在ip_offset变量中。
         2、ndpi_iphdr进行网络层的拆包。这里进行了ipv4和ipv6的检测,我们接下来只对ipv4进行介绍。
         3、GTP隧道协议的处理
         4、packet_processing()函数进一步的包处理
         注:2中的网络层拆包存储在iph变量中,并在packet_processing()中作为ndpi协议检测的数据源
          
packet_processing函数作为ndpi分析的主体,这里通过get_ndpi_flow函数分类会话。然后利用ndpi_detection_process_pac
ket函数进行数据分析得到应用层协议。我们继续往下看看get_ndpi_flow是怎样建立起数据结构的。
       注:get_ndpi_flow6针对ipv6进行了转换,最后还是通过get_ndpi_flow建立

get_ndpi_flow函数:
1、通过传输层拆包获得协议包的源和目的端口(tcp通过ndpi_tcphdr 、udp通过ndpi_udphdr分别进行拆包)
2、结合网络层和传输层的数据,通过源目的ip和端口分类会话
3、以ndpi_flows_root为hash数组,(lower_ip + upper_ip + iph->protocol + lower_port + upper_port) % NUM_ROOTS计算出会话对应的数组位置。然后对于数组的每个单元维护一个二叉查找链表。
4、通过ndpi_tfind函数对二叉树进行查找,如果存在相对应的会话,则返回对应结果。如果不存在,则通过ndpi_tsearch把新的会话插入二叉树中。
node_cmp函数中定义了比较的规则。ndpi_tfind和ndpi_tsearch在<ndpi directory>/src/lib/ndpi_main.c文件中进行的二叉查找的封装。
<script src="https://code.csdn.net/snippets/395766.js" type="text/javascript"></script>
         
整体数据结构
          

三、其他函数
1、setupDetection();//ndpi检测协议的注册,以及参数设置
      通过ndpi提供的一系列函数,注册需要深度检测的协议。大略如下
ndpi_init_detection_module激活cache支持,主要针对一些占用缓存的协议如skype
        ndpi_set_protocol_detection_bitmask2注册需要进行检测的协议
        ndpi_detection_get_sizeof_ndpi_id_struct
        ndpi_detection_get_sizeof_ndpi_flow_struct:获取ndpi_flow_struct和ndpi_id_struct的大小在为二叉树插入新节点时,申请空间用
        变量的初始化
        
2、openPcapFileOrDevice();//pcaplib的初始化准备
errbuf[PCAP_ERRBUF_SIZE]:pcaplib存放错误信息的缓冲区
pcap_open_live打开对应的网卡设备
        注:如果打开失败,或者命令中指定利用pcap_open_offline从文件中读入数据
        pcap_datalink获取当前数据链路的类型,一般为以太网v2
        pcap_compile和pcap_setfilter分别用于编译和设置抓包的过滤规则

3、signal(SIGINT, sigproc);//包含在signal.h头文件中,这里主要交互式信号,如中断做出反应。触发sigproc函数关闭程序   
<script src="https://code.csdn.net/snippets/395789.js" type="text/javascript"></script>
如果产生中断,则调用如上函数关闭pcap和ndpi并且输出结果。

4、closePcapFile();
通过pcap_close函数清除_pcap_handle指针并关闭抓包。

5、printResults(tot_usec);//输出结果

6、terminateDetection();
通过ndpi_tdestroy释放hash数组及其数组上的二叉查找树节点,最后通过ndpi_exit_detection_module结束ndpi程序。

7、static void parseOptions(int argc, char **argv)   /*命令行的实现,这里argc和argv从main中argc和argv参数传递进来。*/
 getopt函数是命令行分析 第三个参数解释:
     1.单个字符,表示选项
     2.单个字符后接一个冒号:表示该选项后必须跟一个参数。参数紧跟在选项后或者以空格隔开。该参数的指针赋给optarg。
     3 单个字符后跟两个冒号,表示该选项后可以跟一个参数,也可以不跟。如果跟一个参数,参数必须紧跟在选项后不能以空格隔开。该参数的指针赋给optarg。
getopt中选项得到的参数传递给全局变量optarg
四、源码附录
       https://code.csdn.net/snippets/395699