标签:
转载 |
分类: 网络/p2p |
Linux 系统随着版本的不同,所支持的捕获机制也有所不同。
2.0 及以前的内核版本使用一个特殊的 socket 类型 SOCK_PACKET,调用形式是 socket(PF_INET, SOCK_PACKET, int protocol),但 Linux 内核开发者明确指出这种方式已过时。Linux 在 2.2 及以后的版本中提供了一种新的协议簇 PF_PACKET 来实现捕获机制。PF_PACKET 的调用形式为 socket(PF_PACKET, int socket_type, int protocol),其中 socket 类型可以是 SOCK_RAW 和 SOCK_DGRAM。SOCK_RAW 类型使得数据包从数据链路层取得后,不做任何修改直接传递给用户程序,而 SOCK_DRRAM 则要对数据包进行加工(cooked),把数据包的数据链路层头部去掉,而使用一个通用结构 sockaddr_ll 来保存链路信息。
使用 2.0 版本内核捕获数据包存在多个问题:首先,SOCK_PACKET 方式使用结构 sockaddr_pkt 来保存数据链路层信息,但该结构缺乏包类型信息;其次,如果参数 MSG_TRUNC 传递给读包函数 recvmsg()、recv()、recvfrom() 等,则函数返回的数据包长度是实际读到的包数据长度,而不是数据包真正的长度。Libpcap 的开发者在源代码中明确建议不使用 2.0 版本进行捕获。
相对 2.0 版本 SOCK_PACKET 方式,2.2 版本的 PF_PACKET 方式则不存在上述两个问题。在实际应用中,用户程序显然希望直接得到"原始"的数据包,因此使用 SOCK_RAW 类型最好。但在下面两种情况下,libpcap 不得不使用 SOCK_DGRAM 类型,从而也必须为数据包合成一个"伪"链路层头部(sockaddr_ll)。
- 某些类型的设备数据链路层头部不可用:例如 Linux 内核的 PPP 协议实现代码对 PPP 数据包头部的支持不可靠。
- 在捕获设备为"any"时:所有设备意味着 libpcap 对所有接口进行捕获,为了使包过滤机制能在所有类型的数据包上正常工作,要求所有的数据包有相同的数据链路头部。
struct socket_filter
{
}
匹配序列很像一个汇编程序,有其自身的操作码,操作数以及分支跳转功能,于是这段匹配序列的执行过程自然就类似一个冯诺依曼机器上单进程的执行绪了,它的本质从执行上讲是一个状态机(从数据角度讲,进程又是一个过滤器,它的名字恰就是过滤器...),很显然其实现应该是一个状态驱动的循环:
while(序列中还有匹配){
}
看看linux实现的代码,它基本就是这么实现的:
int sk_run_filter(struct sk_buff *skb, struct sock_filter *filter, int flen)
{