如何在Linux编程中像tcpdump一样过滤报文?——BPF

本文深入探讨了BPF(Berkeley Packet Filter)在Linux下的应用,特别是在网络报文抓取和过滤中的作用。BPF作为一种高性能的包过滤工具,不仅用于tcpdump等网络分析软件,还可在代码中实现精准的网络数据筛选。文章详细解析了BPF的字节码构造及如何在socket层面设置过滤器,揭示了BPF强大的过滤能力。

在linux下使用tcpdump进行报文抓取是理解和分析网络信息交互过程的重要步骤。相信有不少同学在设计网络程序时有过这样的冲动——能否在代码中也像tcpdump一样过滤获得我想要的报文呢?答案当然是“没问题”。这里介绍的BPF就是编程中使用的工具,当然似乎tcpdump也是利用该工具来实现的。

一、关于BPF

BPF(Berkeley Packet Filter)伯克利包过滤器。其最初构想提出于 1992 年,其目的是为了提供一种过滤包的方法,并且要避免从内核空间到用户空间的无用的数据包复制行为。它最初是由从用户空间注入到内核的一个简单的字节码构成,它在那个位置利用一个校验器进行检查 —— 以避免内核崩溃或者安全问题 —— 并附着到一个套接字上,接着在每个接收到的包上运行。几年后它被移植到 Linux 上,并且应用于一小部分应用程序上(例如,tcpdump)。其简化的语言以及存在于内核中的即时编译器(JIT),使 BPF 成为一个性能卓越的工具。

二、使用BPF

1.  预置条件:创建socket,并确保从socket中读取的是packet,也就是说是 MAC头+IP头+TCP/UDP头。

2.  参考说明:http://www.gsp.com/cgi-bin/man.cgi?section=4&topic=bpf#1

3.  使用步骤:

                    a.  创建socket                   

                    b.  初始化BPF过滤器

                    c.  使用setsockopt将过滤器SO_ATTACH_FILTER 到了socket 上

      注:a和b无先后顺序。

4.  代码分析:

//dump arp 0x0806
struct sock_filter CODE_BPF [] = {
		{0x20, 0, 0, 0x0000000c},
		{0x15, 0, 1, 0x00000806},
		{0x6, 0, 0, 0x00040000},
		{0x6, 0, 0, 0x00000000}
};

int packet_handle_init(struct lib_cap *p)
{
	int sock = -1;
	struct sock_fprog Filter;
	struct sockaddr_ll sll;

	if (NULL == p) 
	{
		return -1;
	}
 
	// init filter settings 
	Filter.len =4;
	Filter.filter = CODE_BPF;
 
	//set default value
	p->ifindex = -1;
	p->fd = -1;
	p->buffer = NULL;
	p->buf_len = 0;
 
	if ( (sock = socket(PF_PACKET, SOCK_RAW, htons(ETHERTYPE_SADP))) < 0)
	{
		return -1;
	}
 
	if ( setsockopt(sock, SOL_SOCKET, SO_ATTACH_FILTER, &Filter, sizeof(Filter)) < 0); 
}

其中sock_fprog和sock_filter为BPF库特固定的结构体,其定义如下:

struct sock_fprog
{
        unsigned short      len;
        struct sock_filter    *filter;
}


struct sock_filter
{
	__u16	code;	/* actual filter code */
	__8		jt;		/* jump true 		*/
	__8		jf;		/* jump false		*/
	__u32	k;		/* Generic multiuse field */
}

三、字节码分析

什么是字节码?即上文代码中定义的结构体struct sock_filter CODE_BPF初始化编码,

//dump arp 0x0806
struct sock_filter CODE_BPF [] = {
		{0x20, 0, 0, 0x0000000c},
		{0x15, 0, 1, 0x00000806},
		{0x6, 0, 0, 0x00040000},
		{0x6, 0, 0, 0x00000000}
};

其具体的含义,需要借助于tcpdump来说明:

上述代码的生成,使用命令tcpdump -dd ether proto 0x0806

{ 0x20, 0, 0, 0x0000000c },
{ 0x15, 0, 1, 0x00000806 },
{ 0x6, 0, 0, 0x00040000 },
{ 0x6, 0, 0, 0x00000000 }

其意义即过滤以太网协议中类型是0x0806(arp)的数据包。这段数字到底实现了什么功能呢?-d 选项来可获得其意义:

tcpdump -d ether proto 0x0806

(000) ldh    [12]
(001) jeq    #0x806         jt 2  jf 3
(002) ret    #262144
(003) ret    #0

查阅相关资料可知:

ldh:高字节加载, 即从帧第12字节起载入内存,即IP报文中12字节src与dst mac后的两字节,如下图所示

jeq: 如果类型字段是 0x0806 的话就返回 262144个字节,不是的话就返回 0字节。

至此就已达到过滤类型为0x0806数据包的目的。

四、扩展

有过一些tcpdump使用经验的同学可能会发现,CODE_BPF数组的有时候非常复杂,其实这与其过滤条件有关,如:

tcpdump ip -dd -s 2048 host 192.168.1.2

tcpdump ip -d -s 2048 host 192.168.1.2

不同的过滤条件会有不同的过滤BPF code。

通过以上分析和实验过程,可以清楚的看到,tcpdump的强大远远不止抓包分析,其本身也有很多值得去研究的价值。

 

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值