This commit is contained in:
yunwei37
2023-05-19 20:59:53 +00:00
parent 7ba4f1607b
commit bc1908ac9e
4 changed files with 38 additions and 20 deletions

View File

@@ -145,7 +145,13 @@
<div id="content" class="content">
<main>
<h1 id="ebpf-入门实践教程使用-ebpf-进行-tc-流量控制"><a class="header" href="#ebpf-入门实践教程使用-ebpf-进行-tc-流量控制">eBPF 入门实践教程:使用 eBPF 进行 tc 流量控制</a></h1>
<h2 id="tc-程序示例"><a class="header" href="#tc-程序示例">tc 程序示例</a></h2>
<h2 id="背景"><a class="header" href="#背景">背景</a></h2>
<p>Linux 的流量控制子系统Traffic Control, tc在内核中存在了多年类似于 iptables 和 netfilter 的关系tc 也包括一个用户态的 tc 程序和内核态的 trafiic control 框架,主要用于从速率、顺序等方面控制数据包的发送和接收。从 Linux 4.1 开始tc 增加了一些新的挂载点,并支持将 eBPF 程序作为 filter 加载到这些挂载点上。</p>
<h2 id="tc-概述"><a class="header" href="#tc-概述">tc 概述</a></h2>
<p>从协议栈上看tc 位于链路层,其所在位置已经完成了 sk_buff 的分配,要晚于 xdp。为了实现对数据包发送和接收的控制tc 使用队列结构来临时保存并组织数据包,在 tc 子系统中对应的数据结构和算法控制机制被抽象为 qdiscQueueing discipline其对外暴露数据包入队和出队的两个回调接口并在内部隐藏排队算法实现。在 qdisc 中我们可以基于 filter 和 class 实现复杂的树形结构,其中 filter 被挂载到 qdisc 或 class 上用于实现具体的过滤逻辑,返回值决定了该数据包是否属于特定 class。</p>
<p>当数据包到达顶层 qdisc 时,其入队接口被调用,其上挂载的 filter 被依次执行直到一个 filter 匹配成功;此后数据包被送入该 filter 指向的 class进入该 class 配置的 qdisc 处理流程中。tc 框架提供了所谓 classifier-action 机制,即在数据包匹配到特定 filter 时执行该 filter 所挂载的 action 对数据包进行处理,实现了完整的数据包分类和处理机制。</p>
<p>现有的 tc 为 eBPF 提供了 direct-action 模式,它使得一个作为 filter 加载的 eBPF 程序可以返回像 <code>TC_ACT_OK</code> 等 tc action 的返回值,而不是像传统的 filter 那样仅仅返回一个 classid 并把对数据包的处理交给 action 模块。现在eBPF 程序可以被挂载到特定的 qdisc 上,并完成对数据包的分类和处理动作。</p>
<h2 id="编写-ebpf-程序"><a class="header" href="#编写-ebpf-程序">编写 eBPF 程序</a></h2>
<pre><code class="language-c">#include &lt;vmlinux.h&gt;
#include &lt;bpf/bpf_endian.h&gt;
#include &lt;bpf/bpf_helpers.h&gt;
@@ -186,23 +192,21 @@ char __license[] SEC(&quot;license&quot;) = &quot;GPL&quot;;
<pre><code class="language-c">/// @tchook {&quot;ifindex&quot;:1, &quot;attach_point&quot;:&quot;BPF_TC_INGRESS&quot;}
/// @tcopts {&quot;handle&quot;:1, &quot;priority&quot;:1}
</code></pre>
<p>这些注释告诉 TC 将 eBPF 程序附加到网络接口的 ingress 附加点,并指定了 handle 和 priority 选项的值。</p>
<p>这些注释告诉 TC 将 eBPF 程序附加到网络接口的 ingress 附加点,并指定了 handle 和 priority 选项的值。关于 libbpf 中 tc 相关的 API 可以参考 <a href="https://patchwork.kernel.org/project/netdevbpf/patch/20210512103451.989420-3-memxor@gmail.com/">patchwork</a> 中的介绍。</p>
<p>总之,这段代码实现了一个简单的 eBPF 程序,用于捕获数据包并打印出它们的信息。</p>
<h2 id="编译运行"><a class="header" href="#编译运行">编译运行</a></h2>
<p>通过容器编译:</p>
<pre><code class="language-console">docker run -it -v `pwd`/:/src/ yunwei37/ebpm:latest
</code></pre>
<p>or compile with <code>ecc</code>:</p>
<p>或是通过 <code>ecc</code> 编译:</p>
<pre><code class="language-console">$ ecc tc.bpf.c
Compiling bpf object...
Packing ebpf object and config into package.json...
</code></pre>
<p>并通过 <code>ecli</code> 运行:</p>
<pre><code class="language-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.
......
</code></pre>
<p>The <code>tc</code> output in <code>/sys/kernel/debug/tracing/trace_pipe</code> should look
something like this:</p>
<p>可以通过如下方式查看程序的输出:</p>
<pre><code class="language-console">$ sudo cat /sys/kernel/debug/tracing/trace_pipe
node-1254811 [007] ..s1 8737831.671074: 0: Got IP packet: tot_len: 79, ttl: 64
sshd-1254728 [006] ..s1 8737831.674334: 0: Got IP packet: tot_len: 79, ttl: 64
@@ -210,7 +214,12 @@ something like this:</p>
node-1254811 [007] ..s1 8737831.674550: 0: Got IP packet: tot_len: 71, ttl: 64
</code></pre>
<h2 id="总结"><a class="header" href="#总结">总结</a></h2>
<p>TODO</p>
<p>本文介绍了如何向 TC 流量控制子系统挂载 eBPF 类型的 filter 来实现对链路层数据包的排队处理。基于 eunomia-bpf 提供的通过注释向 libbpf 传递参数的方案,我们可以将自己编写的 tc BPF 程序以指定选项挂载到目标网络设备,并借助内核的 sk_buff 结构对数据包进行过滤处理。</p>
<p>更多的例子和详细的开发指南,请参考 eunomia-bpf 的官方文档:<a href="https://github.com/eunomia-bpf/eunomia-bpf">https://github.com/eunomia-bpf/eunomia-bpf</a></p>
<p>完整的教程和源代码已经全部开源,可以在 <a href="https://github.com/eunomia-bpf/bpf-developer-tutorial">https://github.com/eunomia-bpf/bpf-developer-tutorial</a> 中查看。</p>
<h2 id="参考"><a class="header" href="#参考">参考</a></h2>
<p><a href="http://just4coding.com/2022/08/05/tc/">http://just4coding.com/2022/08/05/tc/</a></p>
<p><a href="https://arthurchiao.art/blog/understanding-tc-da-mode-zh/">https://arthurchiao.art/blog/understanding-tc-da-mode-zh/</a></p>
</main>

