libpcap-mmap分析(五)

一、架构图

1、Linux 网络栈
在这里插入图片描述
2、Libpcap 抓包系统模型
在这里插入图片描述
3、libpcap 机制涉及的相关内容在这里插入图片描述
4、基于 libpcap 的抓包工具和常规应用程序使用不同的包处理路径(注意:此处的 libpcap 是基于 PF_PACKET 的标准版本)
在这里插入图片描述
5、细化了 libpcap 的包处理路径(但没有明确指明 BPF 和 tap)
在这里插入图片描述
6、更加细化了 libpcap 的包处理路径,并明确给出了 libpcap 的组成部分有 BPF 和 tap ;但没有提及 libpcap 是在数据链路层上通过旁路进行的包处理,实际通过网络分接头(tap)从位于数据链路层的 NIC driver 中进行的数据包拷贝;
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
7、基于 BPF 和 ZC BPF 的标准 libpcap 实现是如何进行数据包读取的
在这里插入图片描述

二、数据包接收

在这里插入图片描述
这张图给出了很多信息:

  • 可能导致丢包问题的两处缓冲区:Ring Buffer 和 poll_queue (per CPU);其中 poll_queue 的 size 由内核参数 net.core.netdev_max_backlog 决定,默认为 1000 ;
  • 数据包(由网卡驱动程序)通过 DMA 方式从 NIC Rx queue 拷贝到操作系统内核内存中(Ring Buffer中保存的是内存索引);
  • IRQ 和 softirq

在这里插入图片描述
这张图提供的有用信息:

  • 图中 Poll List 即前图中的 Poll_queue (per CPU) ;
  • Poll List 每个 CPU 一个,也称之为backlog ,注意和图中 Socket backlog 进行区分;
  • 图中给出了 IP 层和 TCP 层的位置,以及两种类型backlog 所处位置;

在这里插入图片描述
上图给出了(单队列网卡)包处理相关的更多信息:

  • 物理层:NIC RX queue、NIC memory 和 NIC controller ;
  • 链路层:NIC driver、NAPI、Ring Buffer
  • 网络层:PF_PACKET 数据包通过 DMA 方式从物理层 NIC RX queue 拷贝到操作系统内存中;
  • NIC memory 中维护的是用于在 Ring Buffer 中进行位置定位的指针;而 Ring Buffer 中的内容为指向保存 DMA 拷贝包的操作系统内存的指针; NIC controller 通过 NIC memory 中的信息定位 Ring Buffer 的 head 位置,之后触发 IRQ 给 CPU0 ; CPU0 通过 NIC driver、NAPI 机制、PF_PACKET,以及 softirq 完成对操作系统内存中数据包的获取;
    在这里插入图片描述

多队列网卡与单队列网卡的差别:

  • NIC memory 维护多个 Ring Buffer 的 head/tail 索引信息;
  • 链路层中包含多个 Ring Buffer(针对每个 RX queue 存在一个)

三、libpcap抓包原理

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

3.1 libpcap 工作原理

libpcap 主要由两部份组成:

  • 网络分接头(Network Tap)
  • 数据过滤器(Packet Filter)

网络分接头从网络设备驱动程序(NIC driver)中收集数据拷贝,过滤器决定是否接收该数据包。Libpcap 利用 BSD Packet Filter (BPF) 算法对网卡接收到的链路层数据包进行过滤。

BPF 算法的基本思想:在有 BPF 监听的网络中,网卡驱动将接收到的数据包复制一份交给 BPF 过滤器,过滤器根据用户定义的规则决定是否接收此数据包以及需要拷贝该数据包的哪些内容,然后将过滤后的数据交给与过滤器相关联的上层应用程序。

libpcap 的包捕获机制:在数据链路层加一个旁路处理。当一个数据包到达网络接口时,libpcap 首先利用已经创建的类型为 PF_PACKET 的 Socket ,从位于链路层中的 NIC driver 中获得数据包的拷贝,再通过 Tap 函数将数据包发给 BPF 过滤器。BPF 过滤器根据用户已经定义好的过滤规则对数据包进行逐一匹配,匹配成功则放入内核缓冲区,进而传递给用户缓冲区,匹配失败则直接丢弃。如果没有设置过滤规则,所有数据包都将放入内核缓冲区,并传递给用户缓冲区。

3.2 libpcap 的抓包框架

  • pcap_lookupdev() 函数用于查找网络设备,返回可被 pcap_open_live() 函数调用的网络设备名指针。
  • pcap_open_live()函数用于打开网络设备,并且返回用于捕获网络数据包的数据包捕获描述字。对于此网络设备的操作都要基于此网络设备描述字。
  • pcap_lookupnet() 函数获得指定网络设备的网络号和掩码。
  • pcap_compile()函数用于将用户制定的过滤策略编译到过滤程序中。
  • pcap_setfilter() 函数用于设置过滤器。
  • pcap_loop() 函数pcap_dispatch() 函数用于捕获数据包,捕获后还可以进行处理,此外 pcap_next() 和 pcap_next_ex()两个函数也可以用来捕获数据包。
  • pcap_close() 函数用于关闭网络设备,释放资源。

3.3 libpcap-mmap

libpcap-mmap 是 libpcap 的一个改进版本,它们捕获数据包的结构相同。不同的地方主要有以下两点:

libpcap 使用固定大小的存储缓冲器保持缓冲器来完成数据包从内核缓冲区到用户缓冲区的传递,而 libpcap-mmap 设计了一个大小可以配置的循环缓冲器,允许用户程序和内核程序同时对该循环缓冲器的不同数据区域进行直接的读取。

在 libpcap 中,当网卡接收到一个数据包之后,网卡驱动程序通过 DMA 方式调用系统函数 netif_rx() 将数据包从网卡(Rx queue)拷贝到核心态内存,应用程序想访问位于核心态内存的数据时,就必须将数据包从核心态内存中拷贝到用户态内存中(即两次拷贝问题),这种方式会占用了很多系统资源,降低数据包捕获的性能以及对数据包的处理能力。而 libpcap-mmap 采用 MMAP 技术,建立核心态内存和用户态内存的映射,将系统分配给网卡设备文件的核心态内存映射到一块用户态内存,这样应用程序就可以通过系统函数 recvfrom() 把数据包从网卡设备文件对应的核心态内存上直接传送到用户态内存;

原文链接:https://my.oschina.net/moooofly/blog/898798