libpcap在libvirt虚拟化环境下捕获数据包不完整的一种情况分析
在虚拟化环境下镜像流量交给suricata分析,遇到了文件还原不完整的问题,同环境下使用tcpdump抓包查看可以看到捕获的数据包存在大包被截断的情况。这里分析一下为什么tcpdump会出现数据包被截断的情况以及什么条件下会触发该情况,suricata的问题不完全一致,但是基本思路是一样的。
虚拟机操作系统:CentOS Linux release 7.5.1804
参考libpcap版本: Date: Tue Nov 5 00:10:15 2019 -0800, 79817f 数据包截断
tcpdump捕获的文件查看如图,可以看到捕获到的数据包不完整。
tcpdump捕获文件 截断发生条件
因为所处虚拟化环境,外部转发进大数据包的情况比较多样,暂时不考虑外部,仅关注转发进大数据包后本地捕获被截断的情况,这里可以用http下载一个较大文件模拟。 tcpdump参数中指定特定网卡,比如。这里如果指定网卡为any则不会发生截断。
sudo tcpdump -i eth0 -nn tcp port 9090 -w longin.pcap 网卡参数中gro、lro、tso、gso等各种offload相关配置均为off状态。suricata在启动时会主动设置相关配置到off。 [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记录的系统调用区别,发现一个系统调用的参数有明显区别。 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.c 4139行函数 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的判断