一篇深入解析BTF 实践指南

news2024/9/9 4:35:25

 BPF 是 Linux 内核中基于寄存器的虚拟机,可安全、高效和事件驱动的方式执行加载至内核的字节码。与内核模块不同,BPF 程序经过验证以确保它们终止并且不包含任何可能锁定内核的循环。BPF 程序允许调用的内核函数也受到限制,以确保最大的安全性以防止非法的访问。

尽管 BPF 为编写事件驱动的内核空间代码提供了一种有效的解决方案,但开发人员的体验仍无法与其他编程语言或框架相提并论。BPF 开发的两个最重要的问题是缺乏简单的调试和可移植性。

为了缓解这些问题,我们转向 BTF[1] 。BTF 是针对 BPF 程序的类型信息进行编码文件格式,通过 BPF 程序的类型信息进行编码,为程序提供更好的内省(introspection)和可见性。本文我们将介绍 BPF 的典型局限性以及如何使用 BTF 来克服。

请注意,本文使用术语 BPF 来表示  eBPF[2] (扩展的 Berkeley 数据包过滤器),eBPF 扩展 “ 经典 ” cBPF。

1. BPF 的常见限制

在 BPF 程序的开发和运行过程中,我们会经常会面临调试限制和可移植性问题,如前所述。

1.1 调试限制

几乎所有现代编程语言都有对应的调试器,通过调试器可以帮助我们更好了解正在运行的程序。例如,GDB[3] 是 C 和 C++ 的常用调试器,除其他外,基于 GDB 我们可以打印正在运行的程序中的变量值。

图 GDB 变量打印

但是很不幸,BPF 程序并没有类似的这样的工具。尽管检查数据只是调试的一小部分,但为 BPF 实现类似的结果可以为未来的广泛调试工具打开一扇门。为了实现这一点,BPF 需要知道关于程序的相关的部分元数据。

这类关于类型信息的元数据,正是 BTF 封装的内容。

1.2 可移植性

BPF 程序在内核空间中运行,可以访问内部内核状态和数据结构。但是,并没有办法保证内核数据结构和类型在不同内核版本是相同的,甚至相同内核版本的不同机器之间也可能不同(这可能取决于内核编译选项)。这意味着在一台机器上编译的 BPF 程序并不能保证在另一台机器上正确运行。

假设 BPF 程序正在从内核结构中读取一个字段,该字段位于距结构开头的偏移量 8 处。现在在更高版本的内核中,在该变量之前添加了其他字段,导致访问的字段的偏移量变成了 24,这会导致 BPF 程序在偏移量 8 读取的数据可能为垃圾数据。类似情况,也可能会发生某些字段最终得到在后续内核版本中的重命名。例如,在内核版本 4.6 和 4.7 之间,thread_struct 的 fs 字段可能会重命名为 fsbase 。最后,还可能是因为配置禁用了某些功能并编译了部分结构,导致可能 BPF 程序在不同的内核配置上运行。

所有上述这些场景的存在,意味着你不能在当前机器上编译 BPF 程序并将二进制文件分发到其他系统。

一个标准的解决方案是使用 BPF Compiler Collection (BCC)[4] 。使用 BCC,你通常将 BPF 程序作为纯字符串嵌入到用户空间程序(例如,Python 程序)中。在目标机器上执行期间,BCC 使用其嵌入式 Clang/LLVM 组合并使用本地安装的内核头文件动态编译程序。

然而,这种方法引入了更多问题。首先,Clang/LLVM 组合非常庞大,将其嵌入到应用程序中会导致二进制文件大小过大。它还占用大量资源,并且会在编译期间耗尽大量资源。最后,这种方法需要在目标机器上安装内核头文件,但情况可能并非总是如此。

解决方案是 BPF CO-RE(一次编译 —— 随处运行)。使用 BTF,我们可以消除在目标机器上安装内核头文件或将 Clang/LLVM 嵌入应用程序并在目标机器上编译的需要。

2. BTF 是什么?

如前所述,BTF 是编码 BPF 程序和 map 结构等相关的调试信息的元数据格式。BTF 可以将元数据数据类型、函数信息和行信息编码成一种紧凑的格式。

