0x1:應用層流程
基于Linux kernel source v5.13
1.加載bpf.o檔案并處理elf section資訊
1.int bpf_object__open(char *path) //引數是bpf.o檔案路徑
-- __bpf_object__open(const char *path, const void *obj_buf, size_t obj_buf_sz, const struct bpf_object_open_opts *opts)//讀取obj檔案,決議elf中section資訊,
-- obj = bpf_object__new(path, obj_buf, obj_buf_sz, obj_name); //創建并初始化obj結構體
err = bpf_object__elf_init(obj); //讀取elf檔案
err = err ? : bpf_object__check_endianness(obj); //判斷大小端
err = err ? : bpf_object__elf_collect(obj); //讀取elf節資訊(license / version / maps / .reloc / .text)
err = err ? : bpf_object__collect_externs(obj); //讀取btf section
err = err ? : bpf_object__finalize_btf(obj); //讀取需要 btf處理的data section
err = err ? : bpf_object__init_maps(obj, opts); //讀取map資訊(user map / global data map / btf map / kconfig map)
err = err ? : bpf_object__collect_relos(obj); //讀取重定位資訊
2.加載obj檔案到內核
2.int bpf_object__load(struct bpf_object *obj) //加載第一步生成的obj結構體
-- bpf_object__load_xattr(struct bpf_object_load_attr *attr)
-- err = bpf_object__probe_loading(obj); //加載bpf prog到內核(這里加載的是未經過修改的bpf代碼)
err = err ? : bpf_object__load_vmlinux_btf(obj, false); //讀取內核vmlinux資訊
err = err ? : bpf_object__resolve_externs(obj, obj->kconfig); //讀取內核kconfig /vmlinux / kallsysm資訊
err = err ? : bpf_object__sanitize_and_load_btf(obj); // BPF_BTF_LOAD 加載btf資訊
err = err ? : bpf_object__sanitize_maps(obj); // 判斷內核支持的map種類
err = err ? : bpf_object__init_kern_struct_ops_maps(obj);
err = err ? : bpf_object__create_maps(obj); //BPF_MAP_CREATE 創建map
err = err ? : bpf_object__relocate(obj, attr->target_btf_path); //處理bpf代碼重定位資訊
err = err ? : bpf_object__load_progs(obj, attr->log_level); //這里加載經過重定位 btf修改的bpf代碼 ****
-- libbpf__bpf_prog_load(const struct bpf_prog_load_params *load_attr)
-- sys_bpf_prog_load(union bpf_attr *attr, unsigned int size)
//呼叫sys_bpf(BPF_PROG_LOAD, attr, size) 完成bpf prog的加載
union bpf_attr attr; 是一個union結構,根據bpf_type的不同,產生不同的結構,具體可以在kernel source/include/uapi/linux/bpf.h中查看
0x2:內核流程
define __NR_bpf 321 //呼叫號在x64下為321
static inline int sys_bpf(enum bpf_cmd cmd, union bpf_attr *attr, unsigned int size)
{
return syscall(__NR_bpf, cmd, attr, size);
}
sys_bpf()
-- __SYS_CALL(_NR_bpf, cmd, attr, size)
-- SYSCALL_DEFINE3(bpf, cmd, uattr, size)
/kernel/bpf/syscall.c/
SYSCALL_DEFINE3(bpf, int, cmd, union bpf_attr __user *, uattr, unsigned int, size) {
//這個函式就是內核處理應用層bpf相關操作的總入口,根據cmd引數的不同,產生不同結構的struct bpf_attr
... ...
copy_from_user(&attr, uattr, size); //拷貝虛擬地址內容到內核中
security_bpf(cmd, &attr, size); //LSM 框架支持 截止目前v5.13,只實作了幾個函式,和selinux/appamor相差甚遠
switch (cmd) {
case BPF_MAP_CREATE:
err = map_create(&attr); //創建map
break;
case BPF_PROG_LOAD:
err = bpf_prog_load(&attr, uattr); //加載bpf程式
break;
default:
err = -EINVAL;
break;
}
... ...
}
重點看看bpf prog加載流程,熟悉verfiy機制和jit機制
static int bpf_prog_load(union bpf_attr *attr, union bpf_attr __user *uattr)
{
... ...
license_is_gpl_compatible(license); // 開源許可證判斷
if (is_net_admin_prog_type(type) && !capable(CAP_NET_ADMIN) && !capable(CAP_SYS_ADMIN)) //如果是net相關型別,判斷所需權限是否滿足
if (is_perfmon_prog_type(type) && !perfmon_capable()) //判斷是追蹤相關型別,判斷所需權限是否滿足
bpf_prog_alloc(bpf_prog_size(attr->insn_cnt), GFP_USER); //給 struct bpf_prog 申請記憶體,該結構是bpf在內核中的實體
copy_from_user(prog->insns, u64_to_user_ptr(attr->insns),bpf_prog_insn_size(prog)) //拷貝bpf位元組碼到內核
bpf_check(&prog, attr, uattr); //bpf verify機制核心
-- 1.呼叫replace_map_fd_with_map_ptr將eBPF匯編中的fd替換為對應的map結構體地址,
-- 2.check_subprogs檢查所有條件跳轉指令都位于相應subprog內(本eBPF函式內)
-- 3.check_cfg采用深度優先演算法確保函式分支不存在回圈和存在執行不到的指令,
-- 4.do_check函式檢查暫存器和引數的合法性,
-- 5.呼叫fix_call_args函式對多bpf函式的prog進行jit (多sub prog在這里jit,單prog的在下面bpf_prog_select_runtime進行jit)
bpf_prog_select_runtime(prog, &err); //bpf jit機制核心,將bpf位元組碼編譯為目標平臺匯編代碼
bpf_audit_prog(prog, BPF_AUDIT_LOAD); //列印一條prog load 的 audit資訊
perf_event_bpf_event(prog, PERF_BPF_EVENT_PROG_LOAD, 0); //通過perf機制加載到對應的hook api中
err = bpf_prog_new_fd(prog);//回傳給應用層bpf prog的fd資訊,后續應用層用該fd進行操作(詳細可以看libbpf如何通過fd操作map)
... ...
}
struct bpf_prog {
u16 pages; /* 分配page數 */
u16 jited:1, /* prog是否已經jit過*/
jit_requested:1,/* 是否需要jit */
undo_set_mem:1, /* Passed set_memory_ro() checkpoint */
gpl_compatible:1, /* Is filter GPL compatible? */
cb_access:1, /* Is control block accessed? */
dst_needed:1, /* Do we need dst entry? */
blinded:1, /* 常量致盲 */
is_func:1, /* eBPF func? 大多數情況是 */
kprobe_override:1, /* 是否是overrided kprobe */
has_callchain_buf:1; /* callchain buffer allocated? */
enum bpf_prog_type type; /* prog型別,eg kprobe 、tracepoint*/
enum bpf_attach_type expected_attach_type; /* For some prog types */
u32 len; /* eBPF指令個數 */
u32 jited_len; /* eBPF匯編指令代碼總長度 */
u8 tag[BPF_TAG_SIZE];
struct bpf_prog_aux *aux; /* Auxiliary fields */
struct sock_fprog_kern *orig_prog; /* Original BPF program */
unsigned int (*bpf_func)(const void *ctx,
const struct bpf_insn *insn);/* 存放jit后的可執行匯編 */
/* 不支持jit,需要模擬,x64支持jit,不需要模擬 */
union {
struct sock_filter insns[0]; /* 從用戶態拷貝來的eBPF原程式 */
struct bpf_insn insnsi[0];
};
};
第一引數cmd
enum bpf_cmd {
BPF_MAP_CREATE, //前五個是操作Map的
BPF_MAP_LOOKUP_ELEM,
BPF_MAP_UPDATE_ELEM,
BPF_MAP_DELETE_ELEM,
BPF_MAP_GET_NEXT_KEY,
BPF_PROG_LOAD, //eBPF位元組碼加載
BPF_OBJ_PIN,
BPF_OBJ_GET,
BPF_PROG_ATTACH,
BPF_PROG_DETACH,
BPF_PROG_TEST_RUN,
BPF_PROG_GET_NEXT_ID,
BPF_MAP_GET_NEXT_ID,
BPF_PROG_GET_FD_BY_ID,
BPF_MAP_GET_FD_BY_ID,
BPF_OBJ_GET_INFO_BY_FD,
BPF_PROG_QUERY,
BPF_RAW_TRACEPOINT_OPEN,
BPF_BTF_LOAD, //加載btf資訊
BPF_BTF_GET_FD_BY_ID,
BPF_TASK_FD_QUERY,
BPF_MAP_LOOKUP_AND_DELETE_ELEM,
BPF_MAP_FREEZE,
BPF_BTF_GET_NEXT_ID,
BPF_MAP_LOOKUP_BATCH,
BPF_MAP_LOOKUP_AND_DELETE_BATCH,
BPF_MAP_UPDATE_BATCH,
BPF_MAP_DELETE_BATCH,
BPF_LINK_CREATE,
BPF_LINK_UPDATE,
BPF_LINK_GET_FD_BY_ID,
BPF_LINK_GET_NEXT_ID,
BPF_ENABLE_STATS,
BPF_ITER_CREATE,
};
BPF MAP 型別
enum bpf_map_type {
BPF_MAP_TYPE_UNSPEC = 0,
BPF_MAP_TYPE_HASH = 1, //哈希表
BPF_MAP_TYPE_ARRAY = 2, //陣列映射,已針對快速查找速度進行了優化,通常用于計數器
BPF_MAP_TYPE_PROG_ARRAY = 3, //對應eBPF程式的檔案描述符陣列;用于實作跳轉表和子程式以處理特定的資料包協議
BPF_MAP_TYPE_PERF_EVENT_ARRAY = 4, // linux kernel 4.4 存盤指向struct perf_event的指標,用于讀取和存盤perf事件計數器
BPF_MAP_TYPE_PERCPU_HASH = 5, //每個CPU的哈希表
BPF_MAP_TYPE_PERCPU_ARRAY = 6, //每個CPU的陣列
BPF_MAP_TYPE_STACK_TRACE = 7, //存盤堆疊跟蹤
BPF_MAP_TYPE_CGROUP_ARRAY = 8, //存盤指向控制組的指標
BPF_MAP_TYPE_LRU_HASH = 9, //僅保留最近使用專案的哈希表
BPF_MAP_TYPE_LRU_PERCPU_HASH = 10, //每個CPU的哈希表,僅保留最近使用的專案
BPF_MAP_TYPE_LPM_TRIE = 11, //最長前綴匹配樹,適用于將IP地址匹配到某個范圍
BPF_MAP_TYPE_ARRAY_OF_MAPS = 12,
BPF_MAP_TYPE_HASH_OF_MAPS = 13,
BPF_MAP_TYPE_DEVMAP = 14, //用于存盤和查找網路設備參考
BPF_MAP_TYPE_SOCKMAP = 15, //存盤和查找套接字,并允許使用BPF輔助函式進行套接字重定向
BPF_MAP_TYPE_CPUMAP = 16,
BPF_MAP_TYPE_XSKMAP = 17,
BPF_MAP_TYPE_SOCKHASH = 18,
BPF_MAP_TYPE_CGROUP_STORAGE = 19,
BPF_MAP_TYPE_REUSEPORT_SOCKARRAY = 20,
BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE = 21,
BPF_MAP_TYPE_QUEUE = 22,
BPF_MAP_TYPE_STACK = 23,
BPF_MAP_TYPE_SK_STORAGE = 24,
BPF_MAP_TYPE_DEVMAP_HASH = 25,
BPF_MAP_TYPE_STRUCT_OPS = 26,
BPF_MAP_TYPE_RINGBUF = 27, // linux kernel 5.8 Perf Buffer增強版
BPF_MAP_TYPE_INODE_STORAGE = 28,
};
詳細介紹: BFP MAP介紹
BPF PROG 型別
【helper函式使用范圍】不同型別eBPF程式可以使用的eBPF helper函式范圍
enum bpf_prog_type {
BPF_PROG_TYPE_UNSPEC,
BPF_PROG_TYPE_SOCKET_FILTER, //網路資料包過濾器
BPF_PROG_TYPE_KPROBE, //確定是否應觸發kprobe
BPF_PROG_TYPE_SCHED_CLS, //網路流量控制分類器
BPF_PROG_TYPE_SCHED_ACT, //網路流量控制操作
BPF_PROG_TYPE_TRACEPOINT, //確定是否應觸發跟蹤點
BPF_PROG_TYPE_XDP, //從設備驅動程式接收路徑運行的網路資料包篩選器
BPF_PROG_TYPE_PERF_EVENT, //確定是否應該觸發性能事件處理程式
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,
};
0x3:BPF暫存器
eBPF從bpf的兩個32位暫存器擴展到10個64位暫存器R0~R9和一個只讀堆疊幀暫存器,并支持call指令,更加貼近現代64位處理器硬體
R0對應rax, 函式回傳值
R1對應rdi, 函式引數1
R2對應rsi, 函式引數2
R3對應rdx, 函式引數3
R4對應rcx, 函式引數4
R5對應r8, 函式引數5
R6對應rbx, callee保存
R7對應r13, callee保存
R8對應r14, callee保存
R9對應r15, callee保存
R10對應rbp,只讀堆疊幀暫存器
0x4:內核路徑
/Documentation/bpf/btf.rst
/include/uapi/linux/bpf_common.h 和 /include/uapi/linux/bpf.h 定義了指令集
/samples/bpf 相關的樣例
/tools/bpf/bpftool 工具,用來除錯bpf
/tools/testing/selftests/bpf 測驗代碼
本文由博客一文多發平臺 OpenWrite 發布!
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/394973.html
標籤:其他
上一篇:另一個角度看元宇宙與RPA:人工世界、平行員工與RPA
下一篇:eBPF撰寫避坑指南
