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的包头就简单多了