kata-container 的tap设备在哪里创建的?虚拟机内还是虚拟机外

现在我需要确认 setupTCFiltering 和 tapNetworkPair 被调用时是否在 netns 上下文内。让我回顾一下调用链。

从 VethEndpoint.Attach(veth_endpoint.go 第 109 行):

func (endpoint *VethEndpoint) Attach(ctx context.Context, s *Sandbox) error {
	...
	h := s.hypervisor
	if err := xConnectVMNetwork(ctx, endpoint, h); err != nil {  // ← 这里调用
		...
	}
	return h.AddDevice(ctx, endpoint, NetDev)
}

Attach 是在 addSingleEndpoint 中被调用的。让我们看看 addSingleEndpoint 和 scanEndpointsInNs 中的调用上下文。前面看到 scanEndpointsInNs(第 455 行):

if err := doNetNS(nsPath, func(_ ns.NetNS) error {
	ep, addErr := n.addSingleEndpoint(ctx, s, netInfo, hotplug)
	...
});

所以 addSingleEndpoint(包括里面的 endpoint.Attach → xConnectVMNetwork → setupTCFiltering/tapNetworkPair)都是在 doNetNS(nsPath) 中执行的,即在容器的网络命名空间内。

同样,AddEndpoints 在传入 endpointsInfo 时也通过 doNetNS(n.netNSPath, ...) 包裹(第 643 行)。

现在我有完整的答案了。

答案:TAP 设备在虚拟机外部(宿主机的容器网络命名空间内)创建

明确结论

kata-containers 的 TAP 设备是在虚拟机外部创建的,具体位置是容器的网络命名空间(netns)内,创建者是运行在宿主机上的 shim-v2 运行时进程,而不是虚拟机内部的 agent。

详细分析

TAP 设备的创建逻辑位于 setupTCFiltering(TC Filter 默认模型)或 tapNetworkPair(MacVtap 模型)中,两者都调用 createLink:

// setupTCFiltering (network_linux.go 第 1096 行)
tapLink, fds, err := createLink(netHandle, netPair.TAPIface.Name, &netlink.Tuntap{}, queues)

而 createLink(第 729 行)最终执行的是 netHandle.LinkAdd(newLink)(第 775 行),通过 netlink 系统调用在当前所在的内核网络命名空间里创建 tapN_kata TAP 接口。

关键:调用发生在哪个 netns?

关键在于调用链的执行上下文:

scanEndpointsInNs (network_linux.go 第 455 行)
  └─ doNetNS(nsPath, ...)          ← 进入容器的 netns
       └─ addSingleEndpoint
            └─ endpoint.Attach
                 └─ xConnectVMNetwork
                      └─ setupTCFiltering
                           └─ createLink  →  netlink.LinkAdd  ← TAP 在此创建

doNetNS(第 1359 行)会通过 targetNS.Set() 切换到目标网络命名空间(即容器的 netns,n.netNSPath),然后才执行回调。因此 createLink 里的 netlink.LinkAdd 是在容器的网络命名空间中执行的。

所以 TAP 设备创建在:

  • 宿主机上(不是 VM 内)
  • 容器的网络命名空间内(netns)

为什么在 VM 外创建 TAP?

kata-containers 的核心设计是:虚拟机(guest)不直接操作宿主机的 TAP 设备。TAP 的 fd 是在宿主机侧创建好之后,通过传文件描述符(fd passing)传给 hypervisor(QEMU/CLH),作为 VM 的虚拟网卡(virtio-net 后端)。具体:

  1. 宿主机侧 createLink 创建 TAP,并把 tapLink.Fds(fd)存入 netPair.VMFds
  2. 这些 fd 通过 h.AddDevice(ctx, endpoint, NetDev) 传给 hypervisor
  3. hypervisor(QEMU)用这个 fd 作为 virtio-net 设备的后端,实现 VM 内网卡与宿主 TAP 的数据交换
  4. TC 过滤器把容器 netns 内 CNI 创建的 veth(eth0)流量重定向到 TAP,实现双向连通

虚拟机内部发生了什么?

VM 内部不需要也不存在 TAP 设备。VM 内的网络配置(kataAgent.setupNetworks)只是:

  • 通过 agent 的 updateInterfaces 配置 VM 内 virtio 网卡的 MAC/IP/MTU
  • 通过 updateRoutes 配置路由
  • 通过 addARPNeighbors 配置 ARP 邻居

这些配置信息来自宿主机侧扫描时保存的 NetworkInfo(IP、路由、邻居),让 VM 内的 virtio 网卡(对应宿主的 TAP/veth)拥有正确的网络配置,实现容器与 VM 内进程的网络互通。

对比两种模型

模型 TAP 设备 创建位置 VM 内部
TC Filter(默认) tapN_kata(netlink.Tuntap) 宿主机容器 netns 内 virtio-net 网卡(对应 TAP fd)
MacVtap tapN_kata(netlink.Macvtap) 宿主机容器 netns 内(挂在父接口上) virtio-net 网卡(对应 macvtap fd)

总结:TAP 设备一律创建在宿主机的容器网络命名空间内(虚拟机外),由 shim-v2 运行时通过 netlink 创建,其 fd 传给 hypervisor 作为 VM 网卡的后端。VM 内部只有对应的 virtio-net 虚拟网卡,没有 TAP 设备。

posted @ 2026-10-19 15:49  rincloud  阅读(10)  评论(0)    收藏  举报