File:  [Local Repository] / badi / public_scripts / switchgate / switchgate
Revision 1.8: download - view: text, annotated - select for diffs - revision graph
Tue Mar 28 21:44:24 2017 UTC (7 years, 1 month ago) by adi
Branches: MAIN
CVS tags: HEAD
fixed multiple dhcp interfaces configured in explicit order (as dhcp.xyz interfaces)

#!/bin/bash

# switchhgate 0.0.5
# (c) 2005-2010 under GPL by Adrian Zaugg

# switchgate pings a set of hosts to determine the state of the internet connectivity. If
# the connection is down, it can change the default gateway to an alternative gateway.

## Settings
#

# Where the config files reside
CONFIG_PATH="/etc/switchgate"

# Path where to find dhcp information file from switchgate-helper,
# the dhclient-exit-hook helper program
DHCP_TMP_DIR=/tmp/switchgate

# eMail address to send alert messages, if not operating on the
# standard default gateway
ALERT_EMAIL="adi@ente.limmat.ch"

# Host set to ping. A file containing IP addresses, each on a single line.
# Do not include any local hosts, list only hosts located behind your gateways.
HOSTSET_FILE="$CONFIG_PATH/hostset"

# The file defining fixed and dynamic gateway preference
GATEWAYS_FILE="$CONFIG_PATH/gateways"

# path to fping
PING=/usr/bin/fping

# answer of fping to reachable hosts
ALIVE_ANSWER="is alive"

# set to an empty string to avoid debug output
# to "low" for a few output and to anything else
# for verbose output
DEBUG=


# -------- Do not edit below this line --------

PINGARGS="-A -p25 -t100"


## Function declarations
#

