Your problem is well known as dynamic failover and supported by Linux with its bonding driver. It will switch to the next connection as long as it is connected.
The first step is to bring up all connections. I prefer to use systemd-networkd so Use systemd-networkd for general networking. You will find examples in section ♦ Create interface file for a wired connection and in section ♦ Create interface file for a WiFi connection. For the ppp connection you have an interface ppp0 when it is connected. You know how to do it. For the *.network file to this interface, modify the example for the wired connection. I assume you have now interfaces eth0, wlan0 and ppp0. Be sure you can ping each connection:
rpi ~$ ping -c3 -I eth0 google.com
rpi ~$ ping -c3 -I wlan0 google.com
rpi ~$ ping -c3 -I ppp0 google.com
It is no problem when all interfaces are up. The kernel will use the interface with the lowest metric 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 stateful 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. First disable the discrete *.network
files:
rpi ~$ sudo -Es
rpi ~# cd /etc/systemd/network/
rpi ~# mv 04-wired.network 04-wired.network~
rpi ~# mv 08-wifi.network 08-wifi.network~
rpi ~# mv 12-ppp.network 12-ppp.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=bond
Kind=bond
[Bond]
Mode=active-backup
# primary slave is defined in *eth.network
MIIMonitorSec=500ms
MinLinks=1
EOF
root@raspberrypi:~ # cat >/etc/systemd/network/16-bond0-add-primary.network <<EOF
[Match]
Name=e*
[Network]
Bond=bond0
PrimarySlave=yes
EOF
root@raspberrypi:~ # cat >/etc/systemd/network/20-bond0-add-secondary.network <<EOF
[Match]
Name=wl* ppp*
[Network]
Bond=bond0
EOF
root@raspberrypi:~ # cat >/etc/systemd/network/24-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, ppp0) 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 modify 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: 0
Permanent HW addr: dc:a6:32:4c:08:1c
Slave queue ID: 0
Slave Interface: ppp0
MII Status: up
Speed: Unknown
Duplex: Unknown
Link Failure Count: 0
Permanent HW addr: dc:a6:32:4c:08:1d
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
all 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
pi@raspberrypi:~ $ sudo ip link set ppp0 down
pi@raspberrypi:~ $ sudo ip link set ppp0 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.
Another example about bonding you can find at Howto migrate from networking to systemd-networkd with dynamic failover.
references:
[2] man systemd.netdev
[3] man systemd.network
[4] https://wiki.debian.org/Bonding
[5] https://www.kernel.org/doc/Documentation/networking/bonding.txt