主流数据包捕获技术的比较与分析

前言

在网络安全领域中数据包捕获技术的应用十分广 泛,在当前高速网络环境中,如何实时、高效、完整、快速捕获数据包是能否准确分析网络数据的基础和网络安全防护系统的关键技术。目前已经有从硬件/软件/软硬结合多种解决方案被不断的提出,用于提升数据包转发与捕获的性能。
本文主要目的从原理上分析比较目前主流的数据包捕获技术。写作思路:

  • 介绍传统数据包捕获技术-->分析其原理-->指出其存在的问题
  • 针对传统数据包捕获技术存在的问题给出解决方法-->介绍几种改善的数据包捕获技术

传统Linux数据包捕获技术流程和性能分析

传统Linux数据包捕获技术流程

传统的数据包捕获技术(典型的libpcap)是利用linux协议栈在数据链路层增加一个旁路处理,不干扰系统自身的网路协议栈的处理,对发送和接收的数据包通过Linux内核做过滤和缓冲处理,最后直接传递给上层应用程序。

libpcap数据包捕获流程

流程:

  1. 数据包到达网卡设备。网卡设备(NIC)依据配置进行DMA操作。( 「第1次拷贝」 :网卡寄存器->内核为网卡分配的缓冲区ring buffer)

  2. 第一次cpy完成后,网卡发送中断,唤醒处理器。网卡驱动程序通过 NAPI机制 从ring buffer中读取,填充内核skbuff结构( 「第2次拷贝」 :内核网卡缓冲区ring buffer->内核专用数据结构skbuff)

  3. 网卡驱动触发软中断调用netif_receive_skb来处理数据包:
    3.1 遍历嗅探器ptype_all,如果有注册抓包程序,由网络分接口进入BPF过滤器,将规则匹配的报文拷贝到系统内核缓存 ( 「第3次拷贝」 )。BPF为每一个要求服务的抓包程序关联一个filter和两个buffer。BPF分配buffer 且通常情况下它的额度是4KB the store buffer 被使用来接收来自适配器的数据;the hold buffer被使用来拷贝包到应用程序。
    3.2 调用handle_bridge处理网桥。
    3.3 调用handle_macvlan处理vlan。
    3.4 根据skb->protocol字段确定上层协议并提交给网络层处理,进入网络协议栈,进行高层处理。

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

网卡驱动触发软中断调用netif_receive_skb来处理数据包

传统Linux数据包捕获技术性能分析

研究者们发现,Linux内核协议栈在数据包的收发过程中,内存拷贝操作的时间开销占了整个处理过程时间开销的65%,此外层间传递的系统调用时间也占据了8%~10%。

协议栈的主要问题:

  1. 针对单个数据包级别的频繁的资源分配和释放。
    每当一个数据包到达网卡,系统就会分配一个分组描述符用于存储数据包的信息和头部,直到分组传送到用户态空间,其描述符才被释放。此外,sk_buff庞大的数据结构中的大部分信息对于大多数网络任务而言都是无用的。分配和释放描述符的过程在高达 14.88Mpps 环境下是一个显著的时间上的开销。此外,sk_buff庞大的数据结构中的大部分信息对于大多数网络 任务而言都 是无用的。每个分组的sk_buff的分配消耗近1200个CPU 周期,而释放需要1100个周期。事实上,对于一个大小为64B的分组,sk_buff相关操作消耗了整个接收处理的 CPU 利用率的63%。

  2. 流量的串行访问。
    现代网卡包括多个硬件的接收端扩展(receiver-side scaling, RSS)队列可以将分组按照五元组散列函数分配到不同的接收队列。使用这种技术,分组的捕获过程可以被并行化,因为每个RSS队列可以映射到一个特定的CPU核,并且可以对应相应的NAPI线程。这样整个捕获过程就可以做到并行化。
    但是问题出现在之上的层次,Linux中的协议栈在网络层和传输层需要分析合并的所有数据包:
    ①所有流量在一个单一模块中被处理,产生性能瓶颈;
    ②用户进程不能够从一个单一的RSS队列接收消息。
    这就造成了上层应用无法利用现代硬件的并行化处理能力,这种在用户态分配流量先后序列的过程降低了系统的性能,丢失了驱动层面所获得的加速。此外,从不同队列合并的流量可能会产生额外的乱序分组。


    瓶颈在Linux上层协议栈
  3. 从驱动到用户态的数据拷贝。
    从网卡收到数据包到应用取走数据的一次 DMA 过程中,存在至少2次数据包的复制:从驱动中 DMA 的访问内存到内核的数据包缓存,以及从内核数据包缓存到应用程序。一次复制操作的消耗取决于数据包长度,一般为500~2 000个 CPU 周期之间。上述过程中更糟糕的是,当数据包很小时,逐包复制的效率更加低下,这是由于每个操作恒定的开销引起的。

  4. 内核到用户空间的上下文切换。
    从应用程序的视角来看,它需要执行系统调用来接收每个分组.每个系统调用包含一次从用户态到内核态的上下文切换,随之而来的是大量的CPU时间消耗。在每个数据包上执行系统调用时产生的上下文切换可能消耗近1 000个CPU周期。

  5. 跨内存访问。
    例如,当接收一个64 B分组时,cache未命中造成了额外13.8%的CPU周期的消耗。另外,在一个基于NUMA的系统中,内存访问的时间取决于访问的存储节点。因此,cache未命中在跨内存块访问环境下会产生更大的内存访问延迟,从而导致性能下降。