View File

@@ -3143,7 +3143,13 @@ Retrying.
<p><a href="https://github.com/leodido/demo-cloud-native-ebpf-day">https://github.com/leodido/demo-cloud-native-ebpf-day</a></p>
<p><a href="https://aya-rs.dev/book/programs/lsm/#writing-lsm-bpf-program">https://aya-rs.dev/book/programs/lsm/#writing-lsm-bpf-program</a></p>
<div style="break-before: page; page-break-before: always;"></div><h1 id="ebpf-入门实践教程使用-ebpf-进行-tc-流量控制"><a class="header" href="#ebpf-入门实践教程使用-ebpf-进行-tc-流量控制">eBPF 入门实践教程:使用 eBPF 进行 tc 流量控制</a></h1>
<h2 id="tc-程序示例"><a class="header" href="#tc-程序示例">tc 程序示例</a></h2>
<h2 id="背景-5"><a class="header" href="#背景-5">背景</a></h2>
<p>Linux 的流量控制子系统Traffic Control, tc在内核中存在了多年类似于 iptables 和 netfilter 的关系tc 也包括一个用户态的 tc 程序和内核态的 trafiic control 框架,主要用于从速率、顺序等方面控制数据包的发送和接收。从 Linux 4.1 开始tc 增加了一些新的挂载点,并支持将 eBPF 程序作为 filter 加载到这些挂载点上。</p>
<h2 id="tc-概述"><a class="header" href="#tc-概述">tc 概述</a></h2>
<p>从协议栈上看tc 位于链路层,其所在位置已经完成了 sk_buff 的分配,要晚于 xdp。为了实现对数据包发送和接收的控制tc 使用队列结构来临时保存并组织数据包,在 tc 子系统中对应的数据结构和算法控制机制被抽象为 qdiscQueueing discipline其对外暴露数据包入队和出队的两个回调接口并在内部隐藏排队算法实现。在 qdisc 中我们可以基于 filter 和 class 实现复杂的树形结构,其中 filter 被挂载到 qdisc 或 class 上用于实现具体的过滤逻辑,返回值决定了该数据包是否属于特定 class。</p>
<p>当数据包到达顶层 qdisc 时,其入队接口被调用,其上挂载的 filter 被依次执行直到一个 filter 匹配成功;此后数据包被送入该 filter 指向的 class进入该 class 配置的 qdisc 处理流程中。tc 框架提供了所谓 classifier-action 机制,即在数据包匹配到特定 filter 时执行该 filter 所挂载的 action 对数据包进行处理,实现了完整的数据包分类和处理机制。</p>
<p>现有的 tc 为 eBPF 提供了 direct-action 模式,它使得一个作为 filter 加载的 eBPF 程序可以返回像 <code>TC_ACT_OK</code> 等 tc action 的返回值,而不是像传统的 filter 那样仅仅返回一个 classid 并把对数据包的处理交给 action 模块。现在eBPF 程序可以被挂载到特定的 qdisc 上,并完成对数据包的分类和处理动作。</p>
<h2 id="编写-ebpf-程序-5"><a class="header" href="#编写-ebpf-程序-5">编写 eBPF 程序</a></h2>
<pre><code class="language-c">#include &lt;vmlinux.h&gt;
#include &lt;bpf/bpf_endian.h&gt;
#include &lt;bpf/bpf_helpers.h&gt;
@@ -3184,23 +3190,21 @@ char __license[] SEC(&quot;license&quot;) = &quot;GPL&quot;;
<pre><code class="language-c">/// @tchook {&quot;ifindex&quot;:1, &quot;attach_point&quot;:&quot;BPF_TC_INGRESS&quot;}
/// @tcopts {&quot;handle&quot;:1, &quot;priority&quot;:1}
</code></pre>
<p>这些注释告诉 TC 将 eBPF 程序附加到网络接口的 ingress 附加点,并指定了 handle 和 priority 选项的值。</p>
<p>这些注释告诉 TC 将 eBPF 程序附加到网络接口的 ingress 附加点,并指定了 handle 和 priority 选项的值。关于 libbpf 中 tc 相关的 API 可以参考 <a href="https://patchwork.kernel.org/project/netdevbpf/patch/20210512103451.989420-3-memxor@gmail.com/">patchwork</a> 中的介绍。</p>
<p>总之,这段代码实现了一个简单的 eBPF 程序,用于捕获数据包并打印出它们的信息。</p>
<h2 id="编译运行-7"><a class="header" href="#编译运行-7">编译运行</a></h2>
<p>通过容器编译:</p>
<pre><code class="language-console">docker run -it -v `pwd`/:/src/ yunwei37/ebpm:latest
</code></pre>
<p>or compile with <code>ecc</code>:</p>
<p>或是通过 <code>ecc</code> 编译:</p>
<pre><code class="language-console">$ ecc tc.bpf.c
Compiling bpf object...
Packing ebpf object and config into package.json...
</code></pre>
<p>并通过 <code>ecli</code> 运行:</p>
<pre><code class="language-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.
......
</code></pre>
<p>The <code>tc</code> output in <code>/sys/kernel/debug/tracing/trace_pipe</code> should look
something like this:</p>
<p>可以通过如下方式查看程序的输出:</p>
<pre><code class="language-console">$ sudo cat /sys/kernel/debug/tracing/trace_pipe
node-1254811 [007] ..s1 8737831.671074: 0: Got IP packet: tot_len: 79, ttl: 64
sshd-1254728 [006] ..s1 8737831.674334: 0: Got IP packet: tot_len: 79, ttl: 64
@@ -3208,7 +3212,12 @@ something like this:</p>
node-1254811 [007] ..s1 8737831.674550: 0: Got IP packet: tot_len: 71, ttl: 64
</code></pre>
<h2 id="总结-17"><a class="header" href="#总结-17">总结</a></h2>
<p>TODO</p>
<p>本文介绍了如何向 TC 流量控制子系统挂载 eBPF 类型的 filter 来实现对链路层数据包的排队处理。基于 eunomia-bpf 提供的通过注释向 libbpf 传递参数的方案,我们可以将自己编写的 tc BPF 程序以指定选项挂载到目标网络设备,并借助内核的 sk_buff 结构对数据包进行过滤处理。</p>
<p>更多的例子和详细的开发指南,请参考 eunomia-bpf 的官方文档:<a href="https://github.com/eunomia-bpf/eunomia-bpf">https://github.com/eunomia-bpf/eunomia-bpf</a></p>
<p>完整的教程和源代码已经全部开源,可以在 <a href="https://github.com/eunomia-bpf/bpf-developer-tutorial">https://github.com/eunomia-bpf/bpf-developer-tutorial</a> 中查看。</p>
<h2 id="参考-1"><a class="header" href="#参考-1">参考</a></h2>
<p><a href="http://just4coding.com/2022/08/05/tc/">http://just4coding.com/2022/08/05/tc/</a></p>
<p><a href="https://arthurchiao.art/blog/understanding-tc-da-mode-zh/">https://arthurchiao.art/blog/understanding-tc-da-mode-zh/</a></p>
<div style="break-before: page; page-break-before: always;"></div><h1 id="bpf-features-by-linux-kernel-version"><a class="header" href="#bpf-features-by-linux-kernel-version">BPF Features by Linux Kernel Version</a></h1>
<h2 id="ebpf-support"><a class="header" href="#ebpf-support">eBPF support</a></h2>
<div class="table-wrapper"><table><thead><tr><th>Kernel version</th><th>Commit</th></tr></thead><tbody>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long