Skip to the content.

ipvlan

关联话题 ./macvlan.md

设计哲学与演进背景

为什么需要 ipvlan?


macvlan (解决 bridge 性能问题)
        ┌─────────┐
        │ 容器 A  │──┐ 独立 MAC
        └─────────┘  │
                     ├────► eth0 (物理网卡)
        ┌─────────┐  │
        │ 容器 B  │──┘ 独立 MAC
        └─────────┘
        问题: 交换机可能限制单端口 MAC 数量,promiscuous 模式

ipvlan (解决 macvlan 的交换机兼容性问题)
        ┌─────────┐
        │ 容器 A  │──┐ 共享 MAC
        └─────────┘  │      通过 IP 区分
                     ├────► eth0 (物理网卡)
        ┌─────────┐  │
        │ 容器 B  │──┘ 共享 MAC
        └─────────┘

ipvlan 的诞生动机(Google 内部需求驱动,2014 年由 Mahesh Bandewar 开发):

  1. 数据中心交换机限制: 企业级交换机通常限制单端口学习 MAC 地址数量(如 32/64/128 个),大规模容器部署时 macvlan 会触发端口安全策略
  2. Promiscuous 模式性能问题: 大量 macvlan 设备可能迫使网卡进入 promiscuous 模式,导致所有流量走软件路径
  3. L3 路由简化: 在云环境中,网络通常基于 L3 路由而非 L2 交换,ipvlan L3 模式更符合云网络模型

解决了什么问题

问题 传统方案 ipvlan 方案 效果
MAC 地址耗尽 macvlan 每个容器独立 MAC 共享物理网卡 MAC 单端口 MAC 数量从 N 降为 1
交换机端口安全告警 需要配置 port-security 天然规避 减少运维干预
Bridge 性能开销 veth+bridge 引入软交换 直接绑定物理网卡 延迟降低 10-30%,吞吐提升
Netfilter 开销 bridge 走 iptables L3 模式绕过 CPU 使用率降低
云网络兼容性 需要 Overlay 封装 原生 IP 直连 与 VPC 网络无缝集成

带来了什么问题( Trade-offs )

1. 网络可见性降低

问题: 所有容器共享同一 MAC,传统网络工具(tcpdump 在交换机上)无法区分流量来源
影响: 网络排障困难,安全审计复杂

2. 与现有网络的兼容性

问题:
- DHCP 服务器通常基于 MAC 分配 IP,共享 MAC 导致 DHCP 冲突
- ARP 协议在 L3 模式被禁用,某些依赖 ARP 的应用异常
- 需要静态 IP 或专门的路由配置

影响: 无法即插即用,需要重新设计网络架构

3. 调试复杂度增加

问题: 跨 namespace 的流量转发在内核中完成,难以追踪
对比: bridge 可以通过 brctl showmacs 查看 MAC 表,ipvlan 需要查看内核哈希表

4. 生态支持有限

现状:
- Docker 默认不支持 ipvlan(需要 --network ipvlan 手动指定)
- Kubernetes CNI 生态中主流是 Calico/Cilium/Flannel,原生 ipvlan 支持有限
- 多数云厂商的托管 K8s 不提供 ipvlan 网络选项

生产环境使用情况

使用频率: 相对小众,特定场景

场景 使用 ipvlan 主流方案
公有云容器 极少 VPC CNI (AWS/Azure/GCP)
私有云/裸金属 K8s 部分使用 Calico BGP / Cilium
超大规模容器 (10k+/node) 考虑使用 ipvlan / SR-IOV
NFV/VNF 较多使用 ipvlan / DPDK
边缘计算 部分使用 Flannel / Cilium