提高捕获效率的技术

目前高性能报文捕获引擎中常用的提高捕获效率的技术

  1. 预分配和重用内存资源。
    开始分组接收之前,预先分配好将要到达的数据包所需的内存空间用来存储数据和元数据(分组描述符)。尤其在加载网卡驱动程序时就分配好 N 个描述符队列(每个硬件队列和设备一个)。
    同样,当一个数据包被传送到用户空间,其对应的描述符也不会被释放,而是重新用于存储新到达的分组.得益于这一策略,在每个数据包分配/释放所产生的性能瓶颈得到了消除.此外,也可以通过简化sk_buff的数据结构来减少内存开销。
  2. 数据包采用并行直接通道传递。
    为了解决序列化的访问流量,需要建立从RSS队列到应用之间的直接并行数据通道。这种技术通过特定的RSS队列、特定的CPU核和应用三者的绑定来实现性能的提升。Intel XL710 NIC 可以选用对称哈希,保证同一条流的往返数据包被分配到相同的CPU核上时,避免造成低效的跨核访问。
  3. 内存映射。
    使用这种方法,应用程序的内存区域可以映射到内核态的内存区域,应用能够在没有中间副本的情况下读写这片内存区域。用这种方式我们可以使应用直接访问网卡的DMA内存区域,这种技术被称为零拷贝.但零拷贝也存在潜在的安全问题,向应用暴露出网卡环形队列和寄存器会影响系统的安全性和稳定性 。
  4. 数据包的批处理。
    为了避免对每个数据包的重复操作的开销,可以使用对数据包的批量处理。这个策略将数据包划分为组,按组分配缓冲区,将它们一起复制到内核/用户内存.运用这种技术减少了系统调用以及随之而来的上下文切换的次数;同时也减少了拷贝的次数,从而减少了平摊到处理和复制每个数据包的开销。但由于分组必须等到一个批次已满或定时器期满才会递交给上层,批处理技术的主要问题是延迟抖动以及接收报文时间戳误差的增加。
  5. 亲和性与预取。
    由于程序运行的局部性原理,为进程分配的内存必须与正在执行它的处理器操作的内存块一致,这种技术被称为内存的亲和性。CPU亲和性是一种技术,它允许进程或线程在指定的处理器核心上运行。在内核与驱动层面,软件和硬件中断可以用同样的方法指定具体的CPU核或处理器来处理,称为中断亲和力。每当一个线程希望访问所接收的数据,如果先前这些数据已被分配到相同CPU核的中断处理程序接收,则它们在本地cache能够更容易被访问到。

