Multi-Gateway change script for pfSense

Since pfSense is not actually rerouting router traffic itself (such as DNS, VPN, …) but only incoming traffic when a gateway goes down and another one is configured in the same gateway group, I have written the following script that you can use in a cron job. It will change the IPv4 default route for basically all traffic not specifically treated via FW rules – including the internal services.

  • MOBILE1 needs to be set to your second gateway, in my case a mobile LTE device
  • MOBILE2 and MOBILE3 need to be set to rarely used IPs – so the LTE traffic going there is not too much as
  • MOBILE2 and MOBILE3 need to be statically routed via LTE, always, to check their reachability
  • WAN1 needs to be set to your main gateway, in my case a FritzBox
  • WAN2 and WAN3 need to be set to pages you usually want to reach, but it is not so bad to be unreachable in case of a downtime of the WAN gateway as
  • WAN2 and WAN3 need to be statically routed via WAN, always, to check their reachability

The script will log changes and send mails to the email address configured in pfSense.

#!/usr/local/bin/bash

PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin:/root/bin

ROUTE=/sbin/route
LOG=/root/switched.txt

# Destinations, die man fast nie braucht (Traffic-Beschraenkung via MOBILE) und von MOBILE gehen sollten, muessen via MOBILE geroutet werden!
# Router selber
MOBILE1=192.168.166.1
# www.t-online.de
MOBILE2=217.6.164.162
# www.lrz.de
MOBILE3=129.187.255.234

# Normale IPs, muessen via WAN geroutet werden!
# Router selber
WAN1=192.168.178.1
# www.berlin.de
WAN2=212.45.111.17
# www.hamburg.de
WAN3=212.1.41.12

TRIES=3
SLEEP=0

WAN_SUCCESS=0
MOBILE_SUCCESS=0

# Router does not count
let MIN_TIMES=$TRIES*2

if [ $(netstat -nr4 | grep default | grep $WAN1 | wc -l) -eq 1 ]
then
  ON_WAN=1
  ON_MOBILE=0
else
  ON_WAN=0
  ON_MOBILE=1
fi

for try in $(seq 1 $TRIES)
do
  for ip in $WAN1 $WAN2 $WAN3;
  do
    ping -t 2 -c 1 $ip 2>/dev/null >/dev/null && let WAN_SUCCESS=$WAN_SUCCESS+1
    sleep $SLEEP
  done
done
echo "3 WAN IPs reachable $WAN_SUCCESS times in $TRIES runs, minimum is $MIN_TIMES."

if [ $WAN_SUCCESS -ge $MIN_TIMES ] && [ $ON_WAN -eq 1 ]
then
  echo "Enough WAN results and on WAN, nothing to do, exiting!"
  exit
elif [ $WAN_SUCCESS -ge $MIN_TIMES ] && [ $ON_MOBILE -eq 1 ]
then
  echo "Enough WAN results but on MOBILE, change back to WAN"
  echo -n "Switching to WAN at " >> $LOG
  date >> $LOG
  echo "" | /usr/local/bin/mail.php -s"Switching to WAN"

  $ROUTE del default && $ROUTE add default $WAN1
  exit
fi

# Not enough WAN results, continue with MOBILE checks/actions

for try in $(seq 1 $TRIES)
do
  for ip in $MOBILE1 $MOBILE2 $MOBILE3;
  do
    ping -t 2 -c 1 $ip 2>/dev/null >/dev/null && let MOBILE_SUCCESS=$MOBILE_SUCCESS+1
    sleep $SLEEP
  done
done
echo "3 MOBILE IPs reachable $MOBILE_SUCCESS times in $TRIES runs, minimum is $MIN_TIMES."

if [ $MOBILE_SUCCESS -ge $MIN_TIMES ] && [ $ON_MOBILE -eq 1 ]
then
  echo "Enough MOBILE results and on MOBILE, nothing to do, exiting!"
  exit
elif [ $MOBILE_SUCCESS -ge $MIN_TIMES ] && [ $ON_WAN -eq 1 ]
then
  echo "Enough MOBILE results but on WAN, change to MOBILE"
  echo -n "Switching to MOBILE at " >> $LOG
  date >> $LOG
  echo "" | /usr/local/bin/mail.php -s"Switching to MOBILE"

  $ROUTE del default && $ROUTE add default $MOBILE1
  exit
fi