20 分钟后,KVM 来宾上的 IPv6 连接丢失

Uit*_*tto 6 networking debian ipv6 kvm-virtualization

我在 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,但没有成功。

连接丢失后,我从主机上的物理网卡获取了 TCP 转储,它显示有回显请求离开和回显回复到达接口,但是从来宾网卡获取的 tcpdump 我只能看到离开的请求网卡。

主机上的 /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
Run Code Online (Sandbox Code Playgroud)

来宾上的 /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
Run Code Online (Sandbox Code Playgroud)

# 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
Run Code Online (Sandbox Code Playgroud)

# 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
Run Code Online (Sandbox Code Playgroud)

# ip -6 neigh 在主机上:

2a01:xxxx:xxxx:xxxx::20 dev enp2s0  FAILED
fe80::1 dev enp2s0 lladdr xx:xx:xx:8d:22:06 router STALE
Run Code Online (Sandbox Code Playgroud)

# ip -6 neigh 来宾:

fe80::1 dev enp2s0 lladdr xx:xx:xx:8d:22:06 router REACHABLE
Run Code Online (Sandbox Code Playgroud)

可能与主机上的 /etc/sysctl.conf 相关的内容:

net.ipv4.ip_forward=1
net.ipv4.conf.enp2s0.send_redirects=0
net.ipv6.conf.all.forwarding=1
Run Code Online (Sandbox Code Playgroud)

可能来自 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
Run Code Online (Sandbox Code Playgroud)

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>
Run Code Online (Sandbox Code Playgroud)

由于我已经为此苦苦挣扎了大约两周并阅读了几乎所有具有类似问题的相关帖子,因此我看到 Hetzner 显然做了某种可疑的 IPv6 实现。我已经联系过他们,但他们想知道我自己会遇到路由问题。这可能是真的,因为 20 分钟后我仍然收到物理网卡上的回声回复,即使它们没有转发给来宾。

那么,来自IPv6'ers的任何想法吗?

更新:

所以 Hetzner 向我证实,2a01:xxxx:xxxx:xxxx::/64 网络被路由到物理接口的链路本地地址。重新启动网络时,NDP 条目会保留 20 分钟,但之后会被删除,因为 VM 没有以正确的链路本地地址响应,因为它具有不同的 MAC 地址。

似乎我不能在这里使用 macvtap-interfaces,但我必须为此创建一个桥梁。但是,我想知道为什么当 IPv4 仍然有效时,主机无法使用 IPv6 看到来宾(反之亦然)。我认为这将允许我直接从主链接本地地址路由流量。

T. *_*lut 8

我在 Hetzner 服务器上遇到了同样的问题,但使用的是 VirtualBox 而不是 KVM。

问题:

Hetzner 将在您的 /64 子网中具有任何目标 IP 的所有 IPv6 数据包路由到您的物理主机的 MAC 地址。这意味着,如果您从 Internet 中的某个位置向具有与主机相同前缀的 IPv6 地址的 VM 发送 ping,Hetzer 的网关不会执行邻居请求来查找 VM 的 MAC 地址,而是简单地转发ICMP 包到您主机的 MAC。这就是为什么您可以在物理主机上而不是在 VM 上看到回显回复的原因:它针对的是您主机的 MAC,而不是 VM 的 MAC。

但是,Hetzner 的 IPv6 实现中似乎存在一个错误(或者可能是故意完成的,我不知道):如果 VM 发送邻居请求以查找网关的 MAC 地址(fe80::1)它使用它的全球 IPv6 IP 作为请求的源地址,不知何故 Hetzner 的网关似乎更新了它的内部 IPv6 到 MAC 地址表。在接下来的 20 分钟内,Hetzner 的网关会将任何以虚拟机 IPv6 地址为目标的数据包发送到虚拟机 MAC 地址。如果在 20 分钟内没有来自 VM 的 MAC 和 VM 的全局 IP 的进一步请求发送到网关,则它会回退以将 IPv6 数据包发送到主机的 MAC。

现在您的 VM - 就在网络启动之后,可能是因为此时未分配链接本地地址 - ONCE 使用其全局 IPv6 地址作为源发送请求,因此“意外”更新了 Hetzner 的 MAC 地址表一次。在运行时,VM 仍然不断发送请求以查找网关的 MAC 地址以使其 MAC 地址表保持最新,但它使用它的链路本地 IPv6 地址来这样做(从 IPv6 的角度来看这完全没问题)但是不会更新 Hetzner 网关的 MAC 地址表。这就是为什么在 VM 启动后 IPv6 似乎可以完全工作但只有 20 分钟的原因。

解决方案:

有一个肮脏的解决方案和一个干净的解决方案:

  • 肮脏的解决方案:您的 VM 必须不时地(假设每 5 分钟)使用网关的全球 IPv6 地址发送对网关 MAC 地址的请求。这很棘手:您的 VM 将发送请求,但使用的是本地链路 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
    
    Run Code Online (Sandbox Code Playgroud)
  • 清洁解决方案:不要使用桥接。我现在使用仅主机网络。这意味着 VM 连接到单独的 NIC (vboxnet0)。我添加了一个 IPv6 路由,将所有流量从主机转发到 VM 的 IPv6 地址:

    ip -6 route add <my IPv6 pefix>::20 dev vboxnet0
    
    Run Code Online (Sandbox Code Playgroud)

在 VM 上,我使用主机的本地链路 IPv6 地址作为默认 GW。为了允许主机在其全局 IPv6 IP 上连接 VM,我将同一 /64 子网中的另一个 IP 分配给 vboxnet0。对我来说,这非常有效。