Subversion Repositories ALCASAR

Rev

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