linux 下的 包过滤器 BPF

一, 导论

    BPF(Berkeley Packet Filter)伯克利包过滤器。 是在linux 平台下的一个包过滤器。使用此过滤器可以在socket编程时非常方便的实现各种过滤规则。

    首先,要确保从socket中读取的是packet,也就是说是 MAC头+IP头+TCP/UDP头。

    关于BPF的相关介绍可以查看英文文档:http://www.gsp.com/cgi-bin/man.cgi?section=4&topic=bpf#1

二, BPF的使用

    首先来看一段实际应用中的代码:

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

	// tcpdump -dd ether proto 0x8033
	struct sock_filter bpf_code [] = {
		{0x20, 0, 0, 0x0000000c},
		{0x15, 0, 1, 0x00000033},
		{0x6, 0, 0, 0x00000200},
		{0x6, 0, 0, 0x00000000}
	};
	if (NULL == p) 
	{
		return -1;
	}

	// init filter settings 
	Filter.len =4;
	Filter.filter = bpf_code;

	//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); 

	....
}

以上代码首先定义并初始化了一个bpf过滤器 Filter, 然后将其 SO_ATTACH_FILTER 到了socket 上。

这里用到了两个结构体:sock_fprog和sock_filter:

sock_fprog

struct sock_fprog
{
        unsigned short      len;
        struct sock_filter    *filter;
}
sock_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 bpf_code [] = {
		{0x20, 0, 0, 0x0000000c},
		{0x15, 0, 1, 0x00000033},
		{0x6, 0, 0, 0x00000200},
		{0x6, 0, 0, 0x00000000}
	};
下面将分析这一串数字是如何产生的,以及它的意义。

三, BPFcode 生成方法

    在注释中有一段提示 tcpdump -dd ether proto 0x8033。 

tcpdump 是linux 中调试网络的一个工具, 实际上tcpdump 就是用利用BPF原理编写的一个工具,所以tcpdump 提供了一个生成bpf code 的一个命令行:

linux 下的 包过滤器 BPF_第1张图片

这段数字的意义就是过滤以太网协议中类型是 0x8033的数据包(某某IT公司的产品自定义的一个数据包)。这段数字到底实现了什么功能呢? tcpdump 提供了 -d 选项来阐述这段数字的意义:

linux 下的 包过滤器 BPF_第2张图片

分析这段代码可知,

 ldh 是高位加载, 即从帧的第12位开始加载进内存,第12位就是去除6位src mac 与 6位 dst mac 的数据报类型字段。

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

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

小结:

        细心的朋友可能会发现,bpf_code 数组的第三行最后一列 0x00000060 与源代码中的 0x00000020 并不一样!这是因为tcpdump 的默认返回字节是96个字节, 而实际需要抓取512(0x00000200, 16*16*2)个字节的数据。关于这点给tcpdump 指定长度字段-s就可以了:

linux 下的 包过滤器 BPF_第3张图片

      linux 下的 BPF 是包过滤的利器, 结合tcpdump 工具,能非常快速的实现各种个性化的包过滤需求! 



你可能感兴趣的:(socket)