乐趣区

关于linux:运维人不得不了解的eBPF入门指南新手建议收藏

eBPF(Extended Berkeley Packet Filter)的外围是驻留在 kernel 的高效虚拟机。最后的目标是高效网络过滤框架,前身是 BPF,所以咱们先理解下 BPF

BPF

框架


上图是 BPF 的地位和框架,须要留神的是 kernel 和 user 应用了 buffer 来传输数据,防止频繁上下文切换。BPF 虚拟机十分简洁,由累加器、索引寄存器、存储、隐式程序计数器组成。

示例

接下来咱们看一下示例,过滤所有 ip 报文,能够应用 tcpdump -d ip 查看:


(000) ldh      [12]                             // 链路层第 12 字节的数据加载到寄存器,ethertype 字段
(001) jeq      #0x800           jt 2    jf 3    // 比拟寄存器的 ethertype 字段是否为 IP 类型,true 跳到 2,false 跳到 3
(002) ret      #65535                           // 返回 true
(003) ret      #0                               // 返回 0 

BPF 只应用了 4 条虚拟机指令,就能提供十分有用的 IP 报文过滤。


tcpdump -d tcp

(000) ldh      [12]                             // 链路层第 12 字节的数据 (2 字节) 加载到寄存器,ethertype 字段
(001) jeq      #0x86dd          jt 2    jf 7    // 判断是否为 IPv6 类型,true 跳到 2,false 跳到 7
(002) ldb      [20]                             // 链路层第 20 字节的数据 (1 字节) 加载到寄存器,IPv6 的 next header 字段
(003) jeq      #0x6             jt 10    jf 4    // 判断是否为 TCP,true 跳到 10,false 跳到 4
(004) jeq      #0x2c            jt 5    jf 11   // 可能是 IPv6 分片标记,true 跳到 5,false 跳到 11
(005) ldb      [54]                             // 我编不上来了...
(006) jeq      #0x6             jt 10    jf 11   // 判断是否为 TCP,true 跳到 10,false 跳到 11
(007) jeq      #0x800           jt 8    jf 11   // 判断是否为 IP 类型,true 跳到 8,false 跳到 11
(008) ldb      [23]                             // 链路层第 23 字节的数据 (1 字节) 加载到寄存器,next proto 字段
(009) jeq      #0x6             jt 10    jf 11   // 判断是否为 TCP,true 跳到 10,false 跳到 11
(010) ret      #65535                           // 返回 true
(011) ret      #0                               // 返回 0 

以上是 freebsd 的 BPF,Linux 中应该不叫这个,叫 LSF,本人看吧。

eBPF

eBPF 初识

Linux kernel 3.18 版本开始蕴含了 eBPF,绝对于 BPF 做了一些重要改良,首先是效率,这要归功于 JIB 编译 eBPF 代码;其次是利用范畴,从网络报文扩大到个别事件处理;最初不再应用 socket,应用 map 进行高效的数据存储。

依据以上的改良,内核开发人员在不到两年半的事件,做出了包含网络监控、限速和系统监控。

目前 eBPF 能够合成为三个过程:

  • 以字节码的模式创立 eBPF 的程序。编写 C 代码,将 LLVM 编译成驻留在 ELF 文件中的 eBPF 字节码。
  • 将程序加载到内核中,并创立必要的 eBPF-maps。eBPF 具备用作 socket filter,kprobe 处理器,流量管制调度,流量管制操作,tracepoint 解决,eXpress Data
    Path(XDP),性能监测,cgroup 限度,轻量级 tunnel 的程序类型。
  • 将加载的程序 attach 到零碎中。依据不同的程序类型 attach 到不同的内核零碎中。程序运行的时候,启动状态并且开始过滤,剖析或者捕捉信息。

2016 年 10 月的 NetDev 1.2 大会上,Netronome 的 Jakub Kicinski 和 Nic Viljoen 发表了题目为“eBPF / XDP 硬件卸载到 SmartNIC”。Nic Viljoen 在其中介绍了 Netronome SmartNIC 上每个 FPC 每秒达到 300 万个数据包,每个 SmartNIC 有 72 到 120 个 FPC,可能最大反对 eBPF 吞吐量 4.3 Tbps!(实践上)

eBPF 入口

接下来咱们以内核版本 4.14 版本为例进行查看。

bpf 的零碎调用

kernel/bpf/syscall.c

bpf 零碎调用的头文件

include/uapi/linux/bpf.h

入口函数

int bpf(int cmd, union bpf_attr *attr, unsigned int size);

kernel/bpf/syscall.c 中的宏定义开展。

eBPF 命令

Linux 零碎的 BPF 零碎调用有 10 个命令,其中 man page 中列出了 6 个:

  • BPF_PROG_LOAD 验证并且加载 eBPF 程序,返回一个新的文件描述符。
  • BPF_MAP_CREATE 创立 map 并且返回指向 map 的文件描述符
  • BPF_MAP_LOOKUP_ELEM 通过 key 从指定的 map 中查找元素,并且返回 value 值
  • BPF_MAP_UPDATE_ELEM 在指定的 map 中创立或者更新元素(key/value 配对)
  • BPF_MAP_DELETE_ELEM 通过 key 从指定的 map 中找到元素并且删除
  • BPF_MAP_GET_NEXT_KEY 通过 key 从指定的 map 中找到元素,并且返回下个 key 值

以上的命令能够分为两大类,加载 eBPF 程序和 eBPF-maps 操作。eBPF-maps 操作有很大的自主性,用于创立 eBPF-maps,从中查找、更新和删除元素,遍历 eBPF-maps(BPF_MAP_GET_NEXT_KEY)

接下来列一下剩下的 4 个命令,在代码中能够看到:

  • BPF_OBJ_PIN 4.4 版本新加的,属于持久性 eBPF。有了这个,eBPF-maps 和 eBPF 程序能够放入 /sys/fs/bpf
  • BPF_OBJ_GET 同上,在这之前,没有工具能创立 eBPF 程序,并且完结,因为会毁坏 filter,而文件系统能够在创立他们的程序退出后仍然保留 eBPF-maps 和 eBPF 程序
  • BPF_PROG_ATTACH 4.10 版本中增加的,将 eBPF 程序 attach 到 cgroup,这样实用于 container
  • BPF_PROG_DETACH 同上。

eBPF-map 类型

Linux 零碎的 BPF 零碎调用有 10 个命令,其中 man page 中列出了 6 个:

BPF_PROG_LOAD 验证并且加载 eBPF 程序,返回一个新的文件描述符。

BPF_MAP_CREATE 创立 map 并且返回指向 map 的文件描述符

BPF_MAP_LOOKUP_ELEM 通过 key 从指定的 map 中查找元素,并且返回 value 值

BPF_MAP_UPDATE_ELEM 在指定的 map 中创立或者更新元素(key/value 配对)

BPF_MAP_DELETE_ELEM 通过 key 从指定的 map 中找到元素并且删除

BPF_MAP_GET_NEXT_KEY 通过 key 从指定的 map 中找到元素,并且返回下个 key 值

以上的命令能够分为两大类,加载 eBPF 程序和 eBPF-maps 操作。eBPF-maps 操作有很大 的自主性,用于创立 eBPF-maps,从中查找、更新和删除元素,遍历 eBPF-maps(BPF_MAP_GET_NEXT_KEY)

接下来列一下剩下的 4 个命令,在代码中能够看到:

  • BPF_OBJ_PIN 4.4 版本新加的,属于持久性 eBPF。有了这个,eBPF-maps 和 eBPF 程序能够放入 /sys/fs/bpf
  • BPF_OBJ_GET 同上,在这之前,没有工具能创立 eBPF 程序,并且完结,因为会毁坏 filter,而文件系统能够在创立他们的程序退出后仍然保留 eBPF-maps 和 eBPF 程序
  • BPF_PROG_ATTACH 4.10 版本中增加的,将 eBPF 程序 attach 到 cgroup,这样实用于 container
  • BPF_PROG_DETACH 同上。

eBPF-map 类型

BPF_MAP_TYPE_UNSPEC
  • BPF_MAP_TYPE_HASH eBPF-maps hash 表,是次要用的前两种形式之一
  • BPF_MAP_TYPE_ARRAY 和下面相似,除了索引像数组一样
  • BPF_MAP_TYPE_PROG_ARRAY将加载的 eBPF 程序的文件描述符保留其值,罕用的是应用数字辨认不同的 eBPF 程序类型,也能够从一个给定 key 值的 eBPF-maps 找到 eBPF 程序,并且跳转到程序中去
  • BPF_MAP_TYPE_PERF_EVENT_ARRAY配合 perf 工具,CPU 性能计数器,tracepoints,kprobes 和 uprobes。能够查看门路 samples/bpf/ 下的 tracex6_kern.c,tracex6_user.c,tracex6_kern.c,tracex6_user.c
  • BPF_MAP_TYPE_PERCPU_HASH 和 BPF_MAP_TYPE_HASH 一样,除了是为每个 CPU 创立
  • BPF_MAP_TYPE_PERCPU_ARRAY 和 BPF_MAP_TYPE_ARRAY一样,除了是为每个 CPU 创立

BPF_MAP_TYPE_STACK_TRACE 用于存储 stack-traces

BPF_MAP_TYPE_CGROUP_ARRAY 查看 skb 的 croup 归属

BPF_MAP_TYPE_LRU_HASH
BPF_MAP_TYPE_LRU_PERCPU_HASH
  • BPF_MAP_TYPE_LPM_TRIE 最业余的用法,LPM(Longest Prefix Match)的一种 trie
  • BPF_MAP_TYPE_ARRAY_OF_MAPS 可能是针对每个 port 的
  • BPF_MAP_TYPE_HASH_OF_MAPS 可能是针对每个 port 的
  • BPF_MAP_TYPE_DEVMAP 可能是定向报文到 dev 的
  • BPF_MAP_TYPE_SOCKMAP 可能是连贯 socket 的

退出移动版