一个险恶bug的深入分析
作者:网络转载 发布时间:[ 2012/6/7 13:16:27 ] 推荐标签:
现在有两种方法可以从内核中拿到数据包:
• “以前的方法”: 对每个数据包的文件描述符调用recvfrom函数。在老版本的内核上,只有这一个函数可用。
• “新方法”:调用poll函数会通知libpcap有一组数据包到达,在内核与libpcap的共享内存中等待读取。比起“老办法”这种方法效率更高(使用的系统调用更少),在近大多数的内核包括Debian Lenny都支持这种办法。
结果是,尽管Debian Lenny的内核支持“新方法”实现的AF_PACKET,但是相应的libpcap却不支持。这意味着tcpdump(依赖于libpcap)只能逐次逐个地从内核中取得数据包。
更新版本的libpcap默认使用“新方法”从内核读取数据包。因为Lenny支持这种用法,我试着构建了一个更新版本的libpcap并修改了tcpdump。在修改过的Lenny内核上测试这个修改,我看到当我在bond上的物理设备进行嗅探时,数据包从RX路径流出。如果我把新的libpcap修改成使用“以前的方法”搜集数据包,没有数据包从RX路径流出。
这意味着在使用“以前的方法”时,要么AF_PACKET有bug,要么多版本的libpcap有bug。
if语句
经过数小时痛苦地阅读代码,我找了一条if语句可以控制libpcap使用“以前的方法”读取数据包。
// From pcap_read_packet in pcap-linux.c:
if (handle->md.ifindex != -1 &&
from.sll_ifindex != handle->md.ifindex)
return 0;
这条if语句会进行索引判断,对比从内核中读取数据包使用的索引号是否和用户通知libpcap监控网络设备使用的索引号相同。如果索引号匹配失败,pcap_read_packet函数会直接返回,不再调用libpcap提供的回调函数。
这段代码是为了防止内核中可能出现的竞争情况,比如虽然已经创建了socket但还没有来得及绑定到一个特定设备上,这种情况下AF_PACKET会在调用socket和bind中间把所有的数据包存储到队列中。
然而,当数据包发向作为bond设备一部分的物理设备时这个检查会失败。
用户向libpcap请求对这个物理设备监控,但正如我们上面看到的,当有数据包到来时内核会用netif_receive_skb中的bond设备指针覆盖dev结构。这样会造成bond设备的索引和物理设备的索引不一致。
这条if语句是为什么发送的数据包在修改了内核以后仍然无法被类似tcpdump或者边界流量测量器捕获。
这个检查在从内核读取数据包的“新方法”中不存在,因为支持新mmap方法的内核不会产生上述代码需要防止的竞争条件。因此,把一个更新版本的libpcap链接到tcpdump上(内核已经过修改)能看到发送给bond上物理设备的数据包。
这个检查在当前版本的libpcap上仍然存在。
总结
计算机能够正常运行真是个奇迹。
相关推荐
更新发布
功能测试和接口测试的区别
2023/3/23 14:23:39如何写好测试用例文档
2023/3/22 16:17:39常用的选择回归测试的方式有哪些?
2022/6/14 16:14:27测试流程中需要重点把关几个过程?
2021/10/18 15:37:44性能测试的七种方法
2021/9/17 15:19:29全链路压测优化思路
2021/9/14 15:42:25性能测试流程浅谈
2021/5/28 17:25:47常见的APP性能测试指标
2021/5/8 17:01:11