主要原因:

  1. overlay 网络的胜利: VXLAN/Geneve overlay 方案(Flannel/Calico VXLAN/Cilium)成为 Kubernetes 主流,它们不依赖底层网络配置,部署更简单
  2. 云厂商锁定: 主流云厂商提供自研 CNI(如 AWS VPC CNI、Azure CNI),直接对接 VPC,ipvlan 优势不明显
  3. CNI 生态: 原生支持 ipvlan 的 CNI 插件较少,且社区活跃度不如 Calico/Cilium

主要应用场景

场景 1: 裸金属大规模容器部署

环境: 自建 IDC,物理机直接跑容器,无虚拟化层
需求: 单机 1000+ 容器,需要直连物理网络
优势: 避免 bridge 开销,避免 MAC 地址耗尽
案例: 早期 Docker Swarm 大规模部署,部分头条/字节跳动内部业务

场景 2: NFV (Network Function Virtualization)

环境: 电信云,运行 VNF(虚拟化网络功能)
需求: 网络功能(防火墙、LB)需要直接处理物理流量
优势: L3 模式性能接近物理网卡,适合包处理
案例: OpenStack Tacker,某些 5G core 网元部署

场景 3: 需要直连 VPC 的容器

环境: 混合云,容器需要与 VM 处于同一 L2/L3 网络
需求: 容器 IP 可被外部直接访问,无需 NAT
优势: 容器作为"一等公民"接入 VPC
案例: AWS ECS 早期支持 ipvlan,某些金融企业合规要求

场景 4: 高性能网络测试/基准测试

环境: 网络性能测试工具开发
需求: 最小化虚拟化开销,测试物理网卡极限
优势: 代码路径最短,接近裸机性能

与主流 CNI 的关系

┌─────────────────────────────────────────────────────────────┐
│                    Kubernetes CNI 生态                       │
├─────────────────────────────────────────────────────────────┤
│  Overlay 方案 (主流)                                         │
│  ├── Flannel (VXLAN)        - 简单易用,性能一般              │
│  ├── Calico (VXLAN/IPIP)    - 功能丰富,企业常用              │
│  └── Cilium (VXLAN/路由)    - eBPF 加速,云原生首选           │
│                                                             │
│  Underlay/直连方案 (特定场景)                                 │
│  ├── Calico (BGP)           - 需要交换机支持 BGP              │
│  ├── macvlan                - 需要独立 MAC                    │
│  ├── ipvlan                 - 本文主角,共享 MAC              │
│  └── SR-IOV                 - 硬件虚拟化,性能最优             │
│                                                             │
│  云厂商方案                                                  │
│  ├── AWS VPC CNI            - 直接分配 ENI 弹性网卡           │
│  ├── Azure CNI              - 对接 Azure VPC                  │
│  └── GCP GKE Networking     - 对接 GCP VPC                    │
└─────────────────────────────────────────────────────────────┘

ipvlan 的定位: Underlay 方案中的轻量级选择,介于 macvlan 和 SR-IOV 之间,在不需要硬件虚拟化但需要直连物理网络时使用。

核心概念

ipvlan 是 Linux 内核提供的一种虚拟网络设备,与 macvlan 类似,但关键区别在于:所有 slave 设备共享 master 设备的 MAC 地址,通过 L3 (IP 层) 进行多路复用/解复用

┌─────────────────────────────────────────────────────────────┐
│                         Host                                │
│  ┌─────────────┐      ┌─────────────┐                       │
│  │   NS: ns1   │      │   NS: ns2   │                       │
│  │  ┌───────┐  │      │  ┌───────┐  │                       │
│  │  │ ipvl0 │  │      │  │ ipvl1 │  │                       │
│  │  └───┬───┘  │      │  └───┬───┘  │                       │
│  └──────┼──────┘      └──────┼──────┘                       │
│         │                    │                              │
│         └────────────────────┘                              │
│                     │                                       │
│              ┌──────┴──────┐                                │
│              │  eth0 (master)│  ← 共享 MAC, L3 分发          │
│              └─────────────┘                                │
└─────────────────────────────────────────────────────────────┘

与 macvlan 的关键区别

