#!/bin/sh
#
#     Shorewall WAN Interface monitor - V4.4
#
#     Inspired by Angsuman Chakraborty's gwping script.
#
#     This program is under GPL [http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt]
#
#     (c) 2009 - Tom Eastep (teastep@shorewall.net)
#
#	This program is free software; you can redistribute it and/or modify
#	it under the terms of Version 2 of the GNU General Public License
#	as published by the Free Software Foundation.
#
#	This program is distributed in the hope that it will be useful,
#	but WITHOUT ANY WARRANTY; without even the implied warranty of
#	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
#	GNU General Public License for more details.
#
#	You should have received a copy of the GNU General Public License
#	along with this program; if not, write to the Free Software
#	Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#
#  For information about this script, see  http://www.shorewall.net/MultiISP.html#swping.
#
###########################################################################################
#
# IP Family == 4 or 6
#
FAMILY=4
#
# The command to run when the status of a line changes. Can include multiple commands
# separated by semicolons (";").
#
COMMAND=

if [ $FAMILY -eq 4 ]; then
    if [ -f /usr/share/shorewall-lite/lib.base ]; then
	. /usr/share/shorewall-lite/lib.base
	[ -f /etc/shorewall-lite/params ] && . /etc/shorewall-lite/params
	[ -n "${COMMAND:="/sbin/shorewall-lite restart; /sbin/ip -4 route ls"}" ]
	CONFDIR=/etc/shorewall-lite
	VARDIR=/var/lib/shorewall-lite
    elif [ -f /usr/share/shorewall/lib.base ]; then
	. /usr/share/shorewall/lib.base
	[ -f /etc/shorewall/params ] && . /etc/shorewall/params
	[ -n "${COMMAND:="/sbin/shorewall restart -f; /sbin/ip -4 route ls"}" ]
	CONFDIR=/etc/shorewall
	VARDIR=/var/lib/shorewall
    fi
else
    if [ -f /usr/share/shorewall6-lite/lib.base ]; then
	. /usr/share/shorewall6-lite/lib.base
	[ -f /etc/shorewall6-lite/params ] && . /etc/shorewall6-lite/params
	[ -n "${COMMAND:="/sbin/shorewall6-lite restart; /sbin/ip -4 route ls"}" ]
	CONFDIR=/etc/shorewall6-lite
	VARDIR=/var/lib/shorewall6-lite
    elif [ -f /usr/share/shorewall6/lib.base ]; then
	. /usr/share/shorewal6l/lib.base
	[ -f /etc/shorewall6/params ] && . /etc/shorewall6/params
	[ -n "${COMMAND:="/sbin/shorewall6 restart -f; /sbin/ip -4 route ls"}" ]
	CONFDIR=/etc/shorewall6
	VARDIR=/var/lib/shorewall6
    fi
fi

[ -f ${CONFDIR}/vardir ] && . ${CONFDIR}/vardir

#
# Interfaces to monitor -- you may use shell variables from your params file
#
IF1=eth0
IF2=eth1
#
# Sites to Ping. Must depend only on routes in the 'main' routing table. If not specified,
# the interface is assumed to be managed by dhcpcd and the script uses the gateway address
# from /var/lib/dhcpcd/dhcpcd-${IFx}.info
#
TARGET1=xxx.xxx.xxx.xxx
TARGET2=yyy.yyy.yyy.yyy
#
# How often to ping
#
PING_INTERVAL=5
#
# Value for ping's -W option
#
PING_TIMEOUT=2
#
# This many successive pings must succeed for the interface to be marked up when it is down
#
UP_COUNT=5
#
# This many successive pings must fail for the interface to be marked down when it is up
#
DOWN_COUNT=2
#################################################################################################
# Variables private to the script
#################################################################################################
up=0
down=1

if1_state=$up
if2_state=$up

last_if1_ping=$up
last_if2_ping=$up

state_changed=

current_if1_ping=
current_if2_ping=

count1=0
count2=0

[ $FAMILY -eq 4 ] && ping=ping || ping=ping6
#################################################################################################
# Determine the GATEWAY of a DHCP interface
#################################################################################################
get_target() {

    local GATEWAYS
    GATEWAYS=

    if [ -f /var/lib/dhcpcd/dhcpcd-${1}.info ]; then
	eval $(grep ^GATEWAYS=  /var/lib/dhcpcd/dhcpcd-${1}.info 2> /dev/null)
	[ -n "$GATEWAYS" ] && GATEWAYS=${GATEWAYS%,*} && echo $GATEWAYS
    fi
}
#
# Script starts here
#
rm -f $VARDIR/${IF1}.status
rm -f $VARDIR/${IF2}.status

while : ; do
    target=$TARGET1

    [ -n "$target" ] || target=$(get_target $IF1)

    if [ -n "$target" ]; then
	$ping -n -W $PING_TIMEOUT -I $IF1 -c 1 $target > /dev/null 2>&1 && current_if1_ping=0 || current_if1_ping=1
    else
	current_if1_ping=1
    fi

    if [ $current_if1_ping -ne $last_if1_ping ]; then
	last_if1_ping=$current_if1_ping
	count1=1
    elif [ $current_if1_ping -ne $if1_state ]; then
	count1=$(($count1 + 1 ))
    fi

    case $if1_state in
	0)
	    #
	    # Interface is currently up
	    #
	    if [ $count1 -ge $DOWN_COUNT ]; then
		state_changed=Yes
		count1=0
		echo "$IF1 is Down!"
		if1_state=1
	    fi
	    ;;
	1)
	    #
	    # Interface is currently down
	    #
	    if [ $count1 -ge $UP_COUNT ]; then
		state_changed=Yes
		count1=0
		echo "$IF1 is Up!"
		if1_state=0
	    fi
	    ;;
    esac

    target=$TARGET2

    [ -n "$target" ] || target=$(get_target $IF2)

    if [ -n "$target" ]; then
	$ping -n -W $PING_TIMEOUT -I $IF2 -c 1 $target > /dev/null 2>&1 && current_if2_ping=0 || current_if2_ping=1
    else
	current_if2_ping=1
    fi

    if [ $current_if2_ping -ne $last_if2_ping ]; then
	last_if2_ping=$current_if2_ping
	count2=1
    elif [ $current_if2_ping -ne $if2_state ]; then
	count2=$(($count2 + 1 ))
    fi

    case $if2_state in
	0)
	    #
	    # Interface is currently up
	    #
	    if [ $count2 -ge $DOWN_COUNT ]; then
		state_changed=Yes
		count2=0
		echo "$IF2 is Down!"
		if2_state=1
	    fi
	    ;;
	1)
	    #
	    # Interface is currently down
	    #
	    if [ $count2 -ge $UP_COUNT ]; then
		state_changed=Yes
		count2=0
		echo "$IF2 is Up!"
		if2_state=0
	    fi
	    ;;
    esac

    if [ -n "$state_changed" ]; then
	#
	# One of the interfaces changed state -- restart Shorewall
	#
	echo $if1_state > $VARDIR/${IF1}.status
	echo $if2_state > $VARDIR/${IF2}.status
	eval $COMMAND
	state_changed=
    fi

    sleep $PING_INTERVAL
done