http://blog.csdn.net/maeom/article/details/6092457
BPF:Berkeley Packet Filter
英文高手可以直接看原文:http://www.gsp.com/cgi-bin/man.cgi?section=4&topic=bpf#1
或许有一部分人会看不太懂,那么结合下面第二部分的封包结构,就会容易一些。let's go
首先,要确保我们从socket中读取的是packet,也就是说是 MAC头+IP头+TCP/UDP头,这个样子的才可以。当然,如果用linux下lpf也可以在IP包上做过滤。一样的道理。
BPF(Berkeley Packet Filter)
(1) 需要包含的头文件
- #include <sys/types.h>
- #include <sys/time.h>
- #include <sys/ioctl.h>
- #include <net/bpf.h>
(2) FILTER MACHINE
- //这个就是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),
- };
下面我们来看下网络包的格式,才能对上面如何编写。温习一下网络结构
Ethernet Header
EthDHost :
Destination address (6 bytes). //目的MAC地址
EthSHost
Source address (6 bytes).
EthType
Encapsulated packet type (2 bytes). It is ETHERTYPE_IP for IP based communication. //所承载的协议,IP协议则为ETHERTYPE_IP
上面为IP头格式,一行为32为,即4 bytes
下面来看看TCP头和UDP头
TCP的头这么复杂和IP头差不多了。
UDP的包头就简单多了