在 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_opstc_cls_act_verifier_ops

tc-bpf 的 bpf_redirect

tc-bpf 使用 bpf_redirect 转发网络包也分为两个阶段:

  1. bpf_redirect() helpers 执行。
  2. 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 抓到。