===============================================================================================================
之前一个大哥的博文:
https://blog.csdn.net/dog250/article/details/102982948 实现一个基于XDP/eBPF的学习型网桥
我留言:
这几天学习过程中正好想到了这个问题,就见到了这篇文,受教了。 一点小建议,可以强调这一点,XDP helper_function BPF_FUNC_redirect 和 BPF_FUNC_redirect_map,只能实现到一个egress interface 的 packet redirect,无法实现 到多个 interfaces 的 multicast flooding,所以无法在 xdp_prog 中做到 flooding of MC packet or unknown UC DMAC packet,所以需要XDP_PASS到 bridge 中 做 flooding。
今天偶然再想到这个问题:
当前,eBPF XDP 的 BPF_FUNC_redirect 和 BPF_FUNC_redirect_map,是只能将 packet 重定向到 另一个 interface 的 egress 的。
没有 将一个 packet 重定向到 多个 interfaces 的机制。
那么,如果一定要在 XDP 中 实现,多播 的机制呢? 可以怎样来做到呢? 那只能自己动手改了。
这里说一说自己的思路。 # 没环境和时间去代码。
===============================================================================================================
先说明一下,XDP helper_func 的 BPF_FUNC_redirect 和 BPF_FUNC_redirect_map 的内部实现机制。
redirect 分为两个 stages:
stage #1. xdp_prog 调用 helper_function,只是 make redirect decision,将 redirect_info 记录在 per-CPU variable "bpf_redirect_info" 中
struct bpf_redirect_info {
u32 flags;
u32 tgt_index;
void *tgt_value;
struct bpf_map *map;
u32 kern_flags;
};
stage #2. 真正的 redirect 操作,是从 xdp_prog 执行返回到 hook point 之后,再由 返回之后的代码 来实现的,根据 redirect_info 来进行实际的 redirect 操作,大致是 enqueue to and flush (__ flush 到 target_dev 的 "ndo_xdp_xmit()" method
下面说一说,如何就这两个 stages 进行修改。
===============================================================================================================
--- 先在 per-CPU variable "struct bpf_redirect_info" 中,增加 fields
struct bpf_redirect_info {
u32 flags;
u32 tgt_index;
+ u32 tgt_mc_index[64]; # 随意定个64的大小。
void *tgt_value;
struct bpf_map *map;
u32 kern_flags;
};
===============================================================================================================
--- stage #1 的修改. 新定义一个 helper_func: BPF_FUNC_redirect_map_multicast( struct bpf_map *map, u32 key[], u64 flags )
可以模仿着:
BPF_CALL_3(bpf_xdp_redirect_map, struct bpf_map *, map, u32, ifindex,
u64, flags)
来实现。
大致逻辑是:
将 devmap 的 多个 ifindex,存放进 per-CPU variable "struct bpf_redirect_info->tgt_mc_index[]" array 即可。
return XDP_REDIRECT_MC; # 新定义的一个 return value。
===============================================================================================================
--- stage #2 的修改 --- 对于 generic_xdp,如何修改 stage #2。
int do_xdp_generic(struct bpf_prog *xdp_prog, struct sk_buff *skb)
act = netif_receive_generic_xdp(skb, &xdp, xdp_prog);
if (act != XDP_PASS) {
switch (act) {
case XDP_REDIRECT:
err = xdp_do_generic_redirect(skb->dev, skb,
&xdp, xdp_prog);
break;
#
# 这个 xdp_do_generic_redirect_mc(),可以模仿 xdp_do_generic_redirect() 来写。
#
# 其大致逻辑是:
#
# 从 skb 创建多个 skb clone,然后 发送到
# "struct bpf_redirect_info->tgt_mc_index[]" 指定的多个 net_device。
#
+ case XDP_REDIRECT_MC:
+
+ err = xdp_do_generic_redirect_mc(skb->dev, skb, &xdp, xdp_prog ).
+
+ break;
+
===============================================================================================================
--- stage #2 的修改 --- 如何修改 stage #2。 --- 这会比较复杂,因为 native_xdp 都是在具体的 physical NIC driver 中的,所以,需要修改不同的 physical NIC driver 的相关code。并且,有非常多的细节需要处理。
大致逻辑,也是类似 generic_xdp 的 修改。
这里以 ixgbe driver 来说一下大致思路。
ixgbe_run_xdp(struct ixgbe_adapter *adapter,
struct ixgbe_ring *rx_ring,
struct xdp_buff *xdp)
case XDP_REDIRECT:
err = xdp_do_redirect(adapter->netdev, xdp, xdp_prog);
break;
#
# 模仿着 xdp_do_redirect() 来写。
#
# 其中,创建从原始的 "xdp_buff",复制创建多份 "xdp_buff"。
#
#
# [*][*] 注意,需要增加 xdp_buff 所引用到的 packet data 所在的 page 的 page count。 # --- 以和发送完毕之后的 xdp_return_frame() 处理 相对应。
#
+
+ case XDP_REDIRECT_MC:
+ err = xdp_do_redirect_mc(adapter->netdev, xdp, xdp_prog);
+
+ break;
+
===============================================================================================================