What is the systemd-networkd equivalent of post-up? (dynamic bridge MAC configuration)

Solution 1:

It turns out that due to the heavy modular concept of systemd and systemd-networkd the scripting question needs to be tackled from a different angle: instead of looking for scripting with the bridge .netdev definition, the systemd way is to have a (very small) one-shot .service unit that is wanted by the bridge .netdev.

As a side note: it seems that in more recent Linux kernels, the kernel bridges actually don't use the dynamically changing lowest MAC48 scheme for the bridge MAC48 anymore. Instead, they create a static MAC48 for the bridge itself. So, in the very strict sense this solution is not really needed anymore, unless one prefers to use a "real" hardware MAC48; which is what is done here in the following service unit.

The necessary new service unit (in lieu of the old post-up from /etc/network/interfaces) lives in /etc/systemd/system/bridge-stable-mac.service and assigns the MAC48 from (built-in, fixed) wlan0 to the bridge itself:

[Service]
Type=oneshot
ExecStart=/bin/bash -c "/bin/echo 'br0 available, setting MAC ' `/bin/cat /sys/class/net/wlan0/address`"
ExecStart=/bin/bash -c "/sbin/ip link set br0 address `/bin/cat /sys/class/net/wlan0/address`"

[Install]
WantedBy=sys-subsystem-net-devices-br0.device

The central point here is the WantedBy= clause: whenever br0 starts, then this service should be run (exactly once, Type=oneshot). Systemd is really neat here, as it doesn't need to edit the existing device definition in order to add our dependency, but instead calculates this dependency using our inverse WantedBy= link. This is really where I think that systemd does shine.

The service unit above assumes that your bridge is named br0. You should use a corresponding .netdev file to define this bridge br0. For instance, in /etc/systemd/network/10-br0.netdev:

[NetDev]
Name=br0
Kind=bridge

When it comes to hotplugging bridge ports, systemd actually does this already out-of-the-box, which is very neat; in /etc/systemd/network/10-br0-ports.network:

[Match]
Name=eth0 wlan0

[Network]
Bridge=br0

That's it!