OS与抓包技术


近期在研究 libpcap 和 PF_RING,写篇文章作为总结。

PF_RING 使用

(To be continued)

Snort

https://www.snort.org/

Linux 网络栈

Linux 网络栈

Libpcap 抓包系统模型

Subsystems involved in the capturing process

上图为 libpcap 机制涉及的相关内容(简化);

Libpcap 的包捕获机制

上图为 libpcap 机制涉及的相关内容(详细);

tcpdump程序调用模型

上图标明基于 libpcap 的抓包工具和常规应用程序使用不同的包处理路径(注意:此处的 libpcap 是基于 PF_PACKET 的标准版本);

Elements involved in the capture process

这张图细化了 libpcap 的包处理路径(但没有明确指明 BPF 和 tap);

libpcap工作原理
packet path
抓包流程

以上三张图更加细化了 libpcap 的包处理路径,并明确给出了 libpcap 的组成部分有 BPF 和 tap ;但没有提及 libpcap 是在数据链路层上通过旁路进行的包处理,实际通过网络分接头(tap)从位于数据链路层的 NIC driver 中进行的数据包拷贝;

BPF and ZC BPF

上图描述了基于 BPF 和 ZC BPF 的标准 libpcap 实现是如何进行数据包读取的;

数据包接收

linux-server-packet-fig

这张图给出了很多信息:

  • 可能导致丢包问题的两处缓冲区: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 存在一个)

PF_RING 相关

Hardware Acceleration

硬件加速卡的实现:FPGA + NIC Memory Map 关键:专用、昂贵

PF_RING DNA

PF_RING DNA 对 Vanilla PF_RING 的改进:NIC NPU + NIC Memory Map 关键:常规 Commodity NIC 即可达到专用硬件加速卡效果;

PF_RING

PF_RING Internals

Socket Packet Ring (PF_RING)
PF_RING Internals

值得注意的一个细节:

  • netif_receive_skb() 对应 NAPI
  • netif_rx() 对应 No NAPI

PF_RING Packet Journey

PF_RING Packet Journey - 1
PF_RING Packet Journey - 2

libpcap v.s. PF_RING

PF_RING and legacy architecture

PF_RING 的地位和 PF_PACKET 是类似的,本质上都是向内核注册了一种 socket 类型;图中有一个地方画错了,即 Linux Network Stack 上方应该是非抓包的其它服务,如 telnet 和 tftp 等;

PF_RING and PF_PACKET under Linux

基于 PF_PACKET 和 PF_RING 两种方式下的包处理路径对比:明显 PF_RING 路径更短;这也就是为何 PF_RING 速度更快,更不容易丢包的原因;

另外,需要注意,Ethernet driver 和 Low-level packet reception 之间的 netif_rx()/netif_receive_skb() 分别对应了 No NAPI 和 NAPI ;而从 Ethernet driver 上进行包搬移操作的应该是之前提及的、作为 libpcap 组成部分之一的、网络分接头 tap ;

PF_RING is a replacement for PF_PACKET that not only uses memory mapping instead of processing expensive buffer copies from kernel space to userspace, but it also uses ring buffers making to transportation in a more efficient way.

NAPI v.s. TNAPI

NAPI

Enhanced NIC Drivers_Linux NAPI
Linux NAPI Limitations

上面两图说明:传统的 NAPI + RSS 方案存在问题,即 NAPI 对多 RX queue 是顺序轮询的,并且在 driver 和用户应用的接口层存在 Merge & Split 等资源竞争问题;

TNAPI

Enhanced NIC Drivers_TNAPI

上图说明:基于 TNAPI + PF_RING 解决了传统 NAPI 方案遇到的问题;

Explaiting PF_RING Multi-Queue_nProbe
nCap and Legacy

libpcap 抓包原理

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

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 过滤器根据用户已经定义好的过滤规则对数据包进行逐一匹配,匹配成功则放入内核缓冲区,进而传递给用户缓冲区,匹配失败则直接丢弃。如果没有设置过滤规则,所有数据包都将放入内核缓冲区,并传递给用户缓冲区。

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() 函数用于关闭网络设备,释放资源。

libpcap-mmap

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

  • libpcap 使用固定大小的存储缓冲器保持缓冲器来完成数据包从内核缓冲区到用户缓冲区的传递,而 libpcap-mmap 设计了一个大小可以配置的循环缓冲器,允许用户程序和内核程序同时对该循环缓冲器的不同数据区域进行直接的读取。
  • 在 libpcap 中,当网卡接收到一个数据包之后,网卡驱动程序通过 DMA 方式调用系统函数 netif_rx() 将数据包从网卡(Rx queue)拷贝到核心态内存,应用程序想访问位于核心态内存的数据时,就必须将数据包从核心态内存中拷贝到用户态内存中(即两次拷贝问题),这种方式会占用了很多系统资源,降低数据包捕获的性能以及对数据包的处理能力。而 libpcap-mmap 采用 MMAP 技术,建立核心态内存和用户态内存的映射,将系统分配给网卡设备文件的核心态内存映射到一块用户态内存,这样应用程序就可以通过系统函数 recvfrom() 把数据包从网卡设备文件对应的核心态内存上直接传送到用户态内存;

Libpcap is one of the more vastly open source library for packet capturing and uses by default PF_PACKET protocol in order to transfer the packets from the driver to the userspace.

It is the de facto library that facilitates the packet transition from kernel onto the userspace is libpcap. It provides an API for the programmer to select the capturing interface (device) and gives the ability to compile Linux Packet Filters (LPF) into the kernel for selective packet capturing based on the 5 tuple (protocol, source/destination IP address and source/destination port).

One important feature of PF_RING is the way it exchanges packets between user space and kernel: Monitoring applications usually access a library like libpcap to retrieve captured packets from the kernel. Libpcap is an abstraction from the operating systems’ capturing mechanisms and allows to run a capturing application on several operating systems without porting it to the special capturing architecture. Back in 2004, the then current libpcap version 0.9.8 used a copy operation to pass packets from the kernel to the user space on Linux. An unofficial patch against that libpcap version from Phil Woods existed, which replaced the copy operation by a shared memory area that was used to exchange packets between kernel and application. This modification will be called MMAP throughout the rest of the paper. PF RING uses a similar structure to exchange packets by default. Libpcap version 1.0.0, which was released in late 2008, is the first version that ships built-in shared memory (SHM) exchange support; hence the patch from Phil Woods is not longer necessary.


文章作者: sfc9982
版权声明: 本博客所有文章除特別声明外,均采用 CC BY-NC-ND 4.0 许可协议。转载请注明来源 sfc9982 !
  目录