虚拟机网络配置
虚拟网络结构、功能需按照需求配置,典型例子:
- NAT 模式:为 guests 局域网分配局域网 IP
- Guest -> Internet:使用 NAT 技术依托 host 的网络连接互联网
- Guest <- Internet:需额外在 host 上配置端口转发
- Guest <-> Guest:可访问
- Guest --> Host:可访问
- Guest <-- Host:可访问
- 隔离模式:为 guests 单独搭建一套网络
- Guest -> Internet:无
- Guest <-> Guest:可访问
- Guest 与 Host:无法相互访问
- 共享网卡:相当于使用交换机连接 guest 和 host 的一个网口
- Guest -> Internet:需要 guest 自行通过宽带拨号/DHCP等手段获取 IP
- 网卡直通
- 路由模式
参考资料:
- libvirt: Virtual Networking
- libvirt: Networking
- libvirt: Network XML format
- IPv6 address - Wikipedia
- Unique local address - Wikipedia
- RFC3484 Default Address Selection for IPv6
此外,本文使用大量从 libvirt: Virtual Networking 下载的图片。
Virtual Network Switches
libvirt 使用名为 virtual network switch (VNS)的虚拟器件提供虚拟网络。如果读者熟悉 OSI 模型,多半下意识认为这个 “switch” 对应链路层,功能应该与一个交换机类似。但此 switch 非彼 switch,取决于具体网络配置,virtual network switch 可能工作在链路层,也可能工作在网络层。
Libvirt 将 Virtual Network Switches 表示成这个样子:
虽然名字中带着个 "switch",但他的设备名却显示为 “virbr0”,其中的“br” 暗示着是一个网桥。更让人迷惑的是,VNS 可以实现 NAT,一种通常是路由器才会提供的功能,所以 VNS 多多少少也能理解成一个路由器。
严格来说,switch、bridge、router 应该是不同的东西,VNS 提供又能实现这三种设备的功能,所以最好忽略这些文绉绉的名词定义,不要过多纠结。
NAT 模式
基本使用
NAT 模式是 libvirt 的默认网络模式,VNS:
- 为虚拟机分配局域网 IPv4 地址
- 对 guest 发往 Internet 的数据包实施 SNAT,为 guest 提供网络访问
- 默认阻拦 Internet 到 guest 的数据包,除非它属于从一个从 guest 发起的连接
- 允许 guest 间、guest <--> host 的相互访问
网络结构图:
典型配置文件例:
<network>
<name>net_name</name>
<uuid>UUID</uuid>
<forward mode='nat' />
<bridge name='virbr0' stp='on' delay='0'/>
<mac address='52:54:00:4e:3c:17'/>
<ip address='192.168.122.1' netmask='255.255.255.0'>
<dhcp>
<range start='192.168.122.2' end='192.168.122.254'/>
</dhcp>
</ip>
</network>
使 Guest 可从 Internet 访问
NAT 模式下 guest 只有局域网 IP 地址,若希望从互联网直接访问 guest,需先将数据发送到具有公网 IP 地址的 host,host 再通过端口转发将数据转发到 guest。本质上是做了一个 DNAT。
配置流程:
-
打开 Linux 的数据包转发,
sudo vim /etc/sysctl.conf
,添加一行:然后执行
sysctl -p /etc/sysctl.conf
应用更改 -
使用
iptables
配置 DNAT
但 iptables 的配置复杂且易错,还是使用程序自动配置为好。libvirt: forwarding incoming connections 中提供了 3 种自动脚本,个人推荐这个 Python 项目:
该脚本将在虚拟机启动时自动启用 DNAP,在虚拟机关机时取消。比较好用
启用 IPv6
libvirt 默认只为 guests 分配 IPv4 地址,如要为 guests 分配 IPv6 地址,需修改网络的 xml 配置为:
<network>
<name>net_name</name>
<uuid>UUID</uuid>
<forward mode='nat' />
<bridge name='virbr0' stp='on' delay='0'/>
<mac address='52:54:00:4e:3c:17'/>
<ip address='192.168.122.1' netmask='255.255.255.0'>
<dhcp>
<range start='192.168.122.2' end='192.168.122.254'/>
</dhcp>
</ip>
<ip family='ipv6' address='fdXX:XXXX:XXXX:NNNN::1' prefix='64'>
<dhcp>
<range start='fdXX:XXXX:XXXX:NNNN::10' end='fdXX:XXXX:XXXX:NNNN::ff'/>
</dhcp>
</ip>
</network>
核心是添加了
<ip family='ipv6' address='fdXX:XXXX:XXXX:NNNN::1' prefix='64'>
<dhcp>
<range start='fdXX:XXXX:XXXX:NNNN::10' end='fdXX:XXXX:XXXX:NNNN::ff'/>
</dhcp>
</ip>
- 需要将 IP 地址中的
X
、N
更改为随机的 16 进制字母 fdXX:XXXX:XXXX::
属于fc00::/7
地址段,是 unique local address (ULA),可类比为 IPv4 中的局域网地址段X
构成 IPv6 中的 prefix 部分,N
构成 subnet ID 部分,一般没啥影响,随机一个地址即可
配置文件修改完成后,使用如下指令重启这个虚拟网络:
现在进入 guest,输入 ip -c addr
,应该能看到获取到的 IPv6 地址。现在,你可以使用 IPv6 协议进行:
- 该网络下 guests 间通讯
- host 与该网络的 guests 间通讯
但 libvirt 默认关闭 IPv6 NAT,所以现在 guest 仍然无法使用 IPv6 协议访问 Internet。
启用 IPv6 NAT
虽然 IPv6 并不推荐使用 NAT,但也有些需要使用 IPv6 的场景,例如:
- Host 只能通过 IPv6 上网
- 你是中国大陆用户:
- IPv6 网络环境比 IPv4 网络环境更加纯净,网速更快、没有那么拥挤、网络审查较少……
- 网络运营商为你的 Host 提供的 IPv4 是局域网 IPv4,但为你提供公网 IPv6 地址,并且你希望能直接从互联网访问 guest
- 你正在使用中国教育网(CERNET):为了鼓励 IPv6 发展,大部分高校并不收取 IPv6 流量费用,你希望顺应国家政策使用 IPv6 狠狠地薅国家羊毛
可更具下属教程开启 IPv6 NAT:
-
在 Host 开启 IPv6 转发
-
将 VNS 的配置修改为:
现在你的 guest 能正常使用 IPv6 访问 Internet 了,但有个小瑕疵:
- 如果 Linux 系统既有 IPv4 地址又有公网 IPv6 地址,系统优先使用 IPv6 进行通讯
- 但因为分配给 guests 的是
fc00::/8
的 UCL 地址,系统会优先使用 IPv4 进行通讯
操作系统为什么这么做?
-
RFC3484 Default Address Selection for IPv6 定义了系统具有多个 IP 地址时如何选择的规则,默认偏好使用 IPv6 地址
-
几乎所有操作系统都遵循 RFC3484 的规则
-
因为 ULA 的混乱历史,ULA 地址很长一段时间内不可能被路由到。按照 RFC3484 的规则,系统将尝试使用 ULA 地址发起请求,但因为 ULA 地址不被路由,这样的请求多半会因超时失败,然后系统再尝试使用更低优先级的 IPv4 地址发起连接
-
这样“超时-重试”的过程引入大量网络延迟,所以大部分操作系统将 ULA 地址优先级降低到 IPv4 地址以下
为解决该问题,需要编辑 /etc/gai.conf
文件:
-
sudo vim /etc/gai.conf
-
找到
-
取消注释,并添加一行,使上述内容成为
-
保存,退出,验证系统偏好:
在命令行运行
系统会按照该指令列出的顺序建立连接。
如果是 IPv6 地址在前,说明系统偏好 IPv6,例如:
2a01:4f8:c0c:bd0a::1 STREAM ident.me 2a01:4f8:c0c:bd0a::1 DGRAM 2a01:4f8:c0c:bd0a::1 RAW 49.12.234.183 STREAM 49.12.234.183 DGRAM 49.12.234.183 RAW
反之如果 IPv4 地址在前,说明系统偏好 IPv4,例如:
上述修改背后的原理:
-
RFC3484 Section 6 规定排序规则,与本例相关的是:
- Rule 5: Prefer matching label
- Rule 6: Prefer higher precedence
- 且 Rule 5 优先级比 Rule 6 高
-
考虑默认配置:
label ::1/128 0 label ::/0 1 label 2002::/16 2 label ::/96 3 label ::ffff:0:0/96 4 label fec0::/10 5 label fc00::/7 6 label 2001:0::/32 7 precedence ::1/128 50 precedence ::/0 40 precedence 2002::/16 30 precedence ::/96 20 precedence ::ffff:0:0/96 10
ident.me
的地址- IPv6 为
2a01:4f8:c0c:bd0a::1
匹配到 label 1 - IPv4 为
49.12.234.183
匹配到 label 4
本机的地址:
- IPv6 为
fd12:8848:a2a2:1::74
匹配到 label 6 (注意是最长前缀匹配,不是按照列表先后顺序匹配) - IPv4 为
192.168.122.154
匹配到 label 4
按照 Rule 5,
ident.me
和本机192.168.122.154
的 label 相同,被优选选择,所以使用 IPv4 地址建立连接。 - IPv6 为
-
添加
label fd00::/8 1
一行后,fd12:8848:a2a2:1::74
被匹配到 label 1。- IPv4 和 IPv6 的本机 label 和目标 label 相同,Rule 5 不能排出顺序
- 考虑 Rule 6,IPv6 优先级为 40、IPv4 优先级为 10,所以选择 IPv6 建立连接
配置 IPv6 DNAT
与 IPv4 不同,IPv6 暂时没有现成的 DNAT 自动配置脚本。 管理员可通过如下指令配置 DNAT:
GUEST_IP=xxxxxxxx
GUEST_PORT=22
VNS_DEV=virbr0
HOST_PORT=4023
sudo ip6tables -I FORWARD -o $VNS_DEV -p tcp -d $GUEST_IP --dport $GUEST_PORT -j ACCEPT
sudo ip6tables -t nat -I PREROUTING -p tcp --dport $HOST_PORT -j DNAT --to "[$GUEST_IP]:$GUEST_PORT"
如果需要自动配置,可以考虑使用该仓库:GJCav/libvirt-hook-qemu
该仓库从 saschpe/libvirt-hook-qemu fork 出,添加了对 IPv6 的支持,在作者的电脑上工作良好,已经向原作者提出 PR,正在等待审核
其他内容
等待施工....