Configuring systemd for a LXD Front Bridge with better IPv6 virtualization

Traffic
Bridging your virtual network

by Craig Miller

Bridging is the act of forwarding packets at the ethernet layer. Setting up a front bridge (br0) requires that the host is ethernet attached to the rest of your network. Wifi can not be used, as bridging between Wifi and ethernet requires more than a simple cable plug-in.

The advantage of bridging is that the LXD containers will get their IPv4 and IPv6 addresses from the upstream router. Each Container will have a stable IPv6 address which you can put into DNS.

A front bridge

Unfortunately LXD creates a lxdbr0 default bridge which is not connected to anything. The default network settings are not very IPv6 friendly. By creating a front bridge, your LXD containers can connect directly to the real network.

In the diagram below, br0 is what I am calling a front bridge, everything other than the physical ethernet jack is connected to br0, including the host. Depending on your Linux Distro, this can be daunting to some. Systemd doesn't help, as it hasn't really simplified linux networking.

Virtual router Network

Configuring systemd for a front bridge in 6 easy steps

While it is possible to do the configuration below via ssh alone, it may be handy to have a local monitor & keyboard handy in the event something goes wrong.

The following steps work on a Raspberry Pi running Raspian, or any Linux Distro using systemd.


Set up the bridge (br0)

  1. Install brctl the utility which controls/creates linux bridges. And install the ifupdown package which will be used later.
sudo apt-get install bridge-utils ifupdown
  1. Edit the /etc/network/interfaces file to automatically set up the bridge br0 and attach the ethernet device. Add the following lines:
iface br0 inet dhcp
    bridge_ports eth0
    bridge_stp off
    bridge_fd 0
    bridge_maxwait 0
iface br0 inet6 dhcp

Because Ubuntu uses systemd we must let systemd know about the bridge, or the IPv6 default route will disappear after about 5 minutes (not good).

  1. Create/Edit /etc/systemd/network/br0.network file, and add the following:
[Match]
Name=br0

[Network]
DHCP=yes

Lastly, in order to make this all work when the Pi is rebooted, we have to hack at /etc/rc.local a bit to make sure the bridge is brought up and systemd is minding it at boot up time.

  1. Create/Edit /etc/rc.local and add the following, and don't forget to make it executable.
#!/bin/bash
#
## put hacks here

# fix for br0 interface
/sbin/ifup br0
# kick networkd as well
/bin/systemctl restart systemd-networkd
echo "Bridge is up"
exit 0

Make it executable:

$ sudo chmod 754 /etc/rc.local
  1. Modern versions of systemd have disabled rc.local, so it must be re-enabled.

Enable /etc/rc.local in systemd Create /etc/systemd/system/rc-local.service

[Unit]
 Description=/etc/rc.local Compatibility
 ConditionPathExists=/etc/rc.local

[Service]
 Type=forking
 ExecStart=/etc/rc.local start
 TimeoutSec=0
 StandardOutput=tty
 RemainAfterExit=yes
 SysVStartPriority=99

[Install]
 WantedBy=multi-user.target

enable rc.local

sudo systemctl enable rc-local

start rc.local service

sudo systemctl start rc-local.service
sudo systemctl status rc-local.service

Ignore the systemd warning, it should work.

  1. Finally, reboot, login and see that the Pi br0 network is up
$ ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host 
       valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel master br0 state UP group default qlen 1000
    link/ether b8:27:eb:6c:02:88 brd ff:ff:ff:ff:ff:ff
3: br0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
    link/ether b8:27:eb:6c:02:88 brd ff:ff:ff:ff:ff:ff
    inet 192.168.215.141/24 brd 192.168.215.255 scope global dynamic br0
       valid_lft 1995525700sec preferred_lft 1995525700sec
    inet6 2001:db8:ebbd:2080::9c5/128 scope global noprefixroute 
       valid_lft forever preferred_lft forever
    inet6 2001:db8:ebbd:2080:ba27:ebff:fe6c:288/64 scope global mngtmpaddr noprefixroute 
       valid_lft forever preferred_lft forever
    inet6 fe80::ba27:ebff:fe6c:288/64 scope link 
       valid_lft forever preferred_lft forever

As you can see, br0 has all the IPv4 and IPv6 addresses which is what we want. Now you can go back to headless access (via ssh) if you are like me, and the Pi is just sitting on a shelf.

Whew, that seems like a lot of steps, and believe me, it is way easier on a non-systemd linux distro (e.g. Artix, Devuan, or Alpine), but I understand that most people are running a systemd-based distro.

Now you are ready to use LXD Containers with really excellent IPv6.


Notes:


29 September 2021