BPF分析及使用方式

在看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));

你可能感兴趣的:(无线网络)