BPF(Berkeley Packet Filter)伯克利包过滤器。 BPF允许用户空间程序将一个过滤(filter)附加到任何的套接字(socket)上面用来允许或不允许某些类型的数据通过socket。
通常,大多数对包 socket 上 socket filter 的使用已经被 libpcap 高层次的语法所覆盖,作为应用开发人员应当坚持使用。libpcap wraps是它的封装层。
除非:
用户空间的应用程序 include
头文件包含以下的相关结构:
struct sock_filter { /* Filter block */
__u16 code; /* Actual filter code */
__u8 jt; /* Jump true */
__u8 jf; /* Jump false */
__u32 k; /* Generic multiuse field */
};
这样的结构被组装成一个4元数组,包含:code、jt、jf和K值。jt和jf是跳转偏移量,k是一个通用值提供给code使用。
struct sock_fprog { /* Required for SO_ATTACH_FILTER. */
unsigned short len; /* Number of filter blocks */
struct sock_filter __user *filter;
};
其中 *filter 指向结构为 struct sock_filter 的 BPF过滤代码(code)。
在Linux上,BPF比在BSD上简单的多。只需要创建你的 filter 代码,通过 setsockopt 调用来完成:
对socket过滤,将一个指向 struct sock_fprog 结构的指针 &Filter 通过 setsockopt 系统调用传递给内核,具体格式如下:
setsockopt(sockfd, SOL_SOCKET, SO_ATTACH_FILTER, &Filter, sizeof(Filter));
setsockopt(sockfd, SOL_SOCKET, SO_DETACH_FILTER, &Filter, sizeof(Filter));
setsockopt(sockfd, SOL_SOCKET, SO_LOCK_FILTER, &Filter, sizeof(Filter));
setsockopt 调用SO_ATTACH_FILTER,将 tcpdump -dd
的结果内容添加到 struct sock_filter code[] = {} 结构体中,将 Filter.filter 指向这个code数组,Filter.len 设置长度,就可以用setsockopt设置过滤器了。
setsockopt 调用SO_DETACH_FILTER不需要任何参数,调用SO_LOCK_FILTER来预防filter被解绑附带一个整数参数0或1.
注意: socket filter没有限制仅仅用在PF_PACKET socket上,也可以用于其他socket家族。
tcpdump 是linux 中调试网络的一个工具, 实际上 tcpdump 就是用利用BPF原理编写的一个工具,所以tcpdump 提供了一个生成 bpf code 的选项 -dd 。
使用 tcpdump 命令加上 -dd 选项来生成 bpf code,命令格式如下:
tcpdump -dd -i eth0 port 22
注意:
用tcpdump生成的BPF代码只能用于SOCK_RAW的socket,这类socket是可以直接操作数据链路层的。如果你打算将BPF用于ip层等较高层次的socket,那么你需要手工修改部分行的code.k,也就是修改如ldh [12]当中的[12]这个数值,因为这个数值的偏移量是按照从链路层开始计算得到的,在没有链路层之后,这个值就发生了变化,这个是需要注意的。
附加一个socket filter到一个PF_PACKET socket上,为了让所有IPv4/IPv6 port 22的包通过,这个socket上所有其他的包将会被丢弃。
代码如下:
#include
#include
#include
#include
/* ... */
/* From the example above: tcpdump -dd -i eth0 port 22 */
struct sock_filter code[] = {
{ 0x28, 0, 0, 0x0000000c },
{ 0x15, 0, 8, 0x000086dd },
{ 0x30, 0, 0, 0x00000014 },
{ 0x15, 2, 0, 0x00000084 },
{ 0x15, 1, 0, 0x00000006 },
{ 0x15, 0, 17, 0x00000011 },
{ 0x28, 0, 0, 0x00000036 },
{ 0x15, 14, 0, 0x00000016 },
{ 0x28, 0, 0, 0x00000038 },
{ 0x15, 12, 13, 0x00000016 },
{ 0x15, 0, 12, 0x00000800 },
{ 0x30, 0, 0, 0x00000017 },
{ 0x15, 2, 0, 0x00000084 },
{ 0x15, 1, 0, 0x00000006 },
{ 0x15, 0, 8, 0x00000011 },
{ 0x28, 0, 0, 0x00000014 },
{ 0x45, 6, 0, 0x00001fff },
{ 0xb1, 0, 0, 0x0000000e },
{ 0x48, 0, 0, 0x0000000e },
{ 0x15, 2, 0, 0x00000016 },
{ 0x48, 0, 0, 0x00000010 },
{ 0x15, 0, 1, 0x00000016 },
{ 0x6, 0, 0, 0x00040000 },
{ 0x6, 0, 0, 0x00000000 },
};
struct sock_fprog bpf = {
.len = ARRAY_SIZE(code),
.filter = code,
};
sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
if (sock < 0)
/* ... bail out ... */
ret = setsockopt(sock, SOL_SOCKET, SO_ATTACH_FILTER, &bpf, sizeof(bpf));
if (ret < 0)
/* ... bail out ... */
/* ... */
close(sock);