ipvlan分析

macvlan

  • 虚拟接口一个拥有独立 MAC 虚拟设备;

  • 根据目的 MAC 转发;

  • 适用一些需要独立 MAC 地址场景,部分平台、老旧网络设备隔离容器网络。

ipvlan

  • 所有接口共享一个 MAC 地址(就是物理接口的);

  • 转发根据 目的 IP 地址 判定;

  • 适合三层 IP 网络,转发快(一次查找);

  • 交换支持多个 MAC 地址。

                    +--------------------+
Incoming skb -----> | ipvlan_handle_l2() |
                    +--------------------+
                            |
         +------------------+------------------+
         |                                     |
  [组播] 是                                 否 [单播]
         |                                     |
调用 external_frame()                  转发给 l3 逻辑
判断是否应复制广播                    ipvlan_handle_l3()
         |
 skb_clone() 并 enqueue 到队列

 

 

rx_handler_result_t ipvlan_handle_frame(struct sk_buff **pskb)
{
    struct sk_buff *skb = *pskb;
    struct ipvl_port *port = ipvlan_port_get_rcu(skb->dev);

    if (!port)
        return RX_HANDLER_PASS;

    switch (port->mode) {
    case IPVLAN_MODE_L2:
        return ipvlan_handle_mode_l2(pskb, port);
    case IPVLAN_MODE_L3:
        return ipvlan_handle_mode_l3(pskb, port);
#ifdef CONFIG_IPVLAN_L3S
    case IPVLAN_MODE_L3S:
        return RX_HANDLER_PASS;
#endif
    }

    /* Should not reach here */
    WARN_ONCE(true, "ipvlan_handle_frame() called for mode = [%hx]\n",
              port->mode);
    kfree_skb(skb);
    return RX_HANDLER_CONSUMED;
}
static rx_handler_result_t ipvlan_handle_mode_l2(struct sk_buff **pskb,
                         struct ipvl_port *port)
{
    struct sk_buff *skb = *pskb;
    struct ethhdr *eth = eth_hdr(skb);
    rx_handler_result_t ret = RX_HANDLER_PASS;

    if (is_multicast_ether_addr(eth->h_dest)) {
        if (ipvlan_external_frame(skb, port)) {//如果是外部流量
            struct sk_buff *nskb = skb_clone(skb, GFP_ATOMIC);

            /* External frames are queued for device local
             * distribution, but a copy is given to master
             * straight away to avoid sending duplicates later
             * when work-queue processes this frame. This is
             * achieved by returning RX_HANDLER_PASS.
             */
            if (nskb) {//是否应当将其发给主接口(如 eth0)之外的其他命名空间(比如其他容器)
                ipvlan_skb_crossing_ns(nskb, NULL);
                //通过 ipvlan_multicast_enqueue() 把它入队,供其他容器通过此端口后续异步接收处理
                ipvlan_multicast_enqueue(port, nskb, false);
            }
        }
    } else {
        /* Perform like l3 mode for non-multicast packet */
    //L2 模式只特别处理了组播帧,其它行为近似于 L3。
        ret = ipvlan_handle_mode_l3(pskb, port);
    }

    return ret;
}

static bool ipvlan_external_frame(struct sk_buff *skb, struct ipvl_port *port)
{
    struct ethhdr *eth = eth_hdr(skb);
    struct ipvl_addr *addr;
    void *lyr3h;
    int addr_type;
    //判断帧的源 MAC 是否与主设备(如 eth0)的 MAC 相同
    if (ether_addr_equal(eth->h_source, skb->dev->dev_addr)) {
        /*如果相同,说明这个包 是从本地机器发出去的包回环(loopback)回来
            是否是我们自己发出的广播包?
            如果成功提取 L3 层头(IPv4/IPv6 arp)*/
        lyr3h = ipvlan_get_L3_hdr(port, skb, &addr_type);
        if (!lyr3h)
            return true;
        /*查找这个目的地址是否是某个子接口的 IP(但传 false,表示是匹配目的地址而非源地址);
如果命中了,则说明这个包是我们内部某个子接口发出的,且目的也是我们自己,就不需要再多播分发;*/
        addr = ipvlan_addr_lookup(port, lyr3h, addr_type, false);
        if (addr)
            return false;
    }

    return true;
}
static rx_handler_result_t ipvlan_handle_mode_l3(struct sk_buff **pskb,
                         struct ipvl_port *port)
{
    void *lyr3h;
    int addr_type;
    struct ipvl_addr *addr;
    struct sk_buff *skb = *pskb;
    rx_handler_result_t ret = RX_HANDLER_PASS;
    // 当前 ipvlan port,上面挂载多个 ipvlan 虚拟接口
    lyr3h = ipvlan_get_L3_hdr(port, skb, &addr_type);
    if (!lyr3h)//获取arp ipv4 头部
        goto out;
    //在这个 ipvlan port 上查找是否有子接口拥有目标 IP 地址
    //(即:看数据包目的地址是不是绑定在某个 ipvlan 虚接口上的);
    addr = ipvlan_addr_lookup(port, lyr3h, addr_type, true);
    if (addr)
        ret = ipvlan_rcv_frame(addr, pskb, false);
    /*调用 ipvlan_rcv_frame(),相当于把这个包“转送”给该子接口;

内部会修改 skb->dev = addr->device,;
返回 RX_HANDLER_ANOTHER,告诉内核该包由别的设备接管了。
然后重走 netifrcv_rx() 重新投递到网络协议栈上;
同时arp请求 相邻子接口信息,L2 模式下是广播到所有接口,异步调用netif_recv处理
L3 是直接查找对应接口,然后进入改接口的协议栈处理
*/ out: return ret; }

 

 

