Configure seamless switchover between wifi and ethernet using bonding

Never again have hanging network shares or dead ssh connections when (un-)plugging your ethernet all while leaving Ubuntu’s settings UI intact.

Why you’d want this

I have a dock at home for my laptop that of course is plugged into ethernet for a faster and more stable connection. So whenever I decide to sit down at my desk my network connection changes. Most websites and browsers can deal with this, some just require a quick reload. However many other tools, most notably nfs and smb (for network file shares), ssh, rsync, … cannot. The connection just times out while the app or share is completely unresponsive, which is even worse than just crashing because you don’t notice immediatley.

The issue here is that your network connections have to switch to a new interface with new ip, gateway etc. And while these settings could very well all be identical, since you’re connecting to the same network on both devices, linux can’t be sure of that. However:

In linux there exists a feature called interface-bonding that (among other fancy things) enables you to combine multiple network interfaces into a single one. And any communication via this combined/bonded interface doesn’t even notice the switch (apart from maybe a hiccup in the packet routing of your Router).

Limitations

This solution assumes that all (bonded) interfaces are connected to the same network at all times. If you were to connect your wifi to network A and plug the ethernet into network B then you’d have no connection at all, as you’re now connected to network B (ethernet > wifi) but linux thinks you’re in network A (as the interface didn’t change).

If you want to network-hop with this config, make sure to fully disconnect from A before connecting to B.

What you need

  • at least two active and working internet connections on your device both to the same Network
  • NetworkManager and nmcli for configuration. There are other tools (see below for netplan) but NetworkManager is the one also used in the Ubuntu Settings leaving the UI intact and still able to configure your network.
  • knowledge of sudo, cat and nano (or some other text-editor)

NetworkManager in 30 seconds

NetworkManager is a daemon that does as its name suggests. For this guide all you need to know is that there are devices and connections. devices, commonly known as interfaces, are both actual hardware (ethernet/wifi cards) aswell as virtual things like the loopback interface. A connection is a configuration for a specific device (connection.interface-name). For example for each wifi network you connect to, a connection (with the same name as the Wifi by default) is created for your network card storing the wifi’s name, password and other details.

nmcli cheat sheet

# show all devices/connections
nmcli device
nmcli connection
# show the details of one specific one
nmcli device show eth0
nmcli connection show "Connection Name"
# edit some parameters
nmcli connection modify "Connection Name" parameter "Value" parameter.subparameter "Value"
# delete some parameter
nmcli connection modify "Connection Name" parameter ""
# delete devices (works for connections too)
nmcli device delete DeviceName
# bring down/up an interface
nmcli device down DeviceName
nmcli device up DeviceName

available parameters

The actual guide

Make sure the interfaces are up and running. nmcli device should give something like:

DEVICE          TYPE      STATE         CONNECTION       
wlp1s0          wifi      connected     NotYourWifi      
enx000ec6300e6b ethenet   connected     Wired Connection 1
p2p-dev-wlp1s0  wifi-p2p  disconnected  --               
lo              loopback  unmanaged     --  

Note the wifi (wlp1s0) and ethernet (enx000ec6300e6b) we will bond are both reporting as connected (disconnected would also be ok, everything else see troubleshooting).

Create the bonding connection MyBondConnection. Configure it as follows

  • type is bond
  • name is MyBondConnection (or anything you like, this is never used)
  • device/interface name is bond0. Note that we don’t have to create this interface manually, it will be created automatically once we enable the connection.
  • bonding parameters:
    • active failover mode, that is use any working connection
    • check connection liveness every 100ms
    • declare the “best” connection to be the ethernet one.

    Check out the LinuxFoundation’s docs for a nice overview of all options.

  • very low priority (large number) when choosing what interface to use, so that other interfaces take priority. This is needed because for some reason the bonding interface claims to be available all the time.
sudo nmcli connection add type bond con-name "MyBondConnection" ifname bond0 bond.options "mode=active-backup,miimon=100,primary=enx000ec6300e6b" ipv4.route-metric 2000 ipv6.route-metric 2000

Now all that’s left to do is configuring the (real) interfaces to act as slave for the bonding interface. And since they are configured via connections we just have to change some parameters in them. I will just alter the existing connections for this, however you can run nmcli connection clone YourExistingConnection YourNewConnection to create a copy of the existing connection and then modify the copy instead.

Note that connection.id just changes the name, you can omit it and the new name if you don’t want that.

nmcli connection modify NotYourWifi slave-type bond master bond0 connection.id "bond0 NotYourWifi"
nmcli connection modify "Wired Connection 1" slave-type bond master bond0 connection.id "bond0 Wired Connection 1"

Most devices don’t reload the applied config when it’s changed, so to do that you need to nmcli device down and up them again.

nmcli device down enx000ec6300e6b
nmcli device up enx000ec6300e6b

If you instead created copies of the connections, you can apply them manually by bringing them up.

nmcli connection up "bond0 NotYourWifi"
nmcli connection up "bond0 Wired Connection 1"

