【转】libpcap实现机制及接口函数

转自: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
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/435651.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

惊!MySQL官网巨变,下载被取消

随着欧盟的批准,甲骨文宣布收购Sun交易完成,与此同时甲骨文一系列动作也迅速展开。 甲骨文因MySQL被欧盟卡了大半年,在中国,MySQL的用户非常多,MySQL的前途无疑让人牵肠挂肚。并购之前,甲骨文说了很多&…

【转】【C++学习笔记】C++异常处理

转自:【C学习笔记】C异常处理!你绝对不能错过的干货! - 知乎 合理地使用C异常处理,能够使我们写出来的程序更加稳定强健,不易崩溃。那么,应该如何使用C异常处理呢?下面,我们就来向大…

【转】源码分析C++的string实现

转自:源码分析C的string实现 - 知乎 我们平时使用C开发过程中或多或少都会使用std::string,但您了解string具体是如何实现的吗,这里程序喵给大家从源码角度分析一下。 读完本文相信您可以回答以下问题: string的常见的实现方式…

做一个项目,平时都用到哪些工具提高效率(上)

做.NET 相关项目,Visual Studio 2008,SQL Server 2000/2005是标准的配置,但是,除此之外,还可以应用哪些工具来提高工作效率,让项目做的更轻松。 1 需要一个代码生成工具,生成实体层,数据访问代…

【转】傅里叶分析之掐死教程(完整版)更新于2014.06.06

转自:傅里叶分析之掐死教程(完整版)更新于2014.06.06 - 知乎 作 者:韩 昊 知 乎:Heinrich 微 博:花生油工人 知乎专栏:与时间无关的故事 谨以此文献给大连海事大学的吴楠老师,柳…

wince6.0编译命令分析

下面从CSDN帮助文档简要说明vs2005下面的系统编译命令,这些命令位于Build->Advanced Build Commonds 一共有六条命令,分别是: 1.Sysgen 2.Clean Sysgen 3.Build and Sysgen 4.Rebuild and Clean Sysgen 5.Build Current BSP and Subprojec…

【转】C,C++宏中#与##的讲解

转自:C,C宏中#与##的讲解 - MoreWindows - 博客园 文中__FILE__与示例1可以参见《使用ANSI C and Microsoft C中常用的预定义宏》 宏中的#的功能是将其后面的宏参数进行字符串化操作(Stringizing operator),简单说就是在它引用的…

wince对中文字体的支持

最近情况真的是很糟,小小年纪,得了好几样小毛病,弄得每天都提不起精神来,恍恍惚惚的,幸好有妻子的支持,她不论自己多忙多累,一看到我情绪不好,就给与很多安慰,百般鼓励&a…

提示信息的窗口效果

代码 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns"http://www.w3.org/1999/xhtml"><head><title>提示信息的窗口效果<…

【转】ARM 之七 主流编译器(armcc、iar、gcc for arm、LLVM(clang))详细介绍

转自&#xff1a;ARM 之七 主流编译器&#xff08;armcc、iar、gcc for arm、LLVM(clang)&#xff09;详细介绍_itexp-CSDN博客_armcc 必备 在讲解各编译器之前&#xff0c;必须先了解一下以下文件。这些文件在编译器目录下或者编译生成目标平台的可执行程序时经常见到。此外&a…

【转】深入浅出理解有限状态机

转自&#xff1a;深入浅出理解有限状态机 - 知乎 有限状态机是一种用来进行对象行为建模的工具&#xff0c;其作用主要是描述对象在它的生命周期内所经历的状态序列&#xff0c;以及如何响应来自外界的各种事件。在计算机科学中&#xff0c;有限状态机被广泛用于建模应用行为、…

wince 环境变量

wince工程的环境变量在PBInitEnv.bat文件中存储&#xff0c;经测试&#xff0c;如果在下面增加了新的变量&#xff0c;在此文件中会出现

【转】vs平台工具集介绍,vc6~vs2019各IDE对应的工具集版本

转自&#xff1a;vs平台工具集介绍以及安装 - jack_Meng - 博客园 一、什么是平台工具集&#xff1a; 平台工具集其实是MSBuild其目录所在C:\Program Files (x86)\MSBuild\Microsoft.Cpp\v4.0。VS从2010之后开始支持使用之前的版本进行编译&#xff0c;可以在工程属性->常…

基于FPGA的前向纠错算法

目前&#xff0c;无线产品的广泛应用使无线音频和视频的高质量传输成为可能。蓝牙、无限局域网等无线传输设备比较复杂&#xff0c;成本较高&#xff0c;急需 开发一种简便的、仅用于流媒体的无线传输平台&#xff0c;将音频数据实时地发送到移动终端。由于音频数据的实时性&am…

【转】Windows版本,OS内核版本,Windows SDK之间的关系

转自&#xff1a;Windows版本&#xff0c;OS内核版本&#xff0c;Windows SDK之间的关系 - 知乎 前言&#xff1a;我们经常会会被几个概念弄混淆&#xff0c;什么是Windows版本号&#xff0c;什么又是操作同内核版本&#xff0c;开发C的时候什么又是WindowsSDK&#xff0c;实际…

【转】30分钟学会UML类图

转自&#xff1a;30分钟学会UML类图 - 知乎 30分钟学会UML类图 肖继潮 UML图有很多种&#xff0c;但是并非必须掌握所有的UML图&#xff0c;才能完整系统分析和设计工作。一般说来&#xff0c;在UML图中&#xff0c;只要掌握类图、用例图、时序图的使用&#xff0c;就能完成大…

Microsoft SQL Server 2005数据库安装

Microsoft SQL Server 2005数据库&#xff0c;电脑里原有的mssql2000已经不适用了&#xff0c;到网上寻找SQL Server 2005简体中文开发版的下载地址&#xff0c;一开始下载了个学习版的express&#xff0c;可限制太多&#xff0c;还是决心安装开发版本的。到微软的官方网站寻找…

Wince6.0 cleartype

WinCE6.0下显示宋体毛刺很严重&#xff0c;影响显示效果&#xff0c;打开cleartype以后字体显示平滑&#xff0c;但是不知道为什么wince桌面上的中文字体显示乱码&#xff0c;而且自己的引用程序也变的很卡。 开始以为是字库的原因&#xff0c;后来添加了系统的组件以后一切正常…

【转】ubuntu16.04安装配置tftp服务

转自&#xff1a;ubuntu16.04安装配置tftp服务_carspiriter的博客-CSDN博客_ubuntu安装tftp 首先声明&#xff1a;tftp是client客户端&#xff0c;tftpd是server服务器端&#xff0c;d应该指的是daemon。如果你要从别人的tftp服务器端上传/下载东西&#xff0c;就要用到tftp&a…

【转】Dicom中的Image Orientation/Position的理解

转自&#xff1a;Dicom中的Image Orientation/Position的理解 - 知乎 在DICOM中&#xff0c;是通过Image Position和Image Orientation来描述当前的图像和人体坐标系的相对位置的。 打开DCM文件时&#xff0c;会发现下边的两个tag (0020,0032) DS ImagePosition(Patient) &q…