18

Systemd's systemd-networkd can be used to replace the existing networking system on Raspbian.

How does it work with Raspbian on a Raspberry Pi with two interfaces for ethernet and wlan? Can I also realize dynamic failover for them?

Ingo
  • 40,606
  • 15
  • 76
  • 189

2 Answers2

31

Tested on a Raspberry Pi 4B with
Raspbian Buster Lite 2020-02-13 updated on 2020-04-11.
Updates done with sudo apt update && sudo apt full-upgrade && sudo reboot.

It will not work with Raspbian Stretch!
Here you will find the last tested revision for Raspbian Stretch Lite.


♦ Abstract

Using `systemd-networkd` instead of default `dhcpcd` is of course possible. But it is not meaningful in all cases.

networkd is a small and lean service to configure network interfaces, designed mostly for server use cases in a world with hotplugged and virtualized networking. Its configuration is similar in spirit and abstraction level to ifupdown, but you don't need any extra packages to configure bridges, bonds, vlan etc. It is not very suitable for managing WLANs yet; NetworkManager is still much more appropriate for such Desktop use cases. [1]

But for a raspi laying near by a TV or amplifier and doing its work 24/7 for streaming audio or video or for a camera etc., systemd-networkd is a good choice. But you have to do a complete switch. There is no way to mix up with networking and/or dhcpcd. Please also note that NetworkManager isn't supported by Raspbian out of the box.


♦ Step 1: Preparation

For reference I use a fresh flashed SD Card from **Raspbian Buster Lite**.

I will have attention to a headless installation only with ssh. If you are using this, double check typos or so otherwise you are lost with a broken connection. If you want a headless installation then look at SSH (Secure Shell) and follow section 3. Enable SSH on a headless Raspberry Pi (add file to SD card on another machine).

Disable the old stuff. Don't stop any service, only disable them! So it will take effect only on next boot. How to do it just follow to


♦ Step 2: Setup the wired ethernet interface (eth0)

Create this file with your settings. You can just copy and paste this in one block to your command line beginning with `cat` and including both EOF (delimiter EOF will not get part of the file):
pi@raspberrypi: ~$ sudo -Es   # if not already done
root@raspberrypi: ~# cat >/etc/systemd/network/04-eth.network <<EOF
[Match]
Name=e*
[Network]
# to use static IP (with your settings) toggle commenting the next 8 lines.
#Address=192.168.50.60/24
#DNS=84.200.69.80 1.1.1.1
#[Route]
#Gateway=192.168.50.1
#Metric=10
DHCP=yes
[DHCP]
RouteMetric=10
EOF

♦ Step 3: Setup wlan interface (wlan0)

Create this file with your settings:
root@raspberrypi:~ # cat >/etc/systemd/network/08-wifi.network <<EOF
[Match]
Name=wl*
[Network]
# to use static IP (with your settings) toggle commenting the next 8 lines.
#Address=192.168.50.61/24
#DNS=84.200.69.80 1.1.1.1
#[Route]
#Gateway=192.168.50.1
#Metric=20
DHCP=yes
[DHCP]
RouteMetric=20
EOF

Setup wpa_supplicant with this file and your settings for country=, ssid= and psk= and enable it:

root@raspberrypi:~ # cat >/etc/wpa_supplicant/wpa_supplicant-wlan0.conf <<EOF
ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev
update_config=1
country=DE

network={
    ssid="TestNet"
    psk="realyNotMyPassword"
    key_mgmt=WPA-PSK
    proto=RSN WPA
}
EOF

root@raspberrypi:~ # chmod 600 /etc/wpa_supplicant/wpa_supplicant-wlan0.conf
root@raspberrypi:~ # systemctl disable wpa_supplicant.service
root@raspberrypi:~ # systemctl enable wpa_supplicant@wlan0.service
root@raspberrypi:~ # rfkill unblock 0
root@raspberrypi:~ # exit
root@raspberrypi:~ $

Reboot and good luck ;-)

It is possible that the RasPi gets a new ip addresses so you may have to look at it for the next connection with ssh.


♦ Step 4: Bonding wired and wifi interface for failover

You should have both interfaces setup and running as described above. It is no problem when both interfaces are up. The kernel will use the interface with the lowest **metric** first. Here the ethernet interface will be used first. But this has a great disadvantage. As you can see with `ip -4 -brief addr` each interface has it's own ip-address. If the kernel switches the interface because one is gone down it also uses its new source ip-address. This will break any established statefull TCP communication, e.g. ssh, streaming, login sessions and so on. You can use a new connection from the changed source ip address but the old connections are stuck. That isn't really what we want.

The solution of this problem is bonding. We create an interim interface bond0 that does not change its settings. The wired and wifi interface will switch to bond0.

First disable the single ethernet and wifi network files:

pi@raspberrypi:~ $ sudo -Es
root@raspberrypi:~ # cd /etc/systemd/network/
root@raspberrypi:~ # mv 04-eth.network 04-eth.network~
root@raspberrypi:~ # mv 08-wifi.network 08-wifi.network~

Then setup bonding with these four files:

