2021 年的 eBPF

简介: 诞生之初,eBPF 选择了一条非常务实的演进之路,没有选择重新造轮子,从优化 cBPF ([net: filter: rework/optimize internal BPF interpreter's instruction set](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=bd4

诞生之初,eBPF 选择了一条非常务实的演进之路,没有选择重新造轮子,从优化 cBPF (net: filter: rework/optimize internal BPF interpreter's instruction set) 开始并在此基础上不断进化演进。经过社区几年的快速迭代,在 5.10 版本中 eBPF 技术已经非常成熟,生态也已经初具规模,特别是在跟踪、网络和安全领域,明星产品包括 Cillium、BCC 和 Sysdig 等,同时面向 eBPF 开发者的 eBPF Summit 2020 也如约而至。

特性演进

自 Linux 4.19 内核以来,社区一共为 eBPF 带来了超过 160 个新特性和优化改进,主要集中在这三部分:BPF (VM、bytecode、程序类型和数据结构)、网络、安全。

字节码、虚拟机

首先是 BPF 自身能力的增强,包括 BPF 虚拟机、字节码、程序类型和基础数据结构等。截止到 Linux LTS 5.10 版本为例,几个最为亮眼的特性是:

  • 5.1:支持 bpf_spin_lock;
  • 5.3:支持有界循环;
  • 5.6:verifier 允许的最大 BPF 指令数量放宽至 100 万条;
  • 5.10:开始支持 BPF 程序睡眠;

从上面的特性可以看出来,这是一个非常明显的信号,社区想要扩大 eBPF 的能力,不仅仅局限在编写简单的 kprobe 跟踪程序,希望 eBPF 能够逐步地替换掉部分内核的能力,当然这一切都是在保证安全的前提下。

eBPF verifier 允许最大 BPF 指令放宽至 100 万条,这个为 BPF 支持更复杂的场景带来了可能,要知道之前 verifier 的指令上限是 4096,100 万条指令意味着可以将用户态复杂的逻辑放到内核态,以后有可能将内核中的一部分能力通过 eBPF 安全的暴露出来,用户态可以自由的定制并热加载到内核中,同时性能相对于原生没有明显下降。除此之外,类似 nginx 这种性能敏感的应用有可能编译为 eBPF ISA 字节码并在内核中运行,带来更极致的性能。

其次 eBPF verifier 终于谨慎的支持有界循环,而不必使用类似达夫设备 #pragma unroll 的技巧。接下来 bpf_spin_lock 也为编写复杂的算法带来了可能,常见算法基本离不开并发安全。但是 eBPF 第一准则仍是安全!安全!安全!eBPF verifier 决不允许用户载入到内核的 BPF 程序将内核 panic。虽然这些特性只是一小步,但确实简化了用户编写 eBPF 的开发成本。当初不支持有界循环时,例如用户打算编写一个 NAT eBPF 程序在内核态遍历转发 map 是无法实现的,需要在用户态 syscall 操作 map,再通知内核根据确定的 key 得到所需的 value。

程序类型、数据结构

eBPF VM 和字节码的能力增强,新增的程序类型、数据结构和 helper 函数,一同为跟踪、网络和安全领域带来了更多可能性,列举几处:

  • 数据结构:queue、stack、mmap BPF array map、global data 和 ring buffer 等;
  • 跟踪:kprobe/uprobe 支持同时多 probe 等;
  • 网络:sk_local_storage、各种 iterator、struct_ops 和 sk/skb helper 等;
  • 安全:Google 贡献的 bpf-lsm;
  • 其他:CO-RE、status monitor、dispatcher、BTF check asm 更快更安全、完善 ISA 指令、提高 verifer 稳定性等;

生态建设

eBPF 技术的蓬勃向上,离不开周边生态的工具和文档的建设。工欲善其事,必先利其器,对于开发者而言,如何通过工具、库和文档快速验证并构建所需的应用是至关重要的能力。

工具链

几年前,iovisor 为 eBPF 带来了 BCC、bpftrace 等工具,成为了当时 eBPF 在跟踪领域的最佳实践,同时非常多的开发者基于 BCC 的运行时编译的 Python 库二次开发大量的 eBPF 工具。由于 BCC Python 的运行时编译的方式的局限性,BCC 目前正在逐步过渡到基于社区 libbpf 的方案,通过 BTF + CO-RE + libbpf 实现了一次编译,到处运行的能力。同时 libbpf + bpftool 也极大地解放了开发者开发 eBPF 的心智负担,做到 out-of-tree 开发、bundle eBPF object 和 userspace bianry,将 eBPF map 创建、load、attach kprobe 等细节封装,对外提供 skeleton 快速开发。

影响力

社区一开始并没有大力的宣传 eBPF 的能力,但是仍然吸引非常多的开发者参与其中。2019 年 LSFMM 上网络子系统维护者 Miller 所说:"there is no "advertising machine" for this technology"。最近几年,eBPF 社区开始不断的向外发声:各大会议上陆续增加 eBPF 相关讨论,The 2019 Linux Storage, Filesystem, and Memory-Management Summit,同时 eBPF Summit 2020 也如期举行。Cillium 作为最为广泛运用的 eBPF 产品,其中 cillium 多位核心开发者也是 eBPF 子系统的核心开发者。ebpf.io 网站也在 cillium 社区的帮助下,顺利上线并为大家介绍和了解 eBPF 最新特性和场景。

除了社区外,Sysdig、Google、Facebook 等等公司也在大力推广 eBPF。例如 Google Kubernetes Engine 已经宣布将 Cilium 作为数据平面。除此之外,eBPF 的布道者 Brendan Gregg 基于 eBPF 在性能调优等场景编写了新书《BPF Performance Tools》。国内网易、腾讯、字节和华为等厂商也多次发文介绍通过 eBPF 来增强系统的可观察性、加速云原生网络等。

未来展望

当前 eBPF 还有很多可以增强之处,例如随着 eBPF 字节码行数的放宽,可见 eBPF 的程序复杂度和体积也会成倍增加,当前还没有一整套可复用的库帮助用户复用代码,快速构建开发能力。当前用户开发的 eBPF 模式,更多的是类似开发监控脚本等短平快的开发模式。随着 BPF BTF 特性的不断完善,以后可以通过 BTF 让 eBPF 程序感知到内核所提供的 eBPF 库,帮助用户开发重复代码,提高开发效率。同时 eBPF 自身的可调试能力也需要增强,特别是 eBPF 程序经常注入在热路径,如果编写低效的代码会带来额外的开销。

除此之外,eBPF 作为一种字节码,可以提供一种平台无关、高效、安全的通用字节码。例如有内核开发者使用 eBPF 编写内核红外线社区驱动,旨在简化兼容数百个红外线设备。使用 eBPF 编写闭源的 NVIDIA eBPF 驱动。除内核外,在 Rust Conf 2020 上,开发者基于用户态 eBPF VM - uBPF + Rust 编写了通过 eBPF 字节码执行的智能合约,几年前也有相关开发者提出了 common eBPF 能力,将 eBPF 硬化至网卡驱动。OSDI 2020 最佳论文《hXDP: Efficient Software Packet Processing on FPGA NICs》也提出了一种将 eBPF 字节码迁移至 FPGA 设备运行的思路,值的我们继续探索。

附录

eBPF 关键特性一览(自 5.0 内核起):

Linux 5.0

  1. queue / stacks maps
  2. socket lookup
  3. per cpu cgroup local storage
  4. flow dissectors

Linux 5.1

  1. add ip encapsulations headers to packets
  2. add jump32 instruction
  3. access tcp_sock bpf_sock, BPF_FUNC_sk_fullsock & BPF_FUNC_tcp_sock
  4. bpftools dump bpf related parameters
  5. skb_shared_info->gso_segs
  6. support __int128 type
  7. per program stats to monitor usage eBPF
  8. bpf_spin_lock
  9. queue/stack manipulations
  10. avoid unloading xdp prog not attached by samples
  11. RISCV BPF JIT supported

Linux 5.2

  1. kbuild, generate BTF type info for vmlinux and kernel modules
  2. global data support
  3. BPF_CGROUP_SYSCTL call sysctl proc_handler authorized
  4. new arguments for bpf_attr for BPF_PROG_TEST_RUN
  5. bpf sk local storage make bpf networking programming more intuitive
  6. var offset stack access
  7. checking SYN cookies from xdp and tc cls
  8. extend bpf_skb_adjust_room growth to mark inner mac headers
  9. import verifier scalability
  10. opt-in interface for tracepoints to exporse a writeable context
  11. bpf_skb_ecn_set_ce callable from BPF_GROG_TYPE_SCHED_ACT

Linux 5.3

  1. libbpf BTF-to-C dumping support
  2. new way to specify BPF maps
  3. propagating congestion notifications to TCP from cgroupo inet skb egress
  4. SO_DETACH_REUSEPORT_BPF to detach BPF prog from reuseport sk
  5. sock_ops_callback can be selectively enabled on a socket
  6. CGROUP_SKB porg to use bpf_skb_cgroup_id
  7. eliminate zero extensions for sub-register write
  8. export bpf_sock for BPF_PROG_TYPE_CGROUP_SOCK_ADDR prog type
  9. wide aligned stores for some filed of bpf_sock_addr
  10. fq's Earliest Departure Time to HBM
  11. bounded loops
  12. bpf getsockopt and setsockopt hooks

Linux 5.4

  1. additions of multiprobes to kprobe and uprobe events: support more than on probe in the same locations
  2. XDP devmap_hash looking up devices by hashed index
  3. BPF ids in procfs for FD to BTF objects
  4. BPF_BTF_GET_NEXT_ID byf() syscall to list all BTF objs
  5. BPF_F_TEST_STATE_FREQ stress test
  6. export BTF info through /sys/kernel/btf
  7. implement ceneral part of CO_RE
  8. BPF helper to generate SYN cookies
  9. bpftool add net attach/detach command to attach XDP prog
  10. bpftool work with frozen map
  11. bpftool add support for reporting the effective cgroup progs

Linux 5.5

  1. in-kernel BTF to type check BPF assembly code. allows safer and faster BPF tracing
  2. Introduce BPF trampoline to allow kernel code to call into BPF programs with practically zero overhead
  3. support for memory-mapping BPF array maps
  4. Optimize BPF tail calls for direct jumps
  5. Add probe_read_user, probe_read_kernel and probe_read_user_str, probe_read_kernel_str helpers
  6. bpftool: Allow to read btf as raw data
  7. low_dissector: add mode to enforce global BPF flow dissector

Linux 5.6

  1. Introduce the BPF dispatcher, a mechanism to avoid indirect calls and helps to avoid repotlines performance hit
  2. Introduce BPF STRUCT_OPS.
  3. Introduce batch ops that can be added to bpf maps to lookup/lookup_and_delete/update/delete more than 1 element at the time
  4. Emit audit messages upon successful prog load and unload
  5. Program extensions or dynamic re-linking
  6. Introduce static vs global functions and function by function verification, another step toward dynamic re-linking
  7. replacing cgroup-bpf programs attached with BPF_F_ALLOW_MULTI flag so that any program in a list can be updated to a new version without service interruption and order of programs can be preserved
  8. Implements a new BPF feature probe, which increases the maximum program size to 1M

Linux 5.7

  1. Extend SOCKMAP to store listening as well as established sockets
  2. Make BPF and PREEMPT_RT co-exist
  3. BPF programs may want to know whether an skb is gso.
  4. Add bpf_sk_assign eBPF helper, it allows assigning a previously-found socket to the skb as the packet is received towards the stack, to cause the stack to guide the packet towards that socket subject to local routing configuration.
  5. Add bpf_sk_storage_get() and bpf_sk_storage_delete() helper to the bpf_tcp_ca's struct_ops
  6. Provide bpf_sk_storage data during inet_diag's dump
  7. Add support for storing UDP sockets in sockmap and sockhash
  8. Adds bpftool struct_ops to support struct_ops features
  9. Introduce bpftool prog profile command, which uses hardware counters to profile BPF programs
  10. Allow per-file SELinux labeling for bpffs
  11. bpf-lsm: A BPF-based Linux Security Module

Linux 5.8

  1. Introduce CAP_PERFMON to secure system performance monitoring and observability
  2. Introduce CAP_BPF to split BPF operations that are allowed under CAP_SYS_ADMIN into combination of CAP_BPF, CAP_PERFMON, CAP_NET_ADMIN and keep some of them under CAP_SYS_ADMIN. The user process has to have: CAP_BPF to create maps and do other sys_bpf() commands, CAP_BPF and CAP_PERFMON to load tracing programs, and CAP_BPF plus CAP_NET_ADMIN to load networking programs
  3. bpftool: Allow probing for CONFIG_HZ from kernel config
  4. Add get{peer,sock}name cgroup attach types to the BPF sock_addr programs in order to enable rewriting sockaddr structs
  5. Add sk_msg and networking helpers to all networking programs with perfmon_capable() capabilities
  6. Implement a new BPF ring buffer
  7. The bpf iterator provides in-kernel aggregation abilities for kernel data. This can greatly improve performance compared to e.g., iterating all process directories under /proc
  8. Introduce a new bpf_link type for attaching to network namespace
  9. Add rx_queue_mapping to bpf_sock
  10. Sharing bpf runtime stats with BPF_ENABLE_STATS
  11. bpf_{g,s}etsockopt for struct bpf_sock_addr

Linux 5.9

  1. Add a text poke event to record changes to kernel text (i.e. self-modifying code) in order to support tracers like Intel PT decoding through jump labels, kprobes and ftrace trampolines
  2. Add a new BPF program type named BPF_PROG_TYPE_SK_LOOKUP
  3. XDP link: Following cgroup and netns examples, implement bpf_link support for XDP
  4. Add BPF_CGROUP_INET_SOCK_RELEASE hook. Sometimes it's handy to know when the socket gets freed.
  5. Add support of SO_KEEPALIVE flag and TCP related options to bpf_setsockopt() routine.
  6. Add d_path helper
  7. Add new BPF link operation that allows processes with BPF link FD to force-detach it from respective BPF hook, similarly how BPF link is auto-detached when such BPF hook (e.g., cgroup, net_device, netns, etc) is removed.
  8. Expose socket storage to BPF_PROG_TYPE_CGROUP_SOCK
  9. Implement bpf iterator for map elements. User can have a bpf program in kernel to run with each map element, do checking, filtering, aggregation, modifying values etc.
  10. Iterator for tcp and udp sockets. This gives great flexibility for users to examine kernel data structure without using e.g. /proc/net
  11. Introduces a new helper bpf_get_task_stack()

Linux 5.10

  1. Sockmap iterator
  2. BPF TCP header options
  3. Introduce minimal support for sleepable progs
  4. Add a kernel module with user mode driver that populates bpffs with two BPF iterators
  5. Add tcp_notsent_lowat bpf setsockopt
  6. BTF support for ksyms
  7. Add support attaching freplace BPF programs to multiple targets. This is needed to support incremental attachment of multiple XDP programs using the libxdp dispatcher model
  8. Allow updating sockmap / sockhash from BPF
  9. Implement link_query for bpf iterators
目录
相关文章
|
3月前
|
Linux 数据安全/隐私保护 Perl
bpf对内核的观测
bpftrace 可以对线上项目的系统调用的函数的进行观测,对观测的结果做出分析
44 0
|
存储 Rust 安全
服务网格eBPF应用探索之(一)eBPF基础知识
1)技术背景在eBPF诞生之前,对内核的调试和开发有着相当高的门槛,不仅要十分熟悉庞大的内核代码及开发流程,同时重新编译内核后若希望生效还需要重启OS,开发效率也相当低下。而eBPF提供了相当友好的内核开发/观测机制,即:由用户编写符合一定规范的代码,编译后加载至内核,内核会在指定的时机执行这段代码,内核同时还会将Hook点相关的上下文传递给这段代码供使用,代码可以修改上下文,或是通过返回值来改变
695 0
服务网格eBPF应用探索之(一)eBPF基础知识
|
2月前
|
存储 安全 编译器
eBPF是如何工作的
【2月更文挑战第1天】
|
2月前
|
存储 安全 Ubuntu
eBPF程序如何跟内核进行交互
【2月更文挑战第4天】 一个完整的 eBPF 程序,通常包含用户态和内核态两部分:用户态程序需要通过 BPF 系统调用跟内核进行交互,进而完成 eBPF 程序加载、事件挂载以及映射创建和更新等任务;而在内核态中,eBPF 程序也不能任意调用内核函数,而是需要通过 BPF 辅助函数完成所需的任务。尤其是在访问内存地址的时候,必须要借助 bpf_probe_read 系列函数读取内存数据,以确保内存的安全和高效访问。
|
4月前
|
Ubuntu Linux
|
5月前
|
监控 Kubernetes 网络协议
DoorDash 基于 eBPF 的监控实践
DoorDash 基于 eBPF 的监控实践
78 0
|
9月前
|
存储 Kubernetes Cloud Native
Cilium eBPF 网络解析
在上一篇文章中,我们简要地解析了 eBPF 内核独立子系统的基本概念、发展历史、架构模型以及优缺点等,具体可参考:Linux eBPF解析。
196 0