转自:libpcap实现机制及接口函数 - 简书

1.Libpcap 的工作原理

Libpcap的工作原理可以描述为,当一个数据包到达网卡时,通过网络分接口(即旁路机制)将数据包发给BPF过滤器,匹配通过的数据包可以被libpcap利用创建的套接字PF_PACKET从链路层驱动程序中获得。进而在用户空间提供独立于系统的用户级API接口。

流程图示意图

一个数据包的捕捉分为三个主要部分

  • 面向底层包捕获、
  • 面向中间层的数据包过滤
  • 面向应用层的用户接口

这与Linux操作系统对数据包的处理流程是相同的

网卡->网卡驱动->数据链路层->IP层->传输层->应用程序

2、Libpcap的实现机制

这里实现的包捕获机制是在数据链路层增加一个旁路处理,并不干扰系统自身的网路协议栈的处理,对发送和接收的数据包通过Linux内核做过滤和缓冲处理,最后直接传递给上层应用程序。因此libpcap在捕获到达网卡的数据包后绕开了传统linux协议栈处理,直接使用链路层PF_PACKET协议族原始套接字方式向用户空间传递报文。

libpcap捕获报文机制示意图

接下来,对照上图分层解释报文从网卡最终到达用户空间的处理流程:

  • 网络报文的接收源自网络设备(网卡)。

---------------------------物理层

  • 网络设备在接收到一个报文之后,通过中断IRQ告知CPU。网卡驱动程序需要注册对该中断事件的处理函数,以处理接收到的报文。在中断中执行以下操作:
  • 分配一个缓冲区sk_buff,把接收的数据拷贝进去;(第一次拷贝)
  • 对缓冲区结构内的一些参数做初始化以告知较高层协议数据是什么类型skb->protocol;
  • 非NAPI:调用netif_rx( )函数通知内核,将帧放入CPU的softnet_data->input_pkt_queue。netif_rx会调用网络接口函数netif_rx_schedule(使用softdate_net结构中内嵌的backlog_dev作为dev参数)
  • NAPI:帧存放在每个设备自己的队列之中。调用netif_rx_schedule函数(直接以对应设备的dev结构为参数)
  • 然后触发相关联的软IRQ--NET_RX_SOFTIRQ,此时网卡驱动程序已经将输入设备排入轮询列表poll_list,接下来执行net_rx_action函数:
  • 浏览poll_list设备列表,这些设备的入口队列都有数据;
  • 非NAPI设备:执行process_backlog函数(backlog_dev->poll)

若时间片用完或者配额用尽,将该设备放置列表尾部等待下一次中断到来时继续被调用;若处理完input_pkt_queue列表中的全部报文,则将设备退出poll_list同时打开设备中断服务继续监听下一个报文到来。

  • NAPI设备:执行poll函数

dev->poll可以做一些轮询的工作,如果网络设备已经接收了多个报文,可以一次性处理。就算设备此刻所接收到的报文都已经处理完了,驱动程序也可以根据某种方式预判设备在很短的一段时间内还将收到报文,于是依然将自己对应的dev结构留在poll_list中,处于轮询状态。增大了报文接收的平均延时,但避免了大量中断带来的开销。dev设备退出poll_list同时打开设备中断服务。

  • 接着调用netif_receive_skb函数:
  • 如果有抓包程序,由网络分接口进入BPF过滤器,将规则匹配的报文拷贝到系统内核缓存 (第二次拷贝)否则直接丢弃数据包;*注 : linux 在 PF_PACKET 类型的 socket 上支持内核过滤。Linux 内核允许我们把一个名为 LPF(Linux Packet Filter) 的过滤器直接放到 PF_PACKET 类型 socket 的处理过程中,过滤器在网卡接收中断执行后立即执行 *
  • 处理数据链路层的桥接功能;
  • 根据skb->protocol字段确定上层协议并提交给网络层处理-->进入网络协议栈

