BPF
框架
示例
tcpdump -d ip
查看:
(000) ldh [12] // 链路层第12字节的数据加载到寄存器,ethertype字段
(001) jeq #0x800 jt 2 jf 3 // 比较寄存器的ethertype字段是否为IP类型,true跳到2,false跳到3
(002) ret #65535 // 返回true
(003) ret #0 // 返回0
BPF只使用了4条虚拟机指令,就能提供非常有用的IP报文过滤。
tcpdump -d tcp
(000) ldh [12] // 链路层第12字节的数据(2字节)加载到寄存器,ethertype字段
(001) jeq #0x86dd jt 2 jf 7 // 判断是否为IPv6类型,true跳到2,false跳到7
(002) ldb [20] // 链路层第20字节的数据(1字节)加载到寄存器,IPv6的next header字段
(003) jeq #0x6 jt 10 jf 4 // 判断是否为TCP,true跳到10,false跳到4
(004) jeq #0x2c jt 5 jf 11 // 可能是IPv6分片标志,true跳到5,false跳到11
(005) ldb [54] // 我编不下去了...
(006) jeq #0x6 jt 10 jf 11 // 判断是否为TCP,true跳到10,false跳到11
(007) jeq #0x800 jt 8 jf 11 // 判断是否为IP类型,true跳到8,false跳到11
(008) ldb [23] // 链路层第23字节的数据(1字节)加载到寄存器,next proto字段
(009) jeq #0x6 jt 10 jf 11 // 判断是否为TCP,true跳到10,false跳到11
(010) ret #65535 // 返回true
(011) ret #0 // 返回0
以上是freebsd的BPF,Linux中应该不叫这个,叫LSF,自己看吧。
eBPF
eBPF初识
目前eBPF可以分解为三个过程:
以字节码的形式创建eBPF的程序。编写C代码,将LLVM编译成驻留在ELF文件中的eBPF字节码。 将程序加载到内核中,并创建必要的eBPF-maps。eBPF具有用作socket filter,kprobe处理器,流量控制调度,流量控制操作,tracepoint处理,eXpress Data Path(XDP),性能监测,cgroup限制,轻量级tunnel的程序类型。 将加载的程序attach到系统中。根据不同的程序类型attach到不同的内核系统中。程序运行的时候,启动状态并且开始过滤,分析或者捕获信息。
eBPF入口
kernel/bpf/syscall.c
bpf的系统调用include/uapi/linux/bpf.h
bpf系统调用的头文件int bpf(int cmd, union bpf_attr *attr, unsigned int size);
,从 kernel/bpf/syscall.c
中的宏定义展开。eBPF命令
Linux系统的BPF系统调用有10个命令,其中man page中列出了6个:
BPF_PROG_LOAD
验证并且加载eBPF程序,返回一个新的文件描述符。BPF_MAP_CREATE
创建map并且返回指向map的文件描述符BPF_MAP_LOOKUP_ELEM
通过key从指定的map中查找元素,并且返回value值BPF_MAP_UPDATE_ELEM
在指定的map中创建或者更新元素(key/value 配对)BPF_MAP_DELETE_ELEM
通过key从指定的map中找到元素并且删除BPF_MAP_GET_NEXT_KEY
通过key从指定的map中找到元素,并且返回下个key值
以上的命令可以分为两大类,加载eBPF程序和eBPF-maps操作。eBPF-maps操作有很大的自主性,用于创建eBPF-maps,从中查找、更新和删除元素,遍历eBPF-maps(BPF_MAP_GET_NEXT_KEY)
接下来列一下剩下的4个命令,在代码中可以看到:
BPF_OBJ_PIN 4.4版本新加的,属于持久性eBPF。有了这个,eBPF-maps和eBPF程序可以放入/sys/fs/bpf
BPF_OBJ_GET 同上,在这之前,没有工具能创建eBPF程序,并且结束,因为会破坏filter,而文件系统可以在创建他们的程序退出后依然保留eBPF-maps和eBPF程序
BPF_PROG_ATTACH 4.10版本中添加的,将eBPF程序attach到cgroup,这样适用于container
BPF_PROG_DETACH 同上。
eBPF-map 类型
BPF_MAP_TYPE_UNSPEC
BPF_MAP_TYPE_HASH
eBPF-maps hash表,是主要用的前两种方式之一BPF_MAP_TYPE_ARRAY
和上面类似,除了索引像数组一样BPF_MAP_TYPE_PROG_ARRAY
将加载的eBPF程序的文件描述符保存其值,常用的是使用数字识别不同的eBPF程序类型,也可以从一个给定key值的eBPF-maps找到eBPF程序,并且跳转到程序中去BPF_MAP_TYPE_PERF_EVENT_ARRAY
配合perf工具,CPU性能计数器,tracepoints,kprobes和uprobes。可以查看路径samples/bpf/下的tracex6_kern.c,tracex6_user.c,tracex6_kern.c,tracex6_user.cBPF_MAP_TYPE_PERCPU_HASH
和BPF_MAP_TYPE_HASH
一样,除了是为每个CPU创建BPF_MAP_TYPE_PERCPU_ARRAY
和BPF_MAP_TYPE_ARRAY
一样,除了是为每个CPU创建BPF_MAP_TYPE_STACK_TRACE
用于存储stack-tracesBPF_MAP_TYPE_CGROUP_ARRAY
检查skb的croup归属BPF_MAP_TYPE_LRU_HASH
BPF_MAP_TYPE_LRU_PERCPU_HASH
BPF_MAP_TYPE_LPM_TRIE
专业的用法,LPM(Longest Prefix Match)的一种trieBPF_MAP_TYPE_ARRAY_OF_MAPS
可能是针对每个port的BPF_MAP_TYPE_HASH_OF_MAPS
可能是针对每个port的BPF_MAP_TYPE_DEVMAP
可能是定向报文到dev的BPF_MAP_TYPE_SOCKMAP
可能是连接socket的
来源:https://zhaozhanxu.com/2018/04/01/Linux/2018-04-01-Linux-eBPF/