经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 其他 » 网络安全 » 查看文章
eBPF代码流程分析
来源:cnblogs  作者:SenberHu  时间:2021/12/31 8:56:28  对本文有异议

0x1:应用层流程

基于Linux kernel source v5.13

1.加载bpf.o文件并处理elf section信息

  1. 1.int bpf_object__open(char *path) //参数是bpf.o文件路径
  2. -- __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信息。
  3. -- obj = bpf_object__new(path, obj_buf, obj_buf_sz, obj_name); //创建并初始化obj结构体
  4. err = bpf_object__elf_init(obj); //读取elf文件
  5. err = err ? : bpf_object__check_endianness(obj); //判断大小端
  6. err = err ? : bpf_object__elf_collect(obj); //读取elf节信息(license / version / maps / .reloc / .text)
  7. err = err ? : bpf_object__collect_externs(obj); //读取btf section
  8. err = err ? : bpf_object__finalize_btf(obj); //读取需要 btf处理的data section
  9. err = err ? : bpf_object__init_maps(obj, opts); //读取map信息(user map / global data map / btf map / kconfig map)
  10. err = err ? : bpf_object__collect_relos(obj); //读取重定位信息

2.加载obj文件到内核

  1. 2.int bpf_object__load(struct bpf_object *obj) //加载第一步生成的obj结构体
  2. -- bpf_object__load_xattr(struct bpf_object_load_attr *attr)
  3. -- err = bpf_object__probe_loading(obj); //加载bpf prog到内核(这里加载的是未经过修改的bpf代码)
  4. err = err ? : bpf_object__load_vmlinux_btf(obj, false); //读取内核vmlinux信息
  5. err = err ? : bpf_object__resolve_externs(obj, obj->kconfig); //读取内核kconfig /vmlinux / kallsysm信息
  6. err = err ? : bpf_object__sanitize_and_load_btf(obj); // BPF_BTF_LOAD 加载btf信息
  7. err = err ? : bpf_object__sanitize_maps(obj); // 判断内核支持的map种类
  8. err = err ? : bpf_object__init_kern_struct_ops_maps(obj);
  9. err = err ? : bpf_object__create_maps(obj); //BPF_MAP_CREATE 创建map
  10. err = err ? : bpf_object__relocate(obj, attr->target_btf_path); //处理bpf代码重定位信息
  11. err = err ? : bpf_object__load_progs(obj, attr->log_level); //这里加载经过重定位 btf修改的bpf代码 ****
  12. -- libbpf__bpf_prog_load(const struct bpf_prog_load_params *load_attr)
  13. -- sys_bpf_prog_load(union bpf_attr *attr, unsigned int size)
  14. //调用sys_bpf(BPF_PROG_LOAD, attr, size) 完成bpf prog的加载
  15. union bpf_attr attr; 是一个union结构,根据bpf_type的不同,产生不同的结构,具体可以在kernel source/include/uapi/linux/bpf.h中查看

0x2:内核流程

  1. define __NR_bpf 321 //调用号在x64下为321
  2. static inline int sys_bpf(enum bpf_cmd cmd, union bpf_attr *attr, unsigned int size)
  3. {
  4. return syscall(__NR_bpf, cmd, attr, size);
  5. }
  6. sys_bpf()
  7. -- __SYS_CALL(_NR_bpf, cmd, attr, size)
  8. -- SYSCALL_DEFINE3(bpf, cmd, uattr, size)
  9. /kernel/bpf/syscall.c/
  10. SYSCALL_DEFINE3(bpf, int, cmd, union bpf_attr __user *, uattr, unsigned int, size) {
  11. //这个函数就是内核处理应用层bpf相关操作的总入口,根据cmd参数的不同,产生不同结构的struct bpf_attr
  12. ... ...
  13. copy_from_user(&attr, uattr, size); //拷贝虚拟地址内容到内核中
  14. security_bpf(cmd, &attr, size); //LSM 框架支持 截止目前v5.13,只实现了几个函数,和selinux/appamor相差甚远
  15. switch (cmd) {
  16. case BPF_MAP_CREATE:
  17. err = map_create(&attr); //创建map
  18. break;
  19. case BPF_PROG_LOAD:
  20. err = bpf_prog_load(&attr, uattr); //加载bpf程序
  21. break;
  22. default:
  23. err = -EINVAL;
  24. break;
  25. }
  26. ... ...
  27. }

重点看看bpf prog加载流程,熟悉verfiy机制和jit机制

  1. static int bpf_prog_load(union bpf_attr *attr, union bpf_attr __user *uattr)
  2. {
  3. ... ...
  4. license_is_gpl_compatible(license); // 开源许可证判断
  5. if (is_net_admin_prog_type(type) && !capable(CAP_NET_ADMIN) && !capable(CAP_SYS_ADMIN)) //如果是net相关类型,判断所需权限是否满足
  6. if (is_perfmon_prog_type(type) && !perfmon_capable()) //判断是追踪相关类型,判断所需权限是否满足
  7. bpf_prog_alloc(bpf_prog_size(attr->insn_cnt), GFP_USER); //给 struct bpf_prog 申请内存,该结构是bpf在内核中的实例
  8. copy_from_user(prog->insns, u64_to_user_ptr(attr->insns),bpf_prog_insn_size(prog)) //拷贝bpf字节码到内核
  9. bpf_check(&prog, attr, uattr); //bpf verify机制核心
  10. -- 1.调用replace_map_fd_with_map_ptreBPF汇编中的fd替换为对应的map结构体地址。
  11. -- 2.check_subprogs检查所有条件跳转指令都位于相应subprog内(本eBPF函数内)
  12. -- 3.check_cfg采用深度优先算法确保函数分支不存在循环和存在执行不到的指令。
  13. -- 4.do_check函数检查寄存器和参数的合法性。
  14. -- 5.调用fix_call_args函数对多bpf函数的prog进行jit (多sub prog在这里jit,单prog的在下面bpf_prog_select_runtime进行jit)
  15. bpf_prog_select_runtime(prog, &err); //bpf jit机制核心,将bpf字节码编译为目标平台汇编代码
  16. bpf_audit_prog(prog, BPF_AUDIT_LOAD); //打印一条prog load 的 audit信息
  17. perf_event_bpf_event(prog, PERF_BPF_EVENT_PROG_LOAD, 0); //通过perf机制加载到对应的hook api中
  18. err = bpf_prog_new_fd(prog);//返回给应用层bpf prog的fd信息,后续应用层用该fd进行操作(详细可以看libbpf如何通过fd操作map)
  19. ... ...
  20. }
  1. struct bpf_prog {
  2. u16 pages; /* 分配page数 */
  3. u16 jited:1, /* prog是否已经jit过*/
  4. jit_requested:1,/* 是否需要jit */
  5. undo_set_mem:1, /* Passed set_memory_ro() checkpoint */
  6. gpl_compatible:1, /* Is filter GPL compatible? */
  7. cb_access:1, /* Is control block accessed? */
  8. dst_needed:1, /* Do we need dst entry? */
  9. blinded:1, /* 常量致盲 */
  10. is_func:1, /* eBPF func? 大多数情况是 */
  11. kprobe_override:1, /* 是否是overrided kprobe */
  12. has_callchain_buf:1; /* callchain buffer allocated? */
  13. enum bpf_prog_type type; /* prog类型,eg kprobe 、tracepoint*/
  14. enum bpf_attach_type expected_attach_type; /* For some prog types */
  15. u32 len; /* eBPF指令个数 */
  16. u32 jited_len; /* eBPF汇编指令代码总长度 */
  17. u8 tag[BPF_TAG_SIZE];
  18. struct bpf_prog_aux *aux; /* Auxiliary fields */
  19. struct sock_fprog_kern *orig_prog; /* Original BPF program */
  20. unsigned int (*bpf_func)(const void *ctx,
  21. const struct bpf_insn *insn);/* 存放jit后的可执行汇编 */
  22. /* 不支持jit,需要模拟,x64支持jit,不需要模拟 */
  23. union {
  24. struct sock_filter insns[0]; /* 从用户态拷贝来的eBPF原程序 */
  25. struct bpf_insn insnsi[0];
  26. };
  27. };
  1. 第一参数cmd
  2. enum bpf_cmd {
  3. BPF_MAP_CREATE, //前五个是操作Map的
  4. BPF_MAP_LOOKUP_ELEM,
  5. BPF_MAP_UPDATE_ELEM,
  6. BPF_MAP_DELETE_ELEM,
  7. BPF_MAP_GET_NEXT_KEY,
  8. BPF_PROG_LOAD, //eBPF字节码加载
  9. BPF_OBJ_PIN,
  10. BPF_OBJ_GET,
  11. BPF_PROG_ATTACH,
  12. BPF_PROG_DETACH,
  13. BPF_PROG_TEST_RUN,
  14. BPF_PROG_GET_NEXT_ID,
  15. BPF_MAP_GET_NEXT_ID,
  16. BPF_PROG_GET_FD_BY_ID,
  17. BPF_MAP_GET_FD_BY_ID,
  18. BPF_OBJ_GET_INFO_BY_FD,
  19. BPF_PROG_QUERY,
  20. BPF_RAW_TRACEPOINT_OPEN,
  21. BPF_BTF_LOAD, //加载btf信息
  22. BPF_BTF_GET_FD_BY_ID,
  23. BPF_TASK_FD_QUERY,
  24. BPF_MAP_LOOKUP_AND_DELETE_ELEM,
  25. BPF_MAP_FREEZE,
  26. BPF_BTF_GET_NEXT_ID,
  27. BPF_MAP_LOOKUP_BATCH,
  28. BPF_MAP_LOOKUP_AND_DELETE_BATCH,
  29. BPF_MAP_UPDATE_BATCH,
  30. BPF_MAP_DELETE_BATCH,
  31. BPF_LINK_CREATE,
  32. BPF_LINK_UPDATE,
  33. BPF_LINK_GET_FD_BY_ID,
  34. BPF_LINK_GET_NEXT_ID,
  35. BPF_ENABLE_STATS,
  36. BPF_ITER_CREATE,
  37. };
  1. BPF MAP 类型
  2. enum bpf_map_type {
  3. BPF_MAP_TYPE_UNSPEC = 0,
  4. BPF_MAP_TYPE_HASH = 1, //哈希表
  5. BPF_MAP_TYPE_ARRAY = 2, //数组映射,已针对快速查找速度进行了优化,通常用于计数器
  6. BPF_MAP_TYPE_PROG_ARRAY = 3, //对应eBPF程序的文件描述符数组;用于实现跳转表和子程序以处理特定的数据包协议
  7. BPF_MAP_TYPE_PERF_EVENT_ARRAY = 4, // linux kernel 4.4 存储指向struct perf_event的指针,用于读取和存储perf事件计数器
  8. BPF_MAP_TYPE_PERCPU_HASH = 5, //每个CPU的哈希表
  9. BPF_MAP_TYPE_PERCPU_ARRAY = 6, //每个CPU的数组
  10. BPF_MAP_TYPE_STACK_TRACE = 7, //存储堆栈跟踪
  11. BPF_MAP_TYPE_CGROUP_ARRAY = 8, //存储指向控制组的指针
  12. BPF_MAP_TYPE_LRU_HASH = 9, //仅保留最近使用项目的哈希表
  13. BPF_MAP_TYPE_LRU_PERCPU_HASH = 10, //每个CPU的哈希表,仅保留最近使用的项目
  14. BPF_MAP_TYPE_LPM_TRIE = 11, //最长前缀匹配树,适用于将IP地址匹配到某个范围
  15. BPF_MAP_TYPE_ARRAY_OF_MAPS = 12,
  16. BPF_MAP_TYPE_HASH_OF_MAPS = 13,
  17. BPF_MAP_TYPE_DEVMAP = 14, //用于存储和查找网络设备引用
  18. BPF_MAP_TYPE_SOCKMAP = 15, //存储和查找套接字,并允许使用BPF辅助函数进行套接字重定向
  19. BPF_MAP_TYPE_CPUMAP = 16,
  20. BPF_MAP_TYPE_XSKMAP = 17,
  21. BPF_MAP_TYPE_SOCKHASH = 18,
  22. BPF_MAP_TYPE_CGROUP_STORAGE = 19,
  23. BPF_MAP_TYPE_REUSEPORT_SOCKARRAY = 20,
  24. BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE = 21,
  25. BPF_MAP_TYPE_QUEUE = 22,
  26. BPF_MAP_TYPE_STACK = 23,
  27. BPF_MAP_TYPE_SK_STORAGE = 24,
  28. BPF_MAP_TYPE_DEVMAP_HASH = 25,
  29. BPF_MAP_TYPE_STRUCT_OPS = 26,
  30. BPF_MAP_TYPE_RINGBUF = 27, // linux kernel 5.8 Perf Buffer增强版
  31. BPF_MAP_TYPE_INODE_STORAGE = 28,
  32. };