在非 BPF 程序中,这些元数据通常使用 DWARF[5] 格式存储。但是,DWARF 格式的实现还是相当复杂和冗长,并且由于其在大小方面的开销,使其不适合包含在内核中。而 BTF 是一种紧凑而简单的格式,让其可以包含在内核镜像中。

BTF 使用少数类型描述[6] 符之一表示每种数据类型:

  • BTF_KIND_INT
  • BTF_KIND_PTR
  • BTF_KIND_ARRAY
  • BTF_KIND_STRUCT
  • 等等

类型信息存储在生成的 ELF 的 .BTF 部分中。除了类型描述符之外,此部分还对字符串进行编码。函数和行信息存储在 .BTF.ext 部分中。

   资料直通车:Linux内核源码技术学习路线+视频教程内核源码

学习直通车:Linux内核源码内存调优文件系统进程管理设备驱动/网络协议栈

3. BTF 快速入门

3.1 BPF 快速入门

现在让我们通过使用 BTF 漂亮地打印[8] BPF map 的教程进行更多实践,从而显著改进调试。

要开始,我们需要在启用 CONFIG_DEBUG_INFO_BTF 选项的情况下编译 Linux 内核。大多数发行版都启用了此选项,但你可以通过运行以下命令进行检查:

$ zgrep CONFIG_DEBUG_INFO_BTF=y /proc/config.gz
# 可选:grep CONFIG_DEBUG_INFO_BTF=y  /boot/config*

当然,我们还需要在计算机上安装 Clang 和 LLVM[9] 。

由于我们需要将编写 XDP[10] 程序来处理网络设备上的数据包,因此创建一个虚拟网络接口[11] 是个好主意,这样就不会最终失去物理接口中的互联网连接。设置虚拟接口的最简单方法是使用此 repo[12] 。

克隆 repo 并设置一个名为 test1 的虚拟接口:

$ git clone [email]git@github.com[/email]:xdp-project/xdp-tutorial.git
$ cd xdp-tutorial/testenv
$ sudo ./testenv.sh setup --name=test1 --legacy-ip

现在编写一个 BPF 程序来计算接口上接收到的 IPv4 和 IPv6 数据包的数量。文件 xdp_count.c 的文件内容如下:

#include <linux/bpf.h>
#include <bpf/bpf_helpers.h>
#include <linux/if_ether.h>
#include <arpa/inet.h>

struct bpf_map_def SEC("maps") cnt = {
        .type = BPF_MAP_TYPE_ARRAY,
        .key_size = sizeof(__u32),
        .value_size = sizeof(long),
        .max_entries = 2,
};


SEC("xdp_count")
int xdp_count_prog(struct xdp_md *ctx)
{
        void *data_end = (void *)(long)ctx->data_end;
        void *data = (void *)(long)ctx->data;
        __u32 ipv6_key = 0;
        __u32 ipv4_key = 1;
        long *value;
        __u16 h_proto;
        struct ethhdr *eth = data;
        if (data + sizeof(struct ethhdr) > data_end) // This check is necessary to pass verification
                return XDP_DROP;

        h_proto = eth->h_proto;
        if (h_proto == htons(ETH_P_IPV6)) { // Check if IPv6 packet
                value = bpf_map_lookup_elem(&cnt, &ipv6_key);
                if (value)
                        *value += 1;
                return XDP_PASS;
        }
        value = bpf_map_lookup_elem(&cnt, &ipv4_key);
        if (value)
            *value += 1;
        return XDP_PASS;

}

char _license[] SEC("license") = "GPL";

在前面的代码中,名为 cnt 的 BPF map 存储数据包的数量。cnt 是两个元素的数组。IPv6 数据包的数量存储在 key 0 中,IPv4 数据包的数量存储在 key 1 中。

使用 Clang 编译代码:

$ clang -O2 -Wall -g -target bpf -c xdp_count.c -o xdp_count.o

接下来,使用 bpftool 加载程序:

$ sudo bpftool prog load xdp_count.o /sys/fs/bpf/xdp_count type xdp

运行以下命令并记下我们刚加载的程序的 ID 和程序正在使用的 map 的 ID:

$ sudo bpftool prog list

图 bpftool prog 列表的输出

我们还可以通过运行 sudo bpftool map list 来获取 map ID。

图 bpftool map list 的内容输出

