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