# Read gateways from file
function readgwlist {
	if [ -f "$GATEWAYS_FILE" ]; then
		# Ordered list of all gateways in file. Highest preference has entry with index 0, which 
		# is your standard default gateway.
		# dhcp[.ethX] means dhcp supplied gateways in file $CONFIG_PATH/dhcp.ethX
		number_of_gws=0
		if [ -n "$DEBUG" -a "$DEBUG" != "low" ]; then
			echo "Reading config file $GATEWAYS_FILE..."
		fi

		allgates=`grep -ve "^[ 	]*[\#]\+.*$" "$GATEWAYS_FILE" | xargs`
		alldhcpgates=""
		for gate in $allgates; do
			if [[ $(echo "$gate" | grep -c -e "^[[:space:]]*dhcp\..*$") -eq 1 && \
			      -f "$DHCP_TMP_DIR/$gate" ]]; then
				# Read dhcp supplied gate for a specific interface
				alldhcpgates="$alldhcpgates `grep -e "^[^\#].*$" "$DHCP_TMP_DIR/$gate" | xargs`"
			elif [[ $(echo "$gate" | grep -c -e "^[[:space:]]*dhcp[[:space:]]*$") -eq 1 ]]; then
				# Read all dhcp files
				for dhcpfile in $(echo -n $(ls -1 $DHCP_TMP_DIR/dhcp* 2>/dev/null)); do
					alldhcpgates="$alldhcpgates `grep -e "^[^\#].*$" "$dhcpfile" | xargs`"
				done
			else
				# it's a fixed gateway
				addgw="$gate"
				debug_msg="fixed"
				addgateway
			fi

			for dhcpgate in $alldhcpgates; do
				addgw="$dhcpgate"
				debug_msg="$gate"
				addgateway
			done
			unset alldhcpgates
		done
	fi

	if [ "$number_of_gws" -eq 0 ]; then
		echo "No gateways configured. Please edit $GATEWAYS_FILE."
		exit 1
	elif [[ "$number_of_gws" -eq 1 && ! -z $DEBUG ]]; then
		echo "Only 1 gateway configured. No fallback switching possible."
	else
		if [[ -n "$DEBUG" && "$DEBUG" != "low" ]]; then
			echo "... $number_of_gws gateway(s) found."
		fi
	fi
}

# add a gateway to the array of all gateways to possibly switch to
function addgateway {
	addgw=$(echo "$addgw" | tr -d ' ')
	# check no duplicate addition is performed
	if [ $(echo "${GW[*]}" | grep -c "$addgw") -eq 0 ]; then
		GW[$number_of_gws]=$addgw
		if [[ -n "$DEBUG" && "$DEBUG" != "low" ]]; then
			echo -e "\tGateway $number_of_gws: $addgw ($debug_msg)"
		fi
		let "number_of_gws += 1"
	fi
	unset addgw
}

# If one host of the HOSTSET_FILE responds, the conncetion is considered up.
function checkconnection {
   UP=""
   if [ `$PING $PINGARGS < "$HOSTSET_FILE" 2> /dev/null | grep -c "$ALIVE_ANSWER"` -gt 0 ]; then
      UP=true
   fi
   if [ -n "$DEBUG" ]; then
      if [ -n "$UP" ]; then
         echo "The connection is up."
      else
         echo "The connection is down."
      fi
   fi
}

# Is a certain gateway reachable?
function checkgw {
   if [ `$PING $PINGARGS "$NEWGW" 2> /dev/null | grep -c "$ALIVE_ANSWER"` -gt 0 ]; then
      if [ -n "$DEBUG" ]; then
         echo "$NEWGW is reachable."
      fi
    else
      if [ -n "$DEBUG" ]; then
          echo "$NEWGW is not reachable."
      fi
      NEWGW=""
   fi
}

function getcurrentgw {
   iproute_default_gw_txt="$(ip route show scope global)"
   if [ `echo $iproute_default_gw_txt | grep -c default` -gt 1 ]; then
      echo "No support for multiple default gateways. Exiting."
      exit 1
   else
      iproute_default_gw_txt="$(echo "$iproute_default_gw_txt" | grep "default via")"
   fi
   if [ -z "$iproute_default_gw_txt" ]; then
      # no default gateway currently set, set to the highest index, to land on GW0
      CURRENTGW_ID=0
      CURRENTGW=${GW[$CURRENTGW_ID]}
      CURRENTGW_DEV="unknown"
      echo "No default gateway currently set. Setting it now to $CURRENTGW."
      ip route add default via $CURRENTGW
   else
      CURRENTGW=`echo $iproute_default_gw_txt | sed "s/^default via \(\([0-9]\+\.\?\)\{4\}\).*\$/\1/"`
      CURRENTGW_DEV=`echo $iproute_default_gw_txt | sed "s/^default via .* dev \(eth[0-9]\+\).*\$/\1/"`
   fi

   # get index of current gateway
   index=-1
   for NEWGW in ${GW[*]}; do
      let "index += 1"
      if [ "$CURRENTGW" = "$NEWGW" ]; then
         CURRENTGW_ID=$index
	 break
      fi
   done
   if [ -z "$CURRENTGW_ID" ]; then
      echo "The current gateway ($CURRENTGW) is not in the list of available gateways."
      echo "Please edit $GATEWAYS_FILE to correct."
      exit 1
   fi
   if [ "$DEBUG" = "low" ]; then
      echo "Default gateway detected: $CURRENTGW ($CURRENTGW_ID) on dev $CURRENTGW_DEV"
   fi
}

# Set NEWGW and NEWGW_ID to the next gateway from GW[]
function getnextgw {
   if [ -z "$NEWGW_ID" ]; then
      if [ -z "$CURRENTGW_ID" ]; then
         getcurrentgw
         NEWGW_ID=$CURRENTGW_ID
      else
         NEWGW_ID=-1
      fi
   fi
   NEWGW_ID=$[ $NEWGW_ID + 1 ]
   if [ "$NEWGW_ID" -ge "${#GW[*]}" ]; then
      NEWGW_ID=0
   fi
   NEWGW=${GW[$NEWGW_ID]}
}

# Set $NEWGW to the next working gateway
function getnewgw {
   getnextgw
   # Check wheter all gateways are already tested and failed
   if [ "$NEWGW_ID" -eq "$CURRENTGW_ID" ]; then
      if [ -n "$DEBUG" ]; then
         echo "All gateways tried."
      fi
      NEWGW=""
      NEWGW_ID=""
   else
      if [ -n "$DEBUG" ]; then
         echo -n "Trying $NEWGW..."
      fi
      savegw=$NEWGW
      checkgw
      if [ -z "$NEWGW" ]; then
         if [ -n "$DEBUG" ]; then
             echo "No luck. Trying next..."
         fi
         NEWGW=$savegw
         getnewgw
      else
         if [ -n "$DEBUG" ]; then
            echo "Successfully found a new gateway, it's $NEWGW."
         fi
      fi
   fi
}

function switchgw {
   getnewgw
   if [ -n "$NEWGW" ]; then
      if [ "$DEBUG" = "low" ]; then
         echo -n "Switching default gateway now to $NEWGW ($NEWGW_ID)..."
      fi

      iproute_msg="$(ip route change default via $NEWGW 2>&1 )"
      iproute_exit=$?

      # generate some traffic to take effect
      sleep 1
      GARBAGE=`$PING -q -c 5 -t100 -p50 -A -f $HOSTSET_FILE 2> /dev/null`
      sleep 1

      if [ "$DEBUG" = "low" ]; then
         echo "done."
      fi

      checkconnection
      if [ -z "$UP" ]; then
         if [ "$DEBUG" = "low" ]; then
            echo -n "$NEWGW ($NEWGW_ID) is not working, switching back to $CURRENTGW ($CURRENTGW_ID)..."
	 fi
	 ip route change default via $CURRENTGW
	 if [ "$DEBUG" = "low" ]; then
	    echo "done."
	 fi
	 switchgw
      elif [ "$iproute_exit" -eq 0 ]; then
         CURRENTGW=$NEWGW
         CURRENTGW_ID=$NEWGW_ID
      else
	 echo "Failure from iproute: $iproute_msg"
	 echo "However, the connection is working. Continue as if nothing."
      fi
   else
      if [ "$DEBUG" = "low" ]; then
         echo "Not switching."
      fi
   fi
}


## MAIN
#

# Parse writeable dir request (hidden feature)
if [ "$1" = "-C" ]; then
	echo "$DHCP_TMP_DIR"
	exit 0
fi

# Parse debug switch (another hidden feature)
if [ "$1" = "-d" ]; then
	if [ "$2" != "low" ]; then
		echo "Debug mode on."
		DEBUG="choke"
	else
		#echo "Info mode on."
		DEBUG="low"
	fi 
fi

# Check fping existence
if [ ! -x "$PING" ]; then
	echo -e "Error: Ping program not executable or not found at $PING.\nPlease configure the full path in $0."
	exit 1
fi

# Get all gateways, we can forward traffic
readgwlist

# Parse query for current gw  (yet another hidden feature)
if [ "$1" = "-q" ]; then
	DEBUG=
	getcurrentgw
	echo "$CURRENTGW"
	exit 0
fi

# get current gw
getcurrentgw

# check wether host set file is around
if [ ! -e $HOSTSET_FILE ]; then
   echo "No host list found! File $HOSTSET_FILE not found."
   exit 1
fi

# check connection
checkconnection

# If the connection is down, change to the nextgw
if [ -z "$UP" ]; then
   # Some more information of down reason: Diagnose wheter the current gateway is alive 
   NEWGW=$CURRENTGW
   checkgw
   if [ -z "$NEWGW" ]; then
      echo "Gateway $CURRENTGW is down."
   else
      echo "No connection through gateway $CURRENTGW." 
   fi

   NEWGW_ID=$CURRENTGW_ID
   switchgw

else
   # If the current gateway is not the standard default gateway, try if the connection 
   # through the standard default gateway is back
   if [ "$CURRENTGW_ID" != 0 ]; then
      if [ "$DEBUG" = "low" ]; then
         echo "Not operating on standard default gateway (${GW[0]}). Trying to change."
      fi
      NEWGW_ID=""
      switchgw
   else
      # normal operation
      if [[ -n "$DEBUG" && "$DEBUG" != "low" ]]; then
         echo "Normal operation on standard default gateway $CURRENTGW ($CURRENTGW_ID)."
      fi
   fi
fi

# send an eMail if the connection is down or limited
if [ -z "$UP" ]; then
	if [ -z "$NEWGW" ]; then
		EMAIL_BODY="Switchgate on $(hostname -f) reports: No connection!\n\nTime:\t\t$(date)\nGateway:\t$CURRENTGW ($CURRENTGW_ID)"
		EMAIL_SUBJECT="Attention: $(hostname -f) has no connection!"
		echo -e "$EMAIL_BODY" | mail -s "$EMAIL_SUBJECT" "$ALERT_EMAIL"
		if [[ -n "$DEBUG" && "$DEBUG" != "low" ]]; then
			echo "Alert message sent to $ALERT_EMAIL."
		fi
	fi
elif [ "$CURRENTGW_ID" != 0 ]; then
	EMAIL_BODY="Switchgate on $(hostname -f) reports: Not operating on standard default gateway!\n\nTime:\t\t$(date)\nGateway:\t$CURRENTGW ($CURRENTGW_ID)"
	EMAIL_SUBJECT="Attention: $(hostname -f) not operating on standard default gateway!"
	echo -e "$EMAIL_BODY" | mail -s "$EMAIL_SUBJECT" "$ALERT_EMAIL"
	if [[ -n "$DEBUG" && "$DEBUG" != "low" ]]; then
		echo "Alert message sent to $ALERT_EMAIL."
	fi
fi

if [ "$DEBUG" = "low" -o "$DEBUG" = "choke" ]; then
	if [ -n "$UP" ]; then
	   echo
	   echo "Current Default Gateway: $CURRENTGW ($CURRENTGW_ID)"
	   echo
	else
	   echo
	   echo "No connection."
	   echo
	fi
fi
exit 0

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>