由于网络上ebpf相关的文章零散且杂乱,所以便编写了本文,用于指导入门Linux kernel ebpf开发(网络方向)
简介
在3.18及之后的Linux内核中被加入了一些hook点,在这些hook点上可支持开发者动态插入、运行自定义的程序,而这些程序,就被称为ebpf,全称Extended Berkeley Packet Filter(觉得网上的介绍废话太多,此为纯个人理解精简版)
ebpf程序架构
ebpf程序需要分为用户态ebpf app和内核ebpf程序(elf文件),并且大致可以分为以下2类:
1.用户态ebpf应用程序只负责加载/卸载内核ebpf程序,其结构如下图所示:
2.用户态ebpf应用程序不仅负责加载/卸载内核ebpf,并且还与内核ebpf利用map进行数据交互,如下图所示:
ebpf的程序类型
ebpf的程序类型,即内核哪几种类型的ebpf程序,如下图所示(以4.18内核为例):
其中在二三层网络主要会使用到的是BPF_PROG_TYPE_XDP以及BPF_PROG_TYPE_SCHED_CLS(tc)两类程序(目前只有这两类程序可实现drop网络报文)
ebpf程序的编译
对于ebpf用户态应用程序的编译,选择gcc编译器即可
对于kernel eBPF程序的编译,目前推荐使用Clang/LLVM编译器进行编译,其编译流程如下图所示:
Clang/LLVM编译器架构如下图所示:
ebpf程序开发方式的选择
由于ebpf程序需要分为用户态ebpf应用和内核态ebpf程序,根据以上编译原理可知,这两者在一定程度上是可以做到各自独立开发的,如此以来便需要根据实际情况,选择一个合适的开发方式,介绍目前主流的4种开发方式:
1.bcc:运行和编译强绑定,每次运行前都需要编译,且内存占用大(不推荐)
2.原生libbpf:因为是原生libbpf,所以其API的封装更偏灵活,如此它便更适已有用户态app,但需要加入ebpf程序的开发场景(API_docs:https://libbpf.readthedocs.io/en/latest/api.html)
3.libbpf-bootstrap(参考资料:https://nakryiko.com/posts/libbpf-bootstrap/)
libbpf-bootstrap是基于libbpf和BPF CO-RE的开发框架,推荐从零开始开发全新ebpf用户态以及内核态程序时使用(只需要ebpf字节码的也可用此框架,用户态应用main留空即可),因为开发较便捷(封装了一些内容),并且可以利用btf实现不依赖内核版本运行bpf程序,且性能优于bcc
其框架工作流程如下:
(1)clang编译出ebpf字节码elf文件(xxx.bpf.o)
(2)bpftools利用上诉字节码生成骨架文件(xxx.skel.h),其中会嵌入xxx.bpf.o(static inline const void xxx_bpf__elf_bytes(size_t sz))
(3)用户态程序则需要include上诉骨架文件,进行开发,最后使用gcc编译
同时使用xdp和egress的ebpf例程:
https://github.com/avalonLZ/Practices/tree/master/ebpf/xdp_and_tc
替换libbpf-bootstrap,examples/c路径下的tc,再执行make tc即可编译出tc用户态和内核态ebpf程序
4.eunomia-bpf:可以自动生成用户态程序,从零开始开发,且对用户态应用程序定制化要求不高的,可尝试使用此种开发方式
map
map在ebpf程序中起到了数据共享的作用,它是用户态与内核态ebpf程序,以及内核态ebpf程序相互之间数据交互的唯一通道,其类型如下图所示:
其常用map及其使用场景如下(参考资料:https://arthurchiao.art/blog/bpf-advanced-notes-2-zh/):
BPF_MAP_TYPE_HASH//ebpf间共享数据可用(链表实现,省空间)—该结构update等接口是线程安全的
BPF_MAP_TYPE_ARRAY//用户态下发配置给ebpf可用(数组实现,key只能是数组索引)—该结构update等接口非线程安全(用户态应用与内核软中断处理),使用时需要注意
xdp
着重说明下xdp类型的ebpf程序需要关注的点
注意:xdp只有ingress方向的hook点
入参数据结构
ingress hook点
使用bpf_xdp_attach挂载的hook点(内核根据bpf_xdp_attach下发的flags,去选择挂载ebpf的hook点,在kernel dev_change_xdp_fd函数中处理)
XDP_FLAGS_UPDATE_IF_NOEXIST//驱动不支持xdp时,退化使用generic hook点,否则使用dev hook点
XDP_FLAGS_SKB_MODE//强制使用generic hook点
XDP_FLAGS_DRV_MODE//强制使用dev hook点
XDP_FLAGS_HW_MODE//hw offload时使用
tc
着重说明下tc类型的ebpf程序需要关注的点
参考资料:
https://cloud.tencent.com/developer/article/1802121
https://docs.cilium.io/en/latest/bpf/progtypes/#tc-traffic-control
用tc da模式实现,需要有个clsact qdisc,该clasct qdisc支持在egress和ingress两个方向上attach ebpf程序
入参数据结构
ingress hook点
hook点:sch_handle_ingress
注意:添加clsact类型的qdisc后,无法再添加ingress方向的限速,clsact qdisc和ingress qdisc没法共存(egree方向的限速不受影响)
egress hook点
hook点:sch_handle_egress(在qos前执行)
如何观测ebpf对于网络的性能损耗
网络方面,可发64字节小包,通过pps看是否对吞吐性能有影响,时延类似
其他参考资料
http://arthurchiao.art/blog/understanding-ebpf-datapath-in-cilium-zh/
https://zhuanlan.zhihu.com/p/496468540(ebpf对象,生命周期的维护)