BPF程序学习
一、介绍
历史:
cBPF:Linux 2.1.75 -> eBPF:Linux 3.18
eBPF能做什么呢?
二、编写BPF程序
BPF程序类型
bpf_prog_type 该类型定义在/include/uapi/linux/bpf.h。
enum bpf_prog_type { BPF_PROG_TYPE_UNSPEC, BPF_PROG_TYPE_SOCKET_FILTER, BPF_PROG_TYPE_KPROBE, //用于内核动态插桩点kprobes BPF_PROG_TYPE_SCHED_CLS, //用于流量控制分类 BPF_PROG_TYPE_SCHED_ACT, BPF_PROG_TYPE_TRACEPOINT, //用于内核静态跟踪点 BPF_PROG_TYPE_XDP, //用于XDP(eXpress Data Path)程序 BPF_PROG_TYPE_PERF_EVENT, //用于perf_events,包括PMC BPF_PROG_TYPE_CGROUP_SKB, BPF_PROG_TYPE_CGROUP_SOCK, BPF_PROG_TYPE_LWT_IN, BPF_PROG_TYPE_LWT_OUT, BPF_PROG_TYPE_LWT_XMIT, BPF_PROG_TYPE_SOCK_OPS, BPF_PROG_TYPE_SK_SKB, BPF_PROG_TYPE_CGROUP_DEVICE, BPF_PROG_TYPE_SK_MSG, BPF_PROG_TYPE_RAW_TRACEPOINT, BPF_PROG_TYPE_CGROUP_SOCK_ADDR, BPF_PROG_TYPE_LWT_SEG6LOCAL, BPF_PROG_TYPE_LIRC_MODE2, BPF_PROG_TYPE_SK_REUSEPORT, BPF_PROG_TYPE_FLOW_DISSECTOR, BPF_PROG_TYPE_CGROUP_SYSCTL, BPF_PROG_TYPE_RAW_TRACEPOINT_WRITABLE, BPF_PROG_TYPE_CGROUP_SOCKOPT, BPF_PROG_TYPE_TRACING, BPF_PROG_TYPE_STRUCT_OPS, BPF_PROG_TYPE_EXT, BPF_PROG_TYPE_LSM, BPF_PROG_TYPE_SK_LOOKUP, BPF_PROG_TYPE_SYSCALL, /* a program that can execute syscalls */ };
BPF存储类型
BPF程序之间数据通信或和用户程序交互通过存储映射来实现。BPF程序可以创建一个map来存储数据,它会在sys文件系统中保存,详细在/sys/fs/bpf/xdp/globals/该map名
enum bpf_map_type { BPF_MAP_TYPE_UNSPEC, BPF_MAP_TYPE_HASH, //用于哈希表的Map类型:保存key/value 对 BPF_MAP_TYPE_ARRAY, //数组类型 BPF_MAP_TYPE_PROG_ARRAY, BPF_MAP_TYPE_PERF_EVENT_ARRAY, //到perf_event环形缓冲区的接口, 用于将记录发送到用户空间 BPF_MAP_TYPE_PERCPU_HASH, //一个基于每个CPU单独维护的更快哈希表 BPF_MAP_TYPE_PERCPU_ARRAY, //一个基于每个CPU单独维护的更快数组 BPF_MAP_TYPE_STACK_TRACE, //调用栈存储,使用栈ID进行索引 BPF_MAP_TYPE_CGROUP_ARRAY, BPF_MAP_TYPE_LRU_HASH, BPF_MAP_TYPE_LRU_PERCPU_HASH, BPF_MAP_TYPE_LPM_TRIE, BPF_MAP_TYPE_ARRAY_OF_MAPS, BPF_MAP_TYPE_HASH_OF_MAPS, BPF_MAP_TYPE_DEVMAP, BPF_MAP_TYPE_SOCKMAP, BPF_MAP_TYPE_CPUMAP, BPF_MAP_TYPE_XSKMAP, BPF_MAP_TYPE_SOCKHASH, BPF_MAP_TYPE_CGROUP_STORAGE, BPF_MAP_TYPE_REUSEPORT_SOCKARRAY, BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE, BPF_MAP_TYPE_QUEUE, BPF_MAP_TYPE_STACK, //调用栈存储 BPF_MAP_TYPE_SK_STORAGE, BPF_MAP_TYPE_DEVMAP_HASH, BPF_MAP_TYPE_STRUCT_OPS, BPF_MAP_TYPE_RINGBUF, BPF_MAP_TYPE_INODE_STORAGE, BPF_MAP_TYPE_TASK_STORAGE, BPF_MAP_TYPE_BLOOM_FILTER, };
BPF辅助函数
BPF程序不能随意调用内核函数,内核专门提供了BPF可以调用的函数
bpf_map_lookup_elem(map, key) 在map中查找键值key,并返回它的值(指针)
bpf_map_delete_elem(map, key) 根据key值删除map中对应的元素
bpf_ktime_get_ns() 返回系统启动后的时长,单位ns
bpf_trace_printk(fmt, fmt_size, …) 向TraceFS的pipe文件中写入调试信息
三、存储类型map相关函数
1.bpf_map_lookup_elem函数
存在两种定义,分别来自不同的头文件。
在"#include <linux/bpf.h>"中是:
通过键返回映像中该键对应的值,定义如下:
static void *(*bpf_map_lookup_elem)(struct bpf_map *map, void *key)
在“#include <bpf/bpf.h>”中是:
查询映像中是否存在该键值对,查询结果返回到value中,定义如下:
LIBBPF_API int bpf_map_lookup_elem(int fd, const void *key, void *value)
2.bpf_obj_get函数
可从已固定的 /sys/fs/bpf
文件返回文件描述符。此文件描述符可用于进一步的操作,例如读取映射或将程序附加到跟踪点。
定义如下:
int bpf_obj_get(const char *pathname)
3.bpf_map_delete_elem函数
删除映像中对应键的键值对,定义如下:
#include <bpf/bpf.h> LIBBPF_API int bpf_map_delete_elem(int fd, const void *key)
4.bpf_map_get_next_key函数
获取该键在映像顺序中的下一个键,定义如下:
LIBBPF_API int bpf_map_get_next_key(int fd, const void *key, void *next_key)
5.bpf_map_update_elem函数
更新或修改映像中的键值对,定义如下:
LIBBPF_API int bpf_map_update_elem(int fd, const void *key, const void *value, __u64 flags)
6.bpf_probe_read函数
将src的内容拷贝到dst中。内核对于非简单类型的赋值需要进行安全边界的检查,避免在内核中进行越界访问,破坏内核稳定性和安全性的保障。
定义如下:
#define BPF_PROBE_READ_INTO(dst, src, a, ...) ({ \ ___core_read(bpf_probe_read, bpf_probe_read, \ dst, (src), a, ##__VA_ARGS__) \ })
例如:bpf_probe_read(&tcpflags, sizeof(u8), thp)。