前言
在 Cilium 1.11 版本,Cilium 彻底移除了 IPVlan 的代码,代表着 IPVlan 不再是 Cilium 默认支持的组网方式。那么 Cilium 为什么不再需要 IPVlan 了呢?
传统 CNI 模式
在如 Flannel,Calico 等 CNI 实现过程中,会为每一个 Pod 创建一对 veth 设备,一端在 Host ns 内,另一端在 Pod ns 内,在宿主机上有到 pod IP 的路由指向对应的 Host ns 的 veth 设备。当两个 Pod 通信时,流量流程如下图所示(calico bgp 模式):
流量从 pod1 到 pod2,依次经过 pod1 ns 内部网络栈,Host 网络栈和 pod2 ns 内部网络栈。
Cilium Veth 模式
Cilium Veth 模式下没有结构变化,不过没有指向 pod IP 的路由;而是在 pod1 的 lxcxxx1 加上 tc ingress ebpf 应用,将目的 IP 是 Pod2 IP 的流量重定向到 lxcxxx2。
流量从 pod1 到 pod2,依次经过 pod1 ns 内部网络栈,pod2 ns 内部网络栈。ns 之间执行了两次从出口到入口的切换。
Cilium IPVlan 模式
IPVlan 模式下内部转发逻辑直接是 pod 到 pod,不通过 BPF 程序执行到设备的转发,该种模式下,网络命名空间切换更高效,堆栈不需要像外部数据包基于 veth 的数据路径那样被重新遍历。
流量从 pod1 到 pod2,ns 之间只执行了一次从出口到入口的切换。
// Linux v6.0 drivers/net/ipvlan/ipvlan_core.c L564
static int ipvlan_xmit_mode_l3(struct sk_buff *skb, struct net_device *dev)
{
......
if (!ipvlan_is_vepa(ipvlan->port)) {
// 根据目的 IP 查看是否是该网卡和所有 ipvlan 子接口的 IP.
addr = ipvlan_addr_lookup(ipvlan->port, lyr3h, addr_type, true);
if (addr) {
if (ipvlan_is_private(ipvlan->port)) {
consume_skb(skb);
return NET_XMIT_DROP;
}
// 如果是该网卡或子接口的 IP,且 linkup,修改 skb->dev = 接口 dev,由该接口处理。
return ipvlan_rcv_frame(addr, &skb, true);
}
}
......
}
Cilium IPVlan 与 Veth 相比 堆栈需要更少的资源来将数据包推送到另一个网络命名空间,拥有更好的延迟;但是 IPVlan 也有自己的不足,不支持 NAT64,不支持 L7 Policy,Pod 不能拥有 CAP_NET_ADMIN 和 CAP_NET_RAW 特权。
形式逆转
在 linux 内核到 5.10 版本后,bpf Helper 提供了一个新的方法 bpf_redirect_peer;它支持 ebpf 可以直接将流量 redirect 到接口的 veth-pair 对端。
Cilium 在 lxcxxx1 tc ingress ebpf 程序
|- cil_from_container
|- tail_handle_ipv4
|- __tail_handle_ipv4
|- tail_handle_ipv4_cont
|- handle_ipv4_from_lxc
|- ipv4_local_delivery // 判断目的 IP 是本 node pod 上的
|- redirect_ep // 查找该 IP 在 host ns 上的索引
|- ctx_redirect_peer
设置 redirect 的 iface 和 peer flag,转发。
// Linux v6.0 net/core/filter.c L2501
BPF_CALL_2(bpf_redirect_peer, u32, ifindex, u64, flags)
{
struct bpf_redirect_info *ri = this_cpu_ptr(&bpf_redirect_info);
if (unlikely(flags))
return TC_ACT_SHOT;
// redirect 时,加上 flag 是 peer。
ri->flags = BPF_F_PEER;
ri->tgt_index = ifindex;
return TC_ACT_REDIRECT;
}
基于以上,Cilium Veth 模式变化如下
流量从 pod1 到 pod2,ns 之间只执行了一次从出口到入口的切换,即 pod1 ns 到 host ns。
结论
在引入 bpf_redirect_peer 后,利用 veth 即可为 Kubernetes 提供同样的性能优势,鉴于 ipvlan 自身的部分限制;所以,默认支持 ipvlan 已经不是那么重要了。