librtvirt supports IPv6 for virtual machines, but that is usually a bridge solution or a subnet of the /64 the libvirt host is connected to. But I'm in the situation working on a network without IPv6, so I get my IPv6 address from a Wireguard interface. To get IPv6 working in a virtual machine on my host, I do not have a range available on my host machine without extensive changes to the wireguard VPN.
The solution is to use NAT with IPv6.
First, create a new libvirt NAT network with IPv6 enabled and the ranges you want. For the ipv6 range I chose fd00::/64 because that is a private range like 10.x.x.x.
<network connections="1">
<name>virbr1</name>
<uuid>a592c6e0-b9ed-463a-a0a3-3feef8c01b7d</uuid>
<forward mode="nat">
<nat>
<port start="1024" end="65535"/>
</nat>
</forward>
<bridge name="virbr1" stp="on" delay="0"/>
<mac address="52:54:00:74:c6:cf"/>
<domain name="virbr1"/>
<ip address="192.168.100.1" netmask="255.255.255.0">
<dhcp>
<range start="192.168.100.128" end="192.168.100.254"/>
</dhcp>
</ip>
<ip family="ipv6" address="fd00::1" prefix="64">
<dhcp>
<range start="fd00::100" end="fd00::1ff"/>
</dhcp>
</ip>
</network>
Because we want to do nat and I could not find a libvirt network filter rule for doing nat, I used the hook mechanism of libvirt, which means putting a script called network
in /etc/libvirt/hooks
:
#!/bin/bash
VIRT_NET_MATCH="virbr1"
VIRT_BRIDGE_MATCH="virbr1"
IPV6_DEFAULT_INTERFACE="vpn-thuis"
if [ "$1" == "$VIRT_NET_MATCH" ]; then
VIRT_BRIDGE=$VIRT_BRIDGE_MATCH
if [ "$2" == "started" ]; then
echo "Adding NAT MASQUERADING entries for $VIRT_BRIDGE"
ip6tables -t nat -A POSTROUTING -s fd00::/64 -o $IPV6_DEFAULT_INTERFACE -j MASQUERADE
elif [ "$2" == "stopped" ]; then
echo "Stopping $1 ($VIRT_BRIDGE) ..."
ip6tables -t nat -D POSTROUTING -s fd00::/64 -o $IPV6_DEFAULT_INTERFACE -j MASQUERADE
fi
fi
Make sure this file is executable.
IPV6_DEFAULT_INTERFACE
is the mail IPv6 interface over which the natting should take place.
The rest is self explanatory I think. When libvirt brings the network interface online, it calls the /etc/libvirt/hooks/network
script, and this handled the creation or deleten of the NAT rule in the firewall of the host.