Linux 内核抓包功能实现基础(五) 常见问题解析

之前在部门产品上开发了内核抓包模块,基于openwrt平台,通过netfilter框架实现相关功能。核心功能就是在netfilter 的PRE_ROUTING 和 POST_ROUTING链上增加两个钩子函数,实现对报文的匹配,复制和发送功能。功能已经上线三个月,根据反馈结果来看,基本的抓包功能正常,不过也有几个问题需要解决一下,之前陆陆续续写了四篇博客介绍相关功能的开发,在本篇就把产品实际应用中遇到的问题说一下。

1. mac地址缺失问题。

抓包是要抓到报文的所有数据,包括mac首部,ip首部,udp首部以及数据层。如果是进入本机的报文,是含有mac地址的,但是对于本机出去的报文,在POST_ROUTING这个点上,是还没有填充mac首部的,在第四篇中介绍了如何手动查找邻居缓存来填充mac地址,但是实际应用中仍然会出现部分报文没有mac地址,有时候看到这样的报文还是挺困扰的。没有查到邻居缓存,一方面可能是真的还没有邻居缓存,另一方面可能是查找方式不对,因为如果是vlan环境下,还真的查不到!我查找调用的接口是 ip_route_me_harder函数。在这种情况下,想了其它一个方法来work around。既然查找邻居缓存找不到,那就自己建立一个mac缓存。方式就是当抓包开启的时候,在pre_routing链上对每一个报文记录它的来源ip, 来源mac和目的ip,目的mac,然后建立一个链表来缓存,这样当在post_routing上抓包找不到mac的时候就根据目的地址查找mac缓存,地址匹配就可以了。实际测试功能正常。有一点值得注意,在mac缓存的时候千万不要将广播报文也缓存下来,这样只会浪费空间,因为广播报文mac地址已知没必要记录。

2. 报文发送带来内核奔溃问题

抓包的发送接口有两个,一个是ip_local_out,另一个是dev_queue_xmit,后一个接口需要手动填充L2层首部,前不久测试发现一个问题,就是在调用dev_queue_xmit的时候,系统很容易重启,根据重启日志发现是内核奔溃,且每次报的内核错误都不一样,都是和dma相关,和系统部的工程师讨论了一下,发现是网卡驱动处理大包异常导致,抓包的时候,如果是tcp报文,它的报文往往会超过mtu值,这样的报文是无法在网络上传输的,需要分片。网卡有个TSO机制,就是专门利用硬件处理tcp分片。我们抓包的时候是封装在udp里,网卡不支持udp封片,导致收到超大报文后系统不稳定直接挂掉。后来的处理方式是在调用dev_queue_xmit前判断报文大小,如果报文太大的话就调用系统接口ip_loca_out,让协议栈来处理分片。

3. vlan报文

之前遇到一个问题,就是开启抓包后服务器端没收到报文,但是看log发现抓包模块的确有发送出去。使用tcpdump抓包分析才知道在复制报文的时候连同原始报文vlan标签也复制过去,导致发出去的报文是vlan报文,这样服务器肯定收不到,改了一下流程,抓包的时候手动设置skb->vlan_tci = 0;

猜你喜欢

转载自blog.csdn.net/fuyuande/article/details/82718335