在 tc-bpf 里转发网络包,跟 XDP 里转发网络包有什么不同呢?
呃,其实还是那个奇葩的需求,不过最近在给它做性能优化;而在优化的过程中,遇到了一些有趣的知识点。
redirect helpers 对比
helpers |
XDP |
tc-bpf |
bpf_redirect |
有 |
有 |
bpf_redirect_map |
有 |
无 |
bpf_redirect_peer |
无 |
有 |
bpf_redirect_neigh |
无 |
有 |
bpf_clone_redirect |
无 |
有 |
来源:${KERNEL}/net/core/filter.c
里的 xdp_verifier_ops
和 tc_cls_act_verifier_ops
。
tc-bpf 的 bpf_redirect
tc-bpf 使用 bpf_redirect
转发网络包也分为两个阶段:
bpf_redirect()
helpers 执行。
- 对
TC_ACT_REDIRECT
的处理。
bpf_redirect()
helpers 的源代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
// ${KERNEL}/net/core/filter.c
BPF_CALL_2(bpf_redirect, u32, ifindex, u64, flags)
{
struct bpf_redirect_info *ri = this_cpu_ptr(&bpf_redirect_info);
if (unlikely(flags & (~(BPF_F_INGRESS) | BPF_F_REDIRECT_INTERNAL)))
return TC_ACT_SHOT;
ri->flags = flags;
ri->tgt_index = ifindex;
return TC_ACT_REDIRECT;
}
|
接着,看下在 INGRESS 阶段 tc 对 TC_ACT_REDIRECT
的处理逻辑:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
__netif_receive_skb_core() // ${KERNEL}/net/core/dev.c
|-->sch_handle_ingress()
|-->tcf_classify() { // ${KERNEL}/net/sched/cls_api.c
| switch (tcf_classify(skb, miniq->block, miniq->filter_list, &cl_res, false)) {
| case TC_ACT_REDIRECT:
| /* skb_mac_header check was done by cls/act_bpf, so
| * we can safely push the L2 header back before
| * redirecting to another netdev
| */
| __skb_push(skb, skb->mac_len);
| if (skb_do_redirect(skb) == -EAGAIN) {
| __skb_pull(skb, skb->mac_len);
| *another = true;
| break;
| }
| return NULL;
| }
}
|-->skb_do_redirect() // ${KERNEL}/net/core/filter.c
|-->__bpf_redirect()
|-->__bpf_redirect_common()
|-->__bpf_tx_skb()
|-->dev_queue_xmit() // ${KERNEL}/net/core/dev.c
|
最终,可以看到 tc-bpf 的 bpf_redirect
是调用 dev_queue_xmit()
发包的;从而,该网络包会经历目标网络设备的 tc EGRESS 处理阶段,并能够被 tcpdump 抓到。
而 XDP bpf_redirect
调用的是网卡驱动的发包函数 ops->ndo_start_xmit(skb, dev);
直接将网络包发送出去了。
小结
tc INGRESS 里使用 tc-bpf 转发网络包发送出去时,该网络包会经历目标网络设备的 tc EGRESS 处理阶段,并能够被 tcpdump 抓到。