|
|
| 内核空间
|
/
---------------------------数据链路层
/
|
| 用户空间
|
|

  • libpcap绕过了Linux内核收包流程中协议栈部分的处理,使得用户空间API可以直接调用套接字PF_PACKET从链路层驱动程序中获得数据报文的拷贝,将其从内核缓冲区拷贝至用户空间缓冲区(第三次拷贝)

fd=socket(PF_PACKET,sock_RAW,htons(ETH_P_ALL))
libpcap 函数库注册的报文接收类型为 ETH_P_ALL,即接收所有的网络数据帧,其处理函数为 packet_rcv()。该函数工作在数据链路层。

进而调用recvfrom函数获得捕获的报文 (需要进行系统调用):
packet_rcv() 函数将直接调用 skb_queue_tail() 将数据报文存放在代表相应网络连接控制结构(struct sock)的接收队列 receive_queue 中。这样数据报文在接收过程中就绕过了 TCP 层和 IP 层繁琐的协议处理过程。最后,睡眠在 sk 等待队列上的函数 packet_recvmsg() 会接收链路层数据帧并将该数据帧直接拷贝到应用程序缓冲区中。

  • 最后libpcap面向用户空间提供独立于系统的可调用的函数接口

3、BPF过滤器

  • BPF本质上来说是一也个设备驱动(device driver),能够被应用程序用来读取网络上通过这个网络适配器的包。但是BPF又是一个特殊的驱动,因为它并没有直接控制网络适配器,而是网络适配器真正的设备驱动调用BPF来传递数据。

  • BPF正常情况下被用作诊断工具去检查与本机相连的网络的流通状况。一个BPF设备能够配置一个filter,根据这个filter的特征,来忽略或者接收到来的包。

  • BPF拥有两个组件: the network tapthe packet filterthe network tap 收集来自网络设备驱动的包的一个拷贝,并把它专递给监听程序。the packet filter 决定是否接收这个包并且把它拷贝给监听程序

  • BPF为每一个要求服务的抓包程序关联一个filter和两个buffer。BPF分配buffer 且通常情况下它的额度是4KB the store buffer 被使用来接收来自适配器的数据; the hold buffer被使用来拷贝包到应用程序

  • 通常情况下, 当一个包到达网络接口时, 数据链路设备驱动将把它发送到系统协议栈。但是当BPF在这个接口上面监听时,网络设备驱动将首先调用 BPF的network tap函数。这个tap函数将包送入每一个监听程序的filter。而用户定义的filter决定: 是否接收这个包; 每一个包有多少字节将会被保存。如果filter接收这个包, 那么tap 将会从数据链路层驱动的缓存中拷贝这个数目的字节数到 与这个filter关联的store buffer中(store buffer在内核中定义)。同时,网络接口的设备驱动将会重新获得控制权,且正常的协议处理将会进行。

  • 监听进程执行read系统调用去从BPF(hold buffer)接收包,并将阻塞于此。当hold buffer 满的时候(或者当超时发生时),BPF将会拷贝这些数据到进程内存空间,且唤醒这个进程。监听程序能够一次接收多个包。

BPF结构图

4、关键函数

  • 未使用NAPI的网络设备驱动程序通过netif_rx通知内核帧已经接收
skb = dev_alloc_skb(pkt_len + 5) ;
...  ...  ...
if (skb!=NULL) {skb->dev = dev;skb_reserve(skb,2);    //把IP对齐在16字节边界上
...  ...  ...
/*把DATA数据拷贝到sk_buff结构*/
...  ...  ...
skb->protocol = eth_type_trans(skb,dev) ;
netif_rx(skb) ;
dev->last_rx=jiffies;
...  ...  ...}

5、用户级API