此命令为我们提供 map 的名称、类型、键大小、值大小和最大条目数。

现在,将 BPF 程序附加到网络设备。

$ sudo bpftool net attach xdpgeneric id <program_id> dev test1

将 program_id 替换为程序的 ID,并将 device_name 替换为程序附加到的网络设备的名称(例如 enp34s0)。

现在,向该设备发送一些数据包。测试环境脚本已经提供了一个方便的 ping 命令来执行此操作:

$ sudo ./testenv.sh ping # For IPv6
$ sudo ./testenv.sh ping --legacy-ip # For IPv4

打印 map 并检查处理的数据包情况:

$ sudo bpftool map dump id <map_id>

图 map 的打印值

如图所示,map 中有两个预期的元素。这些值采用十六进制格式,并且还取决于运行机器的字节顺序。在截图中,它是小端格式,这意味着已经处理了 22 个 IPv6 和 4 个 IPv4 数据包。

很明显,结果是十六进制的,小端格式,一看就不好调试。因此,我们需要使用 BTF 对 map 进行注释,以便更好地展示。

如下更改 cnt 的声明并将新代码保存在 xdp_count_btf.c 中 -

...
struct {
        __uint(type, BPF_MAP_TYPE_ARRAY);
        __type(key, __u32);
        __type(value, long);
        __uint(max_entries, 2);
} cnt SEC(".maps");
...

请注意,部分名称现在为 .maps,并且地图本身已使用启用 BTF 的宏 __uint 和 __type 进行了注释。

使用 Clang 编译代码:

clang -O2 -Wall -g -target bpf -c xdp_count_btf.c -o xdp_count_btf.o

使用 -g 标志将创建调试信息并生成 BTF。请注意,之前也使用了 -g 标志,因为 libbpf需要它[13] 来加载程序;然而,以前 map 没有被 BTF 注释,所以 bpftool 不能够优雅地进行打印。

验证 BTF 部分是否存在于生成的目标文件中。

$ llvm-objdump -h xdp_count_btf.o

图 llvm-objdump 的输出

如前所述,.BTF 部分包含类型和字符串数据,.BTF.ext 部分对 func_info 和 line_info 数据进行编码。

首先,卸载前面的 BPF 程序。

$ sudo bpftool net detach xdpgeneric dev test1

然后按照类似的过程加载新程序并将其附加到接口,然后对接口发送一些数据:

$ sudo bpftool prog load xdp_count_btf.o /sys/fs/bpf/xdp_count_btf type xdp
$ sudo bpftool prog list
$ sudo bpftool net attach xdpgeneric id <program_id> dev test1
$ sudo ./testenv.sh ping
$ sudo ./testenv.sh ping --legacy-ip

最后,打印新程序对应的 map 。如果一切顺利,这一次的输出会有很大的不同。

图 map 的结构的打印值

它不仅以 JSON 格式打印得很漂亮,而且值也是十进制的,使其更具可读性和易懂性。

3.1 BTF 和 CO-RE

如前所述,BTF 可以启用 CO-RE 使 BPF 程序可移植到不同的内核版本或用户配置。我们也可以通过生成内核本身的 BTF 信息来消除对本地内核头文件的需求:

$ bpftool btf dump file /sys/kernel/btf/vmlinux format c > vmlinux.h

上述命令将创建一个巨大的 vmlinux.h 文件,其中包含所有内核类型,包括作为 UAPI 的一部分公开的类型、内部类型和通过 kernel-devel 可用的类型,以及一些其他地方不可用的更多内部类型。在 BPF 程序中,我们可以只 #include "vmlinux.h" 并删除其他内核头文件,如 <linux/fs.h>、<linux/sched.h>等。

摆脱内核头文件依赖性只是 BTF 可以实现的目标的冰山一角。

4. 结论

BTF 是一个非常强大的工具,可以使 BPF 程序更易于调试和移植。由于它是一项相对较新的技术,因此开发仍在进行中,你可以期待在未来看到大量改进。

本文让你大致了解 BTF 可以实现什么。你可能已经了解了 BPF 的缺点、BPF 是什么以及如何使用 BTF 注解 map 和打印 map 结构。最后,你还了解了 BTF 如何充当通过 CO-RE 增强可移植性的起点。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/393849.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