This will probably ask you again for the password for some reason, no clue why, but it works afterwards.

Assuming you made it here, you now have bonding configured for the network you’re currently attached to. If you want this behaviour for other networks aswell, you have to edit the corresponding wifi connections aswell (the ethernet connection is usually reused regardless of the network you’re connected to).

If nmcli chooses other connections over the bonded one, try to increase the connection.autoconnect-priority and connection.autoconnect-retries of the bonded connection.

Troubleshooting

device is unavailable

Try to bring it up, either it works or it gives you the next error.

nmcli device up enx000ec6300e6b

device is “strictly unmanaged”

In /etc/NetworkManager/NetworkManager.conf under [ifupdown] set managed=true, then restart the NetworkManager

nano /etc/NetworkManager/NetworkManager.conf
sudo systemctl restart NetworkManager

Performance is shit

Check /proc/net/bonding/bond0 to see what the bonding is currently doing and make sure mode and miimon are correct. If the bonding settings aren’t as you expected, check the connection applied to the bond and restart the bond interface.

nmcli device down bond0
nmcli device up bond0

It’s not failing over

Check /proc/net/bonding/bond0 to see if the device you want it to fail over to is listed (when connected).

If not, make sure the connection applied to the interface is the one with the bonding changes.

No internet, but linux says it’s working

Try to curl google.com (or any oher request to the router that gives a response), some routers are confused about where to send the traffic to after switching and figure it out when you send something.

If this doesn’t work try to force the bond to switch over to the other interface by enabling or disabling your primary interface

nmcli connection <down/up> "bond0 Wired Connection 1"

If this gets you back a connection (usually it works when using the wired connection), your wifi card’s firmware is pedantic:

According to some wifi spec, in order to reduce traffic, wifi cards are only allowed to send traffic they “know the origin of” which some manufacturers interpret as “only traffic with a matching MAC address”. You can verify you’re having this issue by watch ifconfig-ing. You should see the TX-Packets on the broken interface climb when you try to access the network but next to no change in the RX-Packets. To fix this we can fool the card into thinking it is the ‘origin’ by changing the MAC address fo the bond-interface to the same as the broken interface.

# figure out the actual hw-addr of the broken interface
# all other sources will report the wrong one
cat /proc/net/bonding/bond0
# Ethernet Channel Bonding Driver: v6.2.0-33-generic

# ...

# Slave Interface: wlp1s0
# MII Status: up
# Speed: Unknown
# Duplex: Unknown
# Link Failure Count: 1
# Permanent HW addr: 98:fd:ef:34:96:ab < this one
# Slave queue ID: 0

# ...

# and change the address via nmcli
nmcli connection modify MyBondConnection ethernet.cloned-mac-address '98:fd:ef:34:96:ab'

# then bring the bond interface down and up
nmcli connection down MyBondConnection
nmcli connection up MyBondConnection

# and all slaves, because ubuntu automatically tries to reapply some default config
nmcli connection up "bond0 NotYourWifi"
nmcli connection up "bond0 Wired Connection 1"

It applied the wrong connection.

Either just open the settings and choose the one you want or bring down the current one and up the one you want.

nmcli device disconnect wlp1s0
nmcli connection up TheBondedConnection

Fuck, go Back

If it breaks or you mess up along the way, you can always delete the added bond connection and interface and revert the slave settings in the other connections.

nmcli connection delete MyBondConnection
nmcli device delete bond0
nmcli connection modify "bond0 NotYourWifi" slave-type "" master "" connection.id NotYourWifi

Further infos

doing it with netplan and networkd instead

Note that this completely breaks the UI and makes connecting to wifi a pain, as it works using networkd. This solution however seems more reliable from my testing and might be fine on headless machines (but why do you frequently plug and replug those?).

Replace /etc/netplan/01-network-manager-all.yaml with the following and make sure there are no other files in that folder (and replace my interface names and connection configurations).

network:
  version: 2
  renderer: networkd
  ethernets:
    enx000ec6300e6b: # name of interface
      dhcp4: false
      dhcp6: false
      optional: true
  wifis:
    wlp1s0: # name of interface
      dhcp4: false
      dhcp6: false
      optional: true
      access-points:
        "NotYourWifi":
          password: "NiceTry"
  bonds:
    bond0:
      dhcp4: true
      dhcp6: true
      dhcp4-overrides: # for some reason DNS via DHCP didn't work
        use-dns: false
      dhcp6-overrides:
        use-dns: false
      nameservers: # best copy the settings from your current state
        addresses:
          - 1.1.1.1
          - 8.8.8.8
      interfaces:
        - enx000ec6300e6b
        - wlp1s0
      parameters:
        mode: active-backup
        mii-monitor-interval: 100
        primary: enx000ec6300e6b

then run

netplan generate
netplan apply

(you cannot try this because that’s not supported with bonding and you can’t use NetworkManager as renderer because that’s also not supported with bond interfaces)

To undo this you can just generate and apply the stock config again.

network:
  version: 2
  renderer: NetworkManager