1)获取数据包捕获描述字
函数名称:pcap_t *pcap_open_live(char *device, int snaplen, int promisc, int to_ms, char *ebuf)
函数功能:获得用于捕获网络数据包的数据包捕获描述字。
参数说明:device参数为指定打开的网络设备名。snaplen参数定义捕获数据的最大字节数。Promisc 指定是否将网络接口置于混杂模式。to_ms参数指*定超时时间(毫秒)。ebuf参数则仅在pcap_open_live()函数出错返回NULL时用于传递错误消息。

2)打开保存捕获数据包文件
函数名称:pcap_t *pcap_open_offline(char *fname, char *ebuf)
函数功能:打开以前保存捕获数据包的文件,用于读取。
参数说明:fname参数指定打开的文件名。该文件中的数据格式与tcpdump和tcpslice兼容。”-“为标准输入。ebuf参数则仅在pcap_open_offline()函数出错返回NULL时用于传递错误消息。

3)转储数据包
函数名称:pcap_dumper_t *pcap_dump_open(pcap_t *p, char *fname)
函数功能:打开用于保存捕获数据包的文件,用于写入。
参数说明:fname参数为”-“时表示标准输出。出错时返回NULL。p参数为调用pcap_open_offline() 或pcap_open_live()函数后返回的pcap结构指针,即网卡句柄。fname参数指定打开的文件名,存盘的文件名。如果返回NULL,则可调用pcap_geterr()函数获取错误消息。

4)查找网络设备
函数名称:char *pcap_lookupdev(char *errbuf)
函数功能:用于返回可被pcap_open_live()或pcap_lookupnet()函数调用的网络设备名指针。
返回值:如果函数出错,则返回NULL,同时errbuf中存放相关的错误消息。

5)获取网络号和掩码

函数名称:int pcap_lookupnet(char *device, bpf_u_int32 *netp,bpf_u_int32 *maskp, char *errbuf)
函数功能:获得指定网络设备的网络号和掩码。
参数说明:netp参数和maskp参数都是bpf_u_int32指针。
返回值:如果函数出错,则返回-1,同时errbuf中存放相关的错误消息。

6)捕获并处理数据包
** 函数名称**:int pcap_dispatch(pcap_t *p, int cnt,pcap_handler callback, u_char *user)
函数功能:捕获并处理数据包。
参数说明:cnt参数指定函数返回前所处理数据包的最大值。cnt= -1表示在一个缓冲区中处理所有的数据包。cnt=0表示处理所有数据包,直到产生以下错误之一:读取到EOF;超时读取。callback参数指定一个带有三个参数的回调函数,这三个参数为:一个从pcap_dispatch()函数传递过来的u_char指针,一个pcap_pkthdr结构的指针,和一个数据包大小的u_char指针。
返回值:如果成功则返回读取到的字节数。读取到EOF时则返回零值。出错时则返回-1,此时可调用pcap_perror()或pcap_geterr()函数获取错误消息。

** 7)捕获和处理数据包**
** 函数名称:int pcap_loop(pcap_t *p, int cnt,pcap_handler callback, u_char *user)
** 函数功能
:功能基本与pcap_dispatch()函数相同,只不过此函数在cnt个数据包被处理或出现错误时才返回,但读取超时不会返回。而如果为pcap_open_live()函数指定了一个非零值的超时设置,然后调用pcap_dispatch()函数,则当超时发生时pcap_dispatch()函数会返回。cnt参数为负值时pcap_loop()函数将始终循环运行,除非出现错误。

** 8)输出数据包**
函数名称:void pcap_dump(u_char *user, struct pcap_pkthdr *h,u_char *sp)
** 函数功能**:向调用pcap_dump_open()函数打开的文件输出一个数据包。该函数可作为pcap_dispatch()函数的回调函数。

参数说明: 参数1: 所建立的文件pcap_dump_open()的返回值,要进行强制转换.;参数2: 数据包特有的内容.;参数 3: 数据包内容指针

9)编译字串至过滤程序
函数名称:int pcap_compile(pcap_t *p, struct bpf_program *fp,char *str, int optimize, bpf_u_int32 netmask)
函数功能:将str参数指定的字符串编译到过滤程序中。
参数说明:fp是一个bpf_program结构的指针,在pcap_compile()函数中被赋值。optimize参数控制结果代码的优化。netmask参数指定本地网络的网络掩码。