目前几种高性能报文捕获引擎

  1. libpcap-mmap
    libpcap-mmap是对旧的libpcap实现的改进,新版本的libpcap基本都采用packet_mmap机制。PACKET_MMAP通过mmap,减少一次内存拷贝( 「第4次拷贝没有了」 ),减少了频繁的系统调用,大大提高了报文捕获的效率。
  2. PF_RING
    我们看到之前libpcap有4次内存拷贝。libpcap_mmap有3次内存拷贝。PF_RING提出的核心解决方案便是减少报文在传输过程中的拷贝次数。我们可以看到,相对与libpcap_mmap来说,pfring允许用户空间内存直接和rx_buffer做mmap。这又减少了一次拷贝 ( 「libpcap_mmap的第2次拷贝」 :rx_buffer->skb)。
    PF-RING ZC实现了DNA(Direct NIC Access 直接网卡访问)技术,将用户内存空间映射到驱动的内存空间,使用户的应用可以直接访问网卡的寄存器和数据。通过这样的方式,避免了在内核对数据包缓存,减少了一次拷贝( 「libpcap的第1次拷贝」 ,DMA到内核缓冲区的拷贝)。这就是完全的零拷贝。其缺点是,只有一个 应用可以在某个时间打开DMA ring(请注意,现在的网卡可以具有多个RX / TX队列,从而就可以在每个队列上同时一个应用程序),换而言之,用户态的多个应用需要彼此沟通才能分发数据包。
  3. DPDK
    pf-ring zc和dpdk均可以实现数据包的零拷贝,两者均旁路了内核,但是实现原理略有不同。pf-ring zc通过zc驱动(也在应用层)接管数据包,dpdk基于UIO实现。
    3.1 UIO+mmap 实现零拷贝(zero copy)
    UIO(Userspace I/O)是运行在用户空间的I/O技术。Linux系统中一般的驱动设备都是运行在内核空间,而在用户空间用应用程序调用即可,而UIO则是将驱动的很少一部分运行在内核空间,而在用户空间实现驱动的绝大多数功能。采用Linux提供UIO机制,可以旁路Kernel,将所有报文处理的工作在用户空间完成。
    3.2 UIO+PMD 减少中断和CPU上下文切换
    DPDK的UIO驱动屏蔽了硬件发出中断,然后在用户态采用主动轮询的方式,这种模式被称为PMD(Poll Mode Driver)。
    与DPDK相比,pf-ring(no zc)使用的是NAPI polling和应用层polling,而pf-ring zc与DPDK类似,仅使用应用层polling。
    3.3 HugePages 减少TLB miss
    在操作系统引入MMU(Memory Management Unit)后,CPU读取内存的数据需要两次访问内存。第一次要查询页表将逻辑地址转换为物理地址,然后访问该物理地址读取数据或指令。
    为了减少页数过多,页表过大而导致的查询时间过长的问题,便引入了TLB(Translation Lookaside Buffer),可翻译为地址转换缓冲器。TLB是一个内存管理单元,一般存储在寄存器中,里面存储了当前最可能被访问到的一小部分页表项。
    引入TLB后,CPU会首先去TLB中寻址,由于TLB存放在寄存器中,且其只包含一小部分页表项,因此查询速度非常快。若TLB中寻址成功(TLB hit),则无需再去RAM中查询页表;若TLB中寻址失败(TLB miss),则需要去RAM中查询页表,查询到后,会将该页更新至TLB中。
    而DPDK采用HugePages ,在x86-64下支持2MB、1GB的页大小,大大降低了总页个数和页表的大小,从而大大降低TLB miss的几率,提升CPU寻址性能。
    3.4 其它优化
    SNA(Shared-nothing Architecture),软件架构去中心化,尽量避免全局共享,带来全局竞争,失去横向扩展的能力。NUMA体系下不跨Node远程使用内存。
    SIMD(Single Instruction Multiple Data),从最早的mmx/sse到最新的avx2,SIMD的能力一直在增强。DPDK采用批量同时处理多个包,再用向量编程,一个周期内对所有包进行处理。比如,memcpy就使用SIMD来提高速度。
    cpu affinity:即 CPU 亲和性。
    3.5 DPDK VS PF-RING
    PF-RING的DNA工作方式下,网卡驱动NAPI的整体流程基本没变,依旧是由报文触发硬中断,软中断进行轮询收包,但DNA工作方式下,网卡的DMA缓冲被映射到了内核中PF-RING的环形缓冲上,而改环形缓冲又被映射到了用户态,故用户态应用程序可直接读取到网卡的报文。在本工作方式下,直接避免了报文的拷贝。在中断方面,PF-RING整体收包的流程依然采取报文触发硬中断,随后软中断进行轮询收包的方式。在始终维持大量报文到来的高速网络环境中,这种基于NAPI的异步中断式收包
    仍然会带来中断活锁以及较高的中断上下文切换开销。在系统调用方面,PF-RING的环形缓冲存在于内核态,当应用程序读取报文时需依赖系统调用从用户态读取内核态中的数据,故PF-RING存在系统调用的开销。DPDK开源时间较短,但是目前当下最热的数据转发技术,商用案例多且得到主流设备厂家的应用,有良好的产业生态链。DPDK借助于用户态I0,使轮询模式的网卡驱动运行在用户态接收报文,一切数据操作都发生在用户态,故与其他两种技术相比,基本避免了系统调用的使用以及用户态内核态上下文切换的开销。与NAPI即异步中断模式相比,在本驱动模式下,网卡的一切中断被关闭,由此消除了中断上下文的切换。每当网卡接收队列填满报文之后填写接收描述符,CPU只需轮询该描述符即可感知是否进行报文处理,该机制保证CPU只专注于对报文的轮询I/O与处理,提高了报文的接收与处理效率。在报文拷贝与存储方面,DPDK通过用户态核心库的缓冲区管理功能在用户态为报文存储提供了预分配内存缓冲区,再者通过环境抽象层EAL直接与硬件资源交互,将预分配内存缓冲区的地址直接写入到网卡寄存器中。由此,网卡将接收到的报文直接存储到了用户态的内存缓冲区中,实现了报文的零拷贝。PF-RINGDNA与DPDK都实现了真正的零拷贝,单纯就拷贝这一行为来说,二者的优化达到了同一程度。但是值得注意的是,由于捕获的报文进行入侵检测分析,故在后期会对存储报文内存进行频繁的读取,相比于PF-RTNG使用常规内存分页技术,DPDK在预分配内存时使用的Hugepage技术,在后期数据处理阶段中,可显著提高内存的访问效率。
  4. Netmap
    4.1 Netmap是一个高性能收发原始数据包的框架,由Luigi Rizzo等人开发完成,其包含了内核模块以及用户态库函数。其目标是,不修改现有操作系统软件以及不需要特殊硬件支持,实现用户态和网卡之间数据包的高性能传递。数据包不经过操作系统内核进行处理,用户空间程序收发数据包时,直接与网卡进行通信。在Netmap框架下,内核拥有数据包池,发送环接收环上的数据包不需要动态申请,有数据到达网卡时,当有数据到达后,直接从数据包池中取出一个数据包,然后将数据放入此数据包中,再将数据包的描述符放入接收环中。内核中的数据包池,通过mmap技术映射到用户空间。用户态程序最终通过netmap_if获取接收发送环netmap_ring,进行数据包的获取发送。
    与硬件解耦 ,只需要对网卡驱动程序稍微做点修改就可以使用此框架(几十行行),传统网卡驱动将数据包传递给操作系统内核中协议栈,而修改后的数据包直接放入Netmap_ring供用户使用。 虚拟交换机场景下,使用Netmap可以实现不同网卡间高效数据转发,将一个网卡的数据放到另外一个网卡上时,只需要将接收环中的packet的描述符放入发送环,不需要拷贝数据,实现数据包的零拷贝。
    4.2 简单 对比 netmap 和 DPDK。
    可能唯一相同之处就是: 将 NIC 的 rx_ring_buffer 和 tx_ring_buffer,映射到 user-level,直接在 user-level 进行 packet RX/TX。
    • 相比 DPDK 来说,netmap 并不能算作一个丰富的SDK。
    • 使用netmap,需要很多手动操作的细节,比如说 手动修改 netmap_shadow_ring,不是很方便
    • rx_/tx_ring_buffer 都是由 netmap.ko 预分配,user-level application 不能动态分配,只能使用,而已。
    • netmap 并不是在 user-level 实现 driver (__ DPDK 的 PMD 是在 user-level )。 而是,在原本的 kernel driver 上打 patch,加入 netmap 自定义的逻辑。Linux 中仍然会有 "ethX" 这样的 "net_device"。 只不过 Linux stack 无法直接向其收发packet,罢了。但是 "ifconfig" 之类的东西,还是可以用的。
    • 仍然会有 中断。对于 packet RX,仍然是 RX IRQ handler -> BH napi_rx_poll(),对于 packet TX,仍然是 TX-DONE IRQ handler -> BH napi_tx_done_poll()。 只不过,netmap 会在 poll() 中加入自定义的逻辑,替换掉driver 原本的逻辑。
    • netmap 只是将 ring_buffer 映射到user-level。但并不将 NIC hw_ring_descriptors 映射到 user-level。 每种不同的NIC 的 descriptor 格式,都不一样。因为设计上的考虑,想要对user-level application表现得common 和 generic ,就不能映射。 几乎全部 NIC HW offload features,比如说 VLAN offload, checksum offload, TSO, 等,都需要特别设置 hw_ring_descriptors 的一些fields。netmap 完全不考虑这些 NIC HW offload features。
    • netmap 也没有像 DPDK 那样规定 lcore thread model 和 进行packet RX/TX时的 multi-thread best practice。对于 netmap来说,thread, priority, CPU affinity, 等等,都不是 netmap 本身规定的内容,而完全由 使用netmap的 programmer 来控制。
    • netmap 只是进行最简单的 packet RX/TX。没有其他任何对 packet 进行处理的 library。比如说,DPDK 提供了对 packet data 进行处理的 GRO, GSO, IP分片/重组,QoS,加解密,等等等 library。 但是,这些都不是 netmap 的内容。


      高性能包捕获方案的比较

综上所述,DPDK高速报文捕获技术更加适合于作为高速报文捕获机制的基础解决方案。


参考:
https://blog.csdn.net/gengzhikui1992/article/details/103142848
https://blog.csdn.net/xzhao28/article/details/109112898

你可能感兴趣的:(主流数据包捕获技术的比较与分析)