MACVLAN(MAC Virtual LAN)是Linux内核提供的一种L2层的网络虚拟化技术,它允许在单个物理接口上创建多个虚拟子接口,每个子接口拥有独立的 MAC地址。与Linux Bridge相比,MACVLAN 减少了数据包处理层级,提供了更简洁的网络架构和更好的性能。MACVLAN通常用于容器网络、虚拟机网络等场景,为容器和虚拟机提供访问外部网络的能力。
本文结合实际的业务场景,基于Linux 5.15内核源码,深入分析MACVLAN的实现原理和工作机制。
MACVLAN 简介 MACVLAN允许在单个物理网络接口(父接口)上创建多个虚拟子接口。每个子接口拥有独立的 MAC 地址,对网络中的其他设备而言,它们就像是独立的物理设备。相对其他网络虚拟化技术,MACVLAN具有如下几个优势:
独立 MAC 地址 : 每个子接口拥有唯一的 MAC 地址,可被网络独立识别
多种工作模式 : 支持私有、VEPA、桥接、直通、源地址五种模式
高性能 : 数据包无需经过额外的桥接层,减少处理开销
简化拓扑 : 无需创建 Linux Bridge,直接通过物理接口通信
MACVLAN实现原理 在Linux内核中,对应有MACVLAN的网络驱动,核心代码位于drivers/net/macvlan.c文件中,主要提供了如下两个关键的数据结构:
struct macvlan_dev: MACVLAN设备,用于表示一个MACVLAN子设备,包含了MACVLAN设备、MACVLAN端口等信息
struct macvlan_port: MACVLAN端口,表示一个MACVLAN端口,包含了MACVLAN子设备的列表和MAC地址哈希表等信息
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 struct macvlan_dev { struct net_device *dev ; struct net_device *lowerdev ; struct macvlan_port *port ; struct hlist_node hlist ; struct list_head list ; enum macvlan_mode mode ; u16 flags; struct macvlan_source_entry __rcu *source_list ; unsigned int macaddr_count; }; struct macvlan_port { struct net_device *dev ; struct hlist_head vlan_hash [MACVLAN_HASH_SIZE ]; struct list_head vlans ; struct rcu_head rcu ; bool passthru; int count; };
对MACVLAN虚拟网卡来说,主要的处理都集中在数据链路层(L2),我们以MACVLAN设备注册与数据包发送为例说明MACVLAN的实现原理。
MACVLAN 的注册流程 MACVLAN设备的创建和注册过程如下,主要包括如下几个步骤:
查找并验证父设备:检查父设备是否存在、是否支持MACVLAN。
初始化 MACVLAN 设备:创建 MACVLAN 设备、初始化MACVLAN端口等
注册 MACVLAN 设备:将 MACVLAN 设备注册到内核网络设备列表中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 static int macvlan_newlink (struct net *src_net, struct net_device *dev, struct nlattr *tb[], struct nlattr *data[]) { struct macvlan_dev *vlan = netdev_priv(dev); struct net_device *lowerdev ; struct macvlan_port *port ; int err; if (!tb[IFLA_LINK]) return -EINVAL; lowerdev = __dev_get_by_index(src_net, nla_get_u32(tb[IFLA_LINK])); if (!lowerdev) return -ENODEV; if (!macvlan_port_exists(lowerdev)) { err = macvlan_port_create(lowerdev); if (err) return err; } port = macvlan_port_get_rtnl(lowerdev); vlan->lowerdev = lowerdev; vlan->dev = dev; vlan->port = port; vlan->mode = MACVLAN_MODE_VEPA; if (tb[IFLA_ADDRESS]) eth_hw_addr_set(dev, nla_data(tb[IFLA_ADDRESS])); else eth_hw_addr_random(dev); list_add_tail_rcu(&vlan->list , &port->vlans); macvlan_hash_add(vlan); port->count++; err = register_netdevice(dev); if (err) goto cleanup; return 0 ; cleanup: macvlan_delete(vlan); return err; }
数据包接收流程 当物理接口接收到数据包时,一般会通过网络软中断进行处理,然后再发送到MACVLAN设备,MACVLAN的处理流程如下:
获取MACVLAN端口结构:通过skb->dev获取MACVLAN端口结构
处理多播/广播包:MACVLAN端口处理多播/广播包的逻辑
源地址检查:防止 MAC 地址欺骗
处理数据包:MACVLAN端口处理数据包
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 static rx_handler_result_t macvlan_handle_frame (struct sk_buff **pskb) { struct macvlan_port *port ; struct sk_buff *skb = *pskb; const struct ethhdr *eth = eth_hdr(skb); const struct macvlan_dev *vlan ; port = macvlan_port_get_rcu(skb->dev); if (!port) return RX_HANDLER_PASS; if (is_multicast_ether_addr(eth->h_dest)) { return macvlan_handle_multicast(pskb, port, eth); } macvlan_forward_source(skb, port, eth->h_source); if (macvlan_passthru(port)) vlan = list_first_or_null_rcu(&port->vlans, struct macvlan_dev, list ); else vlan = macvlan_hash_lookup(port, eth->h_dest); if (!vlan || vlan->mode == MACVLAN_MODE_SOURCE) return RX_HANDLER_PASS; skb->dev = vlan->dev; skb->pkt_type = PACKET_HOST; return RX_HANDLER_ANOTHER; }
数据的发送流程主要在macvlan_queue_xmit函数中,不再赘述,有兴趣的可以自己研究下。
工作模式 MACVLAN 支持五种工作模式,使用位掩码定义:
MACVLAN_MODE_PRIVATE: 私有模式,同一父设备下的虚拟子网卡之间完全隔离,且子网卡与父接口所在主机也完全隔离,所有子网卡仅能与外部网络通信,数据包无需在主机内做任何转发
MACVLAN_MODE_VEPA: 虚拟以太网端口聚合模式,禁止同 MACVLAN实例的子网卡之间直接通信,所有子网卡的数据包(包括同组互访)都必须通过物理网卡转发到外部交换机,由交换机完成数据包的转发 / 过滤 / 隔离,再回传给目标子网卡
MACVLAN_MODE_BRIDGE: 桥接模式,同父设备下的 MACVLAN 可直接通信,组成一个虚拟的网络,子网卡之间的数据可以直接通过内核转发,无需经过物理网卡与外部交换机
MACVLAN_MODE_PASSTHRU: 一个物理网卡仅能创建一个虚拟子网卡,子网卡直接 “直通”物理父接口的二层特性,可复用父接口的 MAC 地址(或指定专属 MAC),子网卡的二层流量直接映射到物理网卡,内核仅做简单的数据包透传
MACVLAN_MODE_SOURCE: 基于源MAC地址过滤网络报文
1 2 3 4 5 6 7 8 enum macvlan_mode { MACVLAN_MODE_PRIVATE = 1 , MACVLAN_MODE_VEPA = 2 , MACVLAN_MODE_BRIDGE = 4 , MACVLAN_MODE_PASSTHRU = 8 , MACVLAN_MODE_SOURCE = 16 , };
应用场景 MACVLAN作为Linux下轻量级的网络虚拟化技术,在容器、虚拟机、虚拟化网络等场景中得到了广泛的应用。
容器网络 让容器直接连接到物理网络,获得独立 IP:
1 2 3 4 5 6 7 8 9 10 11 docker network create -d macvlan \ --subnet=192.168.1.0/24 \ --gateway=192.168.1.1 \ -o parent=eth0 \ macvlan_net docker run --network macvlan_net \ --ip=192.168.1.100 \ -it ubuntu:latest
虚拟机网络 为 KVM/QEMU 虚拟机提供直接网络访问:
1 2 3 4 5 ip link add macvlan0 link eth0 type macvlan mode bridge ip link set macvlan0 address 00:11:22:33:44:55 ip link set macvlan0 up ip addr add 192.168.1.200/24 dev macvlan0
网络隔离 为不同服务创建独立的网络接口,实现服务间隔离:
1 2 3 4 5 6 7 8 9 ip link add web-vlan link eth0 type macvlan mode private ip link add db-vlan link eth0 type macvlan mode private ip link add cache-vlan link eth0 type macvlan mode private ip addr add 10.0.1.10/24 dev web-vlan ip addr add 10.0.1.20/24 dev db-vlan ip addr add 10.0.1.30/24 dev cache-vlan
总结 MACVLAN 是一种轻量级的网络虚拟化技术,通过在单个物理接口上虚拟出多个独立 MAC 地址的子接口,每个网卡都共享父接口的网络链路与物理带宽。相比 Linux Bridge,它减少了数据包处理层级,在性能和简洁性上都具有优势。支持五种不同的工作模式(PRIVATE、VEPA、BRIDGE、PASSTHRU、SOURCE)适应了不同的隔离和通信需求,使其在容器网络、虚拟机网络、服务隔离等场景中得到广泛应用。理解其内核实现机制,有助于在实际部署中做出更合理的网络架构设计。
参考文献
Linux内核源码: https://github.com/torvalds/linux/tree/master/drivers/net/macvlan.c
MACVLAN内核文档: https://www.kernel.org/doc/Documentation/networking/macvlan.txt
Docker MACVLAN文档: https://docs.docker.com/network/macvlan/
Linux IP link