10)指定过滤程序
函数名称:int pcap_setfilter(pcap_t p, struct bpf_program fp)
函数功能:指定一个过滤程序。
参数说明:fp参数是bpf_program结构指针,通常取自pcap_compile()函数调用。
** 返回值
:出错时返回-1;成功时返回0

11)获取下一个数据包
函数名称:u_char pcap_next(pcap_t p, struct pcap_pkthdr *h)
** 函数功能
:返回指向下一个数据包的u_char指针

12)获取数据链路层类型
函数名称:int pcap_datalink(pcap_t *p)
** 函数功能**:返回数据链路层类型,例如DLT_EN10MB

13)获取快照参数值
函数名称:int pcap_snapshot(pcap_t *p)
** 函数功能**:返回pcap_open_live被调用后的snapshot参数值

14)检测字节顺序
函数名称:int pcap_is_swapped(pcap_t *p)
函数功能:返回当前系统主机字节与被打开文件的字节顺序是否不同

15)获取主版本号
** 函数名称**:int pcap_major_version(pcap_t *p)
函数功能:返回写入被打开文件所使用的pcap函数的主版本号

16)获取辅版本号
函数名称:int pcap_minor_version(pcap_t *p)
** 函数功能**:返回写入被打开文件所使用的pcap函数的辅版本号

17)结构赋值
函数名称:int pcap_stats(pcap_t *p, struct pcap_stat *ps)
函数功能:向pcap_stat结构赋值。成功时返回0。这些数值包括了从开始捕获数据以来至今共捕获到的数据包统计。如果出错或不支持数据包统计,则返回-1,且可调用pcap_perror()或pcap_geterr()函数来获取错误消息。

18)获取打开文件名
函数名称:FILE *pcap_file(pcap_t *p)
函数功能:返回被打开文件的文件名。

19)获取描述字号码
函数名称:int pcap_fileno(pcap_t *p)
函数功能:返回被打开文件的文件描述字号码

** 20)显示错误消息**
函数名称:void pcap_perror(pcap_t *p, char *prefix)
函数功能:在标准输出设备上显示最后一个pcap库错误消息。以prefix参数指定的字符串为消息头。

6、参考文档

  • 基于 linux 平台的 libpcap 源代码分析
  • libpcap使用方法
  • libpcap编程小结
  • tcpdump sniffex
  • PF_PACKET
  • 原始套接字 SOCK_RAW
  • udp数据报从网卡驱动到用户空间流程总结
  • PACKET_MMAP实现原理分析
  • linux网络报文接收发送浅析
  • 高速网络环境下基于零拷贝的报文捕获机制
  • 基于Linux平台的libpcap源码分析和优化

14人点赞

日记本

作者:shaarawy18
链接:https://www.jianshu.com/p/ed6db49a3428
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

