From 60758970ed71fd05519625e0ed883405533875d7 Mon Sep 17 00:00:00 2001 From: CH3CHOHCH3 <72899584+CH3CHOHCH3@users.noreply.github.com> Date: Sat, 20 May 2023 04:55:23 +0800 Subject: [PATCH] implement tc tutorial (#34) --- src/20-tc/README.md | 40 +++++++++++++++++++++++++++++++--------- 1 file changed, 31 insertions(+), 9 deletions(-) diff --git a/src/20-tc/README.md b/src/20-tc/README.md index b153656..307a081 100644 --- a/src/20-tc/README.md +++ b/src/20-tc/README.md @@ -1,6 +1,18 @@ # eBPF 入门实践教程:使用 eBPF 进行 tc 流量控制 -## tc 程序示例 +## 背景 + +Linux 的流量控制子系统(Traffic Control, tc)在内核中存在了多年,类似于 iptables 和 netfilter 的关系,tc 也包括一个用户态的 tc 程序和内核态的 trafiic control 框架,主要用于从速率、顺序等方面控制数据包的发送和接收。从 Linux 4.1 开始,tc 增加了一些新的挂载点,并支持将 eBPF 程序作为 filter 加载到这些挂载点上。 + +## tc 概述 + +从协议栈上看,tc 位于链路层,其所在位置已经完成了 sk_buff 的分配,要晚于 xdp。为了实现对数据包发送和接收的控制,tc 使用队列结构来临时保存并组织数据包,在 tc 子系统中对应的数据结构和算法控制机制被抽象为 qdisc(Queueing discipline),其对外暴露数据包入队和出队的两个回调接口,并在内部隐藏排队算法实现。在 qdisc 中我们可以基于 filter 和 class 实现复杂的树形结构,其中 filter 被挂载到 qdisc 或 class 上用于实现具体的过滤逻辑,返回值决定了该数据包是否属于特定 class。 + +当数据包到达顶层 qdisc 时,其入队接口被调用,其上挂载的 filter 被依次执行直到一个 filter 匹配成功;此后数据包被送入该 filter 指向的 class,进入该 class 配置的 qdisc 处理流程中。tc 框架提供了所谓 classifier-action 机制,即在数据包匹配到特定 filter 时执行该 filter 所挂载的 action 对数据包进行处理,实现了完整的数据包分类和处理机制。 + +现有的 tc 为 eBPF 提供了 direct-action 模式,它使得一个作为 filter 加载的 eBPF 程序可以返回像 `TC_ACT_OK` 等 tc action 的返回值,而不是像传统的 filter 那样仅仅返回一个 classid 并把对数据包的处理交给 action 模块。现在,eBPF 程序可以被挂载到特定的 qdisc 上,并完成对数据包的分类和处理动作。 + +## 编写 eBPF 程序 ```c #include @@ -48,17 +60,19 @@ char __license[] SEC("license") = "GPL"; /// @tcopts {"handle":1, "priority":1} ``` -这些注释告诉 TC 将 eBPF 程序附加到网络接口的 ingress 附加点,并指定了 handle 和 priority 选项的值。 +这些注释告诉 TC 将 eBPF 程序附加到网络接口的 ingress 附加点,并指定了 handle 和 priority 选项的值。关于 libbpf 中 tc 相关的 API 可以参考 [patchwork](https://patchwork.kernel.org/project/netdevbpf/patch/20210512103451.989420-3-memxor@gmail.com/) 中的介绍。 总之,这段代码实现了一个简单的 eBPF 程序,用于捕获数据包并打印出它们的信息。 ## 编译运行 +通过容器编译: + ```console docker run -it -v `pwd`/:/src/ yunwei37/ebpm:latest ``` -or compile with `ecc`: +或是通过 `ecc` 编译: ```console $ ecc tc.bpf.c @@ -66,15 +80,13 @@ Compiling bpf object... Packing ebpf object and config into package.json... ``` +并通过 `ecli` 运行: + ```shell $ sudo ecli run ./package.json -... -Successfully started! Please run `sudo cat /sys/kernel/debug/tracing/trace_pipe` to see output of the BPF program. -...... ``` -The `tc` output in `/sys/kernel/debug/tracing/trace_pipe` should look -something like this: +可以通过如下方式查看程序的输出: ```console $ sudo cat /sys/kernel/debug/tracing/trace_pipe @@ -86,4 +98,14 @@ $ sudo cat /sys/kernel/debug/tracing/trace_pipe ## 总结 -TODO +本文介绍了如何向 TC 流量控制子系统挂载 eBPF 类型的 filter 来实现对链路层数据包的排队处理。基于 eunomia-bpf 提供的通过注释向 libbpf 传递参数的方案,我们可以将自己编写的 tc BPF 程序以指定选项挂载到目标网络设备,并借助内核的 sk_buff 结构对数据包进行过滤处理。 + +更多的例子和详细的开发指南,请参考 eunomia-bpf 的官方文档: + +完整的教程和源代码已经全部开源,可以在 中查看。 + +## 参考 + + + + \ No newline at end of file