详细介绍: BFP MAP介绍

  1. BPF PROG 类型
  2. helper函数使用范围】不同类型eBPF程序可以使用的eBPF helper函数范围
  3. enum bpf_prog_type {
  4. BPF_PROG_TYPE_UNSPEC,
  5. BPF_PROG_TYPE_SOCKET_FILTER, //网络数据包过滤器
  6. BPF_PROG_TYPE_KPROBE, //确定是否应触发kprobe
  7. BPF_PROG_TYPE_SCHED_CLS, //网络流量控制分类器
  8. BPF_PROG_TYPE_SCHED_ACT, //网络流量控制操作
  9. BPF_PROG_TYPE_TRACEPOINT, //确定是否应触发跟踪点
  10. BPF_PROG_TYPE_XDP, //从设备驱动程序接收路径运行的网络数据包筛选器
  11. BPF_PROG_TYPE_PERF_EVENT, //确定是否应该触发性能事件处理程序
  12. BPF_PROG_TYPE_CGROUP_SKB, //用于控制组的网络数据包过滤器
  13. BPF_PROG_TYPE_CGROUP_SOCK, //用于控制组的网络数据包筛选器,允许修改套接字选项
  14. BPF_PROG_TYPE_LWT_IN,
  15. BPF_PROG_TYPE_LWT_OUT,
  16. BPF_PROG_TYPE_LWT_XMIT,
  17. BPF_PROG_TYPE_SOCK_OPS,
  18. BPF_PROG_TYPE_SK_SKB,
  19. BPF_PROG_TYPE_CGROUP_DEVICE,
  20. BPF_PROG_TYPE_SK_MSG,
  21. BPF_PROG_TYPE_RAW_TRACEPOINT,
  22. BPF_PROG_TYPE_CGROUP_SOCK_ADDR,
  23. BPF_PROG_TYPE_LWT_SEG6LOCAL,
  24. BPF_PROG_TYPE_LIRC_MODE2,
  25. BPF_PROG_TYPE_SK_REUSEPORT,
  26. BPF_PROG_TYPE_FLOW_DISSECTOR,
  27. BPF_PROG_TYPE_CGROUP_SYSCTL,
  28. BPF_PROG_TYPE_RAW_TRACEPOINT_WRITABLE,
  29. BPF_PROG_TYPE_CGROUP_SOCKOPT,
  30. BPF_PROG_TYPE_TRACING,
  31. BPF_PROG_TYPE_STRUCT_OPS,
  32. BPF_PROG_TYPE_EXT,
  33. BPF_PROG_TYPE_LSM,
  34. };

0x3:BPF寄存器

eBPF从bpf的两个32位寄存器扩展到10个64位寄存器R0~R9和一个只读栈帧寄存器,并支持call指令,更加贴近现代64位处理器硬件

  1. R0对应rax 函数返回值
  2. R1对应rdi 函数参数1
  3. R2对应rsi 函数参数2
  4. R3对应rdx 函数参数3
  5. R4对应rcx 函数参数4
  6. R5对应r8 函数参数5
  7. R6对应rbx callee保存
  8. R7对应r13 callee保存
  9. R8对应r14 callee保存
  10. R9对应r15 callee保存
  11. R10对应rbp,只读栈帧寄存器

0x4:内核路径

  1. /Documentation/bpf/btf.rst
  2. /include/uapi/linux/bpf_common.h /include/uapi/linux/bpf.h 定义了指令集
  3. /samples/bpf 相关的样例
  4. /tools/bpf/bpftool 工具,用来调试bpf
  5. /tools/testing/selftests/bpf 测试代码

本文由博客一文多发平台 OpenWrite 发布!

原文链接:http://www.cnblogs.com/senberhu/p/15735291.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号