From e35bdd019e61e221a61c993fb658659fb71ce02d Mon Sep 17 00:00:00 2001 From: "Craig M. Coffee" Date: Sun, 10 Oct 2010 20:59:38 +0000 Subject: [PATCH] multiwan: 1. option 'health_monitor' 'serial' starts only one background process to monitor the health of any number of wan's, saving system resources (WHR-HP-G54 with only 16 MB memory is a happy wimp :) 2. option 'icmp_count' '3' can be useful to reduce false positives 3. a sample 'mwanfw' for VoIP traffic 4. "/etc/init.d/multiwan single" restores to the initial state of single wan. The "stop" command didn't quite do that, and it's now only good for process shutdown 5. numerous minor code cleanups SVN-Revision: 23388 --- net/multiwan/Makefile | 2 +- net/multiwan/files/etc/config/multiwan | 35 +- net/multiwan/files/etc/init.d/multiwan | 9 +- net/multiwan/files/usr/bin/multiwan | 543 ++++++++++++------------- 4 files changed, 294 insertions(+), 295 deletions(-) diff --git a/net/multiwan/Makefile b/net/multiwan/Makefile index 9bd7a0217..1e8ebf716 100644 --- a/net/multiwan/Makefile +++ b/net/multiwan/Makefile @@ -8,7 +8,7 @@ include $(TOPDIR)/rules.mk PKG_NAME:=multiwan -PKG_VERSION:=1.0.18 +PKG_VERSION:=1.0.19 PKG_RELEASE:=1 include $(INCLUDE_DIR)/package.mk diff --git a/net/multiwan/files/etc/config/multiwan b/net/multiwan/files/etc/config/multiwan index c02854bff..b89096e76 100644 --- a/net/multiwan/files/etc/config/multiwan +++ b/net/multiwan/files/etc/config/multiwan @@ -1,11 +1,18 @@ config 'multiwan' 'config' option 'default_route' 'balancer' + # health_monitor below is defaulted to parallel, and can be set to + # serial to save system resources. + # option 'health_monitor' 'serial' + # option 'debug' '1' config 'interface' 'wan' option 'weight' '10' option 'health_interval' '10' option 'icmp_hosts' 'dns' + # icmp_count is defaulted to 1, and can be increased to reduce + # false positives. + # option 'icmp_count' '3' option 'timeout' '3' option 'health_fail_retries' '3' option 'health_recovery_retries' '5' @@ -23,19 +30,25 @@ config 'interface' 'wan2' option 'dns' '208.67.222.222 208.67.220.220' config 'mwanfw' - option 'src' '192.168.1.0/24' - option 'dst' 'ftp.netlab7.com' - option 'proto' 'tcp' - option 'ports' '21' - option 'wanrule' 'wan2' + option 'src' '192.168.1.0/24' + option 'dst' 'ftp.netlab7.com' + option 'proto' 'tcp' + option 'ports' '21' + option 'wanrule' 'wan2' + +# VoIP traffic goes through wan +# config 'mwanfw' + # option 'src' '192.168.1.0/24' + # option 'proto' 'udp' + # option 'port_type' 'source-ports' + # option 'ports' '5060,16384:16482' + # option 'wanrule' 'wan' config 'mwanfw' option 'src' '192.168.0.3' - option 'proto' 'icmp' - option 'wanrule' 'balancer' + option 'proto' 'icmp' + option 'wanrule' 'balancer' config 'mwanfw' - option 'dst' 'www.whatismyip.com' - option 'wanrule' 'fastbalancer' - - + option 'dst' 'www.whatismyip.com' + option 'wanrule' 'fastbalancer' diff --git a/net/multiwan/files/etc/init.d/multiwan b/net/multiwan/files/etc/init.d/multiwan index 6d0c5b305..87844301e 100755 --- a/net/multiwan/files/etc/init.d/multiwan +++ b/net/multiwan/files/etc/init.d/multiwan @@ -1,8 +1,9 @@ #!/bin/sh /etc/rc.common START=99 +EXTRA_COMMANDS="single" start () { - sh /usr/bin/multiwan agent + /usr/bin/multiwan agent & } stop () { @@ -10,6 +11,10 @@ stop () { } restart () { - sh /usr/bin/multiwan restart + /usr/bin/multiwan restart & +} + +single () { + /usr/bin/multiwan single & } diff --git a/net/multiwan/files/usr/bin/multiwan b/net/multiwan/files/usr/bin/multiwan index be2558dda..fb6e8b1c6 100755 --- a/net/multiwan/files/usr/bin/multiwan +++ b/net/multiwan/files/usr/bin/multiwan @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/sh . /etc/functions.sh @@ -15,26 +15,19 @@ mwnote() { } failover() { - local failover_to - local failover_to_wanid - local failchk - local recovrychk - local wanid - local existing_failover - - failchk=$(query_config failchk $2) - recvrychk=$(query_config recvrychk $2) + local failchk=$(query_config failchk $2) + local recvrychk=$(query_config recvrychk $2) - wanid=$(query_config wanid $2) - failover_to=`uci -q -P /var/state get multiwan.${2}.failover_to` - failover_to_wanid=$(query_config wanid $failover_to) + local wanid=$(query_config wanid $2) + local failover_to=$(uci_get_state multiwan ${2} failover_to) + local failover_to_wanid=$(query_config wanid $failover_to) - existing_failover=$(iptables -n -L FW${wanid}MARK -t mangle | echo $(expr $(wc -l) - 2)) + local existing_failover=$(iptables -n -L FW${wanid}MARK -t mangle | echo $(($(wc -l) - 2))) add() { wan_fail_map=$(echo $wan_fail_map | sed -e "s/${1}\[${failchk}\]//g") - wan_fail_map=$(echo $wan_fail_map${1}[x]) + wan_fail_map="$wan_fail_map${1}[x]" wan_recovery_map=$(echo $wan_recovery_map | sed -e "s/${1}\[${recvrychk}\]//g") update_cache @@ -69,57 +62,44 @@ failover() { } fail_wan() { - local failchk - local recvrychk local new_fail_count - local health_fail_retries - local weight - health_fail_retries=`uci -q -P /var/state get multiwan.${1}.health_fail_retries` - weight=`uci -q -P /var/state get multiwan.${1}.weight` + local health_fail_retries=$(uci_get_state multiwan ${1} health_fail_retries) + local weight=$(uci_get_state multiwan ${1} weight) - failchk=$(query_config failchk $1) - recvrychk=$(query_config recvrychk $1) + local failchk=$(query_config failchk $1) + local recvrychk=$(query_config recvrychk $1) wan_recovery_map=$(echo $wan_recovery_map | sed -e "s/${1}\[${recvrychk}\]//g") if [ -z "$failchk" ]; then + failchk=1 wan_fail_map="$wan_fail_map${1}[1]" - update_cache - if [ "$health_fail_retries" == "1" ]; then - fail_wan $1 - fi - else - if [ "$failchk" != "x" ]; then - new_fail_count=$(expr $failchk + 1) - if [ "$new_fail_count" -lt "$health_fail_retries" ]; then - wan_fail_map=$(echo $wan_fail_map | sed -e "s/${1}\[${failchk}\]/$1\[${new_fail_count}\]/g") - update_cache - else - failover add $1 - refresh_dns - if [ "$weight" != "disable" ]; then - refresh_loadbalancer - fi - fi + fi + if [ "$failchk" != "x" ]; then + new_fail_count=$(($failchk + 1)) + if [ "$new_fail_count" -lt "$health_fail_retries" ]; then + wan_fail_map=$(echo $wan_fail_map | sed -e "s/${1}\[${failchk}\]/$1\[${new_fail_count}\]/g") + else + failover add $1 + refresh_dns + if [ "$weight" != "disable" ]; then + refresh_loadbalancer + fi fi fi + update_cache } recover_wan() { - local failchk - local recvrychk local new_fail_count - local wanid - local health_recovery_retires - local weight - health_recovery_retries=`uci -q -P /var/state get multiwan.${1}.health_recovery_retries` - weight=`uci -q -P /var/state get multiwan.${1}.weight` + local health_recovery_retries=$(uci_get_state multiwan ${1} health_recovery_retries) + local weight=$(uci_get_state multiwan ${1} weight) - failchk=$(query_config failchk $1) - recvrychk=$(query_config recvrychk $1) - wanid=$(query_config wanid $1) + local failchk=$(query_config failchk $1) + local recvrychk=$(query_config recvrychk $1) + local wanid=$(query_config wanid $1) if [ ! -z "$failchk" -a "$failchk" != "x" ]; then wan_fail_map=$(echo $wan_fail_map | sed -e "s/${1}\[${failchk}\]//g") @@ -134,7 +114,7 @@ recover_wan() { recover_wan $1 fi else - new_recovery_count=$(expr $recvrychk + 1) + new_recovery_count=$(($recvrychk + 1)) if [ "$new_recovery_count" -lt "$health_recovery_retries" ]; then wan_recovery_map=$(echo $wan_recovery_map | sed -e "s/${1}\[${recvrychk}\]/$1\[${new_recovery_count}\]/g") update_cache @@ -150,33 +130,20 @@ recover_wan() { } acquire_wan_data() { - local ipaddr - local gateway - local ifname local check_old_map local get_wanid local old_ifname local old_ipaddr local old_gateway - ifname=`uci -q -P /var/state get network.${1}.ifname` - ipaddr=`uci -q -P /var/state get network.${1}.ipaddr` - gateway=`uci -q -P /var/state get network.${1}.gateway` - - if [ -z "$ifname" ]; then - ifname="x" - fi - if [ -z "$ipaddr" ]; then - ipaddr="x" - fi - if [ -z "$gateway" ]; then - gateway="x" - fi + local ifname=$(uci_get_state network ${1} ifname 'x') + local ipaddr=$(uci_get_state network ${1} ipaddr 'x') + local gateway=$(uci_get_state network ${1} gateway 'x') - check_old_map=`echo $wan_id_map 2>&1 | grep -o "$1\["` + check_old_map=$(echo $wan_id_map 2>&1 | grep -o "$1\[") if [ -z $check_old_map ]; then - wancount=`expr $wancount + 1` + wancount=$(($wancount + 1)) if [ $wancount -gt 20 ]; then wancount=20 return @@ -217,7 +184,6 @@ acquire_wan_data() { } update_cache() { - if [ ! -d /tmp/.mwan ]; then mkdir /tmp/.mwan > /dev/null 2>&1 fi @@ -232,17 +198,18 @@ update_cache() { echo "wan_gw_map=\"$wan_gw_map\"" >> /tmp/.mwan/cache echo "wan_fail_map=\"$wan_fail_map\"" >> /tmp/.mwan/cache echo "wan_recovery_map=\"$wan_recovery_map\"" >> /tmp/.mwan/cache + echo "wan_monitor_map=\"$wan_monitor_map\"" >> /tmp/.mwan/cache } query_config() { case $1 in - update) update_cache_data;; ifname) echo $wan_if_map | grep -o "$2\[\w*.*\]" | awk -F "[" '{print $2}' | awk -F "]" '{print $1}';; ipaddr) echo $wan_ip_map | grep -o "$2\[\w*.*\]" | awk -F "[" '{print $2}' | awk -F "]" '{print $1}';; gateway) echo $wan_gw_map | grep -o "$2\[\w*.*\]" | awk -F "[" '{print $2}' | awk -F "]" '{print $1}';; wanid) echo $wan_id_map | grep -o "$2\[\w*.*\]" | awk -F "[" '{print $2}' | awk -F "]" '{print $1}';; failchk) echo $wan_fail_map | grep -o "$2\[\w*.*\]" | awk -F "[" '{print $2}' | awk -F "]" '{print $1}';; recvrychk) echo $wan_recovery_map | grep -o "$2\[\w*.*\]" | awk -F "[" '{print $2}' | awk -F "]" '{print $1}';; + monitor) echo $wan_monitor_map | grep -o "$2\[\w*.*\]" | awk -F "[" '{print $2}' | awk -F "]" '{print $1}';; group) echo $wan_id_map | grep -o "\w*\[$2\]" | awk -F "[" '{print $1}';; esac } @@ -253,43 +220,42 @@ mwan_kill() { sleep 2 } +# For system shutdownl: stop +# A plain stop will leave network in a limp state, without wan access +# stop single: restore to a single wan +# stop restart: restart multiple wan's stop() { - local group - local i - mwan_kill - flush + flush $1 - if [ "$1" != "restart" ]; then + if [ "$1" == "single" ]; then + # ifup is quite expensive--do it only when single wan is requested echo "## Refreshing Interfaces ##" - i=0 - while [ $i -lt $wancount ]; do - i=`expr $i + 1` - group=$(query_config group $i) - # ifdown $group > /dev/null 2>&1 - ifup $group > /dev/null 2>&1 + local i=0 + while [ $((i++)) -lt $wancount ]; do + local group=$(query_config group $i) + ifup $group >&- 2>&- && sleep 1 done echo "## Unloaded, updating syslog and exiting. ##" mwnote "Succesfully Unloaded on $(exec date -R)." - ip route flush cache - rm -r /tmp/.mwan > /dev/null 2>&1 - - else + rm -fr /tmp/.mwan >&- 2>&- + fi + ip route flush cache + if [ "$1" == "restart" ]; then echo "## Restarting Multi-WAN. ##" mwnote "Reinitializing Multi-WAN Configuration." - ip route flush cache - rm -r /tmp/.mwan > /dev/null 2>&1 - /etc/init.d/multiwan start & > /dev/null 2>&1 + rm -fr /tmp/.mwan >&- 2>&- + /etc/init.d/multiwan start >&- 2>&- fi exit } clear_rules() { + local restore_single=$1 local group - local i iptables -t mangle -F PREROUTING iptables -t mangle -F FORWARD @@ -309,35 +275,31 @@ clear_rules() { iptables -t mangle -X LoadBalancer iptables -t mangle -F FastBalancer iptables -t mangle -X FastBalancer + iptables -t mangle -F MultiWanLoadBalancer + iptables -t mangle -X MultiWanLoadBalancer - i=0 - while [ $i -lt $wancount ]; do - i=`expr $i + 1` + local i=0 + while [ $((i++)) -lt $wancount ]; do iptables -t mangle -F FW${i}MARK - done - - i=0 - while [ $i -lt $wancount ]; do - i=`expr $i + 1` iptables -t mangle -X FW${i}MARK done if [ ! -z "$CHKFORQOS" ]; then - iptables -t mangle -F MultiWanQoS iptables -t mangle -X MultiWanQoS i=0 - while [ $i -lt $wancount ]; do - i=`expr $i + 1` + while [ $((i++)) -lt $wancount ]; do group=$(query_config group $i) iptables -t mangle -F qos_${group} iptables -t mangle -F qos_${group}_ct iptables -t mangle -X qos_${group} iptables -t mangle -X qos_${group}_ct done - fi + + [ "$restore_single" == 'single' ] && + /etc/init.d/qos restart > /dev/null 2>&1 } qos_init() { @@ -350,8 +312,6 @@ qos_init() { local execute local iprule local qos_if_test - local i - local p ifname=$(query_config ifname $1) @@ -373,7 +333,7 @@ qos_init() { return fi - queue_count=`expr $queue_count + 1` + queue_count=$(($queue_count + 1)) iptables -t mangle -N qos_${1} iptables -t mangle -N qos_${1}_ct @@ -382,10 +342,10 @@ qos_init() { 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") - i=0 + local i=0 while [ $i -lt $queue_count ]; do - echo "s/\(0x$i \|0x$i\/0xffffffff\)/0x$(expr $2 \* 10 + $i) /g" >> /tmp/.mwan/qos.$1.sedfilter - i=`expr $i + 1` + echo "s/\(0x$i \|0x$i\/0xffffffff\)/0x$(($2 * 10 + $i)) /g" >> /tmp/.mwan/qos.$1.sedfilter + i=$(($i + 1)) done add_qos_iptables=$(echo "$get_wan_iptables" | sed -f /tmp/.mwan/qos.$1.sedfilter) @@ -395,7 +355,7 @@ qos_init() { i=1 while [ $i -lt $queue_count ]; do echo "s/0x$i /0x${2}${i} fw /g" >> /tmp/.mwan/qos.$1.sedfilter - i=`expr $i + 1` + i=$(($i + 1)) done add_qos_tc=$(echo "$get_wan_tc" | sed -f /tmp/.mwan/qos.$1.sedfilter) @@ -404,11 +364,11 @@ qos_init() { i=0 while [ $i -lt $queue_count ]; do - if [ $i -lt $(expr $queue_count - 1) ]; then - ip rule add fwmark 0x$(expr $2 \* 10 + $i + 1) table $(expr $2 + 170) prio $(expr $2 \* 10 + $i + 2) + if [ $i -lt $(($queue_count - 1)) ]; then + ip rule add fwmark 0x$(($2 * 10 + $i + 1)) table $(($2 + 170)) prio $(( $2 * 10 + $i + 2)) fi - iptables -t mangle -A MultiWanQoS -m mark --mark 0x$(expr $2 \* 10 + $i) -j qos_${1} - i=`expr $i + 1` + iptables -t mangle -A MultiWanQoS -m mark --mark 0x$(($2 * 10 + $i)) -j qos_${1} + i=$(($i + 1)) done } @@ -454,10 +414,8 @@ mwanrule() { if [ "$src" == "all" ]; then src=$NULL fi - iptables -t mangle -A MultiWanRules -m mark --mark 0x0\ - ${proto:+-p $proto} \ - ${src:+-s $src} \ - ${dst:+-d $dst} \ + iptables -t mangle -A MultiWanRules ${src:+-s $src} ${dst:+-d $dst} \ + -m mark --mark 0x0 ${proto:+-p $proto -m $proto} \ ${ports:+-m multiport --$port_type $ports} \ -j $wanrule } @@ -480,7 +438,6 @@ refresh_dns() { local failchk local compile_dns local dns_server - local i iptables -F MultiWanDNS -t mangle @@ -489,21 +446,16 @@ refresh_dns() { echo "## Refreshing DNS Resolution and Tables ##" - i=0 - while [ $i -lt $wancount ]; do - i=`expr $i + 1` + local i=0 + while [ $((i++)) -lt $wancount ]; do group=$(query_config group $i) gateway=$(query_config gateway $group) ipaddr=$(query_config ipaddr $group) ifname=$(query_config ifname $group) failchk=$(query_config failchk $group) - dns=`uci -q -P /var/state get multiwan.${group}.dns` - - if [ -z "$dns" -o "$dns" == "auto" ]; then - dns=`uci -q -P /var/state get network.${group}.dns` - fi - + dns=$(uci_get_state multiwan ${group} dns 'auto') + [ "$dns" == "auto" ] && dns=$(uci_get_state network ${group} dns) dns=$(echo $dns | sed -e "s/ /\n/g") if [ ! -z "$dns" -a "$failchk" != "x" -a "$ipaddr" != "x" -a "$gateway" != "x" -a "$ifname" != "x" ]; then @@ -535,7 +487,7 @@ iptables_init() { /etc/init.d/qos restart > /dev/null 2>&1 - IMQ_NFO=`iptables -n -L PREROUTING -t mangle -v | grep IMQ | awk -F " " '{print $6,$12}'` + IMQ_NFO=$(iptables -n -L PREROUTING -t mangle -v | grep IMQ | awk -F " " '{print $6,$12}') iptables -t mangle -F PREROUTING iptables -t mangle -F FORWARD @@ -549,8 +501,7 @@ iptables_init() { iptables -t mangle -N MultiWanQoS i=0 - while [ $i -lt $wancount ]; do - i=`expr $i + 1` + while [ $((i++)) -lt $wancount ]; do qos_init $(query_config group $i) $i done @@ -567,9 +518,8 @@ iptables_init() { echo "## Creating FW Rules ##" i=0 - while [ $i -lt $wancount ]; do - i=`expr $i + 1` - iprule=$(expr $i \* 10) + while [ $((i++)) -lt $wancount ]; do + iprule=$(($i * 10)) iptables -t mangle -N FW${i}MARK iptables -t mangle -A FW${i}MARK -j MARK --set-mark 0x${iprule} iptables -t mangle -A FW${i}MARK -j CONNMARK --save-mark @@ -615,8 +565,7 @@ iptables_init() { fi i=0 - while [ $i -lt $wancount ]; do - i=`expr $i + 1` + while [ $((i++)) -lt $wancount ]; do group=$(query_config group $i) ifname=$(query_config ifname $group) iptables -t mangle -A MultiWanPreHandler -i $ifname -m state --state NEW -j FW${i}MARK @@ -626,7 +575,6 @@ iptables_init() { if [ ! -z "$CHKFORQOS" ]; then iptables -t mangle -A MultiWan -j MultiWanQoS fi - } refresh_loadbalancer() { @@ -638,8 +586,6 @@ refresh_loadbalancer() { local nexthop local pre_nexthop_chk local rand_probability - local total_weight - local i echo "## Refreshing Load Balancer ##" @@ -654,36 +600,34 @@ refresh_loadbalancer() { iptables -F MultiWanLoadBalancer -t mangle - total_weight=0 + local total_weight=0 - i=0 - while [ $i -lt $wancount ]; do - i=`expr $i + 1` + local i=0 + while [ $((i++)) -lt $wancount ]; do group=$(query_config group $i) failchk=$(query_config failchk $group) gateway=$(query_config gateway $group) ifname=$(query_config ifname $group) - weight=`uci -q -P /var/state get multiwan.${group}.weight` + weight=$(uci_get_state multiwan ${group} weight) if [ "$gateway" != "x" -a "$ifname" != "x" -a "$failchk" != "x" -a "$weight" != "disable" ]; then - total_weight=$(expr $total_weight + $weight) + total_weight=$(($total_weight + $weight)) fi done i=0 - while [ $i -lt $wancount ]; do - i=`expr $i + 1` + while [ $((i++)) -lt $wancount ]; do group=$(query_config group $i) failchk=$(query_config failchk $group) gateway=$(query_config gateway $group) ifname=$(query_config ifname $group) - weight=`uci -q -P /var/state get multiwan.${group}.weight` + weight=$(uci_get_state multiwan ${group} weight) if [ "$gateway" != "x" -a "$ifname" != "x" -a "$failchk" != "x" -a "$weight" != "disable" ]; then nexthop="$nexthop nexthop via $gateway dev $ifname weight $weight" - rand_probability=$(expr $(expr $weight \* 100) / $total_weight) - total_weight=$(expr $total_weight - $weight) + rand_probability=$(($weight * 100 / $total_weight)) + total_weight=$(($total_weight - $weight)) if [ $rand_probability -lt 10 ]; then rand_probability="0.0${rand_probability}" @@ -700,7 +644,7 @@ refresh_loadbalancer() { done - pre_nexthop_chk=`echo $nexthop | awk -F "nexthop" '{print NF-1}'` + pre_nexthop_chk=$(echo $nexthop | awk -F "nexthop" '{print NF-1}') if [ "$pre_nexthop_chk" == "1" ]; then ip route add default via $(echo $nexthop | awk -F " " '{print $3}') dev $(echo $nexthop | awk -F " " '{print $5}') proto static table 170 elif [ "$pre_nexthop_chk" -gt "1" ]; then @@ -717,27 +661,24 @@ refresh_routes() { local group local ifname local ipaddr - local i echo "## Refreshing Routing Tables ##" - i=0 - while [ $i -lt $wancount ]; do - i=`expr $i + 1` + local i=0 + while [ $((i++)) -lt $wancount ]; do group=$(query_config group $i) gateway=$(query_config gateway $group) ifname=$(query_config ifname $group) ipaddr=$(query_config ipaddr $group) - ip route flush table $(expr $i + 170) > /dev/null 2>&1 + ip route flush table $(($i + 170)) > /dev/null 2>&1 - for TABLE in $(expr $i + 170); do - ip route | grep -Ev ^default | while read ROUTE; do - ip route add table $TABLE to $ROUTE - done + TABLE=$(($i + 170)) + ip route | grep -Ev ^default | while read ROUTE; do + ip route add table $TABLE to $ROUTE done if [ "$gateway" != "x" -a "$ipaddr" != "x" -a "$ifname" != "x" ]; then - ip route add default via $gateway table $(expr $i + 170) src $ipaddr proto static + ip route add default via $gateway table $(($i + 170)) src $ipaddr proto static route add default gw $gateway > /dev/null 2>&1 fi done @@ -746,7 +687,6 @@ refresh_routes() { } iprules_config() { - local iprule local group local gateway @@ -756,23 +696,22 @@ iprules_config() { gateway=$(query_config gateway $group) ipaddr=$(query_config ipaddr $group) - CHKIPROUTE=`cat /etc/iproute2/rt_tables | grep MWAN${1}` + CHKIPROUTE=$(grep MWAN${1} /etc/iproute2/rt_tables) if [ -z "$CHKIPROUTE" ]; then - echo "$(expr $1 + 170) MWAN${1}" >> /etc/iproute2/rt_tables + echo "$(($1 + 170)) MWAN${1}" >> /etc/iproute2/rt_tables fi - ip rule del prio $(expr $1 \* 10) > /dev/null 2>&1 - ip rule del prio $(expr $1 \* 10 + 1) > /dev/null 2>&1 + ip rule del prio $(($1 * 10)) > /dev/null 2>&1 + ip rule del prio $(($1 * 10 + 1)) > /dev/null 2>&1 if [ "$gateway" != "x" -a "$ipaddr" != "x" ]; then - ip rule add from $ipaddr table $(expr $1 + 170) prio $(expr $1 \* 10) - ip rule add fwmark 0x$(expr $1 \* 10) table $(expr $1 + 170) prio $(expr $(expr $1 \* 10) + 1) + ip rule add from $ipaddr table $(($1 + 170)) prio $(($1 * 10)) + ip rule add fwmark 0x$(($1 * 10)) table $(($1 + 170)) prio $(($1 * 10 + 1)) fi } flush() { - local i - + local restore_single=$1 echo "## Flushing IP Rules & Routes ##" ip rule flush > /dev/null 2>&1 @@ -781,24 +720,22 @@ flush() { ip route flush table 170 > /dev/null - i=0 - while [ $i -lt $wancount ]; do - i=`expr $i + 1` + local i=0 + while [ $((i++)) -lt $wancount ]; do ip route del default > /dev/null 2>&1 - ip route flush table $(expr $i + 170) > /dev/null 2>&1 + ip route flush table $(($i + 170)) > /dev/null 2>&1 done echo "## Clearing Rules ##" - clear_rules > /dev/null 2>&1 + clear_rules $restore_single > /dev/null 2>&1 rm $jobfile > /dev/null 2>&1 } main_init() { - local RP_PATH + local RP_PATH IFACE local group local health_interval - local i echo "## Main Initialization ##" @@ -807,18 +744,16 @@ main_init() { mwan_kill flush - echo "## IP Rules Initialization ##" - CHKIPROUTE=`cat /etc/iproute2/rt_tables | grep LoadBalancer` + CHKIPROUTE=$(grep LoadBalancer /etc/iproute2/rt_tables) if [ -z "$CHKIPROUTE" ]; then echo "#" >> /etc/iproute2/rt_tables echo "170 LoadBalancer" >> /etc/iproute2/rt_tables fi - i=0 - while [ $i -lt $wancount ]; do - i=`expr $i + 1` + local i=0 + while [ $((i++)) -lt $wancount ]; do iprules_config $i done @@ -828,89 +763,49 @@ main_init() { refresh_loadbalancer RP_PATH=/proc/sys/net/ipv4/conf - for IFACE in `ls $RP_PATH`; do + for IFACE in $(ls $RP_PATH); do echo 0 > $RP_PATH/$IFACE/rp_filter done - echo "## Initialization Complete, switching to background mode. ##" - mwnote "Succesfully Initialized on $(exec date -R)." + mwnote "Succesfully Initialized on $(date -R)." fail_start_check - stagger_health_monitors() { - i=0 - while [ $i -lt $wancount ]; do - i=`expr $i + 1` - group=$(query_config group $i) - health_interval=`uci -q -P /var/state get multiwan.${group}.health_interval` - if [ ! -z "$health_interval" -a "$health_interval" != "disable" -a "$health_interval" -gt 0 ]; then - health_monitor $group & - sleep 3 - fi - done - } - - stagger_health_monitors & - bg_task & - - exit + while :; do + schedule_tasks + do_tasks + done } -health_monitor() { - local ipaddr_cur - local gateway_cur - local ifname_cur - local ifname - local ipaddr - local gateway - local failchk - local icmp_hosts - local icmp_hosts_acquire - local default_routes_check - local icmp_test_host - local timeout +monitor_wan() { + local ifname ipaddr gateway icmp_hosts_acquire icmp_test_host local check_test - local health_interval - local check_for_job . /tmp/.mwan/cache - timeout=`uci -q -P /var/state get multiwan.${1}.timeout` - icmp_hosts=`uci -q -P /var/state get multiwan.${1}.icmp_hosts` - health_interval=`uci -q -P /var/state get multiwan.${1}.health_interval` - ifname_cur=$(query_config ifname $1) - ipaddr_cur=$(query_config ipaddr $1) - gateway_cur=$(query_config gateway $1) - - while [ 1 ]; do - - ifname=`uci -q -P /var/state get network.${1}.ifname` - ipaddr=`uci -q -P /var/state get network.${1}.ipaddr` - gateway=`uci -q -P /var/state get network.${1}.gateway` - - if [ -z "$ifname" ]; then - ifname="x" - fi + local timeout=$(uci_get_state multiwan ${1} timeout) + local icmp_hosts=$(uci_get_state multiwan ${1} icmp_hosts) + local icmp_count=$(uci_get_state multiwan ${1} icmp_count '1') + local health_interval=$(uci_get_state multiwan ${1} health_interval) + local ifname_cur=$(query_config ifname $1) + local ipaddr_cur=$(query_config ipaddr $1) + local gateway_cur=$(query_config gateway $1) - if [ -z "$ipaddr" ]; then - ipaddr="x" - fi + while :; do + [ "${health_monitor%.*}" = 'parallel' ] && sleep $health_interval - if [ -z "$gateway" ]; then - gateway="x" - fi + ifname=$(uci_get_state network ${1} ifname 'x') + ipaddr=$(uci_get_state network ${1} ipaddr 'x') + gateway=$(uci_get_state network ${1} gateway 'x') if [ "$ifname_cur" != "$ifname" -o "$ipaddr_cur" != "$ipaddr" -o "$gateway_cur" != "$gateway" ]; then - echo $1.acquire >> $jobfile - exit - else - if [ "$gateway" != "x" ]; then - default_routes_check=`ip route | grep -o $gateway` - if [ -z "$default_routes_check" ]; then - check_for_job=`cat $jobfile 2>&1 | grep -o "route.refresh"` - if [ -z "$check_for_job" ]; then - echo route.refresh >> $jobfile - fi - fi + add_task "$1" acquire + if [ "${health_monitor%.*}" = 'parallel' ]; then + exit + else + return fi + else + [ "$gateway" != "x" ] && ! ip route | grep -o $gateway >&- 2>&- && + add_task route refresh fi if [ "$icmp_hosts" != "disable" -a "$ifname" != "x" -a "$ipaddr" != "x" -a "$gateway" != "x" ]; then @@ -918,10 +813,9 @@ health_monitor() { if [ "$icmp_hosts" == "gateway" -o -z "$icmp_hosts" ]; then icmp_hosts_acquire=$gateway elif [ "$icmp_hosts" == "dns" ]; then - icmp_hosts_acquire=`uci -q -P /var/state get multiwan.$1.dns` - if [ -z "$icmp_hosts_acquire" -o "$icmp_hosts_acquire" == "auto" ]; then - icmp_hosts_acquire=`uci -q -P /var/state get network.$1.dns` - fi + icmp_hosts_acquire=$(uci_get_state multiwan $1 dns 'auto') + [ "$icmp_hosts_acquire" == "auto" ] && + icmp_hosts_acquire=$(uci_get_state network $1 dns) else icmp_hosts_acquire=$icmp_hosts fi @@ -930,45 +824,119 @@ health_monitor() { ping_test() { echo "$icmp_hosts" | while read icmp_test_host; do - ping -c 1 -W $timeout -I $ifname $icmp_test_host 2>&1 | grep -o "round-trip" + ping -c "$icmp_count" -W $timeout -I $ifname $icmp_test_host 2>&1 | grep -o "round-trip" done } check_test=$(ping_test) if [ -z "$check_test" ]; then - echo "$1.fail" >> $jobfile + add_task "$1" fail else - echo "$1.pass" >> $jobfile + add_task "$1" pass fi elif [ "$icmp_hosts" == "disable" ]; then - echo "$1.pass" >> $jobfile + add_task "$1" pass fi - sleep $health_interval + [ "$health_monitor" = 'serial' ] && { + wan_monitor_map=$(echo $wan_monitor_map | sed -e "s/$1\[\w*\]/$1\[$(date +%s)\]/g") + update_cache + break + } done } -bg_task() { +# Add a task to the $jobfile while ensuring +# no duplicate tasks for the specified group +add_task() { + local group=$1 + local task=$2 + grep -o "$group.$task" $jobfile >&- 2>&- || echo "$group.$task" >> $jobfile +} + +# For health_monitor "parallel", start a background monitor for each group. +# For health_monitor "serial", queue monitor tasks for do_tasks. +schedule_tasks() { + local group health_interval monitored_last_at current_time diff delay + local i=0 + + get_health_interval() { + group=$(query_config group $1) + health_interval=$(uci_get_state multiwan ${group} health_interval 'disable') + [ "$health_interval" = "disable" ] && health_interval=0 + } + + [ "$health_monitor" = 'parallel' ] && { + while [ $((i++)) -lt $wancount ]; do + get_health_interval $i + if [ "$health_interval" -gt 0 ]; then + monitor_wan $group & + sleep 1 + fi + done + echo "## Started background monitor_wan ##" + health_monitor="parallel.started" + } + + [ "$health_monitor" = 'serial' ] && { + local monitor_disabled=1 + + until [ -f $jobfile ]; do + current_time=$(date +%s) + delay=$max_interval + i=0 + + while [ $((i++)) -lt $wancount ]; do + get_health_interval $i + if [ "$health_interval" -gt 0 ]; then + monitor_disabled=0 + + monitored_last=$(query_config monitor $group) + [ -z "$monitored_last" ] && { + monitored_last=$current_time + wan_monitor_map="${wan_monitor_map}${group}[$monitored_last]" + update_cache + } + + will_monitor_at=$(($monitored_last + $health_interval)) + diff=$(($will_monitor_at - $current_time)) + [ $diff -le 0 ] && add_task "$group" 'monitor' + + delay=$(($delay > $diff ? $diff : $delay)) + fi + done + + [ "$monitor_disabled" -eq 1 ] && { + # Although health monitors are disabled, still + # need to check up on iptables rules in do_tasks + sleep "$iptables_interval" + break + } + [ $delay -gt 0 ] && sleep $delay + done + } +} + +rule_counter=0 +# Process each task in the $jobfile in FIFO order +do_tasks() { local check_iptables local queued_task - local bg_counter local current_resolv_file - bg_counter=0 - - while [ 1 ]; do + while :; do . /tmp/.mwan/cache - if [ "$bg_counter" -eq 5 ]; then + if [ "$((++rule_counter))" -eq 5 -o "$health_monitor" = 'serial' ]; then check_iptables=$(iptables -n -L MultiWan -t mangle | grep "references" | awk -F "(" '{print $2}' | cut -d " " -f 1) if [ -z "$check_iptables" -o "$check_iptables" -lt 4 ]; then mwnote "Netfilter rules appear to of been altered." - /etc/init.d/multiwan restart & + /etc/init.d/multiwan restart exit fi @@ -978,8 +946,7 @@ bg_task() { refresh_dns fi - bg_counter=0 - + rule_counter=0 fi if [ -f $jobfile ]; then @@ -992,21 +959,31 @@ bg_task() { case $2 in fail) fail_wan $1;; pass) recover_wan $1;; - acquire) acquire_wan_data $1 && health_monitor $1 &;; + acquire) + acquire_wan_data $1 + [ "${health_monitor%.* }" = 'parallel' ] && { + monitor_wan $1 & + echo "## Started background monitor_wan ##" + } + ;; + monitor) monitor_wan $1;; refresh) refresh_routes;; + *) echo "## Unknown task command: $2 ##";; esac } - queued_task=`echo $LINE | awk -F "." '{print $1,$2}'` + queued_task=$(echo $LINE | awk -F "." '{print $1,$2}') execute_task $queued_task done < $jobfile.work rm $jobfile.work fi - bg_counter=$(expr $bg_counter + 1) - - sleep 1 + if [ "$health_monitor" = 'serial' ]; then + break + else + sleep 1 + fi done } @@ -1016,9 +993,8 @@ fail_start_check(){ local ifname local group - i=0 - while [ $i -lt $wancount ]; do - i=`expr $i + 1` + local i=0 + while [ $((i++)) -lt $wancount ]; do group=$(query_config group $i) ifname=$(query_config ifname $group) ipaddr=$(query_config ipaddr $group) @@ -1031,24 +1007,29 @@ fail_start_check(){ } wancount=0 +max_interval=$(((1<<31) - 1)) config_clear config_load "multiwan" -config_get default_route config default_route -config_get debug config debug +config_get default_route config default_route +config_get health_monitor config health_monitor +config_get iptables_interval config iptables_interval '30' +config_get debug config debug + +[ "$health_monitor" = 'serial' ] || health_monitor='parallel' config_foreach acquire_wan_data interface update_cache -CHKFORQOS=`iptables -n -L Default -t mangle 2>&1 | grep "Chain Default"` -CHKFORMODULE=`iptables -m statistic 2>&1 | grep -o "File not found"` +CHKFORQOS=$(iptables -n -L Default -t mangle 2>&1 | grep "Chain Default") +CHKFORMODULE=$(iptables -m statistic 2>&1 | grep -o "File not found") jobfile="/tmp/.mwan/jobqueue" case $1 in agent) silencer main_init;; - restart) silencer stop restart;; stop) silencer stop;; + restart) silencer stop restart;; + single) silencer stop single;; esac - -- 2.30.2