发送

static int ipvlan_xmit_mode_l3(struct sk_buff *skb, struct net_device *dev)
{
    const struct ipvl_dev *ipvlan = netdev_priv(dev);
    void *lyr3h;
    struct ipvl_addr *addr;
    int addr_type;

    lyr3h = ipvlan_get_L3_hdr(ipvlan->port, skb, &addr_type);
    if (!lyr3h)
        goto out;
//模式有:VEPA  +    PRIVATE + NONE-没有设置模型,模式为0
    if (!ipvlan_is_vepa(ipvlan->port)) {
        addr = ipvlan_addr_lookup(ipvlan->port, lyr3h, addr_type, true);
        if (addr) {
            if (ipvlan_is_private(ipvlan->port)) {
                //果当前启用了 IPVLAN_F_PRIVATE,说明不允许容器间通信 → 丢弃。
                consume_skb(skb);
                return NET_XMIT_DROP;
            }
            return ipvlan_rcv_frame(addr, &skb, true);
            /*ru果不是 VEPA 模式(即可能是 bridge 模式):
        调用 ipvlan_addr_lookup 检查这个目标地址是否是本地其他 ipvlan 子接口的 IP。
        如果找到了本地地址 → 表示这是个本地通信流量。*/
        }
    }
out:
    ipvlan_skb_crossing_ns(skb, ipvlan->phy_dev);
    //如果不是本地地址(或是 VEPA 模式),走默认发送流程
    return ipvlan_process_outbound(skb);
}

 

static int ipvlan_process_v4_outbound(struct sk_buff *skb)
{
    const struct iphdr *ip4h = ip_hdr(skb);
    struct net_device *dev = skb->dev;
    struct net *net = dev_net(dev);
    struct rtable *rt;
    int err, ret = NET_XMIT_DROP;
    struct flowi4 fl4 = {
        .flowi4_oif = dev->ifindex,
        .flowi4_tos = RT_TOS(ip4h->tos),
        .flowi4_flags = FLOWI_FLAG_ANYSRC,
        .flowi4_mark = skb->mark,
        .daddr = ip4h->daddr,
        .saddr = ip4h->saddr,
    };

    rt = ip_route_output_flow(net, &fl4, NULL);
    if (IS_ERR(rt))
        goto err;

    if (rt->rt_type != RTN_UNICAST && rt->rt_type != RTN_LOCAL) {
        ip_rt_put(rt);
        goto err;
    }
    skb_dst_set(skb, &rt->dst);
    err = ip_local_out(net, skb->sk, skb);
    if (unlikely(net_xmit_eval(err)))
        dev->stats.tx_errors++;
    else
        ret = NET_XMIT_SUCCESS;
    goto out;
err:
    dev->stats.tx_errors++;
    kfree_skb(skb);
out:
    return ret;
}

 

 相比L2 模式下发送报文:多了路由,L3有路由,L2直接发送

  • ​​性能优势​​:L2 模式绕过了内核路由栈,直接转发数据包。

 

关键行为对比​

​​场景​​​​L2 模式​​​​L3 模式​​
​​本地通信(同一主机)​​ 直接转发,无开销 需走路由栈,性能较低
​​跨子网通信​​ 依赖外部路由 通过主机路由表直接转发
​​ARP 处理​​ 主机代理 ARP 响应 需手动配置或依赖外部设备
​​广播/多播​​ 泛洪到所有 L2 接口 按路由表处理(可能被丢弃)
​​私有模式影响​​ 禁止本地通信 无影响(依赖路由表)

 性能与配置复杂度​​

​​指标​​​​L2 模式​​​​L3 模式​​
​​性能​​ 高(直接二层转发) 中(需路由栈处理)
​​配置复杂度​​ 简单(自动 ARP 代理) 复杂(需手动管理路由/ARP)
​​适用场景​​ 同一子网的高性能通信(如容器) 跨子网通信或需要灵活路由的场景

 

