tc ebpf 实践

1. 用 tc 加载 BPF 程序

给定一个为 tc 编译的 BPF 对象文件 prog.o, 可以通过 tc 命令将其加载到一个网 络设备(netdevice)。但与 XDP 不同,设备是否支持 attach BPF 程序并不依赖驱动 (即任何网络设备都支持 tc BPF)。下面的命令可以将程序 attach 到 em1 的ingress 网络:

$ tc qdisc add dev em1 clsact

$ tc filter add dev em1 ingress bpf da obj prog.o

第一步创建了一个 clsact qdisc (Linux 排队规则,Linux queueing discipline)。

clsact 是一个 dummy qdisc,和 ingress qdisc 类似,可以持有(hold)分类器和 动作(classifier and actions),但不执行真正的排队(queueing)。后面 attach bpf 分类器需要用到它。clsact qdisc 提供了两个特殊的 hook:ingress and egress,分类器可以 attach 到这两个 hook 点。这两个 hook 都位于 datapath 的 关键收发路径上,设备 em1 的每个包都会经过这两个点。这两个 hook 分别会被下面的内 核函数调用:

  • ingress hook:__netif_receive_skb_core() -> sch_handle_ingress()
  • egress hook:__dev_queue_xmit() -> sch_handle_egress()

 类似地,将程序 attach 到 egress hook:

$ tc filter add dev em1 egress bpf da obj prog.o

 

第二条命令,tc filter 选择了在 da(direct-action)模式中使用 bpfda 是 推荐的模式,并且应该永远指定这个参数。粗略地说,da 模式表示 bpf 分类器不需 要调用外部的 tc action 模块。事实上 bpf 分类器也完全不需要调用外部模块,因 为所有的 packet mangling、转发或其他类型的 action 都可以在这单个 BPF 程序内完成 ,因此执行会明显更快。

配置了这两条命令之后,程序就 attach 完成了,接下来只要有包经过这个设备,就会触发 这个程序执行。和 XDP 类似,如果没有使用默认 section 名字,那可以在加载时指定,例 如指定 section 为 foobar

$ tc filter add dev em1 egress bpf da obj prog.o sec foobar

查看已经 attach 的程序:

 

$ tc filter show dev em1 ingress

filter protocol all pref 49152 bpf

filter protocol all pref 49152 bpf handle 0x1 prog.o:[ingress] direct-action id 1 tag c5f7825e5dac396f

$ tc filter show dev em1 egress

filter protocol all pref 49152 bpf

filter protocol all pref 49152 bpf handle 0x1 prog.o:[egress] direct-action id 2 tag b2fd5adc0f262714

输出中的 prog.o:[ingress] 表示 section ingress 中的程序是从 文件 prog.o 加 载的,而且 bpf 工作在 direct-action 模式。上面还打印了程序的 id 和 tag, 其中 tag 是指令流(instruction stream)的哈希,可以关联到对应的对象文件或用perf 查看调用栈信息。id 是一个操作系统层唯一的 BPF 程序标识符,可以用 bpftool 进一步查看或 dump 相关的程序信息。

程序优先级(pref)和句柄(handle

在上面的 show 命令中,tc 还打印出了 pref 49152 和 handle 0x1。如果之前没有 通过命令行显式指定,这两个数据就会自动生成。pref 表示优先级,如果指定了多个分 类器,它们会按照优先级从高到低依次执行;handle 是一个标识符,在加载了同一分类器的多 个实例并且它们的优先级(pref)都一样的情况下会用到这个标识符。因为 在 BPF 的场景下,单个程序就足够了,因此 pref 和 handle 通常情况下都可以忽略。

除非打算后面原子地替换 attached BPF 程序,否则不建议在加载时显式指定 pref 和 handle。显式指定这两个参数的好处是,后面执行 replace 操作时,就不需要再去动 态地查询这两个值。显式指定 pref 和 handle 时的加载命令:

$ tc filter add dev em1 ingress pref 1 handle 1 bpf da obj prog.o sec foobar
 
$ tc filter show dev em1 ingress
filter protocol all pref 1 bpf
filter protocol all pref 1 bpf handle 0x1 prog.o:[foobar] direct-action id 1 tag c5f7825e5dac396f

 对应的原子 replace 命令:将 ingress hook 处的已有程序替换为 prog.o 文件中 foobar section 中的新 BPF 程序

$ tc filter replace dev em1 ingress pref 1 handle 1 bpf da obj prog.o sec foobar

2. 用 tc 删除 BPF 程序

要分别从 ingress 和 egress 删除所有 attach 的程序

$ tc filter del dev em1 ingress
$ tc filter del dev em1 egress

要从 netdevice 删除整个 clsact qdisc(会隐式地删除 attach 到 ingress 和 egress hook 上面的所有程序),执行


$ tc qdisc del dev em1 clsact

3. offload 到网卡

如果网卡驱动支持 tc BPF 程序,那也可以将它们 offload 到网卡 。Netronome 的 nfp 网卡对 XDP 和 tc BPF 程序都支持 offload。

$ tc qdisc add dev em1 clsact
$ tc filter replace dev em1 ingress pref 1 handle 1 bpf skip_sw da obj prog.o
Error: TC offload is disabled on net device.
We have an error talking to the kernel

如果显式以上错误,那需要先启用网卡的 hw-tc-offload 功能:

$ ethtool -K em1 hw-tc-offload on
 
$ tc qdisc add dev em1 clsact
$ tc filter replace dev em1 ingress pref 1 handle 1 bpf skip_sw da obj prog.o
$ tc filter show dev em1 ingress
filter protocol all pref 1 bpf
filter protocol all pref 1 bpf handle 0x1 prog.o:[classifier] direct-action skip_sw in_hw id 19 tag 57cd311f2e27366b

其中的 in_hw 标志表示这个程序已经被 offload 到网卡了。

注意,tc 和 XDP offload 无法同时加载,因此必须要指明是 tc 还是 XDP offload 选项 。

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值