linux 内核抓包功能实现基础(一)设计思路

linux平台下面已经有了抓包工具tcpdump, 非常经典,使用起来也非常方便。但是因为某些系统架构上或者其它方面的原因,有时候tcpdump并不能满足产品实际需要,公司的产品是电信运营商相关的软硬件集成产品,平常在使用的时候经常需要抓包来分析、定位问题,之前的抓包功能是基于tcpdump的基础上的,但是因为系统架构方面的原因,抓包功能始终无法抓到所有进出报文,每次只能抓到特定一部分报文,此外因为抓包之后生成的文件是临时存储在flash中,抓包文件大小就会受到系统存储空间制约,每次抓包只能抓去少量报文。对于电信级通信产品,吞吐量巨大,稍稍抓下包生成的文件大小就会超出限制。有了上述种种不便之处,决定开发一个新的抓包功能,基本功能要求有下面几点:

1. 能够抓到所有报文

2. 能够设置过滤条件进行抓包,包括端口、ip地址和协议类型(TCP、UDP、ARP和ICMP)

3. 具备远程抓包功能。

远程抓包功能就是存在一台抓包服务器,产品抓到的报文能够送到服务器上存储,这样能够使得抓包文件大小不受限制,只要远程服务器存储空间够大。想象这样一个场景:产品在机房运行,运维人员需要抓包分析问题的时候,只需要使用任意一台连接到机房产品的PC,开启远端抓包功能后,产品能够将报文发送到指定的PC机上,这时PC机就相当于抓包服务器,抓包文件大小只受PC存储空间限制。

这样的功能需要实现两个方面,一个是抓包服务器的实现,另一个就是抓包功能的实现。本篇博客以及接下来的博客就详细介绍相关功能的原理、实现以及所遇到的问题。

首先来看一下抓包功能的实现。在linux平台下面,内核抓包功能实现大致有两种(可能还有其它方式,有知道的同学还请告诉我),一种是借助netfilter框架,另一种是使用dev_add_pack注册协议处理函数。对了,这里讲的都是内核抓包,用户层也是可以抓的,使用lipcap就可以很方便的抓取,这个先不提,等讲到抓包服务器实现的时候再来将libpcap的使用。

我们使用的抓包方式是前一种,即netfilter框架,至于后面那一个 dev_add_pack方式,还有一些问题尚未解决,咱先讨论netfilter。

先简介一下netfilter框架,如下图:


netfilter在网络层(IP层)设置了五个hook点,如图中红色方框显示,

1.      NF_IP_PRE_ROUTING : (刚刚进入网络层的数据包通过此点)

2.      NF_IP_LOCAL_IN : (经路由查找后,送往本机的通过此检查点)

3.      NF_IP_FORWARD : (要转发的包通过此检测点)

4.      NF_IP_LOCAL_OUT : (本机进程发出的包通过此检测点)

5.      NF_IP_POST_ROUTING : (通过网络设备出去的包通过此检测点)

因为我们要抓进出的报文,所以分别在报文进出的入口(PRE_ROUTING)和出口(POST_ROUTING)添加了两个钩子函数,如上图绿色方框显示。有了这两个函数,偶就可以对进出的报文做处理了。怎么处理呢,对符合条件的报文该怎么抓呢,抓到之后又该怎么处理?先看下图:


这个图就回答了上面那几个问题,首先抓到报文后,要抓取其L2层以上数据,也就是MAC层之上,其次抓到之后,需要把它送出去,送到服务器上,这时候需要添加新的UDP、IP首部,用来设置远端服务器的地址信息等。UDP的数据部分就是抓到的报文。等到封装好之后,我们就可以把它发送出去,这时候远端服务器收到报文之后就可以读取了。就像下面这样:设置远端服务器地址192.168.11.12:1112,当抓到报文后就将报文复制一份发送到这个地址。下面数据部分可以看到以太网层、IP层首都被封装成UDP数据部分,这样当服务器端收到这样报文后,读取其数据部分,封装成wireshark格式就可以直接读取了。


今天先讲到这,下一篇博客将讨论netfilter的实现部分。

ps:创建了一个linux内核、应用程序开发讨论交流群,欢迎感兴趣的同学加入生气,群号:745510310,热烈欢迎哈