在虚拟化环境下镜像流量交给suricata分析,遇到了文件还原不完整的问题,同环境下使用tcpdump抓包查看可以看到捕获的数据包存在大包被截断的情况。这里分析一下为什么tcpdump会出现数据包被截断的情况以及什么条件下会触发该情况,suricata的问题不完全一致,但是基本思路是一样的。

虚拟机操作系统:CentOS Linux release 7.5.1804
参考libpcap版本:Date: Tue Nov 5 00:10:15 2019 -0800, 79817f

数据包截断

tcpdump捕获的文件查看如图,可以看到捕获到的数据包不完整。

tcpdump捕获文件

截断发生条件

因为所处虚拟化环境,外部转发进大数据包的情况比较多样,暂时不考虑外部,仅关注转发进大数据包后本地捕获被截断的情况,这里可以用http下载一个较大文件模拟。

  1. tcpdump参数中指定特定网卡,比如。这里如果指定网卡为any则不会发生截断。
    sudo tcpdump -i eth0 -nn tcp port 9090 -w longin.pcap
  2. 网卡参数中gro、lro、tso、gso等各种offload相关配置均为off状态。suricata在启动时会主动设置相关配置到off。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    [huyu@bogon ~]$ ethtool -k eth0 | grep offload
    tcp-segmentation-offload: off
    udp-fragmentation-offload: off
    generic-segmentation-offload: off
    generic-receive-offload: off
    large-receive-offload: off [fixed]
    rx-vlan-offload: off [fixed]
    tx-vlan-offload: off [fixed]
    l2-fwd-offload: off [fixed]
    hw-tc-offload: off [fixed]
    rx-udp_tunnel-port-offload: off [fixed]

截断原因

在截断发生条件的判断中已经发现tcpdump中指定网卡与设定为any时表现不同,对比两种参数运行时strace记录的系统调用区别,发现一个系统调用的参数有明显区别。

1
setsockopt(3, SOL_PACKET, PACKET_RX_RING, {block_size=4096, block_nr=1310, frame_size=1600, frame_nr=2620}, 16) = 0

这里与libpcap捕获数据包的方式有关,其使用AF_PACKET捕获数据包,并使用mmap映射一个环形缓冲区用于保存内核拷贝给它的数据包,参考PACKET_MMAP
环形缓冲区的参数中需要设置每个数据包的最大尺寸,也就是frame_size,如果设置的尺寸小于数据包实际尺寸,那么内核在拷贝数据包时只能做截断处理。

libpcap中该设置代码位于pcap-linux.c4139行函数create_ring中,在选择frame_size大小的代码中注释也说明了其选择逻辑。

  • 首先其不会完全按照snapshot length参数设置frame_size,因为这会导致缓冲区中可以保存的数据包数量过少,进而导致捕获性能降低。
  • 其会在snapshot length和可能的最大包尺寸中选择一个较小的值作为frame_size。问题就出在这个“可能的最大数据包尺寸”。
  • 当gro及lro和其他offload相关配置均为关闭时,libpcap认为该以太网卡不会收到尺寸大于MTU的数据包,因此frame_size会设置到一个比较小的值。libpcap判断offload配置的函数为iface_get_offload

libvirt虚拟化环境下,虚拟机对应的tap设备如果没有关闭gso和tso,虚拟机网卡就会收到大包,这里涉及到linux虚拟网络设备的实现,目前暂不分析。

处理方式

截断发生的条件和原因已经找到,避免截断的方式也就很多样了。需要说一下是,suricata文档中明确说明需要关闭网卡的各种offload特性,是为了避免收到大数据包,因为大数据包会影响其对dsize的判断。