在 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 的源代码如下:
// ${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
的处理逻辑:
__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 抓到。