Matteias Collet

Dynamic VPN routing for roaming servers with babeld

Created at: / Last updated at:

This site describes an improvement on top of the Wireguard E2EE site-to-site VPN that allows for automatic route discovery so that servers and devices can be moved across sites without requiring reconfiguration. On top of that it also allows for automatically using failover routes by using multiple relays instead of one statically routed one.

Conceptual overview

The servers configure a static IP address on a dummy interface that is advertised on the local network using babeld . To escape the LAN wireguard tunnels are set up on at least one server/router per site that establishes a site-to-site connection across the relays:

High Level Overview

Compared to the Wireguard setup with radvd routers this removes the requirement of ULA advertising on the local network (though it’s still very useful for VM networks!).

In this configuration, a single ULA range is used for routing, that is shared across all devices - instead of one ULA per site. However, each device is assigned a /128 and routing rules are configured by babeld instead of SLAAC with radvd. This allows for roaming without reconfiguration of each client.

Note that for this configuration it is utterly important that the firewall configurations are correct, as there is no authentication in place. Especially don’t allow advertisments from public/internet-facing interfaces.

Server Configuration

The sections below describe the basic configuration for routing. Any up to date Linux-based OS should work for the edge routers and the relay, the configuration was only tested on an Ubuntu-24.04 minimal server, however.

Interface configuration

The server uses a dummy interface for configuring the static IP. It could technically be configured on a physical interface as well, but this makes it easier to manage and define routing/firewall rules where necessary. To persist the configuration across reboots it’s easiest to configure it via systemd.

Create a device:

# /etc/systemd/network/<pri>-dmesh0.netdev
 
[NetDev]
Name=dmesh0
Kind=dummy

Create the device configuration:

# /etc/systemd/network/<pri>-dmesh0.network
[Match]
Name=dmesh0
 
[Network]
Address=<ULA address>/128

Babeld configuration

Install and configure babeld:

sudo apt install babeld

The following is a configuration template. Of course it needs to be adjusted per server/relay interfaces and ranges.

# /etc/babeld.conf
 
# The interfaces on which to receive/send configurations (wireguard-interfaces are only used towards/between relays)
interface <lan-interface> type wired hello-interval 60
interface <wireguard-interface> type tunnel hello-interval 60
 
# Only accept configurations for the ULA range, except for the device's own IP
in ip <Own ULA IP>/128 deny
in ip <ULA Range>/64 if <lan-interface> allow
in ip <ULA Range>/64 if <wireguard-interface> allow
in deny
 
# Only allow configurations of valid ranges on the selected interfaces
out ip <ULA Range>/64 eq 128 if <lan-interface> allow
out ip <ULA Range>/64 eq 128 if <wireguard-interface> allow
out deny
 
# Only allow redestribution on local
redistribute local allow
redistribute deny

The daemon can then be enabled using systemd:

sudo systemctl enable --now babeld

Note: The service unfortunately does not fail if the configuration is not valid. Errors are in the journal, however.

This is pretty much all configuration that needs to be done. With two servers (and hops in-between, if any) configured they should already be able to communicate with each other.

Troubleshooting

Babeld can be started with debug logs:

babeld -d 2

Advertisments can be captured too (by default, babeld runs on port 6696):

tcpdump -i <interface> udp port 6696

References

Worklog

  • 04.05.2025: Initial configuration
  • 05.05.2025: Minor cleanup