One of the major features I love on FreeBSD is the ability to easily and painlessly configure interface failover between a wired and wireless network interface on a laptop, configurable with just a handful of commands. This has been around since around FreeBSD 6.3, powered by the lagg(4) driver.

In Fedora Linux, at least, this is still a release away (in Fedora 20). Nonetheless, the infrastructure is there, to some extent, but unfortunately making it work is a little painful. Here’s how to do it.

Prerequisites:

  • an understanding of Linux’ emetic network stack,
  • wpa_supplicant and iputils, both yum-installable, and
  • a wired and wireless network that are routed identically: I cannot stress enough how important it is that there are no weird routing behaviours between the wired and wireless interfaces, otherwise traffic will not automatically switch between interfaces.

How to do it:

  1. Disable Notwork Mangler.

    Network Manager is nice, but is missing all the UI elements to make it do what you want (right now). Supposedly, they’re coming in the next major release, but they’re not here now, and it just gets in the way.

     # systemctl stop NetworkManager.service
     # systemctl disable NetworkManager.service
    
  2. Configure interfaces.

    Firstly, do not even attempt to use system-config-network: it has severe problems keeping interface configuration unmangled, and is guaranteed to break any existing configuration you have.

    Take a copy of any of your existing network configuration.

    Create the following files, assuming your wired adapter is at p4p1 and your wireless adapter is wlp7s0. Because I’m a FreeBSD user hankering after old age, I alias the network interfaces to nice things, like re0 and wlan0, that tell me what’s actually there, unlike modern Linux (*gruntle*):

     # cat > /etc/sysconfig/network-scripts/ifcfg-bond0 <<EOF
     DEVICE=bond0
     BOOTPROTO=dhcp
     ONBOOT=yes
     USERCTL=no
     TYPE=Bonding
     EOF
     # cat > /etc/sysconfig/network-scripts/ifcfg-p4p1 <<EOF
     NAME=re0
     DEVICE=p4p1
     BOOTPROTO=none
     ONBOOT=yes
     MASTER=bond0
     SLAVE=yes
     TYPE=Ethernet
     USERCTL=no
     EOF
     # cat > /etc/sysconfig/network-scripts/ifcfg-wlp7s0 <<EOF
     NAME=wlan0
     DEVICE=wlp7s0
     BOOTPROTO=none
     MASTER=bond0
     SLAVE=yes
     TYPE=Wireless
     USERCTL=no
     WPA=yes
     EOF
    

    As a side note, you should probably also configure UUID fields for each interface; use uuidgen(1) to generate some UUIDs.

    Next, you need to configure bonding to load:

     # cat > /etc/modprobe.d/bonding.conf <<EOF
     alias bond0 bonding
     options bond0 mode=active-backup primary=p4p1 miimon=100
     EOF
    
  3. Configure wpa_supplicant.

    wpa_supplicant is one of the prettier evils of Linux networking, in that it does much the same thing everywhere, even when you’re not on Linux. In this case, we need to configure it to know about our network.

     # wpa_passphrase "ESSID" "password" >> /etc/wpa_supplicant/wpa_supplicant.conf
    

    In your favourite text editor, now, open up /etc/wpa_supplicant/wpa_supplicant.conf as root, and finish configuring your network there. For me, my network={ ... } entry looked like:

     network={
         ssid="...ssid..."
         scan_ssid=0
         bssid=XX:XX:XX:XX:XX:XX
         key_mgmt=WPA-PSK
         pairwise=CCMP
         
         #psk="password"
         psk=76bbeaacc1ee60cf14a0c92f2f8f50872399eadce0c2d8ea84112d5e00092a1c
     }
    

    Oooh, you’ll also need to set the following up so wpa_supplicant knows what it’s doing — the -u flag is the only non-obvious one here, and it enables the D-Pus interface so infections like NotworkMangler can break your wireless stack by pushing wpa_supplicant over. I’m leaving it on so I’m future-compatible.

     # echo > /etc/sysconfig/wpa_supplicant <<EOF
     INTERFACES="-iwlp7s0"
     DRIVERS="-Dwext"
     OTHER_ARGS="-u -f /var/log/wpa_supplicant.log -P /var/run/wpa_supplicant.pid"
     EOF
    
  4. Re-enable networking.

    Finally, let’s re-enable the network stack.

     # systemctl enable wpa_supplicant.service
     # systemctl enable network.service
     # systemctl start network.service
    

Update: I fixed it! Everything now works perfectly and comes up on boot.

My configuration has issues, though: I still have to bring up network interfaces manually, by running nearly all of the last commands.

Things I haven’t worked out how to do yet (and thus need to be done manually at each boot at the moment):

  • enslaving interfaces to the bond master and bringing all interfaces up: oddly, none of my interfaces come up on boot properly (weird, I know), so must be manually brought up and enslaved using ifenslave(8). I’m sure the init-scripts must be able to handle this, but apparently they have immense difficulty with that task.

    Of course they do, if you don’t tell them what device to look at. Derp.

  • DHCP, properly: some of my networks are static-preferred-DHCP-available, and others are DHCP-only networks (or rather, I don’t have access to the static allocation management), so some way of working out what network one is on, based on ARP traffic, would be nice.

Things that may be possible (although insane):

  • network failover from wired to wireless to VPN (presumably carried by some other data channel; in my typical usecase, this is 3G packet data).