#!/bin/bash # goal: # change default route if two target hosts cannot be reached # recommendation: # call this via cron each minute start() { # called first after function defintions preparation "$@" if no_input; then help check_only else center fi } center() { if gateway_not_working ; then change_gateway exit fi if second_gateway_used ; then debug "preferred gateway not used." debug "can we switch back to the preferred gateway?" if first_gateway_works ; then debug "switching back to the preferred gateway" use_first_gateway else debug "switching to default gateway not yet possible" fi else debug "preferred gateway used." fi } no_input(){ [ "$GATEWAY1" ] && [ "$GATEWAY2" ] && return 1 return 0 } help(){ echo \ " usage: $MYNAME GATEWAY1 GATEWAY2 [TEST_TARGET1 [TEST_TARGET2]] usage2: $MYNAME (only tests internet connection and shows help on stderr) GATEWAY1 IP address of preferred gateway (or \"dsl\") GATEWAY2 IP address of fallback gateway TEST_TARGET1 IP address of a host only reachable through the two gateways TEST_TARGET2 IP address of another host only reachable through the two gateways " >&2 } check_only() { if gateway_not_working ; then return 1 else return 0 fi } gateway_not_working() { GATEWAY=`get_gateway` if [ -z "$GATEWAY" ] ; then $OUTPUT "discovered: no gateway used!" return 0 fi if $ISUP $TEST_TARGET2 || $ISUP $TEST_TARGET1 ; then debug "could reach $TEST_TARGET1 or $TEST_TARGET2, gateway seems to work" return 1 fi if [ "$DOUBLE_TEST" = 1 ] ; then sleep 5 $OUTPUT "doing second test, line may be busy/flakey:" if isup_debug $TEST_TARGET2 || isup_debug $TEST_TARGET1 ; then $OUTPUT "could reach $TEST_TARGET1 or $TEST_TARGET2 on second try, gateway seems to work" return 1 fi fi $OUTPUT "could not be reached: $TEST_TARGET1 and $TEST_TARGET2, assuming that gateway is not working" return 0 } isup_debug(){ $ISUPDEBUG $1 2>&1 | grep -vE '^Before recvfrom|^After recvfrom' | sed 's/^/ /' } first_gateway_works() { test_gateway $GATEWAY1 ;} second_gateway_works() { test_gateway $GATEWAY2 ;} test_gateway() { TEST_GATEWAY=$1 ORIGINAL_GATEWAY=`get_gateway` debug " first test if the gateway $TEST_GATEWAY itself is reachable..." if ! $ISUP $TEST_GATEWAY ; then debug "gateway $TEST_GATEWAY not even reachable itself! I'm not even trying to use it." return 1 fi debug " ... gateway $TEST_GATEWAY itself is reachable." debug " TEST: starting test for $TEST_GATEWAY ..." set_gateway $TEST_GATEWAY || return 1 if $ISUP $TEST_TARGET2 || $ISUP $TEST_TARGET1 ; then debug " TEST SUCCESSFULL" debug " trying to set gateway that was present before test..." set_gateway $ORIGINAL_GATEWAY debug " TEST finished" return 0 else debug " TEST FAILED" debug " trying to set gateway that was present before test..." set_gateway $ORIGINAL_GATEWAY debug " TEST finished" return 1 fi } set_gateway() { NEW_GATEWAY=$1 OLD_GATEWAY=`get_gateway` if [ "$OLD_GATEWAY" = "$NEW_GATEWAY" ] ; then debug " set_gateway $NEW_GATEWAY: it is already gateway" return 0 fi if [ -z "$NEW_GATEWAY" ] ; then debug " no valid gateway given: \"${NEW_GATEWAY}\"" return 1 fi if [ ! -z "$OLD_GATEWAY" ] ; then debug " unsetting $OLD_GATEWAY as gateway" $ROUTE del default gw $OLD_GATEWAY debug " gateway now: \"`get_gateway`\"" fi debug " setting $NEW_GATEWAY as gateway" if [ "$DEBUG" = 1 ] ; then $ROUTE add default gw $NEW_GATEWAY RETURNVALUE=$? else $ROUTE add default gw $NEW_GATEWAY >/dev/null 2>&1 RETURNVALUE=$? fi GATEWAY=`get_gateway` if [ -z "$GATEWAY" ] || [ $RETURNVALUE -gt 0 ] ; then debug " gateway now: \"\"" debug " setting gateway FAILED, resetting to old gateway: $OLD_GATEWAY" $ROUTE add default gw $OLD_GATEWAY debug " gateway now: \"`get_gateway`\"" return 1 fi } use_first_gateway() { $OUTPUT "setting preferred gateway $GATEWAY1" set_gateway $GATEWAY1 } use_second_gateway() { $OUTPUT "setting fallback gateway $GATEWAY2" set_gateway $GATEWAY2 } second_gateway_used() { GATEWAY=`get_gateway` debug " gateway used: $GATEWAY" if [ "$GATEWAY" = "$GATEWAY2" ] ; then return 0 else return 1 fi } get_gateway() { # the three separators (/): /search pattern/replacement/ # _____________| |________ | # | | | route -n|sed -n 's/^0.0.0.0[^0-9]*\([0-9\.]*\)[^0-9\.].*/\1/p' # ^^^^^^^^^^^^^^^ ^^^^^^^^ ^^^^^^^^^^ ^^ # gateway line gateway the show only what # starts with address rest is matched between # "0.0.0.0 " the first \(and\) } change_gateway() { debug "changing the gateway..." GATEWAY=`get_gateway` if [ -z "$GATEWAY" ] ; then $OUTPUT "no gateway set yet, setting gateway now..." if first_gateway_works; then use_first_gateway elif second_gateway_works; then use_second_gateway else $OUTPUT "none of the gateways work: $GATEWAY1 $GATEWAY2, NO internet connection!" use_first_gateway fi elif [ $GATEWAY = $GATEWAY1 ] ; then if second_gateway_works; then use_second_gateway else $OUTPUT "tried to use gateway $GATEWAY2, but it does not work, $GATEWAY1 remains as gateway" fi elif [ $GATEWAY = $GATEWAY2 ] ; then if first_gateway_works; then use_first_gateway else $OUTPUT "tried to use gateway $GATEWAY1, but it does not work, $GATEWAY2 remains as gateway" fi else $OUTPUT "unknown gateway discovered: $GATEWAY, trying known gateways..." if first_gateway_works; then use_first_gateway elif second_gateway_works; then use_second_gateway else $OUTPUT "none of the gateways work: $GATEWAY1 $GATEWAY2, NO internet connection!" fi fi } update_dsl_gateway() { DSLGWIP=`ifconfig|grep -A 1 '^ppp'|grep 'P-[-a-zA-Z]*P:'|head -n 1|sed 's/^.*P-[-a-zA-Z]*P:[ ]*\([0-9.]*\)[^0-9.].*$/\1/'` if [ -z "$DSLGWIP" ] ; then debug "no IP address of DSL gateway found with ifconfig" if [ -e "$DSLGWIP_CACHE_PATH" ] ; then debug "found file for last known IP address of DSL gateway: $DSLGWIP_CACHE_PATH" DSLGWIP=`cat "$DSLGWIP_CACHE_PATH"` else debug "did not find file for last known IP address of DSL gateway $DSLGWIP_CACHE_PATH" fi if [ -z "$DSLGWIP" ] ; then debug "no IP address of DSL gateway found in the file" else debug "taking $DSLGWIP as IP address of DSL gateway" fi else if [ -e "$DSLGWIP_CACHE_PATH" ] && [ `<"$DSLGWIP_CACHE_PATH"` = "$DSLGWIP" ] ; then debug "already in $DSLGWIP_CACHE_PATH : $DSLGWIP, not overwriting" else debug "storing $DSLGWIP in file for last known IP address of DSL gateway $DSLGWIP_CACHE_PATH" echo -n "$DSLGWIP" > "$DSLGWIP_CACHE_PATH" fi fi echo "$DSLGWIP" } syslog() { logger -t "$MYNAME" -s -p user.warning -- "$@" ;} debug() { [ "$DEBUG" -gt 1 ] && echo "DEBUG: $@" >&2 ;} default_config(){ # two test targets that can be pinged to decide whether "the internet" is reachable TEST_TARGET1=193.99.144.80 # default: heise.de, override with $3 TEST_TARGET2=85.10.241.60 # default: merlinux.de, override with $4 DSL=0 # by default do not assume that primary line is adsl # store IP address of (sometimes changing) DSL provider gateway DSLGWIP_CACHE_PATH="/var/cache/last_dsl_gateway_ip" # try to ping test destinations again after failure before changing anything DOUBLE_TEST=1 ISUP="isup -t 3 -r 3" # wait for returning pong for three seconds (-t 3), make two retries (-r 3 makes two) ISUPDEBUG="isup -t 3 -r 3 -v+ -d" #verbose and debug output DEBUG=0 # 1: extra output, 0: normal output OUTPUT=syslog # where non-debugging messages go ROUTE=route #ROUTE="echo route" # example configuration to not change anything, for tests } local_config(){ [ -e /etc/backup_gateway.conf ] && . /etc/backup_gateway.conf && debug "using local configuration /etc/backup_gateway.conf" } command_line_config(){ MYPATH="$0" ; MYNAME="${MYPATH##*/}" GATEWAY1=$1 # preferred gateway GATEWAY2=$2 # fallback gateway [ "$3" ] && TEST_TARGET1=$3 # two test targets that can be pinged [ "$4" ] && TEST_TARGET2=$4 # to decide whether "the internet" is reachable } preparation(){ default_config local_config command_line_config "$@" [ "$DEBUG" = 1 ] && OUTPUT=echo # OUTPUT: where non-debugging messages go PATH="$PATH":/sbin:/usr/bin which route >/dev/null || exit 1 which isup >/dev/null || exit 1 # http://www.trillke.net/~fifi/download/isup-2.8.2.tar.gz # DSL special handling if [ "$GATEWAY1" = "dsl" ] || [ "$GATEWAY1" = "DSL" ] ; then DSL=1 GATEWAY1=`update_dsl_gateway` # DYNAMIC ip address of GATEWAY fi } start "$@" # contact to author: klaus@trillke.net