代码调用:
char filter_exp[] = "port 80";
1 "port 80" 作为字符串 参与编译 pcap_compile(handle, &fp, filter_exp, 0, net)
2 netmask = mask; // 全局变量网络淹码赋值
bpf_pcap = handle; //内部的全局变量指针
snaplen = snaplen_arg; // capture最大的报文长度
in_buffer =filter_exp; // flex变量赋值,flex就是解析in_buffer的tokens,然后调用yacc(bison)的语法对应的规则
3 init_linktype(linktype_arg);
linktype =linktype_arg; //全局变量,链路层是什么?
off_linktype = 12; //上层协议的类型, 6(源mac)+6(目的mac)+(linktype 上层协议类型)
off_nl = 14; //上层链路的偏移, 网络层开始 ip
4 (void)pcap_parse(); // 调用yacc 解析过滤语法 yacc->lex(flex)返回token
port 80 ==>根据 80 (是一个NUM) ==>id ==> gen_ncode
调用一个生成数字的指令输出
gen_ncode (根据$0 发先前面的修饰符是 port)
==> gen_port
==> gen_linktype(ETHERTYPE_IP)
==> gen_cmp(off_linktype, BPF_H, (bpf_int32)proto);
==> s = new_stmt(BPF_LD|BPF_ABS|BPF_H); 绝对位置加载指令
s->s.k = off_linktype; 加载的位子是12 以太王报文头的上层协议字段 加载两个字节b = new_block(JMP(BPF_JEQ));
b->stmts = s; b->s.k = #800;--------------------------------------------------------------------------------------------------------------------------------------
(000) ldh [12]
(001) jeq #0x800 jt 2 jf 23
--------------------------------------------------------------------------------------------------------------------------------------
...
流图
-------------------------------------------------------------------------------------------------------------------------------------
深入一下流程图 过滤表达式 “port 80”
root(pcap中的全局变量) <struct block 的结构指针>
root->id=0
root->stmts /* side effect stmts 不太理解 */ <struct slist 链表>
$3 = { 链表
s = { 节点数据 code = 40, // 对照指令集: 40=8进制的 50 最低3bit是 "000" 加载指令 接着2bit 是"01"==>BPF_H (即加载半字16bit) 再接着3bit"001" 标识加载的值是BPF_ABS 绝对位置, 整条指令就是 加载决定位置12的half-word(半字) 到累加器A (也就是把以太网链路的协议字段加载到累加器, 用于后续比较) jt = 0x0, jf = 0x0, k = 12 }, next = 0x0 链表指针 表示后面没有链表节点了 }root->s /* branch stmt */ <struct stmt>s = {
code = 21, // =25 (其中5标识跳转指令 接着2bit是“10” =BPF_JEQ 相等则跳转,跳转到哪里,现在还没有确定,与哪个值比,k值2048(0x800)(ip协议)网络序) <struct block>其他字段指定位置 如果是0x800 跳转到et (edge true) 如果不相等 ef( edge false) jt = 0x0, jf = 0x0, k = 2048 }, 进到root->et.succ->stmts$7 = {
s = { code = 48, // 48= 8进制60 加载决定位置为23的一个字节到累加器 23位置的值是什么呢? 以太网头ip前有14个字节(ip头的协议字段) jt = 0x0, jf = 0x0, k = 23 }, next = 0x0 }加载这个标志位之后对应的指令是
root->et.succ.s
$8 = {
code = 21, // 相等跳转指令 jt = 0x0, jf = 0x0, k = 6 // TCP协议 }如果不相等
s = {
code = 21, jt = 0x0, jf = 0x0, k = 17 // udp },
如果有一个不相等
s = {
code = 6, // 返回指令 jt = 0x0, jf = 0x0, k = 0 // 返回值 0 标识忽略此包 } 就这样就一个建立一个 cfg control flow graph....这样的graph 是可以优化的...
icode_to_fcode 就是将这样的graph 转换为 bpf虚拟机识别的指令数组
-------------------------------------------------------------------------------------------------------------------------------------
program->bf_insns = icode_to_fcode(root, &len);
指令数组