特性 macvlan ipvlan
MAC 地址 每个 slave 独立 MAC 共享 master MAC
分发层级 L2 (MAC 层) L3 (IP 层)
交换机兼容性 可能触发端口安全策略 单 MAC 无此问题
promiscuous 模式 大量 slave 可能触发 不会触发
适用场景 需要独立 MAC 交换机限制 MAC 数量

内核数据结构

// drivers/net/ipvlan/ipvlan.h
struct ipvl_dev {
    struct net_device   *dev;           // 虚拟设备本身
    struct list_head    pnode;          // 链表节点
    struct ipvl_port    *port;          // 端口信息
    struct net_device   *phy_dev;       // 物理设备 (master)
    struct list_head    addrs;          // IP 地址列表
    struct ipvl_pcpu_stats __percpu *pcpu_stats;
    DECLARE_BITMAP(mac_filters, IPVLAN_MAC_FILTER_SIZE);
    netdev_features_t   sfeatures;
    u32                 msg_enable;
};

struct ipvl_port {
    struct net_device   *dev;           // master 设备
    struct hlist_head   hlhead[IPVLAN_HASH_SIZE];  // IP 地址哈希表
    spinlock_t          addrs_lock;
    struct list_head    ipvlans;        // 所有 slave 链表
    u16                 mode;           // L2/L3/L3S 模式
    u16                 flags;          // bridge/private/vepa
    // ...
};

struct ipvl_addr {
    struct ipvl_dev     *master;
    union {
        struct in6_addr ip6;
        struct in_addr  ip4;
    } ipu;
    struct hlist_node   hlnode;         // 哈希表节点
    struct list_head    anode;
    ipvl_hdr_type       atype;          // IPVL_IPV4/IPVL_IPV6
};

三种工作模式

L2 模式 (IPVLAN_MODE_L2)

// drivers/net/ipvlan/ipvlan_core.c:ipvlan_xmit_mode_l2()
static int ipvlan_xmit_mode_l2(struct sk_buff *skb, struct net_device *dev)
{
    const struct ipvl_dev *ipvlan = netdev_priv(dev);
    struct ethhdr *eth = skb_eth_hdr(skb);
    struct ipvl_addr *addr;
    void *lyr3h;
    int addr_type;

    // 1. 检查是否是同一主机的 slave 间通信
    if (!ipvlan_is_vepa(ipvlan->port) &&
        ether_addr_equal(eth->h_dest, eth->h_source)) {
        lyr3h = ipvlan_get_L3_hdr(ipvlan->port, skb, &addr_type);
        if (lyr3h) {
            // 根据目的 IP 查找对应的 slave
            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;
                }
                // 直接转发到目标 slave
                ipvlan_rcv_frame(addr, &skb, true);
                return NET_XMIT_SUCCESS;
            }
        }
        // 转发到 master 设备
        dev_forward_skb(ipvlan->phy_dev, skb);
        return NET_XMIT_SUCCESS;

    } else if (is_multicast_ether_addr(eth->h_dest)) {
        // 组播/广播入队处理
        skb_reset_mac_header(skb);
        ipvlan_skb_crossing_ns(skb, NULL);
        ipvlan_multicast_enqueue(ipvlan->port, skb, true);
        return NET_XMIT_SUCCESS;
    }

    // 2. 普通单播,直接通过 master 发出
    skb->dev = ipvlan->phy_dev;
    return dev_queue_xmit(skb);
}

特点:

L3 模式 (IPVLAN_MODE_L3)

// drivers/net/ipvlan/ipvlan_core.c:ipvlan_xmit_mode_l3()
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;

    if (!ipvlan_is_vepa(ipvlan->port)) {
        // 检查目的 IP 是否属于同一 master 的其他 slave
        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;
            }
            // 同一主机内转发,不走物理网络
            ipvlan_rcv_frame(addr, &skb, true);
            return NET_XMIT_SUCCESS;
        }
    }
