Tun/Tap 虚拟网卡
Tun/Tap 驱动程序实现了虚拟网卡的功能, Tun 表示虚拟的是点对点设备, Tap 表示虚拟的是以太网设备, 这两种设备针对网络包实施不同的封装. 利用 Tun/Tap 驱动, 可以将 TCP/IP 协议栈处理好的网络分包传给任何一个使用 Tun/Tap 驱动的进程, 由进程重新处理后再发送到物理链路中.
开源项目 smoltcp、rustdhipv2 都用到了 tun/tap 驱动实现的网络协议栈封装.
Tun/Tap 驱动程序工作原理
作为虚拟网卡驱动, Tun/Tap 驱动程序的数据接收和发送并不直接和真实网卡打交道, 而是 在 Linux 内核中添加了一个 Tun/Tap 虚拟网络设备的驱动程序和一个与之相关连的字符设备 /dev/net/tun, 字符设备 /dev/net/tun 作为用户空间和内核空间交换数据的接口.
当内核将数据包发送到虚拟网络设备时, 数据包被保存在设备相关的一个队 列中, 直到用户空间程序通过打开的字符设备 /dev/net/tun 的描述符读取时, 它才会被拷贝到用户空间的缓冲区中, 其效果就相当于数据包直接发送到了用户空间. 通过系统调用 write 发送数据包时其原理与此类似.
Tun/Tap 驱动程序中包含两个部分: 一部分是字符设备驱动, 还有一部分是网卡驱动部分. 利用网卡驱动部分接收来自 TCP/IP 协议栈的网络分包并发送或者反过来将接收到的网络分包传给协议栈处理; 而字符驱动部分则将网络分包在用户空间和内核空间之间传送, 模拟物理链路的数据接收和发送.
创建和配置虚拟网桥
Tun/Tap 通常用来创建虚拟网卡 Tap 和搭建虚拟网桥, 如图 1 所示.
图 1. 虚拟网桥
创建如上所示的网络拓扑所需步骤如下.
安装两个配置网络所需的软件包
1
2sudo apt install bridge-utils
sudo apt install uml-utilities创建虚拟网桥
br0
1
sudo brctl addbr br0
将主机真实物理网卡的 IP 地址配置为
0.0.0.0
1
sudo ifconfig eth0 0.0.0.0
将主机真实物理网卡添加到虚拟网桥
br0
中1
sudo brctl addif br0 eth0
为虚拟网桥
br0
配置 IP 地址1
2
3
4
5
6方式一, 使用 dhclient 自动配置
sudo dhclient br0
方式二, 使用 ifconfig 手动配置, 需同时配置路由表
sudo ifconfig br0 <ip>
sudo iptables -t nat -A POSTROUTING -s <ip_mask> -j MASQUERADE
sudo sysctl net.ipv4.ip_forward=1创建 tun/tap 虚拟网卡
1
sudo tunctl -t tap0 -u $(whoami)
启动 tun/tap 虚拟网卡, 并将其加入到虚拟网桥
br0
中1
2sudo ifconfig tap0 up
sudo brctl addif br0 tap0可以根据需要对任意数量的 Tap 接口重复最后 2 个步骤, 然后可以为这些 Tap 接口提供 IP 地址, 或者可以开始将 Tap 接口绑定到各种 VM 和仿真器中以相互使用.
删除虚拟网卡/网桥
删除虚拟网卡
1
sudo tunctl -d <tap_name>
将虚拟网卡
tap0
从虚拟网桥br0
中移除1
sudo brctl delif br0 tap0
删除虚拟网桥
1
2sudo ifconfig <br_name> down
sudo brctl delbr <br_name>
样例介绍
Qemu 虚拟机与外部网络通信
- Qemu 通信机制
为了使虚拟机能够与外界通信, Qemu 需要为虚拟机提供网络设备. Qemu 支持的常用网卡包括 NE2000、rtl8139、pcnet32 等. 命令行上使用 -net nic
选项为虚拟机创建虚拟机网卡. 例如, 命令行选项
-net nic,model=pcnet
表示为虚拟机添加一块 pcnet
类型的以太网卡. 如果省略 model
参数, qemu 会默认选择一种网卡类型, 可以在虚拟机启动后执行 lspci
命令查看. 有了虚拟网络设备, 下面的问题是如何用这些设备来联网.
首先, 虚拟机的网络设备连接在qemu 虚拟的 VLAN 中. 每个 qemu 的运行实例是宿主机中的一个进程, 而每个这样的进程中可以虚拟一些 VLAN, 虚拟机网络设备接入这些VLAN中. 当某个 VLAN 上连接的网络设备发送数据帧, 与它在同一个 VLAN 中的其它网络设备都能接收到数据帧. 上面的例子中对虚拟机的 pcnet 网卡没有指定其连接的 VLAN 号, 那么 qemu 默认会将该网卡连入vlan0. 下面这个例子更具一般性:
-net nic,model=pcnet -net nic,model=rtl8139,vlan=1, -net nic,model=ne2k_pci,vlan=1
该命令为虚拟机创建了三块网卡: 其中第一块网卡类型是 pcnet, 连入vlan0; 第二块网卡类型是 rtl8139, 第三块网卡类型是 ne2k_pci, 这两块都连入vlan1. 所以第二块网卡与第三块网卡可以互相通信, 但它们与第一块网卡不能直接通信.
接下来, 各个 VLAN 再通过 qemu 提供的 4 种通信方式与外界联网, 包括: User mode stack、socket、TAP、VDE. 这里重点介绍 TAP 通信方式.
TAP
这种方式首先需要在宿主机中创建并配置一个 TAP 设备, qemu 进程将该 TAP 设备连接到虚拟机 VLAN 中. 其次, 为了实现虚拟机与外部网络的通信, 在宿主机中通常还要创建并配置一个网桥,并将宿主机的网络接口(通常是 eth0)作为该网桥的一个接口. 最后, 只要将 TAP 设备作为网桥的另一个接口, 虚拟机 VLAN 通过 TAP 设备就可以与外部网络完全通信了.
这是因为, 宿主机的 eth0 接口作为网桥的接口, 与外部网络连接; TAP 设备作为网桥的另一个接口, 与虚拟机 VLAN 连接, 这样两个网络就连通了. 此时, 网桥在这两个网络之间转发数据帧.
这里有两个问题需要注意:
(1)网桥的转发工作需要得到内核的支持, 所以在编译宿主机内核时需要选择与桥接相关的配置选项;
(2)当宿主机 eth0 接口作为网桥接口时, 不能为其配置 IP 地址, 而要位将IP地址配置给网桥.
- Qemu 运行实例
假设已经按照图 1 配置好 tap0、br0 以及 eth0 桥接模型, 这里以 ubuntu16-x86_64.iso
为例介绍如何启动 qemu:
1 | SYSTEMISO="./ubuntu-16.04.7-desktop-amd64.iso" |
如果省略 script
和 downscript
参数, qemu 在启动时会以第一个不存在的 tap 接口名(通常是 tap0)为参数去调用 /etc/qemu-ifup 脚本, 而在退出时调用 /etc/qemu-ifdown 脚本. 这两个脚本需要用户自行编写, 其主要作用通常是: 在启动时创建和打开指定的 TAP 接口, 并将该接口添加到虚拟网桥中; 退出时将该接口从虚拟网桥中移除, 然后关闭该接口. 由于配置 TAP 设备的操作前面已经做过了, 所以启动 qemu 时显式地告诉 qemu 不要执行这两个脚本.
注意:如果是桥接模型, 则需要为虚拟网桥 br0
配置路由表项; 如果是 TAP 模型, 则需要为 TAP 虚拟网卡配置路由表项 !!!
参考链接
Howto make a virtual bridged network
Introduction to Linux interfaces for virtual networking
Setting up TUN/TAP networking for QEMU VM’s (and bonus wireguard)
linux 多块网卡 bridge,理解linux虚拟网络设备bridge