FPGA使用GTX实现SFP光纤收发SDI视频 全网首创略显高端 提供工程源码和技术支持

目录1、前言2、设计思路和框架3、vivado工程详解4、上板调试验证并演示5、福利&#xff1a;工程代码的获取1、前言 FPGA实现SDI视频编解码目前有两种方案&#xff1a; 一是使用专用编解码芯片&#xff0c;比如典型的接收器GS2971&#xff0c;发送器GS2972&#xff0c;优点是简…

如何让网络安全的防守技术更强?20年白帽子老江湖告诉你【蓝队】

网络安全的防守技术是网络安全工程师必备技能&#xff0c;只有攻防兼备的白帽子&#xff0c;才算是真正的网安精英。 网络安全的攻击技术在前面我已经讲过了&#xff0c;感兴趣的可以去看看&#xff1a; 90%的人都不算会网络安全&#xff0c;这才是真正的白帽子技术【红队】 . …

蓝桥杯--成绩分析

成绩分析 技巧 求最大值与最小值 if(a[i]>max) maxa[i]; if(a[i]<min) mina[i]; 这里的max为最小值0 这里的min为最大值100 这道题比较简单 题目大意 小蓝给学生们组织了一场考试&#xff0c;卷面总分为 100 分&#xff0c;每个学生的得分都是一个 0 到 100 的整数。 请…

【GO】K8s 管理系统项目33[前端部分–登录和登出]

K8s 管理系统项目[前端部分–登录和登出] 1. 登录登出流程 1.1 登录流程 登入流程总的分为5步: 账号密码验证token生成token验证验证成功进行跳转验证失败返回/login 1.2 登出流程 登出流程就相对简单,分为2步 删除Token跳转/login 2. 登录代码 src/views/login/Login.v…

vue双向绑定原理

Vue双向绑定的原理 vue的双向绑定原理&#xff1a;vue数据的双向绑定是通过数据劫持结合发布者-订阅者模式的方式来实现的。其核心就是通过obj.defineProperty()方法来实现数据的劫持&#xff0c;在数据变化时发布消息给订阅者&#xff0c;触发相应的监听回调。也就是说数据和视…

windows安装Stable Diffusion WebUI及问题解决记录

本文将详细介绍stable diffusion webui的下载、安装及问题解决。 StableDiffusion是2022年发布的深度学习文本到图像生成模型。它主要用于根据文本的描述产生详细图像&#xff0c;尽管它也可以应用于其他任务&#xff0c;如内补绘制、外补绘制&#xff0c;以及在提示词​&#…

AUTOSAR知识点Com(五):CANIf模式PUD Channel

1、概述 每个L-PDU分配给一个专用的物理CAN通道&#xff0c;该通道连接CAN控制器和CAN网络。通过这种方式&#xff0c;所有属于物理通道的L-PDU都可以在处理逻辑上&#xff0c;单一的L-PDU通道组上进行控制。这些逻辑组表示ECU连接到底层CAN网络的所有I-PDU。图 展示了L-PDU信道…

category排序专辑

case1——对有限类型的字段按指定要求排序&#xff1a; #学历分布 xueli_tsdf.pivot_table(index学历,values教师id,aggfunccount,marginsTrue,margins_name总计) xueli_ts[占比]np.round(xueli_ts[教师id]/xueli_ts.loc[总计,教师id],2) xueli_ts.reset_index(inplaceTrue)xu…

Adding Conditional Control to Text-to-Image Diffusion Models

安全验证 - 知乎知乎&#xff0c;中文互联网高质量的问答社区和创作者聚集的原创内容平台&#xff0c;于 2011 年 1 月正式上线&#xff0c;以「让人们更好的分享知识、经验和见解&#xff0c;找到自己的解答」为品牌使命。知乎凭借认真、专业、友善的社区氛围、独特的产品机制…

Java要学到什么程度才能找工作?小白怎么去面试Java岗位?

作为一个Java初学者&#xff0c;到底要学哪些知识点才能找到月薪过万的工作&#xff1f;或者说&#xff0c;怎样才能适应企业的开发流程&#xff0c;不至于进了公司都不知道怎么把需求转换成代码。甚至&#xff0c;唯唯诺诺的加班&#xff0c;一点自信都没有。本期文章&#xf…

