IPv6 routing between Host and Guest

alestark asked:

I am currently facing an issue about my current setup.
I basically have a dedicated server to which a /64 IPv6 subnet have been assigned (2a01:4f8:221:1e81::/64). Connectivity on the host is working fine, as I can ping from there ipv6.google.com.

Also, this is the configuration:

[email protected] ~ # ifconfig vmbr0
vmbr0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 10.250.0.1  netmask 255.255.0.0  broadcast 10.250.255.255
        inet6 2a01:4f8:221:1e81:1:ffff:ffff:ffff  prefixlen 80  scopeid 0x0<global>
        inet6 2a01:4f8:221:1e81:212:ffff:ffff:ffff  prefixlen 80  scopeid 0x0<global>
        inet6 2a01:4f8:221:1e81:253:ffff:ffff:ffff  prefixlen 80  scopeid 0x0<global>
        inet6 2a01:4f8:221:1e81:251:ffff:ffff:ffff  prefixlen 80  scopeid 0x0<global>
        inet6 fe80::70a5:a1ff:fed6:8c0  prefixlen 64  scopeid 0x20<link>
        ether 4a:5a:df:ab:a8:bc  txqueuelen 1000  (Ethernet)
        RX packets 949686094  bytes 162428268530 (151.2 GiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 285868499  bytes 143826674082 (133.9 GiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

[email protected] ~ #

Routing table:

[email protected] ~ # ip -6 route show
2a01:4f8:221:1e81:1::/80 dev vmbr0 proto kernel metric 256 pref medium
2a01:4f8:221:1e81:212::/80 dev vmbr0 proto kernel metric 256 pref medium
2a01:4f8:221:1e81:251::/80 dev vmbr0 proto kernel metric 256 pref medium
2a01:4f8:221:1e81:253::/80 dev vmbr0 proto kernel metric 256 pref medium
2a01:4f8:221:1e81::/64 dev eth0 proto kernel metric 256 pref medium
fe80::/64 dev eth0 proto kernel metric 256 pref medium
fe80::/64 dev vmbr0 proto kernel metric 256 pref medium
default via fe80::1 dev eth0 metric 1024 pref medium
[email protected] ~ #

Problem comes once I ssh into a virtual machine and configure the network.
In this example, I have the following configuration:

[email protected]:~$ cat /etc/network/interfaces | tail -n4
iface eth0 inet6 static
    address 2a01:4f8:101:3172:251::3
    netmask 80
    gateway 2a01:4f8:101.3172:251:ffff:ffff:ffff
[email protected]:~$

So, ifconfig looks like:

[email protected]:~$ sudo ifconfig
[sudo] password for hypnotize:
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 10.250.3.6  netmask 255.255.0.0  broadcast 10.250.255.255
        inet6 fe80::9474:f1ff:fe6f:4bc3  prefixlen 64  scopeid 0x20<link>
        inet6 2a01:4f8:101:3172:251::3  prefixlen 80  scopeid 0x0<global>
        ether 96:74:f1:6f:4b:c3  txqueuelen 1000  (Ethernet)
        RX packets 177  bytes 16270 (15.8 KiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 161  bytes 18477 (18.0 KiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
        inet6 ::1  prefixlen 128  scopeid 0x10<host>
        loop  txqueuelen 0  (Local Loopback)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 0  bytes 0 (0.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

[email protected]:~$

While routing table appears:

[email protected]:~$ ip -6 route show
2a01:4f8:101:3172:251::/80 dev eth0 proto kernel metric 256
fe80::/64 dev eth0 proto kernel metric 256
[email protected]:~$

Unfortunately, when I try to ping6 it says “connect: Network is unreachable”.
I have net.ipv6.conf.all.forwarding enabled.

Do you see any error in my configuration?
Thanks in advance


I have corrected the typo, and now it says:

[email protected]:/home/hypnotize# ping6 ipv6.google.com
PING ipv6.google.com(fra16s20-in-x0e.1e100.net (2a00:1450:4001:821::200e)) 56 data bytes
From 2a01:4f8:101:3172:251::3 (2a01:4f8:101:3172:251::3) icmp_seq=1 Destination unreachable: Address unreachable
From 2a01:4f8:101:3172:251::3 (2a01:4f8:101:3172:251::3) icmp_seq=2 Destination unreachable: Address unreachable
From 2a01:4f8:101:3172:251::3 (2a01:4f8:101:3172:251::3) icmp_seq=3 Destination unreachable: Address unreachable
^C
--- ipv6.google.com ping statistics ---
5 packets transmitted, 0 received, +3 errors, 100% packet loss, time 4032ms

[email protected]:/home/hypnotize#

Also, the routing table, now looks better, at least it has a default entry:

[email protected]:/home/hypnotize# ip -6 route show
2a01:4f8:101:3172:251::/80 dev eth0 proto kernel metric 256
fe80::/64 dev eth0 proto kernel metric 256
default via 2a01:4f8:101:3172:251:ffff:ffff:ffff dev eth0 metric 1024
[email protected]:/home/hypnotize#

My answer:


Like many enterprises, Hetzner runs switch port security on its network switches, meaning that only authorized MAC addresses can talk to the network. This means that the hardware address of the NIC in your server can speak, but the virtual MAC addresses of your virtual machines cannot. Thus it does not work to bridge your VMs directly to the outside world. (If you have ordered extra IPv4 addresses, then Hetzner will allow you to generate virtual MAC addresses for each of them that you can assign to your VMs, which will let this setup work, but if you haven’t got extra IPv4 addresses or don’t want to buy them, then you cannot do.)

I have a KVM virtual machine host on Hetzner, where the VMs are mostly reachable only by IPv6, and this is how I set it up.

Instead of bridging directly to the host NIC, I create virtual networks for each /80, and assign VMs to those virtual networks. The host will then route between the host NIC and the virtual networks, which are not bridged (layer 2) but routed (layer 3).

So the host is configured as:

IPV6ADDR="2a01:4f8:150:43ea::2/64"
IPV6_DEFAULTGW="fe80::1"

The libvirt virtual networks are configured as /80 networks. Virt-manager cannot handle creating /80 networks, so you will need to write the XML yourself. Here is an example:

<network>
  <name>v6net1</name>
  <forward mode='route'/>
  <bridge name='v6net1' stp='on' delay='0'/>
  <domain name='v6net1'/>
  <ip address='192.168.101.1' netmask='255.255.255.0'>
    <dhcp>
      <range start='192.168.101.128' end='192.168.101.254'/>
    </dhcp>
  </ip>
  <ip family='ipv6' address='2a01:4f8:150:43ea:1::1' prefix='80'>
    <dhcp>
      <range start='2a01:4f8:150:43ea:1::1000' end='2a01:4f8:150:43ea:1::ffff'/>
    </dhcp>
  </ip>
</network>

Now, all virtual machines’ NICs are added to the v6net1 network, and get an address in this /80 from DHCPv6. You could assign them manually if you wanted though. This creates a bridge v6net1, but the host NIC is not part of the bridge. Thus traffic is routed between the VMs and the outside world, and when leaving the host the traffic has the host’s MAC address.

This doesn’t cover global IPv4 connectivity, e.g. masquerading or port forwarding. I handle this separately in firewalld, outside of libvirt, and do not use libvirt’s NAT functionality. If you just want a simple NAT so VMs can connect out to IPv4 hosts, and don’t need incoming traffic, use <forward mode='nat'> instead. You could also omit IPv4 setup entirely, for pure IPv6-only VMs.


View the full question and any other answers on Server Fault.

Creative Commons License
This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License.