我在 Hetzner 设置了一台 KVM 虚拟化服务器。Hetzner 为我提供了一个主 IP (95.xxx.xxx.235) 和一个 /29 IPv4 子网 (95.xxx.xxx.184/29) 以及一个 /64 IPv6 网络 (2a01:xxxx:xxxx:xxxx::/64)。
KVM 客户机 (Debian Stretch) 在网络服务重启或重新启动 20 分钟后恰好失去 IPv6 连接。即使连接丢失,我仍可以 ping 默认网关 (fe80::1)。IPv4 连接始终保持正常,没有问题。
目前,接口设置为桥接模式的 macvlan,我也尝试过 VEPA 和私有模式,但没有成功。此外,NIC 类型设置为 e1000,但我也尝试过 virtio,但没有成功。
连接丢失后,我从主机上的物理 NIC 进行了 TCP 转储,结果显示有回显请求离开接口,也有回显答复到达接口,但是从客户 NIC 进行 tcpdump 时,我只能看到离开 NIC 的请求。
主机上的 /etc/network/interfaces:
auto lo
iface lo inet loopback
iface lo inet6 loopback
auto enp2s0
iface enp2s0 inet static
address 95.xxx.xxx.235
netmask 255.255.255.192
gateway 95.xxx.xxx.193
up route add -net 95.xxx.xxx.192 netmask 255.255.255.192 gw 95.xxx.xxx.193 dev enp2s0
iface enp2s0 inet6 static
address 2a01:xxx:xxx:xxx::2
netmask 64
gateway fe80::1
客户机上的 /etc/network/interfaces:
auto lo
iface lo inet loopback
iface lo inet6 loopback
auto ens3
iface ens3 inet static
address 95.xxx.xxx.187
netmask 255.255.255.248
gateway 95.xxx.xxx.185
iface ens3 inet6 static
address 2a01:xxx:xxx:xxx::20
netmask 64
gateway fe80::1
主机上的 # route -6 -n:
Kernel IPv6 routing table
Destination Next Hop Flag Met Ref Use If
2a01:xxxx:xxxx:xxxx::/64 :: U 256 8 1162 enp2s0
fe80::/64 :: U 256 0 0 macvtap0
fe80::/64 :: U 256 0 0 enp2s0
::/0 fe80::1 UG 1024 8 4534 enp2s0
::/0 :: !n -1 1 11069 lo
::1/128 :: Un 0 9 81 lo
2a01:xxxx:xxxx:xxxx::/128 :: Un 0 1 0 lo
2a01:xxxx:xxxx:xxxx::2/128 :: Un 0 9 82 lo
fe80::/128 :: Un 0 1 0 lo
fe80::/128 :: Un 0 1 0 lo
fe80::/128 :: Un 0 1 0 lo
fe80::xxxx:xxxx:xxxx:1069/128 :: Un 0 1 0 lo
fe80::xxxx:xxxx:xxxx:22e1/128 :: Un 0 1 0 lo
fe80::xxxx:xxxx:xxxx:201/128 :: Un 0 2 79 lo
ff00::/8 :: U 256 0 0 macvtap0
ff00::/8 :: U 256 0 0 enp2s0
::/0 :: !n -1 1 11069 lo
来宾上的 # route -6 -n:
Kernel IPv6 routing table
Destination Next Hop Flag Met Ref Use If
2a01:xxxx:xxxx:1414::/64 :: U 256 0 0 ens3
fe80::/64 :: U 256 0 0 ens3
::/0 fe80::1 UG 1024 2 77 ens3
::/0 :: !n -1 1 6846 lo
::1/128 :: Un 0 5 525 lo
2a01:xxxx:xxxx:xxx::20/128 :: Un 0 3 70 lo
fe80::xxxx:xxxx:xxx:22e1/128 :: Un 0 2 6 lo
ff00::/8 :: U 256 0 0 ens3
::/0 :: !n -1 1 6846 lo
# ip -6 neigh 主机:
2a01:xxxx:xxxx:xxxx::20 dev enp2s0 FAILED
fe80::1 dev enp2s0 lladdr xx:xx:xx:8d:22:06 router STALE
# ip -6 neigh 访客:
fe80::1 dev enp2s0 lladdr xx:xx:xx:8d:22:06 router REACHABLE
主机上 /etc/sysctl.conf 中可能包含的相关内容:
net.ipv4.ip_forward=1
net.ipv4.conf.enp2s0.send_redirects=0
net.ipv6.conf.all.forwarding=1
Guest 上的 /etc/sysctl.conf 中可能相关的内容:
net.ipv6.conf.default.accept_ra=2
net.ipv6.conf.default.autoconf=1
net.ipv6.conf.all.accept_ra=2
net.ipv6.conf.all.autoconf=1
net.ipv6.conf.ens3.accept_ra=2
net.ipv6.conf.ens3.autoconf=1
Guest libvirt-config 的可能相关部分:
<interface type='direct' trustGuestRxFilters='yes'>
<mac address='xx:xx:xx:xx:xx:xx'/>
<source dev='enp2s0' mode='bridge'/>
<model type='e1000'/>
<address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x0'/>
</interface>
由于我已经为此苦苦挣扎了大约两周,并且阅读了几乎所有涉及类似问题的相关帖子,我发现 Hetzner 显然做了一些可疑的 IPv6 实现。我已经联系了他们,但他们怀疑我自己会遇到路由问题。这可能是真的,因为 20 分钟后,我仍然会在物理 NIC 上收到回显回复,即使它们没有转发给访客。
那么,IPv6 用户有什么想法吗?
更新:
因此 Hetzner 向我确认,2a01:xxxx:xxxx:xxxx::/64 网络被路由到物理接口的链路本地地址。重新启动网络时,NDP 条目会保留 20 分钟,但之后会被删除,因为虚拟机没有使用正确的链路本地地址进行应答,因为它具有不同的 MAC 地址。
看起来我似乎无法在这里使用 macvtap 接口,但我必须为此创建一个桥接器。但是,我想知道为什么当 IPv4 仍然有效时,主机无法使用 IPv6 看到客户机(反之亦然)。我认为这将允许我直接从主链路本地地址路由流量。
答案1
我在使用 Hetzner 服务器时遇到了同样的问题,但使用的是 VirtualBox 而不是 KVM。
问题:
Hetzner 将所有具有 /64 子网内任何目标 IP 的 IPv6 数据包路由到物理主机的 MAC 地址。这意味着,如果您从互联网上的某个地方向具有与主机相同前缀的 IPv6 地址的 VM 发送 ping,Hetzer 的网关不会进行邻居请求来查找 VM 的 MAC 地址,而只是将 ICMP 数据包转发到主机的 MAC。这就是为什么您可以在物理主机上看到回显回复,但在 VM 上看不到的原因:它针对的是主机的 MAC,而不是 VM 的 MAC。
但是,Hetzner 的 IPv6 实现似乎存在错误(或者可能是故意的,我不知道):如果虚拟机发送邻居请求以查找网关的 MAC 地址(fe80::1),并且它使用其全局 IPv6 IP 作为请求的源地址,Hetzner 的网关似乎会以某种方式更新其内部 IPv6 到 MAC 地址表。在接下来的 20 分钟内,Hetzner 的网关将把任何以虚拟机 IPv6 地址为目标的数据包发送到虚拟机的 MAC 地址。如果在 20 分钟内没有从虚拟机的 MAC 和虚拟机的全局 IP 向网关发送进一步的请求,它会回退到将 IPv6 数据包发送到主机的 MAC。
现在您的虚拟机 - 在网络启动后,可能是因为此时未分配本地链路地址 - ONCE 使用其全局 IPv6 地址作为源发送请求,因此“意外”更新了 Hetzner 的 MAC 地址表。在运行时,虚拟机仍会不断发送请求以查找网关的 MAC 地址以使其 MAC 地址表保持最新,但它使用其本地链路 IPv6 地址来执行此操作(从 IPv6 的角度来看这是完全可以的),但这不会更新 Hetzner 网关的 MAC 地址表。这就是为什么 IPv6 在虚拟机启动后似乎完全正常工作但只能持续 20 分钟的原因。
解决方案:
有一个肮脏的解决方案和一个干净的解决方案:
肮脏的解决方案:您的虚拟机必须不时使用其全局 IPv6 地址(比如说每 5 分钟一次)发送网关 MAC 地址的请求。这很棘手:您的虚拟机将发送请求,但使用其链路本地 IPv6。所以我在这里使用了一个便宜的技巧:我从接口中删除链路本地 IP,发送请求(然后强制使用全局 IP)并重新连接链路本地 IP:
ip -6 addr del fe80::a00:27ff:fecf:e270/64 dev enp0s3 ndisc6 fe80::1 enp0s3 ip -6 addr add fe80::a00:27ff:fecf:e270/64 dev enp0s3
干净的解决方案:不要使用桥接。我现在使用仅主机网络。这意味着虚拟机连接到单独的 NIC(vboxnet0)。我添加了一个 IPv6 路由,将所有流量从主机转发到虚拟机的 IPv6 地址:
ip -6 route add <my IPv6 pefix>::20 dev vboxnet0
在虚拟机上,我使用主机的本地链路 IPv6 地址作为其默认网关。为了允许主机在其全局 IPv6 IP 上连接虚拟机,我将同一 /64 子网中的另一个 IP 分配给 vboxnet0。对我来说,这非常完美。
答案2
我的 Hetzner 服务器遇到了完全相同的问题,但我找到了一个比您的“肮脏的解决方案”更好的解决方案,因为ndisc6
它有一个参数-s
可以指定另一个源 IP 而不是链接本地地址:
ndisc6 -s \<my IPv6 prefix\>::20 fe80::1 enp1s0
因此我编写了一个 cron 作业,每 5 分钟发送一次邻居请求,现在我很好了 :)
答案3
继续建设f0ssie 的回答,我编写了一个 bash 脚本,它可以执行相同的操作,但不依赖于任何硬编码的地址或接口,以便更轻松地跨多台计算机和网络进行部署。请注意,除了ndisc6,你还需要iproute2和杰奇。
#!/bin/bash
set -euo pipefail
default_route=$(ip -6 --json route | jq --exit-status '[.[] | select(.dst == "default")] | first')
default_gateway=$(echo "${default_route}" | jq --raw-output --exit-status '.gateway')
net_device=$(echo "${default_route}" | jq --raw-output --exit-status '.dev')
our_global_addr=$(ip -6 --json addr show dev "${net_device}" scope global | jq --raw-output --exit-status 'first | .addr_info | first | .local')
ndisc6 --source "${our_global_addr}" "${default_gateway}" "${net_device}"
答案4
我找到sysctl -w net.ipv6.conf.all.forwarding=1
很有帮助。不知道为什么。灵感来自https://ipv6-first-guide.hillbrecht.de/#_preparing_the_network_settings_of_the_host