out:
    // 跨 namespace 发送,需要 L3 路由
    ipvlan_skb_crossing_ns(skb, ipvlan->phy_dev);
    return ipvlan_process_outbound(skb);
}

特点:

L3S 模式 (IPVLAN_MODE_L3S)

L3S = L3-Symmetric,与 L3 类似,但支持 iptables/netfilter conntrack。

// drivers/net/ipvlan/ipvlan_l3s.c
// 注册 nf_hooks 在多个 netfilter 钩子点
static const struct nf_hook_ops ipvlan_l3s_nfops[] = {
    {
        .hook     = ipvlan_l3s_nf_hook,
        .pf       = NFPROTO_IPV4,
        .hooknum  = NF_INET_LOCAL_IN,
        .priority = INT_MIN,
    },
    {
        .hook     = ipvlan_l3s_nf_hook,
        .pf       = NFPROTO_IPV4,
        .hooknum  = NF_INET_LOCAL_OUT,
        .priority = INT_MAX,
    },
    // ... IPv6 类似
};

特点:

接收路径 (RX)

// drivers/net/ipvlan/ipvlan_core.c:ipvlan_handle_frame()
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);
    case IPVLAN_MODE_L3S:
        return RX_HANDLER_PASS;  // L3S 使用 netfilter hooks
    }
    // ...
}

// L3 模式接收处理
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;

    // 提取 L3 头部 (IP/ARP/ICMPv6)
    lyr3h = ipvlan_get_L3_hdr(port, skb, &addr_type);
    if (!lyr3h)
        goto out;

    // 根据目的 IP 查找目标 slave
    addr = ipvlan_addr_lookup(port, lyr3h, addr_type, true);
    if (addr)
        ret = ipvlan_rcv_frame(addr, pskb, false);

out:
    return ret;
}

关键机制:

IP 地址管理

// drivers/net/ipvlan/ipvlan_core.c:ipvlan_addr_lookup()
struct ipvl_addr *ipvlan_addr_lookup(struct ipvl_port *port, void *lyr3h,
                                     int addr_type, bool use_dest)
{
    struct ipvl_addr *addr = NULL;

    switch (addr_type) {
    case IPVL_IPV6: {
        struct ipv6hdr *ip6h = (struct ipv6hdr *)lyr3h;
        struct in6_addr *i6addr = use_dest ? &ip6h->daddr : &ip6h->saddr;
        addr = ipvlan_ht_addr_lookup(port, i6addr, true);
        break;
    }
    case IPVL_IPV4: {
        struct iphdr *ip4h = (struct iphdr *)lyr3h;
        __be32 *i4addr = use_dest ? &ip4h->daddr : &ip4h->saddr;
        addr = ipvlan_ht_addr_lookup(port, i4addr, false);
        break;
    }
    case IPVL_ARP: {
        // ARP 处理:根据 target IP 查找
        struct arphdr *arph = (struct arphdr *)lyr3h;
        // ...
        addr = ipvlan_ht_addr_lookup(port, &dip, false);
        break;
    }
    // ...
    }
    return addr;
}

通信模式标志

标志 说明
bridge (默认) slave 间可以直接通信
private slave 间禁止通信
vepa 所有流量强制发送到外部交换机 (802.1Qbg)
// bridge 模式:slave 间直接转发
if (addr) {
    if (ipvlan_is_private(ipvlan->port)) {
        consume_skb(skb);  // private 模式直接丢弃
        return NET_XMIT_DROP;
    }
    ipvlan_rcv_frame(addr, &skb, true);  // bridge 模式直接转发
    return NET_XMIT_SUCCESS;
}

使用场景选择

根据内核文档和实践经验:

场景 推荐方案
交换机限制单端口 MAC 数量 ipvlan
大量容器导致 promiscuous 模式性能下降 ipvlan
不可信容器可能滥用/伪造 L2 ipvlan (共享 MAC 更安全)
需要独立 MAC 地址 macvlan
需要组播/广播支持 macvlan 或 ipvlan L2 模式

