Subversion Repositories ALCASAR

Rev

Rev 1078 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log

Rev Author Line No. Line
219 jeremy 1
#!/bin/bash
328 franck 2
# $Id: alcasar-load_balancing.sh 1469 2014-10-30 21:58:47Z richard $
221 franck 3
 
1068 franck 4
# Generic Load balancer for multiple WAN links - version 1.1 (04 Feb 2011)
5
# (c) 2011 Pau Oliva Fora - http://pof.eslack.org
6
#
7
# Licensed under GPLv3 - for full terms see:
8
# http://www.gnu.org/licenses/gpl-3.0.html
9
#
10
# Adapted and debugged (adr et ping -S) by ALCASAR Team (3abtux@alcasar.net)
11
# (c) 2013  3abtux - http://www.alcasar.net
12
#
13
# Specify each WAN link in a separate column, example:
14
# In this example we have 3 wan links (vlanXXX interfaces) attached to a single
15
# physical interface because we use a vlan-enabled switch between the balancer
16
# machine and the ADSL routers we want to balance. The weight parameter should
1078 franck 17
# be kept to a low integer.
1068 franck 18
#
19
#
20
# Modified by ALCASAR team :
219 jeremy 21
 
672 richard 22
 
1078 franck 23
prog="alcasar-load_balancing.sh"
24
pidfile="/var/run/alcasar-load_balancing.pid"
25
 
1068 franck 26
###############################
27
# MAIN PARAMETERs Configuration
28
###############################
219 jeremy 29
 
1078 franck 30
DIR_ETC="/usr/local/etc"
31
CONF_FILE="$DIR_ETC/alcasar.conf"
32
MULTIWAN=`grep MULTIWAN= $CONF_FILE|cut -d"=" -f2`
33
MULTIWAN=${MULTIWAN:=off}
34
FAILOVER=`grep FAILOVER= $CONF_FILE|cut -d"=" -f2`
35
FAILOVER=${FAILOVER:=30}
1469 richard 36
INTIF=`grep ^INTIF= $CONF_FILE|cut -d"=" -f2`				# INTernal InterFace
1078 franck 37
 
38
 
1068 franck 39
# space separated list of public IPs to ping in watchdog mode
40
# set this to some public ip addresses pingable and always on.
41
TESTIPS="8.8.8.8 192.0.32.10"
219 jeremy 42
 
1068 franck 43
# set to 1 when testing, set to 0 when happy with the results
1078 franck 44
VERBOSE=0
219 jeremy 45
 
1068 franck 46
# CONFIGURATION ENDS HERE
47
###############################
219 jeremy 48
 
49
 
1068 franck 50
if [ $(whoami) != "root" ]; then
51
        echo "You must be root to run this!" ; echo ; exit 1
219 jeremy 52
fi
53
 
1068 franck 54
# Adapter for ALCASAR project
55
CONF_FILE="/usr/local/etc/alcasar.conf"
219 jeremy 56
 
1068 franck 57
# Virtual interfaces creating
58
function create_eth () {
59
	routecmd="ip route replace default scope global"
60
	NBIFACE=`grep "^WAN" $CONF_FILE | wc -l`	# Nbre interfaces virtuelles
61
	i=0
62
	while [ $i -le $NBIFACE ]
63
	do
64
		INT="WAN$i"
65
		echo $INT
66
		ACTIVE=`grep "$INT=" $CONF_FILE | awk -F'"' '{ print $2 }' | awk -F, '{ print $1}'`	# Active
67
		WT=`grep "$INT=" $CONF_FILE | awk -F'"' '{ print $2 }' | awk -F, '{ print $5}'`		# WEIGHT
68
		WT=${WT:-1}
69
		IP=`grep "$INT=" $CONF_FILE | awk -F'"' '{ print $2 }' | awk -F, '{ print $3}' | cut -d"/" -f1`	# @IP
937 franck 70
 
1068 franck 71
		if [ $i -ne 0 ]; then
1469 richard 72
			[ -e /etc/sysconfig/network-scripts/ifcfg-$INTIF:$i ] && ifdown $INTIF:$i && rm -f /etc/sysconfig/network-scripts/ifcfg-$INTIF:$i
1068 franck 73
			IFACE=`grep "$INT=" $CONF_FILE | awk -F'"' '{ print $2 }' | awk -F, '{ print $2}'`	# IFACE
74
			IP_NET=`grep "^$INT=" $CONF_FILE | awk -F'"' '{print $2}' | awk -F, '{ print $3}'`	# IP
75
			NET="`ipcalc -n $IP_NET | cut -d"=" -f2`/`ipcalc -p $IP_NET|cut -d"=" -f2`"
76
			GW=`grep "$INT=" $CONF_FILE | awk -F'"' '{ print $2 }' | awk -F, '{ print $4}'`		# @GW
77
			MTU=`grep "$INT=" $CONF_FILE | awk -F'"' '{ print $2 }' | awk -F, '{ print $6}'`	# MTU
219 jeremy 78
 
1469 richard 79
			# Config $INTIF:$i (Internet)
80
			cat <<EOF > /etc/sysconfig/network-scripts/ifcfg-$INTIF:$i
1068 franck 81
DEVICE=$IFACE
82
BOOTPROTO=static
83
IPADDR=`echo $IP | cut -d"/" -f1`
84
NETMASK=`ipcalc -m $IP_NET | cut -d= -f2`
85
NETWORK=`ipcalc -n $IP_NET | cut -d= -f2`
86
MTU=$MTU
87
ONBOOT=yes
88
NOZEROCONF=yes
89
MII_NOT_SUPPORTED=yes
90
IPV6INIT=no
91
IPV6TO4INIT=no
92
ACCOUNTING=no
93
USERCTL=no
94
EOF
1469 richard 95
			echo "ifup $INTIF:$i"
96
			ifup $INTIF:$i
1068 franck 97
			NET="`ipcalc -n $IP_NET | cut -d"=" -f2`/`ipcalc -p $IP_NET|cut -d"=" -f2`"
98
		else
1469 richard 99
			IFACE="$INTIF"
1068 franck 100
			IP_NET=`grep "^PUBLIC_IP=" $CONF_FILE | awk -F'=' '{print $2}'`			# IP/MSK
101
			IP=`grep "^PUBLIC_IP=" $CONF_FILE | awk -F= '{ print $2 }' | cut -d"/" -f1`	# @IP
102
			GW=`grep "^GW=" $CONF_FILE | awk -F= '{print $2}'`				# @GW
103
#			MTU=`grep "^PUBLIC_MTU=" $CONF_FILE | awk -F= '{print $2}'`			# MTU
104
		fi # End
219 jeremy 105
 
1068 franck 106
		NET="`ipcalc -n $IP_NET | cut -d"=" -f2`/`ipcalc -p $IP_NET|cut -d"=" -f2`"
107
		if [ "$PARAM" == "add" ]; then	
108
			set -x
109
			table=$(($i + 1))
110
			ip route ${PARAM} ${NET} dev ${IFACE} src ${IP} table $table
111
			ip route ${PARAM} default via ${GW} table $table
112
			ip rule ${PARAM} from ${IP} table $table
113
			set +x
114
		fi
115
		echo "	Iface: ${IFACE}"
116
		echo "	IP: ${IP}"
117
		echo "	IP_NET: ${IP_NET}"
118
		echo "	NET: ${NET}"
119
		echo "	GW: ${GW}"
120
		echo "	Weight: ${WT}"
121
		echo "	MTU : ${MTU}"
122
		echo
123
		routecmd="${routecmd} nexthop via ${GW} dev ${IFACE} weight ${WT}"
124
		i=$(($i + 1))
125
	done # End While
219 jeremy 126
 
1068 franck 127
	if [ "$PARAM" == "add" ]; then	
128
		echo "[] Balanced routing:"
129
		# suppress default route
130
		ip route del default scope global
131
		set -x
132
		${routecmd}
133
		set +x
134
		echo
135
	fi
136
 
137
} # end create_eth
219 jeremy 138
 
1068 franck 139
###########################
140
# Fonction virtual Interfaces deleting
141
###########################
142
delete_eth () {
1469 richard 143
	IFACE_COUNT=`ls -l /etc/sysconfig/network-scripts/ifcfg-$INTIF:* | wc -l`
1068 franck 144
	echo $IFACE_COUNT
145
	while [ $IFACE_COUNT -ne 0 ]
146
	do
147
		i=$IFACE_COUNT	
1469 richard 148
		echo "ifdown $INTIF:$i"
149
		ifdown $INTIF:$i
150
		rm -f /etc/sysconfig/network-scripts/ifcfg-$INTIF:$i
1068 franck 151
		IFACE_COUNT=$(($IFACE_COUNT - 1))
152
	done
153
	ip route del default scope global
154
#	ip route add default gw 192.168.1.1
155
}
156
 
219 jeremy 157
 
1068 franck 158
# do not modify below this line unless you know what you're doing :)
159
function getvalue() {
160
        index=$1
161
        VAR=$2
219 jeremy 162
 
1068 franck 163
        n=1
164
        for f in ${VAR} ; do
165
                if [ "${n}" == "${index}" ]; then
166
                        echo "$f"
167
                        break
168
                fi
169
                n=$(($n++))
170
        done
171
}
172
 
173
######################
174
# Fonction de FailOver
175
######################
176
function failover () {
177
 
178
	echo "[] Watchdog started"
179
	# 0 == all links ok, 1 == some link down
180
	STATE=0
181
 
182
	DOWNCOUNT_BAK=0
183
	DOWN_BAK=""
184
	NBIFACE=`grep "^WAN" $CONF_FILE | wc -l`	# Nbre interfaces virtuelles
185
	echo "Nombre interfaces =  "$NBIFACE
1469 richard 186
	WANIFACE[0]="$INTIF"
1068 franck 187
	c=0
188
	while [ $c -le $NBIFACE ]; do
189
		ITH=(`grep "WAN$c=" $CONF_FILE | awk -F'"' '{ print $2 }' | awk -F, '{ print $2}'`)	# IFACE
190
		echo $ITH
191
		WANIFACE="${WANIFACE} $ITH"
192
		echo $WANIFACE
193
		c=$(($c + 1))
194
	done
195
	echo "Liste des interfaces : "${WANIFACE[*]}
196
	# Failover test
197
	while : ; do
198
 
199
		if [ $VERBOSE -eq 1 ]; then
200
			echo "[] Sleeping, state=$STATE"
201
		fi
1078 franck 202
		sleep $FAILOVER
1068 franck 203
 
204
		IFINDEX=1
205
		DOWN=""			# liste des interfaces down
206
		DOWNCOUNT=0		# nombre d'interface down
207
		for iface in $WANIFACE ; do
208
			COUNT=0		# compteur de test
209
			FAIL=0		# Nombre de fois down
1469 richard 210
			# Recup de l'adresse IP dynamiquement
1068 franck 211
			IP=`ifconfig $iface |grep "inet adr" |cut -f 2 -d ":" |awk '{print $1}'`
212
			if [ $i -ne 0 ]; then
213
				GW=`grep "$iface," $CONF_FILE | awk -F'"' '{ print $2 }' | awk -F, '{ print $4}'`		# @GW
214
				WT=`grep "$iface," $CONF_FILE | awk -F'"' '{ print $2 }' | awk -F, '{ print $5}'`		# @WT
215
			else
216
				GW=`grep "^GW=" $CONF_FILE | awk -F= '{print $2}'`			# @GW
217
			fi	
218
			for TESTIP in $TESTIPS ; do
219
				COUNT=$(($COUNT + 1))
220
				ping -W 3 -I $IP -c 1 $TESTIP > /dev/null 2>&1
221
#				ping -W 3 -I $IP -c 1 $TESTIP
222
				# Si ping de la première adresse --> ok  --> stop du test pour l'interface testée
223
				if [ $? -eq 0 ]; then
224
					break
225
				else 
226
					# sinon on compte une erreur
227
					FAIL=$(($FAIL + 1))
228
				fi
229
			done # End of test sur un serveur Internet
230
			# Affichage du nombre de down
231
			echo "FAIL=$FAIL"
232
			# Si nombre de fois down = nombre de tests -->  Iface down --> log dans fichier log avec l'heure
233
			if [ $FAIL -eq $COUNT ]; then
234
				echo "`date +%F-%Hh%mm%Ss` : [WARN] $iface is down!"
235
				# Si etat différent de 1 (déjà tombé) --> changement de l'état général en default
236
				if [ $STATE -ne 1 ]; then
237
					echo "Switching state $STATE -> 1"
238
					STATE=1
239
				fi
240
				# Rajout de l'iface dans la liste des interfaces down
241
				DOWN="${DOWN} $IFINDEX"
242
				echo "DOWN=$DOWN"
243
				# Nombre d'interface down
244
				DOWNCOUNT=$(($DOWNCOUNT + 1))
245
				echo "DOWNCOUNT=$DOWNCOUNT"
246
			fi
247
			IFINDEX=$(($IFINDEX + 1))
248
			echo "IFINDEX =$IFINDEX"
249
		done # End Test Interface in WANIFACE
250
 
251
		#  0 Passerelle down et état précédent différent (retour à la normale)) --> mise à la normale des passerelles 
252
#		if [ $DOWNCOUNT -eq 0 ] && [ $DOWNCOUNT -ne $DOWNCOUNT_BAK ]; then
253
		if [ $DOWNCOUNT -eq 0 ] ; then
254
			if [ $STATE -eq 1 ]; then
255
				echo
256
				echo "[] All links up and running :)"
257
				set -x
258
				${routecmd}
259
				set +x
260
				# Changement de l'état en normal
261
				STATE=0
262
				echo "Switching state 1 -> 0"
263
			fi # End retour etat normal
264
			# if no interface is down, go to the next cycle
265
			continue
266
		# cas ou au moins une passerelle down mais état identique au précédent Test --> rien à changer
267
		else
268
			if [ "$DOWN_BAK" == "$DOWN" ]; then
269
			echo "DOWN_BAK == DOWN = $DOWN"
270
				continue	# --> état identique test precedent --> boucle suivante
271
		# cas ou au moins une passerelle down mais état différent de test précédent --> remplacement par nouvelle règle
272
			else
273
				cmd="ip route replace default scope global"
274
				IFINDEX=1
275
				suffix=""
276
				# Pour chaque interface --> traitement et application de la règle de routage
277
				for iface in $WANIFACE ; do
278
					echo "-------------------------"
279
					echo "iface=$iface"
280
					echo "Index = " $IFINDEX
281
					FAILIF=0
282
					# Pour chaque interface down --> 
283
					echo "Interfaces DOWN = $DOWN"
284
					for lnkdwn in $DOWN ; do
285
						echo "LINKDOWN = "$lnkdown
286
						if [ $lnkdwn -eq $IFINDEX ]; then
287
							FAILIF=1
288
							break			
289
						else
290
							continue
291
						fi
292
					done # End linkdown in DOWN
293
					# Interface en etat normal --> rajout de la règle en mode nexthop
294
					if [ $FAILIF -eq 0 ]; then
295
						IP=`ifconfig $iface |grep "inet adr" |cut -f 2 -d ":" |awk '{print $1}'`
1469 richard 296
						if [ $iface != "$INTIF" ]; then
1068 franck 297
							GW=`grep "$iface," $CONF_FILE | awk -F'"' '{ print $2 }' | awk -F, '{ print $4}'`		# @GW
298
							WT=`grep "$iface," $CONF_FILE | awk -F'"' '{ print $2 }' | awk -F, '{ print $5}'`		# @GW
299
						else
300
							GW=`grep "^GW=" $CONF_FILE | awk -F= '{print $2}'`			# @GW
301
						fi	
302
						echo "GW=$GW"
303
						echo "WT=$WT"
304
						echo "suffix=$sufix"
305
						suffix="${suffix} nexthop via ${GW} dev ${iface} weight ${WT:-1}"
306
					fi # End interface = noFAIL
307
					IFINDEX=$(($IFINDEX + 1))
308
				done # End  iface IN WANIFACE
309
				# Commande globale
310
				cmd="ip route replace default scope global $suffix"
311
 
312
				if [ $VERBOSE -eq 1 ]; then
313
					set -x
314
			#		echo "Avec commentaire : " ${cmd}
315
					${cmd}
316
					set +x
317
					echo
318
				else
319
					${cmd} 2>/dev/null
320
					echo ${cmd}
321
				fi # end Application de la commande de routage globale
322
			fi #
323
			DOWN_BAK=$DOWN	# Enregistrement de l'etat
324
		fi # End 
325
	done
326
} # End of Failover
327
 
328
 
329
#################
330
# Main
331
#################
332
 
333
echo "[] Load balancer for multiple WAN interfaces - v2.1"
334
echo "[] (c) 2011 Pau Oliva Fora <pof> @eslack.org"
335
echo "[] (c) 2013 3abtux ALCASAR  <3abtux> @alcasar.net"
336
echo
337
 
338
case $1 in
339
	create) 
340
		create_eth  		
341
	;;
342
	delete) 
343
		delete_eth  		
344
	;;
345
	start) 
1078 franck 346
                if [ "$MULTIWAN" != "on" ] && [ "$MULTIWAN" != "On" ]; then 
347
		    echo "The MultiGateway is not activated !"
348
		    exit 0
1068 franck 349
		fi
1078 franck 350
                PARAM="add"
351
                create_eth
352
                ip route flush cache
353
                if [ $FAILOVER -eq 0 ]; then 
354
		      echo "The MultiWAN Mode is actived but not failover connectivity !"
355
		      exit 0
356
		fi
357
                echo "Starting down $prog: "
358
                pid=`pidof -x "alcasar-load_balancing.sh"`
359
                if [ $pid != "" ]; then
360
                        echo $pid > $pidfile
361
                fi
362
                touch /var/lock/subsys/alcasar-load_balancing
363
                failover
1068 franck 364
	;;
365
	stop) 
366
		PARAM="del"
1078 franck 367
		echo "Shutting down $prog: "
368
                if [ -f $pidfile ]; then
369
                        pid=`cat $pidfile`
370
                        kill -9 $pid
371
                else
372
                        echo "$prog is not running."
373
                        exit 1
374
                fi
375
                RETVAL=$?
376
                echo
377
                [ $RETVAL -eq 0 ] && rm -f $pidfile && rm -f /var/lock/subsys/alcasar-load_balancing
378
                echo "Delete of virtual interfaces"
379
                delete_eth
380
                echo "Network restart"
381
                service network restart 2>&1 > /dev/null
382
                ip route
383
 
1068 franck 384
	;;
1078 franck 385
	status)
386
                echo "Checking  $prog : "
387
                if [ -f $pidfile ]; then
388
                        pid=`cat $pidfile`
389
                        CHECK=`ps -p $pid --no-heading | awk {'printf $1'}`
390
                        if [ "$CHECK" = "" ]; then
391
                                echo "$prog is NOT running."
392
                        else
393
                                echo "$prog is running !"
394
                        fi
395
                else
396
                                echo "$prog is Not running."
397
                fi	
398
	;;
399
	fail) 
400
		failover 		
401
	;;
1068 franck 402
	*) 
1078 franck 403
		echo "Usage: $0 [start|stop|status|create|delete]" ; echo ; exit 1
1068 franck 404
	;;
405
esac
406
 
407
exit 0