Subversion Repositories ALCASAR

Compare Revisions

Ignore whitespace Rev 1067 → Rev 1068

/scripts/sbin/alcasar-load_balancing.sh
1,78 → 1,371
#!/bin/bash
# $Id$
 
# alcasar-load_balancing.sh
# by BOUIJOUX Franck (3abTux) <3abtux@free.fr>
# This script is distributed under the Gnu General Public License (GPL)
# Generic Load balancer for multiple WAN links - version 1.1 (04 Feb 2011)
# (c) 2011 Pau Oliva Fora - http://pof.eslack.org
#
# Licensed under GPLv3 - for full terms see:
# http://www.gnu.org/licenses/gpl-3.0.html
#
# Adapted and debugged (adr et ping -S) by ALCASAR Team (3abtux@alcasar.net)
# (c) 2013 3abtux - http://www.alcasar.net
#
# Specify each WAN link in a separate column, example:
# In this example we have 3 wan links (vlanXXX interfaces) attached to a single
# physical interface because we use a vlan-enabled switch between the balancer
# machine and the ADSL routers we want to balance. The weight parameter should
# be kept to a low integer, in this case the ADSL line connected to vlan101 and
# vlan102 is 4Mbps and the ADSL line connected to vlan100 is 8Mbps (twice fast)
# so the WEIGHT value in vlan100 is 2 because it is two times faster.
#
#
# Modified by ALCASAR team :
 
# Équilibrage de charge sur 2 ou plusieurs liens internet (en cours d'expérimentation)
# Définion des poids des routes : même poids --> alternance des connexions. Sinon le poids le plus faible est prioritaire
# Load balancing with 2 or more Internet links (experimental)
# Weitght of routes : same --> switch between link. The lowest weight gain the priority.
 
WEIGHT1=1
WEIGHT2=1
#WEIGHT3=3
###############################
# MAIN PARAMETERs Configuration
###############################
# enable link failover watchdog? set to "yes" or "no".
WATCHDOG="yes"
SLEEP="30"
 
# Définition des interfaces :
DEV1=${1-eth0} # defaut eth0
DEV2=${2-eth0:1} # defaut eth0 mais peut être autre chose :-)
#DEV3=${3-eth0:2} # defaut eth0 mais peut être autre chose :-)
# space separated list of public IPs to ping in watchdog mode
# set this to some public ip addresses pingable and always on.
TESTIPS="8.8.8.8 192.0.32.10"
 
# Trouver les adresses pour chaque interface
IP1=`ifconfig $DEV1 | grep inet | awk '{ print $2 }' | awk -F: '{ print $2 }'`
IP2=`ifconfig $DEV2 | grep inet | awk '{ print $2 }' | awk -F: '{ print $2 }'`
#IP3=`ifconfig $DEV3 | grep inet | awk '{ print $2 }' | awk -F: '{ print $2 }'`
# set to 1 when testing, set to 0 when happy with the results
VERBOSE=1
 
# Trouver les passerelles pour chaque interface { ne fonctionne pas bien avec une seule interface } --> forcer les passerelles !
#GW1=`route -n | grep $DEV1 | grep '^0.0.0.0' | awk '{ print $2 }'`
#GW2=`route -n | grep $DEV2 | grep '^0.0.0.0' | awk '{ print $2 }'`
GW1=192.168.1.1
GW2=192.168.1.6
#GW3=192.168.1.6
# CONFIGURATION ENDS HERE
###############################
 
echo "Acces internet depuis $DEV1: IP=$IP1 par la GW=$GW1"
echo " et depuis $DEV2: IP=$IP2 par la GW=$GW2"
#echo " et depuis $DEV3: IP=$IP3 par la GW=$GW3"
 
# Mise en place des routes
 
# Tester si les tables existent sinon les créer
if [ -z "`cat /etc/iproute2/rt_tables | grep '^252'`" ] ; then
echo "252 rt_dev1" >> /etc/iproute2/rt_tables
if [ $(whoami) != "root" ]; then
echo "You must be root to run this!" ; echo ; exit 1
fi
 
if [ -z "`cat /etc/iproute2/rt_tables | grep '^251'`" ] ; then
echo "251 rt_dev2" >> /etc/iproute2/rt_tables
fi
#if [ -z "`cat /etc/iproute2/rt_tables | grep '^250'`" ] ; then
# echo "250 rt_dev3" >> /etc/iproute2/rt_tables
#fi
# Adapter for ALCASAR project
CONF_FILE="/usr/local/etc/alcasar.conf"
 