IPvlan 的 ​​L2 模式​​ 和 ​​L3 模式​​ 在收发包行为上有显著差异,主要体现在 ​​协议层级处理​​、​​通信范围​​、​​ARP/路由依赖​​ 以及 ​​性能开销​​ 等方面。以下是详细的对比分析:


​​1. 接收数据包(RX)​​

​​L2 模式​​

  • ​​处理流程​​:

    1. ​​共享 MAC 地址​​:所有 IPvlan L2 接口共享主机的 MAC 地址。
    2. ​​二层转发​​:数据包通过物理接口直接接收,无需经过主机协议栈。
    3. ​​ARP 代理​​:主机自动响应 ARP 请求(目标为 IPvlan 接口的 IP)。
    4. ​​本地通信​​:若目标地址是同一主机的其他 IPvlan 接口,直接转发(需非私有模式)。
    5. ​​广播/多播​​:泛洪到所有 L2 模式的子接口。
  • ​​特点​​:

    • ​​本地通信高效​​:直接通过二层转发,无额外开销。
    • ​​依赖物理网络​​:跨子网通信需外部路由支持。

​​L3 模式​​

  • ​​处理流程​​:

    1. ​​独立 IP 地址​​:每个 IPvlan 接口有独立的 IP 地址。
    2. ​​三层路由​​:数据包通过主机协议栈处理,依赖路由表。
    3. ​​ARP 自主处理​​:需手动配置 ARP 或依赖外部设备响应。
    4. ​​本地通信受限​​:默认不转发本地流量(需显式配置路由)。
    5. ​​广播/多播​​:按路由表处理,可能被丢弃或转发到外部。
  • ​​特点​​:

    • ​​跨子网支持​​:天然支持跨子网通信。
    • ​​性能开销​​:需经过主机路由栈,增加 CPU 和延迟。

​​2. 发送数据包(TX)​​

​​L2 模式​​

  • ​​处理流程​​:

    1. ​​直接转发​​:数据包通过物理接口直接发送。
    2. ​​目标 MAC 匹配​​:
      • 若目标 MAC 是其他 IPvlan 接口且非私有模式,直接转发。
      • 若目标 MAC 是外部设备,依赖物理网络路由。
    3. ​​广播/多播​​:泛洪到物理接口。
  • ​​特点​​:

    • ​​低延迟​​:无协议栈处理,直接二层转发。
    • ​​依赖物理交换机​​:需交换机支持同 MAC 地址的多接口通信。

​​L3 模式​​

  • ​​处理流程​​:

    1. ​​路由决策​​:通过主机路由表确定出口接口(可能是物理接口或外部网关)。
    2. ​​ARP 请求​​:若目标 IP 不在同一子网,触发 ARP 请求获取网关 MAC。
    3. ​​跨子网通信​​:通过物理接口发送到网关。
  • ​​特点​​:

    • ​​灵活性​​:支持复杂路由策略(如多宿主主机)。
    • ​​性能开销​​:需经过路由栈和 ARP 处理。

​​3. 关键行为对比​​

​​场景​​​​L2 模式​​​​L3 模式​​
​​本地通信(同一主机)​​ 直接转发,无开销 需走路由栈,性能较低
​​跨子网通信​​ 依赖外部路由 通过主机路由表直接转发
​​ARP 处理​​ 主机代理 ARP 响应 需手动配置或依赖外部设备
​​广播/多播​​ 泛洪到所有 L2 接口 按路由表处理(可能被丢弃)
​​私有模式影响​​ 禁止本地通信 无影响(依赖路由表)

​​4. 性能与配置复杂度​​

​​指标​​​​L2 模式​​​​L3 模式​​
​​性能​​ 高(直接二层转发) 中(需路由栈处理)
​​配置复杂度​​ 简单(自动 ARP 代理) 复杂(需手动管理路由/ARP)
​​适用场景​​ 同一子网的高性能通信(如容器) 跨子网通信或需要灵活路由的场景

​​5. 典型示例​​

​​L2 模式示例​​

  • ​​场景​​:同一子网内的两个容器通过 IPvlan L2 模式通信。
  • ​​流程​​:
    1. 容器 A 发送数据包到容器 B(目标 MAC 是容器 B 的 IPvlan 接口 MAC)。
    2. 主机通过 MAC 地址表直接转发到容器 B,无需经过路由栈。

​​L3 模式示例​​

  • ​​场景​​:两个容器位于不同子网,通过 IPvlan L3 模式通信。
  • ​​流程​​:
    1. 容器 A 发送数据包到容器 B(目标 IP 在不同子网)。
    2. 主机路由表将流量转发到网关,网关通过外部网络路由到容器 B。

 

posted @ 2025-05-18 13:04  codestacklinuxer  阅读(101)  评论(0)    收藏  举报