[packages] /etc/functions.sh => /lib/functions.sh
[openwrt/svn-archive/archive.git] / net / multiwan / files / usr / bin / multiwan
1 #!/bin/sh
2
3 . /lib/functions.sh
4
5 silencer() {
6 if [ -z "$debug" -o "$debug" == "0" ]; then
7 $* > /dev/null 2>&1
8 else
9 $*
10 fi
11 }
12
13 mwnote() {
14 logger ${debug:+-s} -p 5 -t multiwan "$1"
15 }
16
17 failover() {
18 local failchk=$(query_config failchk $2)
19 local recvrychk=$(query_config recvrychk $2)
20
21 local wanid=$(query_config wanid $2)
22 local failover_to=$(uci_get_state multiwan ${2} failover_to)
23 local failover_to_wanid=$(query_config wanid $failover_to)
24
25 local existing_failover=$(iptables -n -L FW${wanid}MARK -t mangle | echo $(($(wc -l) - 2)))
26
27 add() {
28
29 wan_fail_map=$(echo $wan_fail_map | sed -e "s/${1}\[${failchk}\]//g")
30 wan_fail_map="$wan_fail_map${1}[x]"
31 wan_recovery_map=$(echo $wan_recovery_map | sed -e "s/${1}\[${recvrychk}\]//g")
32 update_cache
33
34 if [ "$existing_failover" == "2" ]; then
35 if [ "$failover_to" != "balancer" -a "$failover_to" != "fastbalancer" -a "$failover_to" != "disable" -a "$failover_to_wanid" != "$wanid" ]; then
36 iptables -I FW${wanid}MARK 2 -t mangle -j FW${failover_to_wanid}MARK
37 elif [ "$failover_to" == "balancer" ]; then
38 iptables -I FW${wanid}MARK 2 -t mangle -j LoadBalancer
39 elif [ "$failover_to" == "fastbalancer" ]; then
40 iptables -I FW${wanid}MARK 2 -t mangle -j FastBalancer
41 fi
42 fi
43 mwnote "$1 has failed and is currently offline."
44 }
45
46 del() {
47
48 wan_recovery_map=$(echo $wan_recovery_map | sed -e "s/${1}\[${recvrychk}\]//g")
49 wan_fail_map=$(echo $wan_fail_map | sed -e "s/${1}\[${failchk}\]//g")
50 update_cache
51
52 if [ "$existing_failover" == "3" ]; then
53 iptables -D FW${wanid}MARK 2 -t mangle
54 fi
55 mwnote "$1 has recovered and is back online!"
56 }
57
58 case $1 in
59 add) add $2;;
60 del) del $2;;
61 esac
62 }
63
64 fail_wan() {
65 local new_fail_count
66
67 local health_fail_retries=$(uci_get_state multiwan ${1} health_fail_retries)
68 local weight=$(uci_get_state multiwan ${1} weight)
69
70 local failchk=$(query_config failchk $1)
71 local recvrychk=$(query_config recvrychk $1)
72 wan_recovery_map=$(echo $wan_recovery_map | sed -e "s/${1}\[${recvrychk}\]//g")
73
74 if [ -z "$failchk" ]; then
75 failchk=1
76 wan_fail_map="$wan_fail_map${1}[1]"
77 fi
78
79 if [ "$failchk" != "x" ]; then
80 new_fail_count=$(($failchk + 1))
81 if [ "$new_fail_count" -lt "$health_fail_retries" ]; then
82 wan_fail_map=$(echo $wan_fail_map | sed -e "s/${1}\[${failchk}\]/$1\[${new_fail_count}\]/g")
83 else
84 failover add $1
85 refresh_dns
86 if [ "$weight" != "disable" ]; then
87 refresh_loadbalancer
88 fi
89 fi
90 fi
91 update_cache
92 }
93
94 recover_wan() {
95 local new_fail_count
96
97 local health_recovery_retries=$(uci_get_state multiwan ${1} health_recovery_retries)
98 local weight=$(uci_get_state multiwan ${1} weight)
99
100 local failchk=$(query_config failchk $1)
101 local recvrychk=$(query_config recvrychk $1)
102 local wanid=$(query_config wanid $1)
103
104 if [ ! -z "$failchk" -a "$failchk" != "x" ]; then
105 wan_fail_map=$(echo $wan_fail_map | sed -e "s/${1}\[${failchk}\]//g")
106 update_cache
107 fi
108
109 if [ "$failchk" == "x" ]; then
110 if [ -z "$recvrychk" ]; then
111 wan_recovery_map="$wan_recovery_map${1}[1]"
112 update_cache
113 if [ "$health_recovery_retries" == "1" ]; then
114 recover_wan $1
115 fi
116 else
117 new_recovery_count=$(($recvrychk + 1))
118 if [ "$new_recovery_count" -lt "$health_recovery_retries" ]; then
119 wan_recovery_map=$(echo $wan_recovery_map | sed -e "s/${1}\[${recvrychk}\]/$1\[${new_recovery_count}\]/g")
120 update_cache
121 else
122 failover del $1
123 refresh_dns
124 if [ "$weight" != "disable" ]; then
125 refresh_loadbalancer
126 fi
127 fi
128 fi
129 fi
130 }
131
132 acquire_wan_data() {
133 local check_old_map
134 local get_wanid
135 local old_ifname
136 local old_ipaddr
137 local old_gateway
138
139 local ifname=$(uci_get_state network ${1} ifname 'x')
140 local ipaddr=$(uci_get_state network ${1} ipaddr 'x')
141 local gateway=$(uci_get_state network ${1} gateway 'x')
142
143 check_old_map=$(echo $wan_id_map 2>&1 | grep -o "$1\[")
144
145 if [ -z $check_old_map ]; then
146 wancount=$(($wancount + 1))
147 if [ $wancount -gt 20 ]; then
148 wancount=20
149 return
150 fi
151 wan_if_map="$wan_if_map${1}[${ifname}]"
152 wan_id_map="$wan_id_map${1}[${wancount}]"
153 wan_gw_map="$wan_gw_map${1}[${gateway}]"
154 wan_ip_map="$wan_ip_map${1}[${ipaddr}]"
155 else
156 old_ipaddr=$(query_config ipaddr $1)
157 old_gateway=$(query_config gateway $1)
158 old_ifname=$(query_config ifname $1)
159 get_wanid=$(query_config wanid $1)
160
161 wan_if_map=$(echo $wan_if_map | sed -e "s/${1}\[${old_ifname}\]/$1\[${ifname}\]/g")
162 wan_ip_map=$(echo $wan_ip_map | sed -e "s/${1}\[${old_ipaddr}\]/$1\[${ipaddr}\]/g")
163 wan_gw_map=$(echo $wan_gw_map | sed -e "s/${1}\[${old_gateway}\]/$1\[${gateway}\]/g")
164
165 if [ "$old_ifname" != "$ifname" ]; then
166 iptables -D MultiWanPreHandler -t mangle -i $old_$ifname -m state --state NEW -j FW${get_wanid}MARK
167 iptables -A MultiWanPreHandler -t mangle -i $ifname -m state --state NEW -j FW${get_wanid}MARK
168 iptables -D MultiWanPostHandler -t mangle -o $old_$ifname -m mark --mark 0x1 -j FW${get_wanid}MARK
169 iptables -A MultiWanPostHandler -t mangle -o $ifname -m mark --mark 0x1 -j FW${get_wanid}MARK
170 fi
171
172 if [ "$ifname" != "x" -a "$ipaddr" != "x" -a "$gateway" != "x" ]; then
173 failover del $1
174 iprules_config $get_wanid
175 else
176 failover add $1
177 fi
178
179 refresh_routes
180 refresh_loadbalancer
181 refresh_dns
182 update_cache
183 fi
184 }
185
186 update_cache() {
187 if [ ! -d /tmp/.mwan ]; then
188 mkdir /tmp/.mwan > /dev/null 2>&1
189 fi
190
191 rm /tmp/.mwan/cache > /dev/null 2>&1
192 touch /tmp/.mwan/cache
193
194 echo "# Automatically Generated by Multi-WAN Agent Script. Do not modify or remove. #" > /tmp/.mwan/cache
195 echo "wan_id_map=\"$wan_id_map\"" >> /tmp/.mwan/cache
196 echo "wan_if_map=\"$wan_if_map\"" >> /tmp/.mwan/cache
197 echo "wan_ip_map=\"$wan_ip_map\"" >> /tmp/.mwan/cache
198 echo "wan_gw_map=\"$wan_gw_map\"" >> /tmp/.mwan/cache
199 echo "wan_fail_map=\"$wan_fail_map\"" >> /tmp/.mwan/cache
200 echo "wan_recovery_map=\"$wan_recovery_map\"" >> /tmp/.mwan/cache
201 echo "wan_monitor_map=\"$wan_monitor_map\"" >> /tmp/.mwan/cache
202 }
203
204 query_config() {
205 case $1 in
206 ifname) echo $wan_if_map | grep -o "$2\[\w*.*\]" | awk -F "[" '{print $2}' | awk -F "]" '{print $1}';;
207 ipaddr) echo $wan_ip_map | grep -o "$2\[\w*.*\]" | awk -F "[" '{print $2}' | awk -F "]" '{print $1}';;
208 gateway) echo $wan_gw_map | grep -o "$2\[\w*.*\]" | awk -F "[" '{print $2}' | awk -F "]" '{print $1}';;
209 wanid) echo $wan_id_map | grep -o "$2\[\w*.*\]" | awk -F "[" '{print $2}' | awk -F "]" '{print $1}';;
210 failchk) echo $wan_fail_map | grep -o "$2\[\w*.*\]" | awk -F "[" '{print $2}' | awk -F "]" '{print $1}';;
211 recvrychk) echo $wan_recovery_map | grep -o "$2\[\w*.*\]" | awk -F "[" '{print $2}' | awk -F "]" '{print $1}';;
212 monitor) echo $wan_monitor_map | grep -o "$2\[\w*.*\]" | awk -F "[" '{print $2}' | awk -F "]" '{print $1}';;
213 group) echo $wan_id_map | grep -o "\w*\[$2\]" | awk -F "[" '{print $1}';;
214 esac
215 }
216
217 mwan_kill() {
218 local otherpids=$(ps 2>&1 | grep 'multiwan agent' | grep -v $$ | awk '{print $1}')
219 [ -n "$otherpids" ] && kill $otherpids > /dev/null 2>&1
220 sleep 2
221 }
222
223 # For system shutdownl: stop
224 # A plain stop will leave network in a limp state, without wan access
225 # stop single: restore to a single wan
226 # stop restart: restart multiple wan's
227 stop() {
228 mwan_kill
229 flush $1
230
231 if [ "$1" == "single" ]; then
232 # ifup is quite expensive--do it only when single wan is requested
233 echo "## Refreshing Interfaces ##"
234 local i=0
235 while [ $((i++)) -lt $wancount ]; do
236 local group=$(query_config group $i)
237 ifup $group >&- 2>&- && sleep 1
238 done
239
240 echo "## Unloaded, updating syslog and exiting. ##"
241 mwnote "Succesfully Unloaded on $(exec date -R)."
242 rm -fr /tmp/.mwan >&- 2>&-
243 fi
244 ip route flush cache
245
246 if [ "$1" == "restart" ]; then
247 echo "## Restarting Multi-WAN. ##"
248 mwnote "Reinitializing Multi-WAN Configuration."
249 rm -fr /tmp/.mwan >&- 2>&-
250 /etc/init.d/multiwan start >&- 2>&-
251 fi
252
253 exit
254 }
255
256 clear_rules() {
257 local restore_single=$1
258 local group
259
260 iptables -t mangle -D PREROUTING -j MultiWan
261 iptables -t mangle -D FORWARD -j MultiWan
262 iptables -t mangle -D OUTPUT -j MultiWan
263 iptables -t mangle -D POSTROUTING -j MultiWan
264 iptables -t mangle -F MultiWan
265 iptables -t mangle -X MultiWan
266 iptables -t mangle -F MultiWanRules
267 iptables -t mangle -X MultiWanRules
268 iptables -t mangle -F MultiWanDNS
269 iptables -t mangle -X MultiWanDNS
270 iptables -t mangle -F MultiWanPreHandler
271 iptables -t mangle -X MultiWanPreHandler
272 iptables -t mangle -F MultiWanPostHandler
273 iptables -t mangle -X MultiWanPostHandler
274 iptables -t mangle -F LoadBalancer
275 iptables -t mangle -X LoadBalancer
276 iptables -t mangle -F FastBalancer
277 iptables -t mangle -X FastBalancer
278 iptables -t mangle -F MultiWanLoadBalancer
279 iptables -t mangle -X MultiWanLoadBalancer
280
281 local i=0
282 while [ $((i++)) -lt $wancount ]; do
283 iptables -t mangle -F FW${i}MARK
284 iptables -t mangle -X FW${i}MARK
285 done
286
287 if [ ! -z "$CHKFORQOS" ]; then
288 iptables -t mangle -F PREROUTING
289 iptables -t mangle -F FORWARD
290 iptables -t mangle -F OUTPUT
291 iptables -t mangle -F POSTROUTING
292 iptables -t mangle -F MultiWanQoS
293 iptables -t mangle -X MultiWanQoS
294
295 i=0
296 while [ $((i++)) -lt $wancount ]; do
297 group=$(query_config group $i)
298 iptables -t mangle -F qos_${group}
299 iptables -t mangle -F qos_${group}_ct
300 iptables -t mangle -X qos_${group}
301 iptables -t mangle -X qos_${group}_ct
302 done
303 fi
304
305 [ "$restore_single" == 'single' ] &&
306 /etc/init.d/qos restart > /dev/null 2>&1
307 }
308
309 qos_init() {
310 local ifname
311 local queue_count
312 local get_wan_tc
313 local get_wan_iptables
314 local add_qos_iptables
315 local add_qos_tc
316 local execute
317 local iprule
318 local qos_if_test
319
320 ifname=$(query_config ifname $1)
321
322 if [ "$ifname" == "x" ]; then
323 return
324 fi
325
326 qos_if_test=$(echo $qos_if_done | grep $ifname.)
327
328 if [ ! -z "$qos_if_test" ]; then
329 return
330 fi
331
332 qos_if_done=$(echo ${qos_if_done}.${ifname})
333
334 queue_count=$(tc filter list dev $ifname | tail -n 1 | awk -F " " '{print $10}' | sed "s/0x//g")
335
336 if [ -z "$queue_count" ]; then
337 return
338 fi
339
340 queue_count=$(($queue_count + 1))
341
342 iptables -t mangle -N qos_${1}
343 iptables -t mangle -N qos_${1}_ct
344
345 get_wan_tc=$(tc filter list dev $ifname | grep "0x" | sed -e "s/filter /tc filter add dev $ifname /g" -e "s/pref/prio/g" -e "s/fw//g")
346 get_wan_iptables=$(iptables-save | egrep '(-A Default )|(-A Default_ct )' | grep -v "MultiWanQoS" | sed -e "s/Default /qos_${1} /g" -e "s/Default_ct /qos_${1}_ct /g" -e "s/-A/iptables -t mangle -A/g")
347
348
349 local i=0
350 while [ $i -lt $queue_count ]; do
351 echo "s/\(0x$i \|0x$i\/0xffffffff\)/0x$(($2 * 10 + $i)) /g" >> /tmp/.mwan/qos.$1.sedfilter
352 i=$(($i + 1))
353 done
354
355 add_qos_iptables=$(echo "$get_wan_iptables" | sed -f /tmp/.mwan/qos.$1.sedfilter)
356 echo "$add_qos_iptables" | while read execute; do ${execute}; done
357
358 rm /tmp/.mwan/qos.$1.sedfilter
359 i=1
360 while [ $i -lt $queue_count ]; do
361 echo "s/0x$i /0x${2}${i} fw /g" >> /tmp/.mwan/qos.$1.sedfilter
362 i=$(($i + 1))
363 done
364
365 add_qos_tc=$(echo "$get_wan_tc" | sed -f /tmp/.mwan/qos.$1.sedfilter)
366 echo "$add_qos_tc" | while read execute; do ${execute}; done
367 rm /tmp/.mwan/qos.$1.sedfilter
368
369 i=0
370 while [ $i -lt $queue_count ]; do
371 if [ $i -lt $(($queue_count - 1)) ]; then
372 ip rule add fwmark 0x$(($2 * 10 + $i + 1)) table $(($2 + 170)) prio $(( $2 * 10 + $i + 2))
373 fi
374 iptables -t mangle -A MultiWanQoS -m mark --mark 0x$(($2 * 10 + $i)) -j qos_${1}
375 i=$(($i + 1))
376 done
377 }
378
379 mwanrule() {
380 local src
381 local dst
382 local ports
383 local proto
384 local wanrule
385
386 config_get src $1 src
387 config_get dst $1 dst
388 config_get port_type $1 port_type 'dports'
389 config_get ports $1 ports
390 config_get proto $1 proto
391 config_get wanrule $1 wanrule
392
393 if [ -z "$wanrule" ]; then
394 return
395 fi
396
397 if [ "$wanrule" != "balancer" -a "$wanrule" != "fastbalancer" ]; then
398 wanrule=$(query_config wanid ${wanrule})
399 wanrule="FW${wanrule}MARK"
400 elif [ "$wanrule" == "balancer" ]; then
401 wanrule="LoadBalancer"
402 elif [ "$wanrule" == "fastbalancer" ]; then
403 wanrule="FastBalancer"
404 fi
405 if [ "$dst" == "all" ]; then
406 dst=$NULL
407 fi
408 if [ "$proto" == "all" ]; then
409 proto=$NULL
410 fi
411 if [ "$ports" == "all" ]; then
412 ports=$NULL
413 fi
414 add_rule() {
415 if [ "$proto" == "icmp" ]; then
416 ports=$NULL
417 fi
418 if [ "$src" == "all" ]; then
419 src=$NULL
420 fi
421 iptables -t mangle -A MultiWanRules ${src:+-s $src} ${dst:+-d $dst} \
422 -m mark --mark 0x0 ${proto:+-p $proto -m $proto} \
423 ${ports:+-m multiport --$port_type $ports} \
424 -j $wanrule
425 }
426 if [ -z "$proto" -a ! -z "$ports" ]; then
427 proto=tcp
428 add_rule
429 proto=udp
430 add_rule
431 return
432 fi
433 add_rule
434 }
435
436 refresh_dns() {
437 local dns
438 local group
439 local ipaddr
440 local gateway
441 local ifname
442 local failchk
443 local compile_dns
444 local dns_server
445
446 iptables -F MultiWanDNS -t mangle
447
448 rm /tmp/resolv.conf.auto
449 touch /tmp/resolv.conf.auto
450
451 echo "## Refreshing DNS Resolution and Tables ##"
452
453 local i=0
454 while [ $((i++)) -lt $wancount ]; do
455 group=$(query_config group $i)
456 gateway=$(query_config gateway $group)
457 ipaddr=$(query_config ipaddr $group)
458 ifname=$(query_config ifname $group)
459 failchk=$(query_config failchk $group)
460
461 dns=$(uci_get_state multiwan ${group} dns 'auto')
462 [ "$dns" == "auto" ] && dns=$(uci_get_state network ${group} dns)
463 dns=$(echo $dns | sed -e "s/ /\n/g")
464
465 if [ ! -z "$dns" -a "$failchk" != "x" -a "$ipaddr" != "x" -a "$gateway" != "x" -a "$ifname" != "x" ]; then
466 echo "$dns" | while read dns_server; do
467 iptables -t mangle -A MultiWanDNS -d $dns_server -p tcp --dport 53 -j FW${i}MARK
468 iptables -t mangle -A MultiWanDNS -d $dns_server -p udp --dport 53 -j FW${i}MARK
469
470 compile_dns="nameserver $dns_server"
471 echo "$compile_dns" >> /tmp/resolv.conf.auto
472 done
473 fi
474 done
475
476 last_resolv_update=$(ls -l -e /tmp/resolv.conf.auto | awk -F " " '{print $5, $9}')
477 }
478
479 iptables_init() {
480 echo "## IPTables Rule Initialization ##"
481 local iprule
482 local group
483 local ifname
484 local execute
485 local IMQ_NFO
486 local default_route_id
487 local i
488
489 if [ ! -z "$CHKFORQOS" ]; then
490 echo "## QoS Initialization ##"
491
492 /etc/init.d/qos restart > /dev/null 2>&1
493
494 IMQ_NFO=$(iptables -n -L PREROUTING -t mangle -v | grep IMQ | awk -F " " '{print $6,$12}')
495
496 iptables -t mangle -F PREROUTING
497 iptables -t mangle -F FORWARD
498 iptables -t mangle -F POSTROUTING
499 iptables -t mangle -F OUTPUT
500
501 echo "$IMQ_NFO" | while read execute; do
502 iptables -t mangle -A PREROUTING -i $(echo $execute | awk -F " " '{print $1}') -j IMQ --todev $(echo $execute | awk -F " " '{print $2}')
503 done
504
505 iptables -t mangle -N MultiWanQoS
506
507 i=0
508 while [ $((i++)) -lt $wancount ]; do
509 qos_init $(query_config group $i) $i
510 done
511
512 fi
513
514 iptables -t mangle -N MultiWan
515 iptables -t mangle -N LoadBalancer
516 iptables -t mangle -N FastBalancer
517 iptables -t mangle -N MultiWanRules
518 iptables -t mangle -N MultiWanDNS
519 iptables -t mangle -N MultiWanPreHandler
520 iptables -t mangle -N MultiWanPostHandler
521 iptables -t mangle -N MultiWanLoadBalancer
522
523 echo "## Creating FW Rules ##"
524 i=0
525 while [ $((i++)) -lt $wancount ]; do
526 iprule=$(($i * 10))
527 iptables -t mangle -N FW${i}MARK
528 iptables -t mangle -A FW${i}MARK -j MARK --set-mark 0x${iprule}
529 iptables -t mangle -A FW${i}MARK -j CONNMARK --save-mark
530 done
531
532 iptables -t mangle -A LoadBalancer -j MARK --set-mark 0x1
533 iptables -t mangle -A LoadBalancer -j CONNMARK --save-mark
534
535 if [ -z "$CHKFORMODULE" ]; then
536 iptables -t mangle -A FastBalancer -j MARK --set-mark 0x2
537 iptables -t mangle -A FastBalancer -j CONNMARK --save-mark
538 else
539 mwnote "Performance load balancer(fastbalanacer) is unavailable due to current kernel limitations."
540 iptables -t mangle -A FastBalancer -j MARK --set-mark 0x1
541 iptables -t mangle -A FastBalancer -j CONNMARK --save-mark
542 fi
543
544 iptables -t mangle -A MultiWan -j CONNMARK --restore-mark
545 iptables -t mangle -A MultiWan -j MultiWanPreHandler
546 iptables -t mangle -A MultiWan -j MultiWanRules
547 iptables -t mangle -A MultiWan -j MultiWanLoadBalancer
548 iptables -t mangle -A MultiWan -j MultiWanDNS
549 iptables -t mangle -A MultiWan -j MultiWanPostHandler
550
551 iptables -t mangle -I PREROUTING -j MultiWan
552 iptables -t mangle -I FORWARD -j MultiWan
553 iptables -t mangle -I OUTPUT -j MultiWan
554 iptables -t mangle -I POSTROUTING -j MultiWan
555
556
557 refresh_dns
558
559 config_load "multiwan"
560 config_foreach mwanrule mwanfw
561
562 if [ "$default_route" != "balancer" -a "$default_route" != "fastbalancer" ]; then
563 default_route_id=$(query_config wanid $default_route)
564 iptables -t mangle -A MultiWanRules -m mark --mark 0x0 -j FW${default_route_id}MARK
565 elif [ "$default_route" == "fastbalancer" ]; then
566 iptables -t mangle -A MultiWanRules -m mark --mark 0x0 -j FastBalancer
567 else
568 iptables -t mangle -A MultiWanRules -m mark --mark 0x0 -j LoadBalancer
569 fi
570
571 i=0
572 while [ $((i++)) -lt $wancount ]; do
573 group=$(query_config group $i)
574 ifname=$(query_config ifname $group)
575 iptables -t mangle -A MultiWanPreHandler -i $ifname -m state --state NEW -j FW${i}MARK
576 iptables -t mangle -A MultiWanPostHandler -o $ifname -m mark --mark 0x1 -j FW${i}MARK
577 done
578
579 if [ ! -z "$CHKFORQOS" ]; then
580 iptables -t mangle -A MultiWan -j MultiWanQoS
581 fi
582 }
583
584 refresh_loadbalancer() {
585 local group
586 local gateway
587 local ifname
588 local failchk
589 local weight
590 local nexthop
591 local pre_nexthop_chk
592 local rand_probability
593
594 echo "## Refreshing Load Balancer ##"
595
596 ip rule del prio 9 > /dev/null 2>&1
597 ip route flush table 170 > /dev/null 2>&1
598
599 for TABLE in 170; do
600 ip route | grep -Ev ^default | while read ROUTE; do
601 ip route add table $TABLE to $ROUTE
602 done
603 done
604
605 iptables -F MultiWanLoadBalancer -t mangle
606
607 local total_weight=0
608
609 local i=0
610 while [ $((i++)) -lt $wancount ]; do
611 group=$(query_config group $i)
612 failchk=$(query_config failchk $group)
613 gateway=$(query_config gateway $group)
614 ifname=$(query_config ifname $group)
615 weight=$(uci_get_state multiwan ${group} weight)
616 if [ "$gateway" != "x" -a "$ifname" != "x" -a "$failchk" != "x" -a "$weight" != "disable" ]; then
617 total_weight=$(($total_weight + $weight))
618 fi
619 done
620
621 i=0
622 while [ $((i++)) -lt $wancount ]; do
623 group=$(query_config group $i)
624 failchk=$(query_config failchk $group)
625 gateway=$(query_config gateway $group)
626 ifname=$(query_config ifname $group)
627
628 weight=$(uci_get_state multiwan ${group} weight)
629
630 if [ "$gateway" != "x" -a "$ifname" != "x" -a "$failchk" != "x" -a "$weight" != "disable" ]; then
631 nexthop="$nexthop nexthop via $gateway dev $ifname weight $weight"
632
633 rand_probability=$(($weight * 100 / $total_weight))
634 total_weight=$(($total_weight - $weight))
635
636 if [ $rand_probability -lt 10 ]; then
637 rand_probability="0.0${rand_probability}"
638 elif [ $rand_probability -lt 100 ]; then
639 rand_probability="0.${rand_probability}"
640 else
641 rand_probability="1.0"
642 fi
643
644 if [ -z "$CHKFORMODULE" ]; then
645 iptables -A MultiWanLoadBalancer -t mangle -m mark --mark 0x2 -m statistic --mode random --probability $rand_probability -j FW${i}MARK
646 fi
647 fi
648
649 done
650
651 pre_nexthop_chk=$(echo $nexthop | awk -F "nexthop" '{print NF-1}')
652 if [ "$pre_nexthop_chk" == "1" ]; then
653 ip route add default via $(echo $nexthop | awk -F " " '{print $3}') dev $(echo $nexthop | awk -F " " '{print $5}') proto static table 170
654 elif [ "$pre_nexthop_chk" -gt "1" ]; then
655 ip route add proto static table 170 default scope global $nexthop
656 fi
657
658 ip rule add fwmark 0x1 table 170 prio 9
659 ip route flush cache
660 }
661
662 refresh_routes() {
663 local iprule
664 local gateway
665 local group
666 local ifname
667 local ipaddr
668
669 echo "## Refreshing Routing Tables ##"
670
671 local i=0
672 while [ $((i++)) -lt $wancount ]; do
673 group=$(query_config group $i)
674 gateway=$(query_config gateway $group)
675 ifname=$(query_config ifname $group)
676 ipaddr=$(query_config ipaddr $group)
677 ip route flush table $(($i + 170)) > /dev/null 2>&1
678
679 TABLE=$(($i + 170))
680 ip route | grep -Ev ^default | while read ROUTE; do
681 ip route add table $TABLE to $ROUTE
682 done
683
684 if [ "$gateway" != "x" -a "$ipaddr" != "x" -a "$ifname" != "x" ]; then
685 ip route add default via $gateway table $(($i + 170)) src $ipaddr proto static
686 route add default gw $gateway > /dev/null 2>&1
687 fi
688 done
689
690 ip route flush cache
691 }
692
693 iprules_config() {
694 local iprule
695 local group
696 local gateway
697 local ipaddr
698
699 group=$(query_config group $1)
700 gateway=$(query_config gateway $group)
701 ipaddr=$(query_config ipaddr $group)
702
703 CHKIPROUTE=$(grep MWAN${1} /etc/iproute2/rt_tables)
704 if [ -z "$CHKIPROUTE" ]; then
705 echo "$(($1 + 170)) MWAN${1}" >> /etc/iproute2/rt_tables
706 fi
707
708 ip rule del prio $(($1 * 10)) > /dev/null 2>&1
709 ip rule del prio $(($1 * 10 + 1)) > /dev/null 2>&1
710
711 if [ "$gateway" != "x" -a "$ipaddr" != "x" ]; then
712 ip rule add from $ipaddr table $(($1 + 170)) prio $(($1 * 10))
713 ip rule add fwmark 0x$(($1 * 10)) table $(($1 + 170)) prio $(($1 * 10 + 1))
714 fi
715 }
716
717 flush() {
718 local restore_single=$1
719 echo "## Flushing IP Rules & Routes ##"
720
721 ip rule flush > /dev/null 2>&1
722 ip rule add lookup main prio 32766 > /dev/null 2>&1
723 ip rule add lookup default prio 32767 > /dev/null 2>&1
724
725 ip route flush table 170 > /dev/null
726
727 local i=0
728 while [ $((i++)) -lt $wancount ]; do
729 ip route del default > /dev/null 2>&1
730 ip route flush table $(($i + 170)) > /dev/null 2>&1
731 done
732
733 echo "## Clearing Rules ##"
734 clear_rules $restore_single > /dev/null 2>&1
735
736 rm $jobfile > /dev/null 2>&1
737 }
738
739 main_init() {
740 local RP_PATH IFACE
741 local group
742 local health_interval
743
744 echo "## Main Initialization ##"
745
746 mkdir /tmp/.mwan > /dev/null 2>&1
747
748 mwan_kill
749 flush
750
751 echo "## IP Rules Initialization ##"
752
753 CHKIPROUTE=$(grep LoadBalancer /etc/iproute2/rt_tables)
754 if [ -z "$CHKIPROUTE" ]; then
755 echo "#" >> /etc/iproute2/rt_tables
756 echo "170 LoadBalancer" >> /etc/iproute2/rt_tables
757 fi
758
759 local i=0
760 while [ $((i++)) -lt $wancount ]; do
761 iprules_config $i
762 done
763
764 refresh_routes
765 iptables_init
766
767 refresh_loadbalancer
768
769 RP_PATH=/proc/sys/net/ipv4/conf
770 for IFACE in $(ls $RP_PATH); do
771 echo 0 > $RP_PATH/$IFACE/rp_filter
772 done
773 mwnote "Succesfully Initialized on $(date -R)."
774 fail_start_check
775
776 while :; do
777 schedule_tasks
778 do_tasks
779 done
780 }
781
782 monitor_wan() {
783 local ifname ipaddr gateway icmp_hosts_acquire icmp_test_host
784 local check_test
785
786 . /tmp/.mwan/cache
787
788 local timeout=$(uci_get_state multiwan ${1} timeout)
789 local icmp_hosts=$(uci_get_state multiwan ${1} icmp_hosts)
790 local icmp_count=$(uci_get_state multiwan ${1} icmp_count '1')
791 local health_interval=$(uci_get_state multiwan ${1} health_interval)
792 local ifname_cur=$(query_config ifname $1)
793 local ipaddr_cur=$(query_config ipaddr $1)
794 local gateway_cur=$(query_config gateway $1)
795
796 while :; do
797 [ "${health_monitor%.*}" = 'parallel' ] && sleep $health_interval
798
799 ifname=$(uci_get_state network ${1} ifname 'x')
800 ipaddr=$(uci_get_state network ${1} ipaddr 'x')
801 gateway=$(uci_get_state network ${1} gateway 'x')
802
803 if [ "$ifname_cur" != "$ifname" -o "$ipaddr_cur" != "$ipaddr" -o "$gateway_cur" != "$gateway" ]; then
804 add_task "$1" acquire
805 if [ "${health_monitor%.*}" = 'parallel' ]; then
806 exit
807 else
808 return
809 fi
810 else
811 [ "$gateway" != "x" ] && ! ip route | grep -o $gateway >&- 2>&- &&
812 add_task route refresh
813 fi
814
815 if [ "$icmp_hosts" != "disable" -a "$ifname" != "x" -a "$ipaddr" != "x" -a "$gateway" != "x" ]; then
816
817 if [ "$icmp_hosts" == "gateway" -o -z "$icmp_hosts" ]; then
818 icmp_hosts_acquire=$gateway
819 elif [ "$icmp_hosts" == "dns" ]; then
820 icmp_hosts_acquire=$(uci_get_state multiwan $1 dns 'auto')
821 [ "$icmp_hosts_acquire" == "auto" ] &&
822 icmp_hosts_acquire=$(uci_get_state network $1 dns)
823 else
824 icmp_hosts_acquire=$icmp_hosts
825 fi
826
827 icmp_hosts=$(echo $icmp_hosts_acquire | sed -e "s/\,/ /g" | sed -e "s/ /\n/g")
828
829 ping_test() {
830 echo "$icmp_hosts" | while read icmp_test_host; do
831 ping -c "$icmp_count" -W $timeout -I $ifname $icmp_test_host 2>&1 | grep -o "round-trip"
832 done
833 }
834
835 check_test=$(ping_test)
836
837 if [ -z "$check_test" ]; then
838 add_task "$1" fail
839 else
840 add_task "$1" pass
841 fi
842
843 elif [ "$icmp_hosts" == "disable" ]; then
844 add_task "$1" pass
845 fi
846
847 [ "$health_monitor" = 'serial' ] && {
848 wan_monitor_map=$(echo $wan_monitor_map | sed -e "s/$1\[\w*\]/$1\[$(date +%s)\]/g")
849 update_cache
850 break
851 }
852 done
853 }
854
855 # Add a task to the $jobfile while ensuring
856 # no duplicate tasks for the specified group
857 add_task() {
858 local group=$1
859 local task=$2
860 grep -o "$group.$task" $jobfile >&- 2>&- || echo "$group.$task" >> $jobfile
861 }
862
863 # For health_monitor "parallel", start a background monitor for each group.
864 # For health_monitor "serial", queue monitor tasks for do_tasks.
865 schedule_tasks() {
866 local group health_interval monitored_last_at current_time diff delay
867 local i=0
868
869 get_health_interval() {
870 group=$(query_config group $1)
871 health_interval=$(uci_get_state multiwan ${group} health_interval 'disable')
872 [ "$health_interval" = "disable" ] && health_interval=0
873 }
874
875 [ "$health_monitor" = 'parallel' ] && {
876 while [ $((i++)) -lt $wancount ]; do
877 get_health_interval $i
878 if [ "$health_interval" -gt 0 ]; then
879 monitor_wan $group &
880 sleep 1
881 fi
882 done
883 echo "## Started background monitor_wan ##"
884 health_monitor="parallel.started"
885 }
886
887 [ "$health_monitor" = 'serial' ] && {
888 local monitor_disabled=1
889
890 until [ -f $jobfile ]; do
891 current_time=$(date +%s)
892 delay=$max_interval
893 i=0
894
895 while [ $((i++)) -lt $wancount ]; do
896 get_health_interval $i
897 if [ "$health_interval" -gt 0 ]; then
898 monitor_disabled=0
899
900 monitored_last=$(query_config monitor $group)
901 [ -z "$monitored_last" ] && {
902 monitored_last=$current_time
903 wan_monitor_map="${wan_monitor_map}${group}[$monitored_last]"
904 update_cache
905 }
906
907 will_monitor_at=$(($monitored_last + $health_interval))
908 diff=$(($will_monitor_at - $current_time))
909 [ $diff -le 0 ] && add_task "$group" 'monitor'
910
911 delay=$(($delay > $diff ? $diff : $delay))
912 fi
913 done
914
915 [ "$monitor_disabled" -eq 1 ] && {
916 # Although health monitors are disabled, still
917 # need to check up on iptables rules in do_tasks
918 sleep "$iptables_interval"
919 break
920 }
921 [ $delay -gt 0 ] && sleep $delay
922 done
923 }
924 }
925
926 rule_counter=0
927 # Process each task in the $jobfile in FIFO order
928 do_tasks() {
929 local check_iptables
930 local queued_task
931 local current_resolv_file
932
933 while :; do
934
935 . /tmp/.mwan/cache
936
937 if [ "$((++rule_counter))" -eq 5 -o "$health_monitor" = 'serial' ]; then
938
939 check_iptables=$(iptables -n -L MultiWan -t mangle | grep "references" | awk -F "(" '{print $2}' | cut -d " " -f 1)
940
941 if [ -z "$check_iptables" -o "$check_iptables" -lt 4 ]; then
942 mwnote "Netfilter rules appear to of been altered."
943 /etc/init.d/multiwan restart
944 exit
945 fi
946
947 current_resolv_file=$(ls -l -e /tmp/resolv.conf.auto | awk -F " " '{print $5, $9}')
948
949 if [ "$last_resolv_update" != "$current_resolv_file" ]; then
950 refresh_dns
951 fi
952
953 rule_counter=0
954 fi
955
956 if [ -f $jobfile ]; then
957
958 mv $jobfile $jobfile.work
959
960 while read LINE; do
961
962 execute_task() {
963 case $2 in
964 fail) fail_wan $1;;
965 pass) recover_wan $1;;
966 acquire)
967 acquire_wan_data $1
968 [ "${health_monitor%.* }" = 'parallel' ] && {
969 monitor_wan $1 &
970 echo "## Started background monitor_wan ##"
971 }
972 ;;
973 monitor) monitor_wan $1;;
974 refresh) refresh_routes;;
975 *) echo "## Unknown task command: $2 ##";;
976 esac
977 }
978
979 queued_task=$(echo $LINE | awk -F "." '{print $1,$2}')
980 execute_task $queued_task
981 done < $jobfile.work
982
983 rm $jobfile.work
984 fi
985
986 if [ "$health_monitor" = 'serial' ]; then
987 break
988 else
989 sleep 1
990 fi
991 done
992 }
993
994 fail_start_check(){
995 local ipaddr
996 local gateway
997 local ifname
998 local group
999
1000 local i=0
1001 while [ $((i++)) -lt $wancount ]; do
1002 group=$(query_config group $i)
1003 ifname=$(query_config ifname $group)
1004 ipaddr=$(query_config ipaddr $group)
1005 gateway=$(query_config gateway $group)
1006
1007 if [ "$ifname" == "x" -o "$ipaddr" == "x" -o "$gateway" == "x" ]; then
1008 failover add $group
1009 fi
1010 done
1011 }
1012
1013 wancount=0
1014 max_interval=$(((1<<31) - 1))
1015
1016 config_clear
1017 config_load "multiwan"
1018 config_get_bool enabled config enabled '1'
1019 [ "$enabled" -gt 0 ] || exit
1020 config_get default_route config default_route
1021 config_get health_monitor config health_monitor
1022 config_get iptables_interval config iptables_interval '30'
1023 config_get debug config debug
1024
1025 [ "$health_monitor" = 'serial' ] || health_monitor='parallel'
1026
1027 config_foreach acquire_wan_data interface
1028
1029 update_cache
1030
1031 CHKFORQOS=$(iptables -n -L Default -t mangle 2>&1 | grep "Chain Default")
1032 CHKFORMODULE=$(iptables -m statistic 2>&1 | grep -o "File not found")
1033
1034 jobfile="/tmp/.mwan/jobqueue"
1035
1036 case $1 in
1037 agent) silencer main_init;;
1038 stop) silencer stop;;
1039 restart) silencer stop restart;;
1040 single) silencer stop single;;
1041 esac