测试验证

使用 net/ipvlan.sh 进行测试:

# 创建两个网络命名空间
sudo ip netns add ns1
sudo ip netns add ns2

# 在物理接口上创建 ipvlan L2 模式
sudo ip link add link ens5 name ipvl1 type ipvlan mode l2
sudo ip link add link ens5 name ipvl2 type ipvlan mode l2

# 移动到对应 namespace
sudo ip link set ipvl1 netns ns1
sudo ip link set ipvl2 netns ns2

# 配置 IP 并启用
sudo ip netns exec ns1 ip addr add 10.0.29.1/16 dev ipvl1
sudo ip netns exec ns1 ip link set ipvl1 up

sudo ip netns exec ns2 ip addr add 10.0.29.2/16 dev ipvl2
sudo ip netns exec ns2 ip link set ipvl2 up

# 测试连通性
sudo ip netns exec ns1 ping 10.0.29.2

内核函数调用链观察

使用 bpftrace/perf 可以观察到 ipvlan 的 TX 路径:

ipvlan_start_xmit+5
xmit_one.constprop.0+93
dev_hard_start_xmit+85
__dev_queue_xmit+1753
ip_finish_output2+587
ip_output+99
__ip_queue_xmit+873
__tcp_transmit_skb+2725
tcp_write_xmit+705
tcp_sendmsg_locked+1113

未来趋势与总结

技术演进方向

ipvlan 的困境与机遇:

2014-2018: 诞生期
  - Google 开源,解决内部大规模容器部署问题
  - Docker/K8s 社区开始关注

2018-2022: 被 overlay 网络压制
  - Calico/Flannel/Cilium 等 overlay 方案成为 K8s 主流
  - 云厂商 CNI 直接对接 VPC,ipvlan 优势不明显
  - 使用场景局限于裸金属+超大规模部署

2022-至今: eBPF 时代的边缘化
  - Cilium 使用 eBPF 实现更高效的数据平面
  - ipvlan 的 L3 模式优势被 eBPF 替代
  - 但在无 eBPF 支持的老内核中仍有价值

ipvlan 不会消失,但会 niche 化:

  1. 老系统维护: 大量 4.x 内核的生产环境无法使用 eBPF
  2. 简单场景: 不需要完整的 K8s CNI,单节点 Docker 部署
  3. NFV 领域: 电信设备商的传统方案延续

选型决策建议

┌─────────────────────────────────────────────────────────────────┐
│                     ipvlan 适用性评估                            │
├─────────────────────────────────────────────────────────────────┤
│  ✅ 使用 ipvlan                                                  │
│     - 裸金属部署,单机 > 500 容器                                 │
│     - 交换机严格限制 MAC 数量(<32)                              │
│     - 需要直连 VPC,不需要 NAT                                    │
│     - 追求极致网络性能,无 overlay 预算                           │
│                                                                 │
│  ❌ 不使用 ipvlan                                                │
│     - 公有云环境(AWS/Azure/GCP)                                 │
│     - 已使用 Calico/Cilium 等 overlay 方案                        │
│     - 需要容器间复杂网络策略(NetworkPolicy)                     │
│     - 运维团队不熟悉底层网络调试                                  │
│     - 需要 DHCP 自动分配 IP                                       │
└─────────────────────────────────────────────────────────────────┘

一句话总结

ipvlan 是容器网络演进中的”中间态”技术 —— 它比 bridge 快,比 macvlan 省 MAC,比 SR-IOV 简单;但在 overlay 网络的便利性和 eBPF 的高性能双重挤压下,其使用场景日益收缩,仅在特定裸金属+大规模+直连物理网络的场景中保留价值。

参考资料

本站所有文章转发 CSDN 将按侵权追究法律责任,其它情况随意。