macvtap
macvtap 是 Linux 内核中的一类虚拟网络设备与配套的字符设备驱动,它把 macvlan 接口和 tap 设备结合起来,让 QEMU、KVM 等虚拟化工具可以像使用传统 tap 设备那样,把虚拟机直接接入物理网络。使用 macvtap 不需要在宿主机上搭建桥接设备,虚拟机会以独立的 MAC 地址出现在物理网段上。该驱动由 Arnd Bergmann 编写,2010 年进入 Linux 内核。
定义
macvtap 是 Linux 内核 提供的一种网络设备类型,它在 macvlan 网络接口的基础上附加了一个 tap 字符设备,使同一套配置既能作为普通网络接口被管理和统计,又能通过字符设备向用户态程序收发数据帧[1]。创建 macvtap 接口的方式与创建 macvlan 接口相同,都用 ip link 命令,区别在于数据通路走字符设备而不是网络协议栈[1]。每个 macvtap 接口都拥有自己的字符设备节点,因此在权限管理上比通用的 tun/tap 驱动更简单[1]。
在内核配置中,该功能对应 CONFIG_MACVTAP 选项,其描述为基于 MAC-VLAN 的 tap 驱动,依赖 CONFIG_MACVLAN,可以编译为名为 macvtap 的模块[1]。在 libvirt 等管理工具中,这类接口以 type='direct' 的形式出现[2]。
原理
macvtap 的核心对象是一条队列,它把一个已打开的字符设备与一个 macvlan 接口连接起来,一个接口上可以存在多条队列,并与底层硬件的多队列相对应。队列通过套接字分配机制创建,收发路径分别由 aio_read 与 aio_write 等一系列文件操作实现,用户态程序可按帧读写[3]。
在数据方向上,来自外部物理接口的报文会被推入队列,等待用户态读取;用户态写入的报文则经由 macvlan 的发送路径送往下层设备[4]。若下层设备同时支持高位 DMA 与分散聚集,驱动还会打开零拷贝标志,使客户机内核与宿主内核之间的数据可以不经过额外复制。当接口存在多条队列时,驱动按报文的接收队列索引或流哈希选择队列,都失败时退回第一条可用队列。
发展历程
macvtap 驱动由 Arnd Bergmann 编写。其提交说明提到,为了让 macvlan 能够被 qemu 等需要 tap 文件描述符的工具使用,该驱动加入了一个小型后端,提供与 tun 驱动接口相同的字符设备,但只保留最少的功能[1]。这份补丁的提交时间为 2010 年 1 月 30 日,由 David S. Miller 于 2010 年 2 月 3 日合并[5]。
2013 年 12 月,Vlad Yasevich 提交补丁,使 macvtap 在收发两侧的行为与 macvlan 基本一致:发送侧改用 dev_queue_xmit,接收侧改用 netif_rx 与 dev_forward_skb,并给 macvtap 网络设备挂上自己的接收处理函数。这次改动之后,用户可以在 macvtap 设备上抓包,并对其施加 tc 动作[4]。此前的实现会在 skb->dev 尚未正确设置时就把报文从网络栈中取走,因此无法抓包[6]。
后续内核继续为其增加多队列支持与网络命名空间支持:多队列版本将队列组织为数组,并区分 numqueues 与 numvtaps;命名空间补丁则让 macvtap 设备类不再在不同网络命名空间之间共享 sysfs 层级[7][8]。
应用
macvtap 主要用于把虚拟机网卡直接接到宿主机的某个物理接口上。libvirt 的域 XML 中,这种连接写成 ,其中 source 元素的 dev 属性指定宿主机网卡名称,mode 属性指定工作模式[9]。IBM 的文档示例中同时给出 virtio 网卡模型与 vhost 驱动,并说明 MAC 地址字段可选,省略时由 libvirtd 自动生成[9]。
模式选项有 4 种。vepa 模式下客户机报文全部发往外部交换机,交换机需支持 hairpin 才能把同一主机上客户机之间的报文送回;bridge 模式下同一主机上的目的地由目标 macvtap 设备直接接收,但收发双方都必须处于 bridge 模式;private 模式下同一主机上的客户机彼此不能直接通信,报文需绕经外部路由器或网关;passthrough 模式把物理接口或 SR-IOV 虚拟功能直接交给一个客户机,该设备不能被多个客户机共享[2]。
手工创建一条 macvtap 接口时,需要指定下层网卡、接口名与模式,随后该接口对应的 /dev/tap 设备节点会自动出现,QEMU 可以打开它并配合 tap 后端使用[10]。由于不需要建立桥接,这类配置通常比传统桥接网络更简单,且每台虚拟机使用独立的 macvtap 接口[10]。在虚拟化场景之外,macvtap 也常与 ipvtap 并列提及,后者基于 ipvlan 而非 macvlan[11]。
局限
最常被提及的限制是宿主机与客户机之间无法直接通信。libvirt 的说明指出,这并非缺陷,而是 macvtap 的既定行为:宿主机物理网卡接入 macvtap 桥的方式,使客户机转发到物理接口的流量无法被回送到宿主机的 IP 协议栈,宿主协议栈发往物理接口的流量同样无法回送到 macvtap 桥再转给客户机[12]。因此客户机可以访问外部网络和同一主机上的其他客户机,却无法访问本机的宿主机[13]。virt-manager 的配置界面也提示,多数配置下 macvtap 无法用于宿主机到客户机的通信[2]。常见绕行办法是为宿主机另建一条 macvtap 接口,或另建一个仅含宿主机与客户机的隔离网络[12]。
另一项限制与源 MAC 地址有关。在 libvirt 社区的讨论中,macvtap 接口不会转发源 MAC 地址与该接口自身 MAC 不同的流量,因此如果客户机内部再架桥,回复报文会在到达客户机前被丢弃;自 libvirt 1.2.10 起,libvirt 会监听客户机内发起的 MAC 地址变更并相应调整宿主机一侧的配置[14]。此外,macvtap 接口不能使用已经连接到 Linux 网桥的网络接口[15]。
参见
参考资料
- github.com 上的网页 [引用日期2026-10-14]
- redhat.com 上的文件 [引用日期2026-10-14]
- Linux-Kernel Archive: [PATCH 3/3] net: macvtap driver [引用日期2026-10-14]
- macvtap [引用日期2026-10-14]
- net: macvtap driver - linux-yocto-4.4 [引用日期2026-10-14]
- 6acf54f1cf0a6747bac9fea26f34cfc5a9029523 [引用日期2026-10-14]
- macvtap [引用日期2026-10-14]
- 17af2bce88d31e65ed73d638bb752d2e13c66ced [引用日期2026-10-14]
- Linux on IBM Systems - Focus sentinel [引用日期2026-10-14]
- README [引用日期2026-10-14]
- Operativni sustavi i računalne mreže - Linux u primjeni(PDF) [引用日期2026-10-14]
- Guest_can_reach_outside_network_but_cant_reach_host_macvtap [引用日期2026-10-14]
- redhat.com 上的网页 [引用日期2026-10-14]
- 'Re: [libvirt-users] Limitations of macvtap devices?' [引用日期2026-10-14]
- book-virt_color_en(PDF) [引用日期2026-10-14]