STM32开发(18)----CubeMX配置RTC

CubeMX配置RTC前言一、什么是RTC&#xff1f;RTC时钟源RTC备份域二、实验过程1.CubeMX配置2.代码实现3.实验结果总结前言 本章介绍使用STM32CubeMX对RTC进行配置的方法&#xff0c;RTC的原理、概念和特点&#xff0c;配置各个步骤的功能&#xff0c;并通过实验方式验证。 一、…

Linux常见漏洞修复

一、nginx 修复TLS1.0&#xff0c;TLS1.1协议漏洞 1、漏洞描述 服务端口漏洞名称加固建议nginx443TLS版本1.0协议检测启用对TLS 1.2或1.3的支持&#xff0c;并禁用对TLS 1.0的支持。nginx443TLS版本1.1协议检测启用对TLS 1.2或1.3的支持&#xff0c;并禁用对TLS 1.1的支持。 …

Linux27 -- 通过抓包观察三次握手和四次挥手、链接的状态(tcp状态转移图)、TIME_WAIT 存在的原因

tcp协议的特点&#xff1a; 面向连接的&#xff0c;可靠的&#xff0c;流式服务 //面试常问&#xff1a; 一、通过抓包观察三次握手、四次挥手 工具&#xff1a;tcpdump 命令 抓冲我到他从他到我的数据包。 需要管理员权限。 运行示例&#xff1a; 进入管理员权限&#xff…

使用Advanced Installer打包程序及运行环境

Advanced Installer 工具版本&#xff1a;20.1.1 设置产品信息 选中右侧【Product Details】输入产品信息 设置文件和文件夹 添加使用VS发布之后的程序文件夹 设置文件夹刷新 选中文件夹&#xff0c;右键选择属性&#xff0c;选中Synchronize标签。启用“Synchronize conten…

InfluxDB 2 介绍与使用 flux查询 数据可视化

一、关键概念 相比V1 移除了database 和 RP&#xff0c;增加了bucket。 V2具有以下几个概念&#xff1a; timestamp、field key、field value、field set、tag key、tag value、tag set、measurement、series、point、bucket、bucket schema、organization 新增的概念&…

微服务 分布式搜索引擎 Elastic Search RestAPI

文章目录⛄引言一、RestAPI⛅导入数据⏰mapping映射分析⚡初始化RestClient二、索引库操作⌚创建索引库✒️删除索引库⚡判断索引库是否存在⛵小结⛄引言 本文参考黑马 分布式Elastic search Elasticsearch是一款非常强大的开源搜索引擎&#xff0c;具备非常多强大功能&#x…

synchronized 与 volatile 关键字

目录1.前言1.synchronized 关键字1. 互斥2.保证内存可见性3.可重入2. volatile 关键字1.保证内存可见性2.无法保证原子性3.synchronized 与 volatile 的区别1.前言 synchronized关键字和volatile是大家在Java多线程学习时接触的两个关键字&#xff0c;很多同学可能学习完就忘记…

QSGS四参数随机生长2D软件 quartet structure generation set

软件简介 AbyssFish四参数随机生长2D软件采用四参数随机生长算法quartet structure generation set (QSGS)&#xff0c;可用于构建二维随机孔隙图。 软件提供图片长度、宽度&#xff1b;随机生长算法的分布概率、生长概率、概率密度&#xff08;暂不考虑多相材料相互作用&…

可视化项目管理,控制项目进度,项目经理需要做好以下工作

对于项目的管理者来说&#xff0c;项目信息透明&#xff0c;能够更容易让管理者发现项目中的问题&#xff0c;及时找到问题的原因和相关任务的责任人。 当项目信息能相对精准地呈现给管理者时&#xff0c;也能促进项目成员也能更加认真负责的完成任务&#xff0c;不会找借口推…

Elasticsearch使用——中级篇

在上一篇&#xff0c;已经导入了大量数据到elasticsearch中&#xff0c;实现了elasticsearch的数据存储功能。但elasticsearch最擅长的还是搜索和数据分析。本篇&#xff0c;研究下elasticsearch的数据搜索功能。分别使用DSL和RestClient实现搜索。1.DSL查询文档elasticsearch的…