For sub dividing the /64 IPv6 subnet into smaller subnets for use with containers inside each VM, I suggest you use a /112 subnet. This allows 65k containers per VM, and has the nice property on being on one of the nibble boundaries before the last colon:
E.g.
2402:9400:0000:0000:0000:0000:0000:0001
XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX
||| |||| |||| |||| |||| |||| ||||
||| |||| |||| |||| |||| |||| |||128
||| |||| |||| |||| |||| |||| ||124
||| |||| |||| |||| |||| |||| |120
||| |||| |||| |||| |||| |||| 116
||| |||| |||| |||| |||| |||112
Source: IPv6 Subnet Cheat Sheet and IPv6 Cheat Sheet Reference | Broadcast | Crucial
Again, we’ll use the routed approach inside the VM so that containers cannot hijack address space outside of their allocate /112 subnet, and so that each LXD inside the VM can control its own automatic allocation using stateful DHCPv6.
For the first VM I allocate the /112 subnet: ipv6.address=2a01:abcd:abcd:abcd:0000:0000:0001::/112
which is the first one after the 0::/112
subnet that we are using for the host’s /128 subnet.
Inside a VM lets do the following:
lxc network create lxdbr0 \
ipv6.address=ipv6.address=2a01:abcd:abcd:abcd:0000:0000:0001:0001/112 \
ipv6.dhcp.stateful=true
Now on the main host we need to add a static route for each VM’s /112 subnet pointing to the VM’s primary IPv6 address so that NDP isn’t required for the /112 subnet (this must made persistent later):
sudo ip -6 r add 2a01:abcd:abcd:abcd:0000:0000:0001::/112 via <VM IPv6 address> dev br0
Now check we can ping the VM’s lxdbr0 IPv6 address 2a01:abcd:abcd:abcd:0000:0000:0001:0001
from the host (and externally).
Now inside the VM you can launch an Ubuntu Focal container using the VM’s lxdbr0 and it should use DHCPv6 stateful to request an IP in the /112 subnet that is globally reachable.
I’ve tested this on my home setup and works perfectly.