在看hostapd源码的过程中,看到接收过滤使用的bpf,下面拷贝hostapd源码:
if (setsockopt(s, SOL_SOCKET, SO_ATTACH_FILTER,
&msock_filter, sizeof(msock_filter))) {
perror("SO_ATTACH_FILTER");
return -1;
}
static struct sock_fprog msock_filter = {
.len = ARRAY_SIZE(msock_filter_insns),
.filter = msock_filter_insns,
};
// msock_filter_insns过长,这里只拷贝一部分
static struct sock_filter msock_filter_insns[] = {
/*
* do a little-endian load of the radiotap length field
*/
/* load lower byte into A */
BPF_STMT(BPF_LD | BPF_B | BPF_ABS, 2),
/* put it into X (== index register) */
BPF_STMT(BPF_MISC| BPF_TAX, 0),
/* load upper byte into A */
BPF_STMT(BPF_LD | BPF_B | BPF_ABS, 3),
/* left-shift it by 8 */
BPF_STMT(BPF_ALU | BPF_LSH | BPF_K, 8),
.....
}
下面进行分析:
使用方式:
设置BPF过滤器是通过setsockopt调用来完成的,格式如下:
setsockopt(sd, SOL_SOCKET, SO_ATTACH_FILTER, &Filter, sizeof(Filter));
这个调用的格式大家都很熟悉了,不清楚的在参数Filter的设置上。Filter的定义是struct sock_fprog Filter; 此结构在linux/filter.h当中有定义:
struct sock_fprog
/*Required for SO_ATTACH_FILTER. */
{
unsigned short len;
/*Number of filter blocks */
struct sock_filter *filter;
};
其中的filter指针指向结构为struct sock_filter的BPF过滤代码。结构同样也在同一个文件当中定义:
struct sock_filter
/*
Filter block */
{
__u16 code; /*Actual filter code */
__u8 jt; /*Jump true */
__u8 jf; /*Jump false */
__u32 k; /*Generic multiuse field */
};
这个类似于汇编的样子。
msock_filter_insns结构体里面的数据,可以看 libpcap里面源码:
#define BPF_STMT(code, k) { (u_short)(code), 0, 0, k }
#define BPF_JUMP(code, k, jt, jf) { (u_short)(code), jt, jf, k }
/* instruction classes */
#define BPF_CLASS(code) ((code) & 0x07)
#define BPF_LD 0x00
#define BPF_LDX 0x01
#define BPF_ST 0x02
#define BPF_STX 0x03
#define BPF_ALU 0x04
#define BPF_JMP 0x05
#define BPF_RET 0x06
#define BPF_MISC 0x07
/* ld/ldx fields */
#define BPF_SIZE(code) ((code) & 0x18)
#define BPF_W 0x00
#define BPF_H 0x08
#define BPF_B 0x10
/* 0x18 reserved; used by BSD/OS */
#define BPF_MODE(code) ((code) & 0xe0)
#define BPF_IMM 0x00
#define BPF_ABS 0x20
#define BPF_IND 0x40
#define BPF_MEM 0x60
#define BPF_LEN 0x80
#define BPF_MSH 0xa0
/* 0xc0 reserved; used by BSD/OS */
/* 0xe0 reserved; used by BSD/OS */
/* alu/jmp fields */
#define BPF_OP(code) ((code) & 0xf0)
#define BPF_ADD 0x00
#define BPF_SUB 0x10
#define BPF_MUL 0x20
#define BPF_DIV 0x30
#define BPF_OR 0x40
#define BPF_AND 0x50
#define BPF_LSH 0x60
#define BPF_RSH 0x70
#define BPF_NEG 0x80
#define BPF_MOD 0x90
#define BPF_XOR 0xa0
#define BPF_JA 0x00
#define BPF_JEQ 0x10
#define BPF_JGT 0x20
#define BPF_JGE 0x30
#define BPF_JSET 0x40
#define BPF_SRC(code) ((code) & 0x08)
#define BPF_K 0x00
#define BPF_X 0x08
/* ret - BPF_K and BPF_X also apply */
#define BPF_RVAL(code) ((code) & 0x18)
#define BPF_A 0x10
/* 0x18 reserved */
/* misc */
#define BPF_MISCOP(code) ((code) & 0xf8)
#define BPF_TAX 0x00
#define BPF_TXA 0x80
下面是我从网上拷贝的一些解释:
链接在:http://blog.csdn.net/maeom/article/details/6092457
http://www.360doc.com/content/06/1026/17/13362_241408.shtml
//这个就是bpf instruction的缩写了
struct bpf_insn {
u_short code;
u_char jt;
u_char jf;
u_long k;
};
BPF_LD //将值拷贝进寄存器(accumulator),没学过汇编。我就当做赋给一个变量了
BPF_LDX //将值拷贝进索引寄存器(index register)
BPF_LD+BPF_W+BPF_ABS A <- P[k:4] //将一个Word 即4 byte赋给寄存器(accumulator)
BPF_LD+BPF_H+BPF_ABS A <- P[k:2] //将一个Half Word 即2 byte赋给寄存器(accumulator)
BPF_LD+BPF_B+BPF_ABS A <- P[k:1] //将一个Byte 赋给寄存器(accumulator)
BPF_LD+BPF_W+BPF_IND A <- P[X+k:4] //偏移X寄存器后,将一个Word 即4 byte赋给寄存器(accumulator)
BPF_LD+BPF_H+BPF_IND A <- P[X+k:2]
BPF_LD+BPF_B+BPF_IND A <- P[X+k:1]
BPF_LD+BPF_W+BPF_LEN A <- len //the packet length 不知道什么意思 :(
BPF_LD+BPF_IMM A <- k //将常量k赋给寄存器(accumulator)
BPF_LD+BPF_MEM A <- M[k] //将一个Word的地址为k的内存部分赋给寄存器(accumulator)
//下面的部分是将值load进index register 大家自己理解吧
BPF_LDX+BPF_W+BPF_IMM X <- k
BPF_LDX+BPF_W+BPF_MEM X <- M[k]
BPF_LDX+BPF_W+BPF_LEN X <- len
BPF_LDX+BPF_B+BPF_MSH X <- 4*(P[k:1]&0xf)
//来看看两个宏
#define BPF_STMT(code, k) { (unsigned short)(code), 0, 0, k }
#define BPF_JUMP(code, k, jt, jf) { (unsigned short)(code), jt, jf, k }
//关键的判断指令忘贴了
BPF_JMP+BPF_JA pc += k
BPF_JMP+BPF_JGT+BPF_K pc += (A > k) ? jt : jf
BPF_JMP+BPF_JGE+BPF_K pc += (A >= k) ? jt : jf
/*
*这个BPF_JEQ用的比较多,就拿它开刀了。 一看就知道,是用来判断是否相等的东西
*这个会判断我们给出的数,和A(也就是accumulator寄存器)的内容是否相等,
*结果就不用我说了,三目运算符。
*/
BPF_JMP+BPF_JEQ+BPF_K pc += (A == k) ? jt : jf
BPF_JMP+BPF_JSET+BPF_K pc += (A & k) ? jt : jf
BPF_JMP+BPF_JGT+BPF_X pc += (A > X) ? jt : jf
BPF_JMP+BPF_JGE+BPF_X pc += (A >= X) ? jt : jf
BPF_JMP+BPF_JEQ+BPF_X pc += (A == X) ? jt : jf
BPF_JMP+BPF_JSET+BPF_X pc += (A & X) ? jt : jf
//返回指令
BPF_RET+BPF_A //接受 A 寄存器中的数量bytes
BPF_RET+BPF_K //接受常量 k bytes
//下面我们就直接看个实例吧
/* 前提条件,这个filter的前提是我们抓的包是将物理层都抓下来的情况下。
* 不懂的 物理头-IP头-TCP/UDP头 的兄弟们结合下面的图吧,方便理解与记忆。
* 这是一个过滤TCP源端口不为79,目的端口为79的包(TCP Finger)
*/
struct bpf_insn insns[] = {
/*物理头,偏移12byte后,指向type*/
BPF_STMT(BPF_LD+BPF_H+BPF_ABS, 12),
/*进行比较,是否为IP协议。 true的话 0, false 10
*这里说一下了,由于本人没学过汇编,对这玩意开始时相当困惑。对于学过汇编的兄弟,应该是小菜吧。
*true,则跳过0条指令执行。 就是继续执行下面的一条指令
*false,则跳过10条指令执行。 数数!结果就数到最后一条。即BPF_STMT(BPF_RET+BPF_K, 0),就返回了
*下面的照这个慢慢分析就OK了。
*/
BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, ETHERTYPE_IP, 0, 10),
BPF_STMT(BPF_LD+BPF_B+BPF_ABS, 23),
BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, IPPROTO_TCP, 0, 8),
BPF_STMT(BPF_LD+BPF_H+BPF_ABS, 20),
BPF_JUMP(BPF_JMP+BPF_JSET+BPF_K, 0x1fff, 6, 0),
BPF_STMT(BPF_LDX+BPF_B+BPF_MSH, 14),
BPF_STMT(BPF_LD+BPF_H+BPF_IND, 14),
BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, 79, 2, 0),
BPF_STMT(BPF_LD+BPF_H+BPF_IND, 16),
BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, 79, 0, 1),
BPF_STMT(BPF_RET+BPF_K, (u_int)-1),
BPF_STMT(BPF_RET+BPF_K, 0),
};
看到上面的东西,那么我们不用考虑这么多 ,可以用就行了。
我们可以直接使用tcpdump直接得到这样的一组类汇编代码
例如 :
$tcpdump ip -d -s 2048 host 192.168.1.2
(000) ldh [12]
(001) jeq #0x800 jt 2 jf 7
(002) ld [26]
(003) jeq #0xc0a80102 jt 6 jf 4
(004) ld [30]
(005) jeq #0xc0a80102 jt 6 jf 7
(006) ret #2048
(007) ret #0
$tcpdump ip -dd -s 2048 host 192.168.1.2
{ 0x28, 0, 0, 0x0000000c },
{ 0x15, 0, 5, 0x00000800 },
{ 0x20, 0, 0, 0x0000001a },
{ 0x15, 2, 0, 0xc0a80102 },
{ 0x20, 0, 0, 0x0000001e },
{ 0x15, 0, 1, 0xc0a80102 },
{ 0x6, 0, 0, 0x00000800 },
{ 0x6, 0, 0, 0x00000000 },
直接拷贝这样的数据到sock_filter里面
struct sock_filter bpf_code[] = {
{ 0x28, 0, 0, 0x0000000c },
{ 0x15, 0, 5, 0x00000800 },
{ 0x20, 0, 0, 0x0000001a },
{ 0x15, 2, 0, 0xc0a80102 },
{ 0x20, 0, 0, 0x0000001e },
{ 0x15, 0, 1, 0xc0a80102 },
{ 0x6, 0, 0, 0x00000800 },
{ 0x6, 0, 0, 0x00000000 }
};
然后直接这样:
struct sock_fprog filter;
filter.len = sizeof(bpf_code)/sizeof(bpf_code[0]);
filter.filter = bpf_code;
setsockopt(sock, SOL_SOCKET, SO_ATTACH_FILTER, &filter, sizeof(filter));