# Virtual interfaces creating
function create_eth () {
routecmd="ip route replace default scope global"
NBIFACE=`grep "^WAN" $CONF_FILE | wc -l` # Nbre interfaces virtuelles
i=0
while [ $i -le $NBIFACE ]
do
INT="WAN$i"
echo $INT
ACTIVE=`grep "$INT=" $CONF_FILE | awk -F'"' '{ print $2 }' | awk -F, '{ print $1}'` # Active
WT=`grep "$INT=" $CONF_FILE | awk -F'"' '{ print $2 }' | awk -F, '{ print $5}'` # WEIGHT
WT=${WT:-1}
IP=`grep "$INT=" $CONF_FILE | awk -F'"' '{ print $2 }' | awk -F, '{ print $3}' | cut -d"/" -f1` # @IP
 
# Tables de routage
ip route add default via $GW1 table rt_dev1
ip route add default via $GW2 table rt_dev2
#ip route add default via $GW3 table rt_dev3
if [ $i -ne 0 ]; then
[ -e /etc/sysconfig/network-scripts/ifcfg-eth0:$i ] && ifdown eth0:$i && rm -f /etc/sysconfig/network-scripts/ifcfg-eth0:$i
IFACE=`grep "$INT=" $CONF_FILE | awk -F'"' '{ print $2 }' | awk -F, '{ print $2}'` # IFACE
IP_NET=`grep "^$INT=" $CONF_FILE | awk -F'"' '{print $2}' | awk -F, '{ print $3}'` # IP
NET="`ipcalc -n $IP_NET | cut -d"=" -f2`/`ipcalc -p $IP_NET|cut -d"=" -f2`"
GW=`grep "$INT=" $CONF_FILE | awk -F'"' '{ print $2 }' | awk -F, '{ print $4}'` # @GW
MTU=`grep "$INT=" $CONF_FILE | awk -F'"' '{ print $2 }' | awk -F, '{ print $6}'` # MTU
 
# Création des règles
ip rule add from $IP1 table rt_dev1
ip rule add from $IP2 table rt_dev2
#ip rule add from $IP3 table rt_dev3
# Config eth0:$i (Internet)
cat <<EOF > /etc/sysconfig/network-scripts/ifcfg-eth0:$i
DEVICE=$IFACE
BOOTPROTO=static
IPADDR=`echo $IP | cut -d"/" -f1`
NETMASK=`ipcalc -m $IP_NET | cut -d= -f2`
NETWORK=`ipcalc -n $IP_NET | cut -d= -f2`
MTU=$MTU
ONBOOT=yes
NOZEROCONF=yes
MII_NOT_SUPPORTED=yes
IPV6INIT=no
IPV6TO4INIT=no
ACCOUNTING=no
USERCTL=no
EOF
echo "ifup eth0:$i"
ifup eth0:$i
NET="`ipcalc -n $IP_NET | cut -d"=" -f2`/`ipcalc -p $IP_NET|cut -d"=" -f2`"
else
IFACE="eth0"
IP_NET=`grep "^PUBLIC_IP=" $CONF_FILE | awk -F'=' '{print $2}'` # IP/MSK
IP=`grep "^PUBLIC_IP=" $CONF_FILE | awk -F= '{ print $2 }' | cut -d"/" -f1` # @IP
GW=`grep "^GW=" $CONF_FILE | awk -F= '{print $2}'` # @GW
# MTU=`grep "^PUBLIC_MTU=" $CONF_FILE | awk -F= '{print $2}'` # MTU
fi # End
 
# Effacer la route par défaut existante
ip route del default
if [ ! -z "`ip route show table main | grep 'nexthop'`" ] ; then
ip route del default scope global
fi
NET="`ipcalc -n $IP_NET | cut -d"=" -f2`/`ipcalc -p $IP_NET|cut -d"=" -f2`"
if [ "$PARAM" == "add" ]; then
set -x
table=$(($i + 1))
ip route ${PARAM} ${NET} dev ${IFACE} src ${IP} table $table
ip route ${PARAM} default via ${GW} table $table
ip rule ${PARAM} from ${IP} table $table
set +x
fi
echo " Iface: ${IFACE}"
echo " IP: ${IP}"
echo " IP_NET: ${IP_NET}"
echo " NET: ${NET}"
echo " GW: ${GW}"
echo " Weight: ${WT}"
echo " MTU : ${MTU}"
echo
routecmd="${routecmd} nexthop via ${GW} dev ${IFACE} weight ${WT}"
i=$(($i + 1))
done # End While
 
# Alterne les liens basés sur chaque route
ip route add default scope global nexthop via $GW1 dev $DEV1 weight $WEIGHT1 \
nexthop via $GW2 dev $DEV2 weight $WEIGHT2
# nexthop via $GW3 dev $DEV3 weight $WEIGHT3
if [ "$PARAM" == "add" ]; then
echo "[] Balanced routing:"
# suppress default route
ip route del default scope global
set -x
${routecmd}
set +x
echo
fi
} # end create_eth
 
###########################
# Fonction virtual Interfaces deleting
###########################
delete_eth () {
IFACE_COUNT=`ls -l /etc/sysconfig/network-scripts/ifcfg-eth0:* | wc -l`
echo $IFACE_COUNT
while [ $IFACE_COUNT -ne 0 ]
do
i=$IFACE_COUNT
echo "ifdown eth0:$i"
ifdown eth0:$i
rm -f /etc/sysconfig/network-scripts/ifcfg-eth0:$i
IFACE_COUNT=$(($IFACE_COUNT - 1))
done
ip route del default scope global
# ip route add default gw 192.168.1.1
}
 
# Purge le cache
ip route flush cache
# do not modify below this line unless you know what you're doing :)
function getvalue() {
index=$1
VAR=$2
 
# Fin de alcasar-load_balancing.sh
n=1
for f in ${VAR} ; do
if [ "${n}" == "${index}" ]; then
echo "$f"
break
fi
n=$(($n++))
done
}
 
######################
# Fonction de FailOver
######################
function failover () {
 
echo "[] Watchdog started"
# 0 == all links ok, 1 == some link down
STATE=0
DOWNCOUNT_BAK=0
DOWN_BAK=""
NBIFACE=`grep "^WAN" $CONF_FILE | wc -l` # Nbre interfaces virtuelles
echo "Nombre interfaces = "$NBIFACE
WANIFACE[0]="eth0" # eth0 par défaut
c=0
while [ $c -le $NBIFACE ]; do
ITH=(`grep "WAN$c=" $CONF_FILE | awk -F'"' '{ print $2 }' | awk -F, '{ print $2}'`) # IFACE
echo $ITH
WANIFACE="${WANIFACE} $ITH"
echo $WANIFACE
c=$(($c + 1))
done
echo "Liste des interfaces : "${WANIFACE[*]}
# Failover test
while : ; do
if [ $VERBOSE -eq 1 ]; then
echo "[] Sleeping, state=$STATE"
fi
sleep $SLEEP
IFINDEX=1
DOWN="" # liste des interfaces down
DOWNCOUNT=0 # nombre d'interface down
for iface in $WANIFACE ; do
COUNT=0 # compteur de test
FAIL=0 # Nombre de fois down
# Recup de l'adresse IP dynamiquement A tester avec le tableau ... ip=${ETH[$i:2]} basé sur iface=${ETH[$i:1]}
IP=`ifconfig $iface |grep "inet adr" |cut -f 2 -d ":" |awk '{print $1}'`
if [ $i -ne 0 ]; then
GW=`grep "$iface," $CONF_FILE | awk -F'"' '{ print $2 }' | awk -F, '{ print $4}'` # @GW
WT=`grep "$iface," $CONF_FILE | awk -F'"' '{ print $2 }' | awk -F, '{ print $5}'` # @WT
else
GW=`grep "^GW=" $CONF_FILE | awk -F= '{print $2}'` # @GW
fi
for TESTIP in $TESTIPS ; do
COUNT=$(($COUNT + 1))
ping -W 3 -I $IP -c 1 $TESTIP > /dev/null 2>&1
# ping -W 3 -I $IP -c 1 $TESTIP
# Si ping de la première adresse --> ok --> stop du test pour l'interface testée
if [ $? -eq 0 ]; then
break
else
# sinon on compte une erreur
FAIL=$(($FAIL + 1))
fi
done # End of test sur un serveur Internet
# Affichage du nombre de down
echo "FAIL=$FAIL"
# Si nombre de fois down = nombre de tests --> Iface down --> log dans fichier log avec l'heure
if [ $FAIL -eq $COUNT ]; then
echo "`date +%F-%Hh%mm%Ss` : [WARN] $iface is down!"
# Si etat différent de 1 (déjà tombé) --> changement de l'état général en default
if [ $STATE -ne 1 ]; then
echo "Switching state $STATE -> 1"
STATE=1
fi
# Rajout de l'iface dans la liste des interfaces down
DOWN="${DOWN} $IFINDEX"
echo "DOWN=$DOWN"
# Nombre d'interface down
DOWNCOUNT=$(($DOWNCOUNT + 1))
echo "DOWNCOUNT=$DOWNCOUNT"
fi
IFINDEX=$(($IFINDEX + 1))
echo "IFINDEX =$IFINDEX"
done # End Test Interface in WANIFACE
 
# 0 Passerelle down et état précédent différent (retour à la normale)) --> mise à la normale des passerelles
# if [ $DOWNCOUNT -eq 0 ] && [ $DOWNCOUNT -ne $DOWNCOUNT_BAK ]; then
if [ $DOWNCOUNT -eq 0 ] ; then
if [ $STATE -eq 1 ]; then
echo
echo "[] All links up and running :)"
set -x
${routecmd}
set +x
# Changement de l'état en normal
STATE=0
echo "Switching state 1 -> 0"
fi # End retour etat normal
# if no interface is down, go to the next cycle
continue
# cas ou au moins une passerelle down mais état identique au précédent Test --> rien à changer
else
if [ "$DOWN_BAK" == "$DOWN" ]; then
echo "DOWN_BAK == DOWN = $DOWN"
continue # --> état identique test precedent --> boucle suivante
# cas ou au moins une passerelle down mais état différent de test précédent --> remplacement par nouvelle règle
else
cmd="ip route replace default scope global"
IFINDEX=1
suffix=""
# Pour chaque interface --> traitement et application de la règle de routage
for iface in $WANIFACE ; do
echo "-------------------------"
echo "iface=$iface"
echo "Index = " $IFINDEX
FAILIF=0
# Pour chaque interface down -->
echo "Interfaces DOWN = $DOWN"
for lnkdwn in $DOWN ; do
echo "LINKDOWN = "$lnkdown
if [ $lnkdwn -eq $IFINDEX ]; then
FAILIF=1
break
else
continue
fi
done # End linkdown in DOWN
# Interface en etat normal --> rajout de la règle en mode nexthop
if [ $FAILIF -eq 0 ]; then
IP=`ifconfig $iface |grep "inet adr" |cut -f 2 -d ":" |awk '{print $1}'`
if [ $iface != "eth0" ]; then
GW=`grep "$iface," $CONF_FILE | awk -F'"' '{ print $2 }' | awk -F, '{ print $4}'` # @GW
WT=`grep "$iface," $CONF_FILE | awk -F'"' '{ print $2 }' | awk -F, '{ print $5}'` # @GW
else
GW=`grep "^GW=" $CONF_FILE | awk -F= '{print $2}'` # @GW
fi
echo "GW=$GW"
echo "WT=$WT"
echo "suffix=$sufix"
suffix="${suffix} nexthop via ${GW} dev ${iface} weight ${WT:-1}"
fi # End interface = noFAIL
IFINDEX=$(($IFINDEX + 1))
done # End iface IN WANIFACE
# Commande globale
cmd="ip route replace default scope global $suffix"
if [ $VERBOSE -eq 1 ]; then
set -x
# echo "Avec commentaire : " ${cmd}
${cmd}
set +x
echo
else
${cmd} 2>/dev/null
echo ${cmd}
fi # end Application de la commande de routage globale
fi #
DOWN_BAK=$DOWN # Enregistrement de l'etat
fi # End
done
} # End of Failover
 
 
#################
# Main
#################
 
echo "[] Load balancer for multiple WAN interfaces - v2.1"
echo "[] (c) 2011 Pau Oliva Fora <pof> @eslack.org"
echo "[] (c) 2013 3abtux ALCASAR <3abtux> @alcasar.net"
echo
 
case $1 in
create)
create_eth
;;
delete)
delete_eth
;;
fail)
failover
;;
start)
PARAM="add"
create_eth
if [ $WATCHDOG != "yes" ]; then
exit 0
fi
ip route flush cache
# RETVAL=$?
# echo
# [ $RETVAL -eq 0 ] && touch /var/lock/subsys/alcasar-load_balancing
touch /var/lock/subsys/alcasar-load_balancing
failover
;;
stop)
PARAM="del"
prog="alcasar-load_balancing.sh"
gprintf $"Shutting down $prog: "
killproc $prog
RETVAL=$?
echo
[ $RETVAL -eq 0 ] && rm -f /var/lock/subsys/alcasar-load_balancing
return $RETVAL
ip route
;;
*)
echo "Usage: $0 [start|stop|create|delete]" ; echo ; exit 1
;;
esac
 
exit 0