root@raspberrypi:~ # cat >/etc/systemd/network/02-bond0.netdev <<EOF
[NetDev]
# status: cat /proc/net/bonding/bond0
Name=bond0
Kind=bond
[Bond]
Mode=active-backup
# primary slave is defined in *eth.network
MIIMonitorSec=500ms
MinLinks=1
EOF

root@raspberrypi:~ # cat >/etc/systemd/network/12-bond0-add-eth.network <<EOF
[Match]
Name=e*
[Network]
Bond=bond0
PrimarySlave=yes
EOF

root@raspberrypi:~ # cat >/etc/systemd/network/16-bond0-add-wifi.network <<EOF
[Match]
Name=wl*
[Network]
Bond=bond0
EOF

root@raspberrypi:~ # cat >/etc/systemd/network/20-bond0-up.network <<EOF
[Match]
Name=bond0
[Network]
# to use static IP (with your settings) toggle commenting the next 4 lines.
DHCP=yes
#Address=192.168.50.60/24
#Gateway=192.168.50.1
#DNS=84.200.69.80 1.1.1.1
EOF

But this is not the whole story. systemd-networkd checks if all interfaces are up before proceeding with startup depending services. With bonding we have slave interfaces (eth0, wlan0) that never signal that they are up. Its only the bond interface that comes up if at least one of its slaves is connected. So the check will fail with errors and long waiting on bootup. To manage this you have to modifiy the systemd-networkd-wait-online.service. How to do it, please follow the instructions at

If finished that, it's time to reboot.

It is possible that the RasPi gets a new ip address so you may have to look at it for the next connection with ssh.

Then you can check the bonding status:

pi@raspberrypi:~ $ cat /proc/net/bonding/bond0
Ethernet Channel Bonding Driver: v3.7.1 (April 27, 2011)

Bonding Mode: fault-tolerance (active-backup)
Primary Slave: eth0 (primary_reselect always)
Currently Active Slave: eth0
MII Status: up
MII Polling Interval (ms): 500
Up Delay (ms): 0
Down Delay (ms): 0

Slave Interface: eth0
MII Status: up
Speed: 1000 Mbps
Duplex: full
Link Failure Count: 0
Permanent HW addr: dc:a6:32:4c:08:1b
Slave queue ID: 0

Slave Interface: wlan0
MII Status: up
Speed: Unknown
Duplex: Unknown
Link Failure Count: 1
Permanent HW addr: dc:a6:32:4c:08:1c
Slave queue ID: 0

Test bonding: with the bonding status above you will see that the Currently Active Slave: will change and the MII Status: is down.

If your are headless, don't down both interfaces together ;-)

pi@raspberrypi:~ $ ip addr
pi@raspberrypi:~ $ sudo ip link set eth0 down
pi@raspberrypi:~ $ sudo ip link set eth0 up
pi@raspberrypi:~ $ sudo ip link set wlan0 down
pi@raspberrypi:~ $ sudo ip link set wlan0 up

Be patient after setting wlan0 up. I may take some time to reconnect to the router and manage bonding. This time ssh will not response.

For a more in-depth review of bonding you may have a look at Dynamic network failover prioritize wifi over ethernet.

references:
[1] /usr/share/doc/systemd/README.Debian.gz
[2] man systemd.netdev
[3] man systemd.network
[4] https://wiki.debian.org/Bonding
[5] https://www.kernel.org/doc/Documentation/networking/bonding.txt

Ingo
  • 40,606
  • 15
  • 76
  • 189
  • Note with regard to the first quote up top: Raspbian never used NetworkManager. It is more an artifact of Fedora and derived systems (which was the first place that systemd, a Redhat supported project, was deployed). – goldilocks Feb 10 '18 at 12:49
  • Also applicable to other Debian based systems not necessarily running on ARM :) Thank you for the concise explanation. – TCB13 Jun 29 '19 at 16:29
  • This worked for my on stretch but on buster I encountered the problem, that my device cant resolve any domain. Any ideas what could be the cause? – user5950 Jul 17 '19 at 12:40
  • @user5950 Maybe there has something changed with buster? I will look at it. Just a moment please. – Ingo Jul 17 '19 at 12:46
  • @Ingo Thank you for the fast replay. I could resolve the problem by adding the line `DNS=192.168.1.1` to `/etc/systemd/network/04-eth.network`. (I am using an setup with static IP)… – user5950 Jul 17 '19 at 19:20
5

To elaborate the answer from @Ingo: please consider using the link

ln -s /run/systemd/resolve/stub-resolv.conf resolv.conf

instead of the link to /run/systemd/resolve/resolv.conf. This enable the "integrated" DNS stub and enables things like per-interface DNS server which could be important if you use VPNs that provide their own DNS server with non-public entries.

Ingo
  • 40,606
  • 15
  • 76
  • 189
Robert James
  • 51
  • 1
  • 1
  • Very interesting, thank you for feedback. Is there any documentation and/or sources of this? If so please edit your answer (using the link below it) and add it there. – Ingo Nov 11 '19 at 09:18