【转】libpcap实现机制及接口函数相关推荐

  1. libpcap实现机制及接口函数

    1.Libpcap 的工作原理 Libpcap的工作原理可以描述为,当一个数据包到达网卡时,通过网络分接口(即旁路机制)将数据包发给BPF过滤器,匹配通过的数据包可以被libpcap利用创建的套接字P ...

  2. libpcap讲解与API接口函数讲解

    ibpcap(Packet Capture Library),即数据包捕获函数库,是Unix/Linux平台下的网络数据包捕获函数库.它是一个独立于系统的用户层包捕获的API接口,为底层网络监测提供了 ...

  3. Go进阶(4): string接口函数+时间接口函数+内置函数+错误处理

    1. string接口函数 len():内建函数,返回字符串/数组的长度,按字节(UTF-):例如,ascii字符占一个字节,汉字占三个字节: r:=[]rune(str):字符串遍历函数,同时可以处 ...

  4. 【LeetCode 总结】Leetcode 题型分类总结、索引与常用接口函数

    文章目录 零. Java 常用接口函数 一. 动态规划 二. 链表 三. 哈希表 四. 滑动窗口 五. 字符串 六. DFS.BFS 七. 二分法 八. 二叉树 九. 偏数学.过目不忘 and 原地算 ...

  5. HCFT和HCFTstar在OTB数据集中测试的接口函数

    HCFT跟踪算法(Hierarchical Convolutional Features for Visual Tracking)是2015年发表在ICCV上的一篇结合相关滤波和深度特征的跟踪算法,作 ...

  6. linux路由相关函数,Linux 路由 学习笔记 之十一 输入、输出路由查找相关的接口函数...

    对于路由功能模块的学习,也已经很长时间了.关于路由项的创建与查找.策略规则相关的创建与查找.路由缓存的创建与查找,都是分开来分析的,没有说明这些模块是如何配合使用的,以及模块之间的联系.本节就分析一下 ...

  7. 加密机PKCS#11国际加密标准解读-概念和常用接口函数(1)

    PKCS#11是使用非常普遍的密码设备接口,在实际应用中,国密的密码设备应用接口规范GMT0018与之作用相同,在技术体系架构中处于类似的位置. 在密码产品的开发中,按照PKCS#11或者GMT001 ...

  8. Hook Com接口函数

    标 题:  [原创]COM接口函数通用Hook方法 作 者: zhangluduo 时 间: 2014-12-08,22:34:20 链 接: http://bbs.pediy.com/showthr ...

  9. WinCE中串口驱动及接口函数介绍(转载)

    作者:ARM-WinCE 在WinCE中,串口驱动实际上就是一个流设备驱动,具体架构如图: 串口驱动本身分为MDD层和PDD层.MDD层对上层的Device Manager提供了标准的流设备驱动接口( ...

最新文章

  1. python趣味编程10例-python趣味编程100例(99个)
  2. 氧气中国·创业创新大赛企业服务专场
  3. rails 5 action cable 服务器部署
  4. 使用Servlet上传多张图片——Dao层(BaseDao.java)
  5. 适合利用计算机模拟的是,计算机模拟在数学建模中的应用
  6. 杭电考研计算机专业课_杭州电子科技大学计算机考研专业课平均分76.03,国家线复试是这个原因!...
  7. 第五章 条件、循环及其他语句
  8. 解决Mac安装LightGBM报错LightGBM and gcc 8 in MacOS: Library not loaded
  9. android lottie字体json,Android Lottie集成及基本用法
  10. Android学习入门
  11. win10 FTP搭建全过程
  12. 怎么获取大量新鲜可用的迅雷白金会员账号!?
  13. python有道批量单词音标整理-Python 批量翻译 使用有道api;
  14. 图文并茂从根上理解装饰器设计模式
  15. 怎样在mac系统里将文件拷贝到移动硬盘教程
  16. 八佰(800)低代码精耕行业细分领域数字化转型
  17. 密码的显示与隐藏php,el-input 标签中密码的显示和隐藏功能的实例代码
  18. 51nod 1387 移数字
  19. 火车售票系统设计 c语言,C语言火车售票系统
  20. ESP8266 Upgrade FOTA升级被折腾那些“小”事

热门文章

  1. idea_取消自动import .*
  2. 【C语言进阶深度学习记录】十 C语言中:struct的柔性数组和union分析
  3. [Linux] linux下安装配置 zookeeper/redis/solr/tomcat/IK分词器 详细实例.
  4. Python标准异常总结
  5. sql 中on和where的区别
  6. vertex shader(5)
  7. HDU1856_More is better
  8. VB用API实现各种对话框(总结)(转载)
  9. [小技巧][JAVA]判断字符串某一位是否是数字/字母
  10. 搭载鸿蒙的油烟机,华为、美的合作:搭载鸿蒙系统的家电来了 三大亮点