LXD IPv6 networking questions (novice)

I’m trying to figure out what options I have for setting up IPv6 on containers. Until now I’ve evaded the topic by disabling IPv6 on the LXD bridge, but it’s time to change that. My grasp of networking is still somewhat limited (hard to find intermediate sources), so bear with me.

IPv6 address assignment

Since my container setups tend to be long-lived, I like to give them neatly (sequentially) numbered IPs. If possible, I’d like to do the same in IPv6.

From what I understand, containers should probably use addresses in the ULA range (fdxx:…), right? LXD bridges seem to pick such an IP as well by default.

So could I use e.g.:

fd42:16b9:e0c9:4065::1    # lxdbr0
fd42:16b9:e0c9:4065::81   # c1
fd42:16b9:e0c9:4065::82   # c2
fd42:16b9:e0c9:4065::83   # c3

If so, which bridge settings would fit this setup? With IPv4 I used ipv4.dhcp=false and set static IPs from within each container (netplan / interfaces(5)).

And would I still – as it were – “NAT” the IPv6 traffic? From what I read, IPv6 proponents are glad to ditch NAT in physical networks, but I assume this doesn’t quite hold true for container setups like these?

My containers will require WAN routing to maintain/update themselves, but probably only see unsolicited ingress traffic (if any) from an HTTP reverse-proxy on the same machine.

Any guidance welcome, thanks!

Hi @Adrian

I’ll try and answer each of those questions:

  1. Assigning IPv6 address to container:

If you are using an LXD managed bridge (the default), then it will have automatically generated a non-globally routable ULA subnet prefix and will then be advertising that prefix to containers connected to the bridge (using dnsmasq with router advertisements enabled).

This means that containers will by default automatically pick an IP in that prefix by using SLAAC by either deriving their IP from their own MAC address, or generating a random IP, or both.

In IPv4 DHCP, one could create static DHCP reservations in dnsmasq using LXD’s ipv4.address property on the NIC, e.g. lxc config device set c1 eth0 ipv4.address=n.n.n.n.

However with IPv6 SLAAC, because the containers are picking their own address without consulting dnsmasq, one cannot use this approach by default.

However, LXD, via dnsmasq, also supports stateful DHCPv6, whereby the router advertisements sent out are modified to instruct the containers to make a DHCPv6 request to dnsmasq, at which point one can use static DHCPv6 reservations, e.g. lxc config device set c1 eth0 ipv6.address=...

You can enable this mode using: lxc network set lxdbr0 ipv6.dhcp.stateful true.

See https://linuxcontainers.org/lxd/docs/master/networks#bridges for more info.

You are also able to just configure static addresses inside the container themselves, you could also specify the gateway statically or let it auto configure from the router advertisements.

A word of warning, if router advertisements are left on (this is, somewhat misleading, configured by the ipv6.dhcp property of the LXD managed network settings) then your container will likely by default still auto configure a SLAAC address, even if you specify a static address. This may result in unexpected behaviour in terms of outbound connections originating from the SLAAC address rather than the static assigned address.

So you may choose to turn off router advertisements altogether, or if you only want some containers to ignore them, then you can set the sysctl setting net.ipv6.conf.eth0.accept_ra = 0 inside the container.

  1. ULA prefixes and NAT

By default LXD configures bridges with non-globally routable ULA prefixes. By non-globally routable I mean that Linux will allow packets to be routed as normal, so you are free to configure the routers you control to route packets however you like without using NAT, however the wider internet will not allow them to be routed and so they cannot be used for external communication.

For this reason, NAT does still exist in IPv6, and LXD makes use of it by default on managed bridges by adding an outbound NAT rule that will translate all outbound traffic from the bridge to take on the host’s IPv6 address.

However, if your ISP has allocated you sufficient address space and allowed you the means to ‘chop it up’ into smaller subnets then there is no reason why you cannot configure the bridge to use a globally routable prefix and disable NAT entirely.

To disable NATv6 you can do lxc network set lxdbr0 ipv6.nat false.

You would then need to ensure that your ISP has routed to your host a prefix sufficient for your purposes (if you are using pure static assignment it can be a very small prefix, however if you want to do DHPv6 or SLAAC it will need to be at least a /64 in size).

Keep in mind that because your host is acting as a router, you would need 2 subnets, one for the ISP -> host interface and one for the LXD bridge interface, then your host can route between those 2 subnets.

If your ISP has only assigned a single IPv6 prefix (or some individual IPv6 addresses) to your host, and has not routed any additional prefixes, then you may find you need to remove the managed LXD bridge and instead create a new bridge that connects to the host’s own parent network, effectively making your containers appear to be directly connected to the host’s parent network. In this way they will stop depending on the local dnsmasq server for DHCP/SLAAC and instead will use the ISP provided services (if any).

However some ISPs will not allow multiple MAC addresses to appear on a single network port, in order to work around this and still avoid NAT and routing multiple prefixes your best bet is to use either routed or ipvlan NIC types.

These allow one or more statically assigned addresses to be used by the container, and join the parent network, whilst still sharing the host’s MAC address.

Hope that helps, as you can see there are a lot of options :slight_smile:


Thank you @tomp for the elaborate response, this covered exactly what I wanted to know and brought me forward! :+1: