Skip to content

Instantly share code, notes, and snippets.

@Apsu
Last active February 12, 2024 07:08
Show Gist options
  • Star 38 You must be signed in to star a gist
  • Fork 17 You must be signed in to fork a gist
  • Save Apsu/5021255 to your computer and use it in GitHub Desktop.
Save Apsu/5021255 to your computer and use it in GitHub Desktop.
An example failover script for dual WAN, using a ping healthcheck and managing default routes appropriately
#!/bin/bash
# Set defaults if not provided by environment
CHECK_DELAY=${CHECK_DELAY:-5}
CHECK_IP=${CHECK_IP:-8.8.8.8}
PRIMARY_IF=${PRIMARY_IF:-eth0}
PRIMARY_GW=${PRIMARY_GW:-1.2.3.4}
BACKUP_IF=${BACKUP_IF:-eth1}
BACKUP_GW=${BACKUP_GW:-2.3.4.5}
# Compare arg with current default gateway interface for route to healthcheck IP
gateway_if() {
[[ "$1" = "$(ip r g "$CHECK_IP" | sed -rn 's/^.*dev ([^ ]*).*$/\1/p')" ]]
}
# Cycle healthcheck continuously with specified delay
while sleep "$CHECK_DELAY"
do
# If healthcheck succeeds from primary interface
if ping -I "$PRIMARY_IF" -c1 "$CHECK_IP" &>/dev/null
then
# Are we using the backup?
if gateway_if "$BACKUP_IF"
then # Switch to primary
ip r d default via "$BACKUP_GW" dev "$BACKUP_IF"
ip r a default via "$PRIMARY_GW" dev "$PRIMARY_IF"
fi
else
# Are we using the primary?
if gateway_if "$PRIMARY_IF"
then # Switch to backup
ip r d default via "$PRIMARY_GW" dev "$PRIMARY_IF"
ip r a default via "$BACKUP_GW" dev "$BACKUP_IF"
fi
fi
done
@csitech
Copy link

csitech commented Mar 22, 2017

Very nice article. Thanks for this.
We apply your script in ours servers and it works perfectly.

@philip-dakno
Copy link

Should [[ "$1" = "$(ip r g "$CHECK_IP" | sed -rn 's/^.*dev ([^ ]*).*$/\1/p')" ]] be [[ "$1" == "$(ip r g "$CHECK_IP" | sed -rn 's/^.*dev ([^ ]*).*$/\1/p')" ]]

@dushyantbangal
Copy link

If the primary network is disconnected, it fails at ip r d default via "$PRIMARY_GW" dev "$PRIMARY_IF" saying
RTNETLINK answers: Network is unreachable.

How can I make it go ahead with the next command that adds the backup as the default?

@stikkx
Copy link

stikkx commented Dec 4, 2018

i have a little problem. If i disconnect the primary network to test the configuration, it switches perfectly to the failover network. But if i plug in it again. The ping from the primary device got no route. Has anyone else this problem ?

@totorss
Copy link

totorss commented Jul 30, 2020

i have a little problem. If i disconnect the primary network to test the configuration, it switches perfectly to the failover network. But if i plug in it again. The ping from the primary device got no route. Has anyone else this problem ?

Hello, my correction : ``
#!/bin/bash
wan0='eth0'
wan1='eth1'
lan1='eth2'

wan0_ip=$( ifconfig $wan0 | awk '/inet/{print $2}' )
wan1_ip=$( ifconfig $wan1 | awk '/inet/{print $2}' )
lan1_ip=$( ifconfig $lan1 | awk '/inet/{print $2}' )

wan0_gw=$( grep dhcp-server /var/lib/dhcp/dhclient.$wan0.leases | tail -1 | awk '{print $3}' | cut -d ";" -f1 )
wan1_gw=$( grep dhcp-server /var/lib/dhcp/dhclient.$wan1.leases | tail -1 | awk '{print $3}' | cut -d ";" -f1 )
lan1_gw=$( grep dhcp-server /var/lib/dhcp/dhclient.$lan1.leases | tail -1 | awk '{print $3}' | cut -d ";" -f1 )

Set defaults if not provided by environment

CHECK_DELAY=5
CHECK_IP0='8.8.8.8'
CHECK_IP1='8.8.4.4'
PRIMARY_IF=$wan1
PRIMARY_GW=$wan1_gw
BACKUP_IF=$wan0
BACKUP_GW=$wan0_gw

Compare arg with current default gateway interface for route to healthcheck IP

gateway_if() {
[[ "$1" == "$(ip r g "$CHECK_IP1" | sed -rn 's/^.dev ([^ ]).*$/\1/p')" ]]
}

ip r d "$CHECK_IP0/32"
ip r a "$CHECK_IP0/32" via "$PRIMARY_GW" dev "$PRIMARY_IF"

Cycle healthcheck continuously with specified delay

while sleep "$CHECK_DELAY"
do

If healthcheck succeeds from primary interface

if ping -I "$PRIMARY_IF" -c1 "$CHECK_IP0" &>/dev/null
then
# Are we using the backup?
if gateway_if "$BACKUP_IF"
then # Switch to primary
ip r d default via "$BACKUP_GW" dev "$BACKUP_IF"
ip r a default via "$PRIMARY_GW" dev "$PRIMARY_IF"
fi
else
# Are we using the primary?
if gateway_if "$PRIMARY_IF"
then # Switch to backup
ip r d default via "$PRIMARY_GW" dev "$PRIMARY_IF"
ip r a default via "$BACKUP_GW" dev "$BACKUP_IF"
fi
fi
done
``

@cotacchevy
Copy link

Goog Morning, I realize tests, but the system return follow message in line 27:
--- 8.8.8.8 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 9.324/9.324/9.324/0.000 ms
PING 8.8.8.8 (8.8.8.8) from 192.168.115.100 enp4s3: 56(84) bytes of data.
/usr/local/bin/failover: 27: /usr/local/bin/failover: [[: not found
64 bytes from 8.8.8.8: icmp_seq=1 ttl=117 time=9.80 ms

What it is?

@BaiBorko
Copy link

BaiBorko commented Jan 31, 2022

Here is my little change of the script . The logic is the same , but if it switches to the failover gateway because of lost ping , the check will continue through the primary interface . When the connectivity from primary interface is restored the script will switch back to the primary gateway.

#!/bin/bash

Set defaults if not provided by environment

CHECK_DELAY=${CHECK_DELAY:-5}
CHECK_IP=${CHECK_IP:-8.8.8.8}
PRIMARY_IF=${PRIMARY_IF:-eth0}
PRIMARY_GW=${PRIMARY_GW:-1.2.3.4}
BACKUP_IF=${BACKUP_IF:-eth1}
BACKUP_GW=${BACKUP_GW:-2.3.4.5}

Compare arg with current default gateway interface for route to healthcheck IP

gateway_if() {
[[ "$1" = "$(ip r g "$CHECK_IP" | sed -rn 's/^.dev ([^ ]).*$/\1/p')" ]]
}

Cycle healthcheck continuously with specified delay

while sleep "$CHECK_DELAY"
do
if gateway_if "$BACKUP_IF"
then
ip r a "$CHECK_IP" via "$PRIMARY_GW" dev "$PRIMARY_IF"
PING_PRIMARY_IF=ping -I "$PRIMARY_IF" -c1 "$CHECK_IP"|grep 'packet loss'|cut -d ' ' -f4
ip r d "$CHECK_IP" via "$PRIMARY_GW" dev "$PRIMARY_IF"
sleep 1
else
PING_PRIMARY_IF=ping -I "$PRIMARY_IF" -c1 "$CHECK_IP"|grep 'packet loss'|cut -d ' ' -f4
fi

If healthcheck succeeds from primary interface

if [ $PING_PRIMARY_IF -eq 1 ]; then
# # Are we using the backup?
if gateway_if "$BACKUP_IF"
then # Switch to primary
ip r d default via "$BACKUP_GW" dev "$BACKUP_IF"
ip r a default via "$PRIMARY_GW" dev "$PRIMARY_IF"
fi
else
# Are we using the primary?
if gateway_if "$PRIMARY_IF"
then # Switch to backup
ip r d default via "$PRIMARY_GW" dev "$PRIMARY_IF"
ip r a default via "$BACKUP_GW" dev "$BACKUP_IF"
fi
fi
done

@Ded1er
Copy link

Ded1er commented Apr 14, 2022

Goog Morning, I realize tests, but the system return follow message
/failover.sh: line 29: -I: command not found
/failover.sh: line 34: [: -eq: unary operator expected
What it is?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment