Subversion Repositories ALCASAR

Rev

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